Merge pull request #4853 from secondlife/release/2025.08

Release/2025.08
master
Jonathan "Geenz" Goodman 2025-12-03 11:50:32 -05:00 committed by GitHub
commit bf347d1580
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
298 changed files with 6622 additions and 2638 deletions

View File

@ -1,6 +1,7 @@
# Replace tabs with spaces
1b68f71348ecf3983b76b40d7940da8377f049b7
33418a77b716e122da9778869cdbabe97c83ff37
6b974724826a038b0db794460b322eb4921da735
# Trim trailing whitespace
a0b3021bdcf76859054fda8e30abb3ed47749e83
8444cd9562a6a7b755fcb075864e205122354192

7
.github/dependabot.yaml vendored Normal file
View File

@ -0,0 +1,7 @@
version: 2
updates:
- package-ecosystem: github-actions
directory: /
schedule:
interval: monthly

View File

@ -1,6 +1,7 @@
name: Build
on:
workflow_dispatch:
pull_request:
push:
branches: ["main", "release/*", "project/*"]
@ -85,7 +86,7 @@ jobs:
variants: ${{ matrix.configuration }}
steps:
- name: Checkout code
uses: actions/checkout@v4
uses: actions/checkout@v5
with:
ref: ${{ github.event.pull_request.head.sha || github.sha }}
@ -94,14 +95,14 @@ jobs:
with:
python-version: "3.11"
- name: Checkout build variables
uses: actions/checkout@v4
uses: actions/checkout@v5
with:
repository: secondlife/build-variables
ref: master
path: .build-variables
- name: Checkout master-message-template
uses: actions/checkout@v4
uses: actions/checkout@v5
with:
repository: secondlife/master-message-template
path: .master-message-template
@ -309,7 +310,7 @@ jobs:
steps:
- name: Sign and package Windows viewer
if: env.AZURE_KEY_VAULT_URI && env.AZURE_CERT_NAME && env.AZURE_CLIENT_ID && env.AZURE_CLIENT_SECRET && env.AZURE_TENANT_ID
uses: secondlife/viewer-build-util/sign-pkg-windows@v2
uses: secondlife/viewer-build-util/sign-pkg-windows@v2.0.4
with:
vault_uri: "${{ env.AZURE_KEY_VAULT_URI }}"
cert_name: "${{ env.AZURE_CERT_NAME }}"

View File

@ -11,8 +11,8 @@ jobs:
pre-commit:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v5
- uses: actions/setup-python@v4
with:
python-version: 3.x
- uses: pre-commit/action@v3.0.0
- uses: pre-commit/action@v3.0.1

View File

@ -7,14 +7,14 @@ repos:
- id: no-trigraphs
- id: copyright
- id: end-of-file
files: \.(cpp|c|h|py|glsl|cmake|txt)$
files: \.(cpp|c|m|mm|h|py|glsl|cmake|txt)$
exclude: language.txt
- id: indent-with-spaces
files: \.(cpp|c|h|inl|py|glsl|cmake)$
files: \.(cpp|c|m|mm|h|inl|py|glsl|cmake)$
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v5.0.0
rev: v6.0.0
hooks:
- id: check-xml
- id: mixed-line-ending
- id: trailing-whitespace
files: \.(cpp|c|h|inl|py|glsl|cmake|yaml|sh)$
files: \.(cpp|c|m|mm|h|inl|py|glsl|cmake|yaml|sh)$

View File

@ -1460,58 +1460,6 @@
<key>name</key>
<string>llphysicsextensions_source</string>
</map>
<key>llphysicsextensions_stub</key>
<map>
<key>platforms</key>
<map>
<key>darwin64</key>
<map>
<key>archive</key>
<map>
<key>hash</key>
<string>f290b000b31f9e36f2489946cbc99f5e</string>
<key>url</key>
<string>http://automated-builds-secondlife-com.s3.amazonaws.com/ct2/59995/563653/llphysicsextensions_stub-1.0.542456-darwin64-542456.tar.bz2</string>
</map>
<key>name</key>
<string>darwin64</string>
</map>
<key>linux64</key>
<map>
<key>archive</key>
<map>
<key>hash</key>
<string>711f4ec769e4b5f59ba25ee43c11bcbc</string>
<key>url</key>
<string>http://automated-builds-secondlife-com.s3.amazonaws.com/ct2/4724/14846/llphysicsextensions_stub-1.0.504712-linux64-504712.tar.bz2</string>
</map>
<key>name</key>
<string>linux64</string>
</map>
<key>windows64</key>
<map>
<key>archive</key>
<map>
<key>hash</key>
<string>2e5f1f7046a49d8b0bc295aa878116bc</string>
<key>url</key>
<string>http://automated-builds-secondlife-com.s3.amazonaws.com/ct2/60043/564063/llphysicsextensions_stub-1.0.542456-windows-542456.tar.bz2</string>
</map>
<key>name</key>
<string>windows64</string>
</map>
</map>
<key>license</key>
<string>internal</string>
<key>license_file</key>
<string>LICENSES/llphysicsextensions.txt</string>
<key>copyright</key>
<string>Copyright (c) 2010, Linden Research, Inc.</string>
<key>version</key>
<string>1.0.542456</string>
<key>name</key>
<string>llphysicsextensions_stub</string>
</map>
<key>llphysicsextensions_tpv</key>
<map>
<key>platforms</key>
@ -2934,6 +2882,38 @@ Copyright (c) 2012, 2014, 2015, 2016 nghttp2 contributors</string>
<key>description</key>
<string>Discord Social SDK</string>
</map>
<key>vhacd</key>
<map>
<key>platforms</key>
<map>
<key>common</key>
<map>
<key>archive</key>
<map>
<key>hash</key>
<string>140d8fc952a10edb5f2d72ab405336019ef32cadfa64f0cfce76c9de4bc6268cbc87cc8cd89d3417fb78b531d441701afc8d016bafe4bd275df2707f7daf1387</string>
<key>hash_algorithm</key>
<string>blake2b</string>
<key>url</key>
<string>https://github.com/AlchemyViewer/3p-vhacd/releases/download/v4.1.0-r2/vhacd-4.1.0-r2-common-18166921729.tar.zst</string>
</map>
<key>name</key>
<string>common</string>
</map>
</map>
<key>license</key>
<string>BSD</string>
<key>license_file</key>
<string>LICENSES/vhacd.txt</string>
<key>copyright</key>
<string>Copyright (c) 2011, Khaled Mamou</string>
<key>version</key>
<string>4.1.0-r2</string>
<key>name</key>
<string>vhacd</string>
<key>description</key>
<string>Voxelized Hierarchical Approximate Convex Decomposition</string>
</map>
</map>
<key>package_description</key>
<map>

View File

@ -70,7 +70,7 @@ installer_Darwin()
{
local package_name="$1"
local package_dir="$(build_dir_Darwin)/newview/"
local pattern=".*$(viewer_channel_suffix ${package_name})_[0-9]+_[0-9]+_[0-9]+_[0-9]+_x86_64\\.dmg\$"
local pattern=".*$(viewer_channel_suffix ${package_name})_[0-9]+_[0-9]+_[0-9]+_[0-9]+_universal\\.dmg\$"
# since the additional packages are built after the base package,
# sorting oldest first ensures that the unqualified package is returned
# even if someone makes a qualified name that duplicates the last word of the base name

View File

@ -70,6 +70,7 @@ add_subdirectory(${LIBS_OPEN_PREFIX}llinventory)
add_subdirectory(${LIBS_OPEN_PREFIX}llmath)
add_subdirectory(${LIBS_OPEN_PREFIX}llmeshoptimizer)
add_subdirectory(${LIBS_OPEN_PREFIX}llmessage)
add_subdirectory(${LIBS_OPEN_PREFIX}llphysicsextensionsos)
add_subdirectory(${LIBS_OPEN_PREFIX}llprimitive)
add_subdirectory(${LIBS_OPEN_PREFIX}llrender)
add_subdirectory(${LIBS_OPEN_PREFIX}llfilesystem)

View File

@ -31,3 +31,8 @@ if(DARWIN)
endif()
target_include_directories(ll::apr SYSTEM INTERFACE ${LIBS_PREBUILT_DIR}/include/apr-1)
# Fix erroneous check for __attribute__ definition introduced with APR 1.7.5, causing lots of "this declaration may not have extern 'C' linkage" errors in VS
file(READ ${LIBS_PREBUILT_DIR}/include/apr-1/apr.h APR_HEADER_CONTENTS)
string(REPLACE "#if !(defined(__attribute__) || defined(__has_attribute))" "#if !defined(__attribute__)" APR_HEADER_CONTENTS "${APR_HEADER_CONTENTS}")
file(WRITE ${LIBS_PREBUILT_DIR}/include/apr-1/apr.h "${APR_HEADER_CONTENTS}")

View File

@ -62,6 +62,7 @@ set(cmake_SOURCE_FILES
UI.cmake
UnixInstall.cmake
Variables.cmake
VHACD.cmake
ViewerMiscLibs.cmake
VisualLeakDetector.cmake
LibVLCPlugin.cmake

View File

@ -29,14 +29,18 @@ if (HAVOK)
#target_link_libraries( llphysicsextensions_impl INTERFACE llphysicsextensionsstub)
else()
target_link_libraries( llphysicsextensions_impl INTERFACE llphysicsextensions)
target_compile_definitions( llphysicsextensions_impl INTERFACE LL_HAVOK=1 )
endif()
target_include_directories( llphysicsextensions_impl INTERFACE ${LIBS_PREBUILT_DIR}/include/llphysicsextensions)
elseif (HAVOK_TPV)
use_prebuilt_binary(llphysicsextensions_tpv)
target_link_libraries( llphysicsextensions_impl INTERFACE llphysicsextensions_tpv)
else (HAVOK)
use_prebuilt_binary(llphysicsextensions_stub)
set(LLPHYSICSEXTENSIONS_SRC_DIR ${LIBS_PREBUILT_DIR}/llphysicsextensions/stub)
target_link_libraries( llphysicsextensions_impl INTERFACE llphysicsextensionsstub)
endif (HAVOK)
target_include_directories( llphysicsextensions_impl INTERFACE ${LIBS_PREBUILT_DIR}/include/llphysicsextensions)
if (NOT DARWIN)
if(WINDOWS)
target_link_libraries( llphysicsextensions_impl INTERFACE ${ARCH_PREBUILT_DIRS}/llphysicsextensions_tpv.lib)
elseif(LINUX)
target_link_libraries( llphysicsextensions_impl INTERFACE ${ARCH_PREBUILT_DIRS}/libllphysicsextensions_tpv.a)
endif()
target_compile_definitions( llphysicsextensions_impl INTERFACE LL_HAVOK=1 )
endif()
target_include_directories( llphysicsextensions_impl INTERFACE ${LIBS_PREBUILT_DIR}/include/llphysicsextensions)
endif ()

9
indra/cmake/VHACD.cmake Normal file
View File

@ -0,0 +1,9 @@
# -*- cmake -*-
include(Prebuilt)
add_library(ll::vhacd INTERFACE IMPORTED)
use_system_binary(vhacd)
use_prebuilt_binary(vhacd)
target_include_directories(ll::vhacd SYSTEM INTERFACE ${LIBS_PREBUILT_DIR}/include/vhacd/)

View File

@ -283,6 +283,7 @@ bool LLPolyMeshSharedData::loadMesh( const std::string& fileName )
LLFILE* fp = LLFile::fopen(fileName, "rb"); /*Flawfinder: ignore*/
if (!fp)
{
LLError::LLUserWarningMsg::showMissingFiles();
LL_ERRS() << "can't open: " << fileName << LL_ENDL;
return false;
}

View File

@ -1890,7 +1890,10 @@ LLGLTexture* LLTexLayerStaticImageList::getTexture(const std::string& file_name,
image_raw->copyUnscaledAlphaMask(alpha_image_raw, LLColor4U::black);
}
tex->createGLTexture(0, image_raw, 0, true, LLGLTexture::LOCAL);
if (!tex->createGLTexture(0, image_raw, 0, true, LLGLTexture::LOCAL))
{
LL_WARNS() << "Failed to create GL texture for image: " << file_name << LL_ENDL;
}
gGL.getTexUnit(0)->bind(tex);
tex->setAddressMode(LLTexUnit::TAM_CLAMP);

View File

@ -338,7 +338,10 @@ bool LLTexLayerParamAlpha::render(S32 x, S32 y, S32 width, S32 height)
// Create the GL texture, and then hang onto it for future use.
if (mNeedsCreateTexture)
{
mCachedProcessedTexture->createGLTexture(0, mStaticImageRaw);
if (!mCachedProcessedTexture->createGLTexture(0, mStaticImageRaw))
{
LL_WARNS() << "Failed to create GL texture for image: " << mCachedProcessedTexture->getID() << LL_ENDL;
}
mNeedsCreateTexture = false;
gGL.getTexUnit(0)->bind(mCachedProcessedTexture);
mCachedProcessedTexture->setAddressMode(LLTexUnit::TAM_CLAMP);

View File

@ -35,7 +35,6 @@
#if LL_WINDOWS
#include "llwin32headers.h"
#include <stdlib.h> // Windows errno
#include <vector>
#else
#include <errno.h>
@ -49,6 +48,86 @@ static std::string empty;
// variants of strerror() to report errors.
#if LL_WINDOWS
// For the situations where we directly call into Windows API functions we need to translate
// the Windows error codes into errno values
namespace
{
struct errentry
{
unsigned long oserr; // Windows OS error value
int errcode; // System V error code
};
}
// translation table between Windows OS error value and System V errno code
static errentry const errtable[]
{
{ ERROR_INVALID_FUNCTION, EINVAL }, // 1
{ ERROR_FILE_NOT_FOUND, ENOENT }, // 2
{ ERROR_PATH_NOT_FOUND, ENOENT }, // 3
{ ERROR_TOO_MANY_OPEN_FILES, EMFILE }, // 4
{ ERROR_ACCESS_DENIED, EACCES }, // 5
{ ERROR_INVALID_HANDLE, EBADF }, // 6
{ ERROR_ARENA_TRASHED, ENOMEM }, // 7
{ ERROR_NOT_ENOUGH_MEMORY, ENOMEM }, // 8
{ ERROR_INVALID_BLOCK, ENOMEM }, // 9
{ ERROR_BAD_ENVIRONMENT, E2BIG }, // 10
{ ERROR_BAD_FORMAT, ENOEXEC }, // 11
{ ERROR_INVALID_ACCESS, EINVAL }, // 12
{ ERROR_INVALID_DATA, EINVAL }, // 13
{ ERROR_INVALID_DRIVE, ENOENT }, // 15
{ ERROR_CURRENT_DIRECTORY, EACCES }, // 16
{ ERROR_NOT_SAME_DEVICE, EXDEV }, // 17
{ ERROR_NO_MORE_FILES, ENOENT }, // 18
{ ERROR_LOCK_VIOLATION, EACCES }, // 33
{ ERROR_BAD_NETPATH, ENOENT }, // 53
{ ERROR_NETWORK_ACCESS_DENIED, EACCES }, // 65
{ ERROR_BAD_NET_NAME, ENOENT }, // 67
{ ERROR_FILE_EXISTS, EEXIST }, // 80
{ ERROR_CANNOT_MAKE, EACCES }, // 82
{ ERROR_FAIL_I24, EACCES }, // 83
{ ERROR_INVALID_PARAMETER, EINVAL }, // 87
{ ERROR_NO_PROC_SLOTS, EAGAIN }, // 89
{ ERROR_DRIVE_LOCKED, EACCES }, // 108
{ ERROR_BROKEN_PIPE, EPIPE }, // 109
{ ERROR_DISK_FULL, ENOSPC }, // 112
{ ERROR_INVALID_TARGET_HANDLE, EBADF }, // 114
{ ERROR_WAIT_NO_CHILDREN, ECHILD }, // 128
{ ERROR_CHILD_NOT_COMPLETE, ECHILD }, // 129
{ ERROR_DIRECT_ACCESS_HANDLE, EBADF }, // 130
{ ERROR_NEGATIVE_SEEK, EINVAL }, // 131
{ ERROR_SEEK_ON_DEVICE, EACCES }, // 132
{ ERROR_DIR_NOT_EMPTY, ENOTEMPTY }, // 145
{ ERROR_NOT_LOCKED, EACCES }, // 158
{ ERROR_BAD_PATHNAME, ENOENT }, // 161
{ ERROR_MAX_THRDS_REACHED, EAGAIN }, // 164
{ ERROR_LOCK_FAILED, EACCES }, // 167
{ ERROR_ALREADY_EXISTS, EEXIST }, // 183
{ ERROR_FILENAME_EXCED_RANGE, ENOENT }, // 206
{ ERROR_NESTING_NOT_ALLOWED, EAGAIN }, // 215
{ ERROR_NO_UNICODE_TRANSLATION, EILSEQ }, // 1113
{ ERROR_NOT_ENOUGH_QUOTA, ENOMEM } // 1816
};
static int set_errno_from_oserror(unsigned long oserr)
{
if (!oserr)
return 0;
// Check the table for the Windows OS error code
for (const struct errentry &entry : errtable)
{
if (oserr == entry.oserr)
{
_set_errno(entry.errcode);
return -1;
}
}
_set_errno(EINVAL);
return -1;
}
// On Windows, use strerror_s().
std::string strerr(int errn)
{
@ -57,7 +136,67 @@ std::string strerr(int errn)
return buffer;
}
typedef std::basic_ios<char,std::char_traits < char > > _Myios;
inline bool is_slash(wchar_t const c)
{
return c == L'\\' || c == L'/';
}
static std::wstring utf8path_to_wstring(const std::string& utf8path)
{
if (utf8path.size() >= MAX_PATH)
{
// By prepending "\\?\" to a path, Windows widechar file APIs will not fail on long path names
std::wstring utf16path = L"\\\\?\\" + ll_convert<std::wstring>(utf8path);
// We need to make sure that the path does not contain forward slashes as above
// prefix does bypass the path normalization that replaces slashes with backslashes
// before passing the path to kernel mode APIs
std::replace(utf16path.begin(), utf16path.end(), L'/', L'\\');
return utf16path;
}
return ll_convert<std::wstring>(utf8path);
}
static unsigned short get_fileattr(const std::wstring& utf16path, bool dontFollowSymLink = false)
{
unsigned long flags = FILE_FLAG_BACKUP_SEMANTICS;
if (dontFollowSymLink)
{
flags |= FILE_FLAG_OPEN_REPARSE_POINT;
}
HANDLE file_handle = CreateFileW(utf16path.c_str(), FILE_READ_ATTRIBUTES,
FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE,
nullptr, OPEN_EXISTING, flags, nullptr);
if (file_handle != INVALID_HANDLE_VALUE)
{
FILE_ATTRIBUTE_TAG_INFO attribute_info;
if (GetFileInformationByHandleEx(file_handle, FileAttributeTagInfo, &attribute_info, sizeof(attribute_info)))
{
// A volume path alone (only drive letter) is not recognized as directory while it technically is
bool is_directory = (attribute_info.FileAttributes & FILE_ATTRIBUTE_DIRECTORY) ||
(iswalpha(utf16path[0]) && utf16path[1] == ':' &&
(!utf16path[2] || (is_slash(utf16path[2]) && !utf16path[3])));
unsigned short st_mode = is_directory ? S_IFDIR :
(attribute_info.FileAttributes & FILE_ATTRIBUTE_REPARSE_POINT ? S_IFLNK : S_IFREG);
st_mode |= (attribute_info.FileAttributes & FILE_ATTRIBUTE_READONLY) ? S_IREAD : S_IREAD | S_IWRITE;
// we do not try to guess executable flag
// propagate user bits to group/other fields:
st_mode |= (st_mode & 0700) >> 3;
st_mode |= (st_mode & 0700) >> 6;
CloseHandle(file_handle);
return st_mode;
}
}
// Retrieve last error and set errno before calling CloseHandle()
set_errno_from_oserror(GetLastError());
if (file_handle != INVALID_HANDLE_VALUE)
{
CloseHandle(file_handle);
}
return 0;
}
#else
// On Posix we want to call strerror_r(), but alarmingly, there are two
@ -108,18 +247,13 @@ std::string strerr(int errn)
}
#endif // ! LL_WINDOWS
// On either system, shorthand call just infers global 'errno'.
std::string strerr()
{
return strerr(errno);
}
int warnif(const std::string& desc, const std::string& filename, int rc, int accept=0)
static int warnif(const std::string& desc, const std::string& filename, int rc, int accept = 0)
{
if (rc < 0)
{
// Capture errno before we start emitting output
int errn = errno;
// For certain operations, a particular errno value might be
// acceptable -- e.g. stat() could permit ENOENT, mkdir() could permit
// EEXIST. Don't warn if caller explicitly says this errno is okay.
@ -176,62 +310,59 @@ int warnif(const std::string& desc, const std::string& filename, int rc, int acc
// static
int LLFile::mkdir(const std::string& dirname, int perms)
{
#if LL_WINDOWS
// permissions are ignored on Windows
std::wstring utf16dirname = ll_convert<std::wstring>(dirname);
int rc = _wmkdir(utf16dirname.c_str());
#else
int rc = ::mkdir(dirname.c_str(), (mode_t)perms);
#endif
// We often use mkdir() to ensure the existence of a directory that might
// already exist. There is no known case in which we want to call out as
// an error the requested directory already existing.
#if LL_WINDOWS
// permissions are ignored on Windows
int rc = 0;
std::wstring utf16dirname = utf8path_to_wstring(dirname);
if (!CreateDirectoryW(utf16dirname.c_str(), nullptr))
{
// Only treat other errors than an already existing file as a real error
unsigned long oserr = GetLastError();
if (oserr != ERROR_ALREADY_EXISTS)
{
rc = set_errno_from_oserror(oserr);
}
}
#else
int rc = ::mkdir(dirname.c_str(), (mode_t)perms);
if (rc < 0 && errno == EEXIST)
{
// this is not the error you want, move along
return 0;
}
#endif
// anything else might be a problem
return warnif("mkdir", dirname, rc, EEXIST);
return warnif("mkdir", dirname, rc);
}
// static
int LLFile::rmdir(const std::string& dirname)
int LLFile::rmdir(const std::string& dirname, int suppress_error)
{
#if LL_WINDOWS
// permissions are ignored on Windows
std::wstring utf16dirname = ll_convert<std::wstring>(dirname);
std::wstring utf16dirname = utf8path_to_wstring(dirname);
int rc = _wrmdir(utf16dirname.c_str());
#else
int rc = ::rmdir(dirname.c_str());
#endif
return warnif("rmdir", dirname, rc);
return warnif("rmdir", dirname, rc, suppress_error);
}
// static
LLFILE* LLFile::fopen(const std::string& filename, const char* mode) /* Flawfinder: ignore */
LLFILE* LLFile::fopen(const std::string& filename, const char* mode)
{
#if LL_WINDOWS
std::wstring utf16filename = ll_convert<std::wstring>(filename);
std::wstring utf16filename = utf8path_to_wstring(filename);
std::wstring utf16mode = ll_convert<std::wstring>(std::string(mode));
return _wfopen(utf16filename.c_str(),utf16mode.c_str());
return _wfopen(utf16filename.c_str(), utf16mode.c_str());
#else
return ::fopen(filename.c_str(),mode); /* Flawfinder: ignore */
#endif
}
LLFILE* LLFile::_fsopen(const std::string& filename, const char* mode, int sharingFlag)
{
#if LL_WINDOWS
std::wstring utf16filename = ll_convert<std::wstring>(filename);
std::wstring utf16mode = ll_convert<std::wstring>(std::string(mode));
return _wfsopen(utf16filename.c_str(),utf16mode.c_str(),sharingFlag);
#else
llassert(0);//No corresponding function on non-windows
return NULL;
return ::fopen(filename.c_str(),mode);
#endif
}
// static
int LLFile::close(LLFILE * file)
{
int ret_value = 0;
@ -242,9 +373,10 @@ int LLFile::close(LLFILE * file)
return ret_value;
}
// static
std::string LLFile::getContents(const std::string& filename)
{
LLFILE* fp = fopen(filename, "rb"); /* Flawfinder: ignore */
LLFILE* fp = LLFile::fopen(filename, "rb");
if (fp)
{
fseek(fp, 0, SEEK_END);
@ -261,42 +393,80 @@ std::string LLFile::getContents(const std::string& filename)
return LLStringUtil::null;
}
int LLFile::remove(const std::string& filename, int supress_error)
// static
int LLFile::remove(const std::string& filename, int suppress_error)
{
#if LL_WINDOWS
std::wstring utf16filename = ll_convert<std::wstring>(filename);
int rc = _wremove(utf16filename.c_str());
// Posix remove() works on both files and directories although on Windows
// remove() and its wide char variant _wremove() only removes files just
// as its siblings unlink() and _wunlink().
// If we really only want to support files we should instead use
// unlink() in the non-Windows part below too
int rc = -1;
std::wstring utf16filename = utf8path_to_wstring(filename);
unsigned short st_mode = get_fileattr(utf16filename);
if (S_ISDIR(st_mode))
{
rc = _wrmdir(utf16filename.c_str());
}
else if (S_ISREG(st_mode))
{
rc = _wunlink(utf16filename.c_str());
}
else if (st_mode)
{
// it is something else than a file or directory
// this should not really happen as long as we do not allow for symlink
// detection in the optional parameter to get_fileattr()
rc = set_errno_from_oserror(ERROR_INVALID_PARAMETER);
}
else
{
// get_fileattr() failed and already set errno, preserve it for correct error reporting
}
#else
int rc = ::remove(filename.c_str());
#endif
return warnif("remove", filename, rc, supress_error);
return warnif("remove", filename, rc, suppress_error);
}
int LLFile::rename(const std::string& filename, const std::string& newname, int supress_error)
// static
int LLFile::rename(const std::string& filename, const std::string& newname, int suppress_error)
{
#if LL_WINDOWS
std::wstring utf16filename = ll_convert<std::wstring>(filename);
std::wstring utf16newname = ll_convert<std::wstring>(newname);
int rc = _wrename(utf16filename.c_str(),utf16newname.c_str());
// Posix rename() will gladly overwrite a file at newname if it exists, the Windows
// rename(), respectively _wrename(), will bark on that. Instead call directly the Windows
// API MoveFileEx() and use its flags to specify that overwrite is allowed.
std::wstring utf16filename = utf8path_to_wstring(filename);
std::wstring utf16newname = utf8path_to_wstring(newname);
int rc = 0;
if (!MoveFileExW(utf16filename.c_str(), utf16newname.c_str(), MOVEFILE_REPLACE_EXISTING | MOVEFILE_COPY_ALLOWED))
{
rc = set_errno_from_oserror(GetLastError());
}
#else
int rc = ::rename(filename.c_str(),newname.c_str());
#endif
return warnif(STRINGIZE("rename to '" << newname << "' from"), filename, rc, supress_error);
return warnif(STRINGIZE("rename to '" << newname << "' from"), filename, rc, suppress_error);
}
// Make this a define rather than using magic numbers multiple times in the code
#define LLFILE_COPY_BUFFER_SIZE 16384
// static
bool LLFile::copy(const std::string& from, const std::string& to)
{
bool copied = false;
LLFILE* in = LLFile::fopen(from, "rb"); /* Flawfinder: ignore */
LLFILE* in = LLFile::fopen(from, "rb");
if (in)
{
LLFILE* out = LLFile::fopen(to, "wb"); /* Flawfinder: ignore */
LLFILE* out = LLFile::fopen(to, "wb");
if (out)
{
char buf[16384]; /* Flawfinder: ignore */
char buf[LLFILE_COPY_BUFFER_SIZE];
size_t readbytes;
bool write_ok = true;
while(write_ok && (readbytes = fread(buf, 1, 16384, in))) /* Flawfinder: ignore */
while (write_ok && (readbytes = fread(buf, 1, LLFILE_COPY_BUFFER_SIZE, in)))
{
if (fwrite(buf, 1, readbytes, out) != readbytes)
{
@ -315,33 +485,62 @@ bool LLFile::copy(const std::string& from, const std::string& to)
return copied;
}
int LLFile::stat(const std::string& filename, llstat* filestatus)
// static
int LLFile::stat(const std::string& filename, llstat* filestatus, int suppress_error)
{
#if LL_WINDOWS
std::wstring utf16filename = ll_convert<std::wstring>(filename);
int rc = _wstat(utf16filename.c_str(),filestatus);
std::wstring utf16filename = utf8path_to_wstring(filename);
int rc = _wstat64(utf16filename.c_str(), filestatus);
#else
int rc = ::stat(filename.c_str(),filestatus);
int rc = ::stat(filename.c_str(), filestatus);
#endif
// We use stat() to determine existence (see isfile(), isdir()).
// Don't spam the log if the subject pathname doesn't exist.
return warnif("stat", filename, rc, ENOENT);
return warnif("stat", filename, rc, suppress_error);
}
// static
unsigned short LLFile::getattr(const std::string& filename, bool dontFollowSymLink, int suppress_error)
{
#if LL_WINDOWS
// _wstat64() is a bit heavyweight on Windows, use a more lightweight API
// to just get the attributes
int rc = -1;
std::wstring utf16filename = utf8path_to_wstring(filename);
unsigned short st_mode = get_fileattr(utf16filename, dontFollowSymLink);
if (st_mode)
{
return st_mode;
}
#else
llstat filestatus;
int rc = dontFollowSymLink ? ::lstat(filename.c_str(), &filestatus) : ::stat(filename.c_str(), &filestatus);
if (rc == 0)
{
return filestatus.st_mode;
}
#endif
warnif("getattr", filename, rc, suppress_error);
return 0;
}
// static
bool LLFile::isdir(const std::string& filename)
{
llstat st;
return stat(filename, &st) == 0 && S_ISDIR(st.st_mode);
return S_ISDIR(getattr(filename));
}
// static
bool LLFile::isfile(const std::string& filename)
{
llstat st;
return stat(filename, &st) == 0 && S_ISREG(st.st_mode);
return S_ISREG(getattr(filename));
}
// static
bool LLFile::islink(const std::string& filename)
{
return S_ISLNK(getattr(filename, true));
}
// static
const char *LLFile::tmpdir()
{
static std::string utf8path;
@ -368,75 +567,8 @@ const char *LLFile::tmpdir()
return utf8path.c_str();
}
/***************** Modified file stream created to overcome the incorrect behaviour of posix fopen in windows *******************/
#if LL_WINDOWS
LLFILE * LLFile::_Fiopen(const std::string& filename,
std::ios::openmode mode)
{ // open a file
static const char *mods[] =
{ // fopen mode strings corresponding to valid[i]
"r", "w", "w", "a", "rb", "wb", "wb", "ab",
"r+", "w+", "a+", "r+b", "w+b", "a+b",
0};
static const int valid[] =
{ // valid combinations of open flags
ios_base::in,
ios_base::out,
ios_base::out | ios_base::trunc,
ios_base::out | ios_base::app,
ios_base::in | ios_base::binary,
ios_base::out | ios_base::binary,
ios_base::out | ios_base::trunc | ios_base::binary,
ios_base::out | ios_base::app | ios_base::binary,
ios_base::in | ios_base::out,
ios_base::in | ios_base::out | ios_base::trunc,
ios_base::in | ios_base::out | ios_base::app,
ios_base::in | ios_base::out | ios_base::binary,
ios_base::in | ios_base::out | ios_base::trunc
| ios_base::binary,
ios_base::in | ios_base::out | ios_base::app
| ios_base::binary,
0};
LLFILE *fp = 0;
int n;
ios_base::openmode atendflag = mode & ios_base::ate;
ios_base::openmode norepflag = mode & ios_base::_Noreplace;
if (mode & ios_base::_Nocreate)
mode |= ios_base::in; // file must exist
mode &= ~(ios_base::ate | ios_base::_Nocreate | ios_base::_Noreplace);
for (n = 0; valid[n] != 0 && valid[n] != mode; ++n)
; // look for a valid mode
if (valid[n] == 0)
return (0); // no valid mode
else if (norepflag && mode & (ios_base::out | ios_base::app)
&& (fp = LLFile::fopen(filename, "r")) != 0) /* Flawfinder: ignore */
{ // file must not exist, close and fail
fclose(fp);
return (0);
}
else if (fp != 0 && fclose(fp) != 0)
return (0); // can't close after test open
// should open with protection here, if other than default
else if ((fp = LLFile::fopen(filename, mods[n])) == 0) /* Flawfinder: ignore */
return (0); // open failed
if (!atendflag || fseek(fp, 0, SEEK_END) == 0)
return (fp); // no need to seek to end, or seek succeeded
fclose(fp); // can't position at end
return (0);
}
#endif /* LL_WINDOWS */
#if LL_WINDOWS
/************** input file stream ********************************/
llifstream::llifstream() {}

View File

@ -41,8 +41,9 @@ typedef FILE LLFILE;
#include <sys/stat.h>
#if LL_WINDOWS
// windows version of stat function and stat data structure are called _stat
typedef struct _stat llstat;
// The Windows version of stat function and stat data structure are called _stat64
// We use _stat64 here to support 64-bit st_size and time_t values
typedef struct _stat64 llstat;
#else
typedef struct stat llstat;
#include <sys/types.h>
@ -56,35 +57,110 @@ typedef struct stat llstat;
# define S_ISDIR(x) (((x) & S_IFMT) == S_IFDIR)
#endif
// Windows C runtime library does not define this and does not support symlink detection in the
// stat functions but we do in our getattr() function
#ifndef S_IFLNK
#define S_IFLNK 0xA000 /* symlink */
#endif
#ifndef S_ISLNK
#define S_ISLNK(x) (((x) & S_IFMT) == S_IFLNK)
#endif
#include "llstring.h" // safe char* -> std::string conversion
/// LLFile is a class of static functions operating on paths
/// All the functions with a path string input take UTF8 path/filenames
class LL_COMMON_API LLFile
{
public:
// All these functions take UTF8 path/filenames.
static LLFILE* fopen(const std::string& filename,const char* accessmode); /* Flawfinder: ignore */
static LLFILE* _fsopen(const std::string& filename,const char* accessmode,int sharingFlag);
/// open a file with the specified access mode
static LLFILE* fopen(const std::string& filename, const char* accessmode); /* Flawfinder: ignore */
///< 'accessmode' follows the rules of the Posix fopen() mode parameter
/// "r" open the file for reading only and positions the stream at the beginning
/// "r+" open the file for reading and writing and positions the stream at the beginning
/// "w" open the file for reading and writing and truncate it to zero length
/// "w+" open or create the file for reading and writing and truncate to zero length if it existed
/// "a" open the file for reading and writing and position the stream at the end of the file
/// "a+" open or create the file for reading and writing and position the stream at the end of the file
///
/// in addition to these values, "b" can be appended to indicate binary stream access, but on Linux and Mac
/// this is strictly for compatibility and has no effect. On Windows this makes the file functions not
/// try to translate line endings. Windows also allows to append "t" to indicate text mode. If neither
/// "b" or "t" is defined, Windows uses the value set by _fmode which by default is _O_TEXT.
/// This means that it is always a good idea to append "b" specifically for binary file access to
/// avoid corruption of the binary consistency of the data stream when reading or writing
/// Other characters in 'accessmode' will usually cause an error as fopen will verify this parameter
/// @returns a valid LLFILE* pointer on success or NULL on failure
static int close(LLFILE * file);
/// retrieve the content of a file into a string
static std::string getContents(const std::string& filename);
///< @returns the content of the file or an empty string on failure
// perms is a permissions mask like 0777 or 0700. In most cases it will
// be overridden by the user's umask. It is ignored on Windows.
// mkdir() considers "directory already exists" to be SUCCESS.
/// create a directory
static int mkdir(const std::string& filename, int perms = 0700);
///< perms is a permissions mask like 0777 or 0700. In most cases it will be
/// overridden by the user's umask. It is ignored on Windows.
/// mkdir() considers "directory already exists" to be not an error.
/// @returns 0 on success and -1 on failure.
static int rmdir(const std::string& filename);
static int remove(const std::string& filename, int supress_error = 0);
static int rename(const std::string& filename,const std::string& newname, int supress_error = 0);
//// remove a directory
static int rmdir(const std::string& filename, int suppress_error = 0);
///< pass ENOENT in the optional 'suppress_error' parameter
/// if you don't want a warning in the log when the directory does not exist
/// @returns 0 on success and -1 on failure.
/// remove a file or directory
static int remove(const std::string& filename, int suppress_error = 0);
///< pass ENOENT in the optional 'suppress_error' parameter
/// if you don't want a warning in the log when the directory does not exist
/// @returns 0 on success and -1 on failure.
/// rename a file
static int rename(const std::string& filename, const std::string& newname, int suppress_error = 0);
///< it will silently overwrite newname if it exists without returning an error
/// Posix guarantees that if newname already exists, then there will be no moment
/// in which for other processes newname does not exist. There is no such guarantee
/// under Windows at this time. It may do it in the same way but the used Windows API
/// does not make such guarantees.
/// @returns 0 on success and -1 on failure.
/// copy the contents of file from 'from' to 'to' filename
static bool copy(const std::string& from, const std::string& to);
///< @returns true on success and false on failure.
static int stat(const std::string& filename,llstat* file_status);
static bool isdir(const std::string& filename);
static bool isfile(const std::string& filename);
static LLFILE * _Fiopen(const std::string& filename,
std::ios::openmode mode);
/// return the file stat structure for filename
static int stat(const std::string& filename, llstat* file_status, int suppress_error = ENOENT);
///< for compatibility with existing uses of LL_File::stat() we use ENOENT as default in the
/// optional 'suppress_error' parameter to avoid spamming the log with warnings when the API
/// is used to detect if a file exists
/// @returns 0 on success and -1 on failure.
/// get the file or directory attributes for filename
static unsigned short getattr(const std::string& filename, bool dontFollowSymLink = false, int suppress_error = ENOENT);
///< a more lightweight function on Windows to stat, that just returns the file attribute flags
/// dontFollowSymLinks set to true returns the attributes of the symlink if it is one, rather than resolving it
/// we pass by default ENOENT in the optional 'suppress_error' parameter to not spam the log with
/// warnings when the file or directory does not exist
/// @returns 0 on failure and a st_mode value with either S_IFDIR or S_IFREG set otherwise
/// together with the three access bits which under Windows only the write bit is relevant.
/// check if filename is an existing directory
static bool isdir(const std::string& filename);
///< @returns true if the path is for an existing directory
/// check if filename is an existing file
static bool isfile(const std::string& filename);
///< @returns true if the path is for an existing file
/// check if filename is a symlink
static bool islink(const std::string& filename);
///< @returns true if the path is pointing at a symlink
/// return a path to the temporary directory on the system
static const char * tmpdir();
};

View File

@ -188,6 +188,17 @@ public:
<< childout.peek(0, peeklen) << "..." << LL_ENDL;
}
// Handle any remaining stderr data (partial lines) the same way as we do
// for stdout: log it.
LLProcess::ReadPipe& childerr(mChild->getReadPipe(LLProcess::STDERR));
if (childerr.size())
{
LLProcess::ReadPipe::size_type
peeklen((std::min)(LLProcess::ReadPipe::size_type(50), childerr.size()));
LL_WARNS("LLLeap") << "Final stderr " << childerr.size() << " bytes: "
<< childerr.peek(0, peeklen) << "..." << LL_ENDL;
}
// Kill this instance. MUST BE LAST before return!
delete this;
return false;

View File

@ -80,7 +80,7 @@ void LLQueuedThread::shutdown()
mRequestQueue.close();
}
S32 timeout = 100;
S32 timeout = 50;
for ( ; timeout>0; timeout--)
{
if (isStopped())
@ -101,19 +101,34 @@ void LLQueuedThread::shutdown()
}
QueuedRequest* req;
S32 active_count = 0;
S32 queued_count = 0;
bool has_active = false;
lockData();
while ( (req = (QueuedRequest*)mRequestHash.pop_element()) )
{
if (req->getStatus() == STATUS_QUEUED || req->getStatus() == STATUS_INPROGRESS)
if (req->getStatus() == STATUS_INPROGRESS)
{
++active_count;
has_active = true;
req->setFlags(FLAG_ABORT | FLAG_AUTO_COMPLETE);
continue;
}
if (req->getStatus() == STATUS_QUEUED)
{
++queued_count;
req->setStatus(STATUS_ABORTED); // avoid assert in deleteRequest
}
req->deleteRequest();
}
if (active_count)
unlockData();
if (queued_count)
{
LL_WARNS() << "~LLQueuedThread() called with active requests: " << active_count << LL_ENDL;
LL_WARNS() << "~LLQueuedThread() called with unpocessed requests: " << queued_count << LL_ENDL;
}
if (has_active)
{
LL_WARNS() << "~LLQueuedThread() called with active requests!" << LL_ENDL;
ms_sleep(100); // last chance for request to finish
printQueueStats();
}
mRequestQueue.close();
@ -570,7 +585,12 @@ LLQueuedThread::QueuedRequest::QueuedRequest(LLQueuedThread::handle_t handle, U3
LLQueuedThread::QueuedRequest::~QueuedRequest()
{
llassert_always(mStatus == STATUS_DELETE);
if (mStatus != STATUS_DELETE)
{
// The only method to delete a request is deleteRequest(),
// it should have set the status to STATUS_DELETE
LL_ERRS() << "LLQueuedThread::QueuedRequest deleted with status " << mStatus << LL_ENDL;
}
}
//virtual

View File

@ -1348,10 +1348,6 @@ bool gunzip_file(const std::string& srcfile, const std::string& dstfile)
} while(gzeof(src) == 0);
fclose(dst);
dst = NULL;
#if LL_WINDOWS
// Rename in windows needs the dstfile to not exist.
LLFile::remove(dstfile, ENOENT);
#endif
if (LLFile::rename(tmpfile, dstfile) == -1) goto err; /* Flawfinder: ignore */
retval = true;
err:
@ -1399,10 +1395,6 @@ bool gzip_file(const std::string& srcfile, const std::string& dstfile)
gzclose(dst);
dst = NULL;
#if LL_WINDOWS
// Rename in windows needs the dstfile to not exist.
LLFile::remove(dstfile);
#endif
if (LLFile::rename(tmpfile, dstfile) == -1) goto err; /* Flawfinder: ignore */
retval = true;
err:

View File

@ -54,14 +54,29 @@ std::string apr_strerror_helper(apr_status_t rv)
*****************************************************************************/
#define ensure_equals_(left, right) \
ensure_equals(STRINGIZE(#left << " != " << #right), (left), (right))
do { \
auto _left_val = (left); \
auto _right_val = (right); \
if (_left_val != _right_val) { \
std::string _msg = std::string(#left) + " != " + std::string(#right); \
tut::ensure_equals(_msg, _left_val, _right_val); \
} else { \
tut::ensure_equals("", _left_val, _right_val); \
} \
} while(0)
#define aprchk(expr) aprchk_(#expr, (expr))
static void aprchk_(const char* call, apr_status_t rv, apr_status_t expected=APR_SUCCESS)
{
tut::ensure_equals(STRINGIZE(call << " => " << rv << ": " << apr_strerror_helper
(rv)),
rv, expected);
if (rv != expected)
{
std::string msg = std::string(call) + " => " + std::to_string(rv) + ": " + apr_strerror_helper(rv);
tut::ensure_equals(msg, rv, expected);
}
else
{
tut::ensure_equals("", rv, expected);
}
}
/**
@ -78,11 +93,14 @@ static std::string readfile(const std::string& pathname, const std::string& desc
std::string use_desc(desc);
if (use_desc.empty())
{
use_desc = STRINGIZE("in " << pathname);
use_desc = "in " + pathname;
}
std::ifstream inf(pathname.c_str());
std::string output;
tut::ensure(STRINGIZE("No output " << use_desc), bool(std::getline(inf, output)));
if (!std::getline(inf, output))
{
tut::ensure("No output " + use_desc, false);
}
std::string more;
while (std::getline(inf, more))
{
@ -108,8 +126,8 @@ void waitfor(LLProcess& proc, int timeout=60)
{
yield();
}
tut::ensure(STRINGIZE("process took longer than " << timeout << " seconds to terminate"),
i < timeout);
std::string msg = "process took longer than " + std::to_string(timeout) + " seconds to terminate";
tut::ensure(msg, i < timeout);
}
void waitfor(LLProcess::handle h, const std::string& desc, int timeout=60)
@ -119,8 +137,8 @@ void waitfor(LLProcess::handle h, const std::string& desc, int timeout=60)
{
yield();
}
tut::ensure(STRINGIZE("process took longer than " << timeout << " seconds to terminate"),
i < timeout);
std::string msg = "process took longer than " + std::to_string(timeout) + " seconds to terminate";
tut::ensure(msg, i < timeout);
}
/**
@ -153,7 +171,8 @@ struct PythonProcessLauncher
try
{
mPy = LLProcess::create(mParams);
tut::ensure(STRINGIZE("Couldn't launch " << mDesc << " script"), bool(mPy));
std::string msg = "Couldn't launch " + mDesc + " script";
tut::ensure(msg, bool(mPy));
}
catch (const tut::failure&)
{
@ -214,7 +233,8 @@ struct PythonProcessLauncher
mParams.args.add(out.getName());
run();
// assuming the script wrote to that file, read it
return readfile(out.getName(), STRINGIZE("from " << mDesc << " script"));
std::string desc = "from " + mDesc + " script";
return readfile(out.getName(), desc);
}
LLProcess::Params mParams;

View File

@ -100,6 +100,7 @@ const std::string HTTP_IN_HEADER_LOCATION("location");
const std::string HTTP_IN_HEADER_RETRY_AFTER("retry-after");
const std::string HTTP_IN_HEADER_SET_COOKIE("set-cookie");
const std::string HTTP_IN_HEADER_USER_AGENT("user-agent");
const std::string HTTP_IN_HEADER_X_CONTENT_TYPE_OPTIONS("x-content-type-options");
const std::string HTTP_IN_HEADER_X_FORWARDED_FOR("x-forwarded-for");
const std::string HTTP_CONTENT_LLSD_XML("application/llsd+xml");
@ -122,6 +123,7 @@ const std::string HTTP_CONTENT_IMAGE_BMP("image/bmp");
const std::string HTTP_NO_CACHE("no-cache");
const std::string HTTP_NO_CACHE_CONTROL("no-cache, max-age=0");
const std::string HTTP_NOSNIFF("nosniff");
const std::string HTTP_VERB_INVALID("(invalid)");
const std::string HTTP_VERB_HEAD("HEAD");

View File

@ -190,6 +190,7 @@ extern const std::string HTTP_IN_HEADER_LOCATION;
extern const std::string HTTP_IN_HEADER_RETRY_AFTER;
extern const std::string HTTP_IN_HEADER_SET_COOKIE;
extern const std::string HTTP_IN_HEADER_USER_AGENT;
extern const std::string HTTP_IN_HEADER_X_CONTENT_TYPE_OPTIONS;
extern const std::string HTTP_IN_HEADER_X_FORWARDED_FOR;
//// HTTP Content Types ////
@ -215,5 +216,6 @@ extern const std::string HTTP_CONTENT_IMAGE_BMP;
//// HTTP Cache Settings ////
extern const std::string HTTP_NO_CACHE;
extern const std::string HTTP_NO_CACHE_CONTROL;
extern const std::string HTTP_NOSNIFF;
#endif

View File

@ -576,10 +576,6 @@ bool LLCrashLogger::init()
std::string old_log_file = gDirUtilp->getExpandedFilename(LL_PATH_LOGS, "crashreport.log.old");
std::string log_file = gDirUtilp->getExpandedFilename(LL_PATH_LOGS, "crashreport.log");
#if LL_WINDOWS
LLAPRFile::remove(old_log_file);
#endif
LLFile::rename(log_file.c_str(), old_log_file.c_str());
// Set the log file to crashreport.log

View File

@ -1,28 +1,28 @@
/**
/**
* @file lldir_utils_objc.mm
* @brief Cocoa implementation of directory utilities for macOS
*
* $LicenseInfo:firstyear=2020&license=viewerlgpl$
* Second Life Viewer Source Code
* Copyright (C) 2020, Linden Research, Inc.
*
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation;
* version 2.1 of the License only.
*
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
*
* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
*/
#if LL_DARWIN
//WARNING: This file CANNOT use standard linden includes due to conflicts between definitions of BOOL
@ -39,18 +39,18 @@ std::string getSystemTempFolder()
tempDir = @"/tmp";
result = std::string([tempDir UTF8String]);
}
return result;
}
//findSystemDirectory scoped exclusively to this file.
//findSystemDirectory scoped exclusively to this file.
std::string findSystemDirectory(NSSearchPathDirectory searchPathDirectory,
NSSearchPathDomainMask domainMask)
{
std::string result;
@autoreleasepool {
NSString *path = nil;
// Search for the path
NSArray* paths = NSSearchPathForDirectoriesInDomains(searchPathDirectory,
domainMask,
@ -60,10 +60,10 @@ std::string findSystemDirectory(NSSearchPathDirectory searchPathDirectory,
path = [paths objectAtIndex:0];
//HACK: Always attempt to create directory, ignore errors.
NSError *error = nil;
[[NSFileManager defaultManager] createDirectoryAtPath:path withIntermediateDirectories:YES attributes:nil error:&error];
result = std::string([path UTF8String]);
}
}
@ -88,7 +88,7 @@ std::string getSystemResourceFolder()
NSString *bundlePath = [[NSBundle mainBundle] resourcePath];
result = std::string([bundlePath UTF8String]);
}
return result;
}
@ -102,7 +102,7 @@ std::string getSystemApplicationSupportFolder()
{
return findSystemDirectory (NSApplicationSupportDirectory,
NSUserDomainMask);
}
#endif // LL_DARWIN

View File

@ -103,9 +103,6 @@ bool LLFileSystem::renameFile(const LLUUID& old_file_id, const LLAssetType::ETyp
const std::string old_filename = LLDiskCache::metaDataToFilepath(old_file_id, old_file_type);
const std::string new_filename = LLDiskCache::metaDataToFilepath(new_file_id, new_file_type);
// Rename needs the new file to not exist.
LLFileSystem::removeFile(new_file_id, new_file_type, ENOENT);
if (LLFile::rename(old_filename, new_filename) != 0)
{
// We would like to return false here indicating the operation

View File

@ -554,11 +554,6 @@ public:
}
if (!opj_setup_encoder(encoder, &parameters, image))
{
return false;
}
U32 width_tiles = (rawImageIn.getWidth() >> 6);
U32 height_tiles = (rawImageIn.getHeight() >> 6);
@ -572,6 +567,19 @@ public:
height_tiles = 1;
}
if (width_tiles == 1 || height_tiles == 1)
{
// Images with either dimension less than 32 need less number of resolutions otherwise they error
int min_dim = rawImageIn.getWidth() < rawImageIn.getHeight() ? rawImageIn.getWidth() : rawImageIn.getHeight();
int max_res = 1 + (int)floor(log2(min_dim));
parameters.numresolution = max_res;
}
if (!opj_setup_encoder(encoder, &parameters, image))
{
return false;
}
U32 tile_count = width_tiles * height_tiles;
U32 data_size_guess = tile_count * TILE_SIZE;

View File

@ -774,6 +774,7 @@ void LLPermissions::importLLSD(const LLSD& sd_perm)
}
}
fixOwnership();
fix();
}

View File

@ -5713,6 +5713,8 @@ bool LLVolumeFace::cacheOptimize(bool gen_tangents)
{
try
{
// providing mIndices should help avoid unused vertices
// but those should have been filtered out on upload
vert_count = static_cast<S32>(meshopt_generateVertexRemapMulti(&remap[0], nullptr, data.p.size(), data.p.size(), mos, stream_count));
}
catch (std::bad_alloc&)
@ -5722,10 +5724,16 @@ bool LLVolumeFace::cacheOptimize(bool gen_tangents)
}
}
if (vert_count < 65535 && vert_count != 0)
// Probably should be using meshopt_remapVertexBuffer instead of remaping manually
if (vert_count < 65535 && vert_count > 0)
{
//copy results back into volume
resizeVertices(vert_count);
if (mNumVertices == 0)
{
LLError::LLUserWarningMsg::showOutOfMemory();
LL_ERRS("LLCoros") << "Failed to allocate memory for resizeVertices(" << vert_count << ")" << LL_ENDL;
}
if (!data.w.empty())
{
@ -5738,13 +5746,27 @@ bool LLVolumeFace::cacheOptimize(bool gen_tangents)
{
U32 src_idx = i;
U32 dst_idx = remap[i];
if (dst_idx >= (U32)mNumVertices)
if (dst_idx == U32_MAX)
{
// Unused indices? Probably need to resize mIndices
dst_idx = mNumVertices - 1;
llassert(false);
LL_DEBUGS_ONCE("LLVOLUME") << "U32_MAX destination index, substituting" << LL_ENDL;
}
else if (dst_idx >= (U32)mNumVertices)
{
dst_idx = mNumVertices - 1;
// Shouldn't happen, figure out what gets returned in remap and why.
llassert(false);
LL_DEBUGS_ONCE("LLVOLUME") << "Invalid destination index, substituting" << LL_ENDL;
}
if (src_idx >= (U32)data.p.size())
{
// data.p.size() is supposed to be equal to mNumIndices
src_idx = (U32)(data.p.size() - 1);
llassert(false);
LL_DEBUGS_ONCE("LLVOLUME") << "Invalid source index, substituting" << LL_ENDL;
}
mIndices[i] = dst_idx;
mPositions[dst_idx].load3(data.p[src_idx].mV);
@ -5778,7 +5800,7 @@ bool LLVolumeFace::cacheOptimize(bool gen_tangents)
}
else
{
if (vert_count == 0)
if (vert_count <= 0)
{
LL_WARNS_ONCE("LLVOLUME") << "meshopt_generateVertexRemapMulti failed to process a model or model was invalid" << LL_ENDL;
}

View File

@ -138,7 +138,22 @@ LLCoprocedureManager::LLCoprocedureManager()
LLCoprocedureManager::~LLCoprocedureManager()
{
close();
try
{
close();
}
catch (const boost::fibers::fiber_error&)
{
LL_WARNS() << "Fiber error during ~LLCoprocedureManager()" << LL_ENDL;
}
catch (const std::exception& e)
{
// Shutting down, just log it
LL_WARNS() << "Exception during ~LLCoprocedureManager(): " << e.what() << LL_ENDL;
}
mPropertyQueryFn.clear();
mPropertyDefineFn.clear();
mPoolMap.clear();
}
void LLCoprocedureManager::initializePool(const std::string &poolName, size_t queue_size)
@ -365,6 +380,22 @@ LLCoprocedurePool::LLCoprocedurePool(const std::string &poolName, size_t size, s
LLCoprocedurePool::~LLCoprocedurePool()
{
try
{
close(); // should have been closed already, but shouldn't hurt
mStatusListener.disconnect();
mPendingCoprocs.reset();
mCoroMapping.clear();
}
catch (const boost::fibers::fiber_error&)
{
LL_WARNS() << "Fiber error during ~LLCoprocedurePool() " << mPoolName << LL_ENDL;
}
catch (const std::exception& e)
{
// Shutting down, just log it
LL_WARNS() << "Exception " << e.what() << " during ~LLCoprocedurePool() in " << mPoolName << LL_ENDL;
}
}
//-------------------------------------------------------------------------

View File

@ -0,0 +1,47 @@
# -*- cmake -*-
project(llphysicsextensionsos)
include(00-Common)
include(LLCommon)
include(LLMath)
include(VHACD)
set(llphysicsextensionsos_SOURCE_FILES
llconvexdecomposition.cpp
llconvexdecompositionvhacd.cpp
llpathinglib.cpp
LLPathingLibStubImpl.cpp
llphysicsextensions.cpp
LLPhysicsExtensionsStubImpl.cpp
)
set(llphysicsextensionsos_HEADER_FILES
CMakeLists.txt
llconvexdecomposition.h
llconvexdecompositionvhacd.h
llpathinglib.h
LLPathingLibStubImpl.h
llphysicsextensions.h
LLPhysicsExtensionsStubImpl.h
)
set_source_files_properties(${llphysicsextensionsos_HEADER_FILES}
PROPERTIES HEADER_FILE_ONLY TRUE)
list(APPEND llphysicsextensionsos_SOURCE_FILES ${llphysicsextensionsos_HEADER_FILES})
add_library (llphysicsextensionsos ${llphysicsextensionsos_SOURCE_FILES})
target_include_directories(llphysicsextensionsos INTERFACE ${CMAKE_CURRENT_SOURCE_DIR})
target_link_libraries(llphysicsextensionsos
llcommon
llmath
ll::vhacd)
if(WINDOWS)
target_compile_options(llphysicsextensionsos PRIVATE /bigobj)
endif()
# Add tests

View File

@ -0,0 +1,109 @@
/**
* @file LLPathingLibStubImpl.cpp
* @author prep@lindenlab.com
* @brief A stubbed implementation of LLPathingLib
*
* $LicenseInfo:firstyear=2012&license=viewerlgpl$
* Second Life Viewer Source Code
* Copyright (C) 20112010, Linden Research, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation;
* version 2.1 of the License only.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
#include "linden_common.h"
#include "llpathinglib.h"
#include "LLPathingLibStubImpl.h"
#include "llsd.h"
//=============================================================================
LLPathingLibImpl::LLPathingLibImpl()
{
}
LLPathingLibImpl::~LLPathingLibImpl()
{
}
LLPathingLib* LLPathingLibImpl::getInstance()
{
return NULL;
}
LLPathingLib::LLPLResult LLPathingLibImpl::initSystem()
{
return LLPL_NOT_IMPLEMENTED;
}
LLPathingLib::LLPLResult LLPathingLibImpl::quitSystem()
{
return LLPL_NOT_IMPLEMENTED;
}
LLPathingLib::LLPLResult LLPathingLibImpl::extractNavMeshSrcFromLLSD( const LLSD::Binary& dataBlock, int dir )
{
return LLPL_NOT_IMPLEMENTED;
}
void LLPathingLibImpl::processNavMeshData()
{
}
LLPathingLibImpl::LLPLResult LLPathingLibImpl::generatePath( const PathingPacket& pathingPacket )
{
return LLPL_NOT_IMPLEMENTED;
}
void LLPathingLibImpl::setNavMeshMaterialType( LLPLCharacterType materialType )
{
}
void LLPathingLibImpl::setNavMeshColors( const NavMeshColors& color )
{
}
void LLPathingLibImpl::renderNavMesh()
{
}
void LLPathingLibImpl::renderNavMeshEdges()
{
}
void LLPathingLibImpl::renderNavMeshShapesVBO( U32 shapeRenderFlags )
{
}
void LLPathingLibImpl::renderPath()
{
}
void LLPathingLibImpl::renderPathBookend( LLRender& gl, LLPathingLib::LLPLPathBookEnd type )
{
}
void LLPathingLibImpl::cleanupVBOManager()
{
}
void LLPathingLibImpl::cleanupResidual()
{
}

View File

@ -0,0 +1,78 @@
/**
* @file LLPathingLibSubImpl.h
* @author prep@lindenlab.com
* @brief A stubbed implementation of LLPathingLib
*
* $LicenseInfo:firstyear=2012&license=viewerlgpl$
* Second Life Viewer Source Code
* Copyright (C) 2012, Linden Research, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation;
* version 2.1 of the License only.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
#ifndef LL_PATHING_LIB_H
#define LL_PATHING_LIB_H
#include "llpathinglib.h"
class LLSD;
//=============================================================================
class LLPathingLibImpl : public LLPathingLib
{
public:
LLPathingLibImpl();
virtual ~LLPathingLibImpl();
// Obtain a pointer to the actual implementation
static LLPathingLib* getInstance();
static LLPathingLib::LLPLResult initSystem();
static LLPathingLib::LLPLResult quitSystem();
//Extract and store navmesh data from the llsd datablock sent down by the server
virtual LLPLResult extractNavMeshSrcFromLLSD( const LLSD::Binary& dataBlock, int dir );
//Stitch any stored navmeshes together
virtual void processNavMeshData();
//Method used to generate and visualize a path on the viewers navmesh
virtual LLPLResult generatePath( const PathingPacket& pathingPacket );
//Set the material type for the heatmap type
virtual void setNavMeshMaterialType( LLPLCharacterType materialType );
//Set the various navmesh colors
virtual void setNavMeshColors( const NavMeshColors& color );
//The entry method to rendering the client side navmesh
virtual void renderNavMesh();
//The entry method to rendering the client side navmesh edges
virtual void renderNavMeshEdges();
//The entry method to render the client navmesh shapes VBO
virtual void renderNavMeshShapesVBO( U32 shapeRenderFlags );
//The entry method to render the clients designated path
virtual void renderPath();
//The entry method to render the capsule bookends for the clients designated path
virtual void renderPathBookend( LLRender& gl, LLPathingLib::LLPLPathBookEnd type );
//Method to delete any vbo's that are currently being managed by the pathing library
virtual void cleanupVBOManager();
//Method to cleanup any allocations within the implementation
virtual void cleanupResidual();
};
#endif //LL_PATHING_LIB_H

View File

@ -0,0 +1,51 @@
/**
* @file LLPhysicsExtensionsStubImpl.cpp
* @author prep@lindenlab.com
* @brief A stubbed implementation of LLPhysicsExtensions
*
* $LicenseInfo:firstyear=2012&license=viewerlgpl$
* Second Life Viewer Source Code
* Copyright (C) 2012, Linden Research, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation;
* version 2.1 of the License only.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
#include "linden_common.h"
#include "llphysicsextensions.h"
#include "LLPhysicsExtensionsStubImpl.h"
//=============================================================================
LLPhysicsExtensionsImpl::LLPhysicsExtensionsImpl()
{
}
LLPhysicsExtensionsImpl::~LLPhysicsExtensionsImpl()
{
}
bool LLPhysicsExtensionsImpl::initSystem()
{
return false;
}
bool LLPhysicsExtensionsImpl::quitSystem()
{
return false;
}

View File

@ -0,0 +1,46 @@
/**
* @file LLPhysicsExtensionsSubImpl.h
* @author prep@lindenlab.com
* @brief A stubbed implementation of LLPhysicsExtensions
*
* $LicenseInfo:firstyear=2012&license=viewerlgpl$
* Second Life Viewer Source Code
* Copyright (C) 2012, Linden Research, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation;
* version 2.1 of the License only.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
#ifndef LL_PHYSICS_EXTENSIONS_STUB_IMPL_H
#define LL_PHYSICS_EXTENSIONS_STUB_IMPL_H
#include "llphysicsextensions.h"
//=============================================================================
class LLPhysicsExtensionsImpl : public LLPhysicsExtensions
{
public:
LLPhysicsExtensionsImpl();
virtual ~LLPhysicsExtensionsImpl();
static bool initSystem();
static bool quitSystem();
};
#endif //LL_PHYSICS_EXTENSIONS_STUB_IMPL_H

View File

@ -0,0 +1,83 @@
/**
* @file llconvexdecomposition.cpp
* @author falcon@lindenlab.com
* @brief Inner implementation of LLConvexDecomposition interface
*
* $LicenseInfo:firstyear=2011&license=lgpl$
* Second Life Viewer Source Code
* Copyright (C) 2011, Linden Research, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation;
* version 2.1 of the License only.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
#include "linden_common.h"
#include "llconvexdecompositionvhacd.h"
#include "llconvexdecomposition.h"
bool LLConvexDecomposition::s_isInitialized = false;
// static
bool LLConvexDecomposition::isFunctional()
{
return LLConvexDecompositionVHACD::isFunctional();
}
// static
LLConvexDecomposition* LLConvexDecomposition::getInstance()
{
if ( !s_isInitialized )
{
return nullptr;
}
else
{
return LLConvexDecompositionVHACD::getInstance();
}
}
// static
LLCDResult LLConvexDecomposition::initSystem()
{
LLCDResult result = LLConvexDecompositionVHACD::initSystem();
if ( result == LLCD_OK )
{
s_isInitialized = true;
}
return result;
}
// static
LLCDResult LLConvexDecomposition::initThread()
{
return LLConvexDecompositionVHACD::initThread();
}
// static
LLCDResult LLConvexDecomposition::quitThread()
{
return LLConvexDecompositionVHACD::quitThread();
}
// static
LLCDResult LLConvexDecomposition::quitSystem()
{
return LLConvexDecompositionVHACD::quitSystem();
}

View File

@ -0,0 +1,231 @@
/**
* @file llconvexdecomposition.cpp
* @brief LLConvexDecomposition interface definition
*
* $LicenseInfo:firstyear=2011&license=lgpl$
* Second Life Viewer Source Code
* Copyright (C) 2011, Linden Research, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation;
* version 2.1 of the License only.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
#ifndef LL_CONVEX_DECOMPOSITION
#define LL_CONVEX_DECOMPOSITION
typedef int bool32;
#if defined(_WIN32) || defined(_WIN64)
#define LLCD_CALL __cdecl
#else
#define LLCD_CALL
#endif
struct LLCDParam
{
enum LLCDParamType
{
LLCD_INVALID = 0,
LLCD_INTEGER,
LLCD_FLOAT,
LLCD_BOOLEAN,
LLCD_ENUM
};
struct LLCDEnumItem
{
const char* mName;
int mValue;
};
union LLCDValue
{
float mFloat;
int mIntOrEnumValue;
bool32 mBool;
};
union LLCDParamDetails
{
struct {
LLCDValue mLow;
LLCDValue mHigh;
LLCDValue mDelta;
} mRange;
struct {
int mNumEnums;
LLCDEnumItem* mEnumsArray;
} mEnumValues;
};
const char* mName;
const char* mDescription;
LLCDParamType mType;
LLCDParamDetails mDetails;
LLCDValue mDefault;
int mStage;
// WARNING: Only the LLConvexDecomposition implementation
// should change this value
int mReserved;
};
struct LLCDStageData
{
const char* mName;
const char* mDescription;
bool32 mSupportsCallback;
};
struct LLCDMeshData
{
enum IndexType
{
INT_16,
INT_32
};
const float* mVertexBase;
int mVertexStrideBytes;
int mNumVertices;
const void* mIndexBase;
IndexType mIndexType;
int mIndexStrideBytes;
int mNumTriangles;
};
struct LLCDHull
{
const float* mVertexBase;
int mVertexStrideBytes;
int mNumVertices;
};
enum LLCDResult
{
LLCD_OK = 0,
LLCD_UNKOWN_ERROR,
LLCD_NULL_PTR,
LLCD_INVALID_STAGE,
LLCD_UNKNOWN_PARAM,
LLCD_BAD_VALUE,
LLCD_REQUEST_OUT_OF_RANGE,
LLCD_INVALID_MESH_DATA,
LLCD_INVALID_HULL_DATA,
LLCD_STAGE_NOT_READY,
LLCD_INVALID_THREAD,
LLCD_NOT_IMPLEMENTED
};
// This callback will receive a string describing the current subtask being performed
// as well as a pair of numbers indicating progress. (The values should not be interpreted
// as a completion percentage as 'current' may be greater than 'final'.)
// If the callback returns zero, the decomposition will be terminated
typedef int (LLCD_CALL *llcdCallbackFunc)(const char* description, int current_progress, int final_progress);
class LLConvexDecomposition
{
public:
// Obtain a pointer to the actual implementation
static LLConvexDecomposition* getInstance();
/// @returns false if this is the stub
static bool isFunctional();
static LLCDResult initSystem();
static LLCDResult initThread();
static LLCDResult quitThread();
static LLCDResult quitSystem();
// Generate a decomposition object handle
virtual void genDecomposition(int& decomp) = 0;
// Delete decomposition object handle
virtual void deleteDecomposition(int decomp) = 0;
// Bind given decomposition handle
// Commands operate on currently bound decomposition
virtual void bindDecomposition(int decomp) = 0;
// Sets *paramsOut to the address of the LLCDParam array and returns
// the number of parameters
virtual int getParameters(const LLCDParam** paramsOut) = 0;
// Sets *stagesOut to the address of the LLCDStageData array and returns
// the number of stages
virtual int getStages(const LLCDStageData** stagesOut) = 0;
// Set a parameter by name. Pass enum values as integers.
virtual LLCDResult setParam(const char* name, float val) = 0;
virtual LLCDResult setParam(const char* name, int val) = 0;
virtual LLCDResult setParam(const char* name, bool val) = 0;
// Set incoming mesh data. Data is copied to local buffers and will
// persist until the next setMeshData call
virtual LLCDResult setMeshData( const LLCDMeshData* data, bool vertex_based ) = 0;
// Register a callback to be called periodically during the specified stage
// See the typedef above for more information
virtual LLCDResult registerCallback( int stage, llcdCallbackFunc callback ) = 0;
// Execute the specified decomposition stage
virtual LLCDResult executeStage(int stage) = 0;
virtual LLCDResult buildSingleHull() = 0 ;
// Gets the number of hulls generated by the specified decompositions stage
virtual int getNumHullsFromStage(int stage) = 0;
// Populates hullOut to reference the internal copy of the requested hull
// The data will persist only until the next executeStage call for that stage.
virtual LLCDResult getHullFromStage( int stage, int hull, LLCDHull* hullOut ) = 0;
virtual LLCDResult getSingleHull( LLCDHull* hullOut ) = 0 ;
// TODO: Implement lock of some kind to disallow this call if data not yet ready
// Populates the meshDataOut to reference the utility's copy of the mesh geometry
// for the hull and stage specified.
// You must copy this data if you want to continue using it after the next executeStage
// call
virtual LLCDResult getMeshFromStage( int stage, int hull, LLCDMeshData* meshDataOut) = 0;
// Creates a mesh from hullIn and temporarily stores it internally in the utility.
// The mesh data persists only until the next call to getMeshFromHull
virtual LLCDResult getMeshFromHull( LLCDHull* hullIn, LLCDMeshData* meshOut ) = 0;
// Takes meshIn, generates a single convex hull from it, converts that to a mesh
// stored internally, and populates meshOut to reference the internally stored data.
// The data is persistent only until the next call to generateSingleHullMeshFromMesh
virtual LLCDResult generateSingleHullMeshFromMesh( LLCDMeshData* meshIn, LLCDMeshData* meshOut) = 0;
//
/// Debug
virtual void loadMeshData(const char* fileIn, LLCDMeshData** meshDataOut) = 0;
private:
static bool s_isInitialized;
};
#endif //LL_CONVEX_DECOMPOSITION

View File

@ -0,0 +1,492 @@
/**
* @file llconvexdecompositionvhacd.cpp
* @author rye@alchemyviewer.org
* @brief A VHACD based implementation of LLConvexDecomposition
*
* $LicenseInfo:firstyear=2025&license=viewerlgpl$
* Second Life Viewer Source Code
* Copyright (C) 2025, Linden Research, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation;
* version 2.1 of the License only.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
#include "linden_common.h"
#include "llmath.h"
#include "v3math.h"
#include <string.h>
#include <memory>
#define ENABLE_VHACD_IMPLEMENTATION 1
#include "VHACD.h"
#include "llconvexdecompositionvhacd.h"
constexpr S32 MAX_HULLS = 256;
constexpr S32 MAX_VERTICES_PER_HULL = 256;
bool LLConvexDecompositionVHACD::isFunctional()
{
return true;
}
LLConvexDecomposition* LLConvexDecompositionVHACD::getInstance()
{
return LLSimpleton::getInstance();
}
LLCDResult LLConvexDecompositionVHACD::initSystem()
{
createInstance();
return LLCD_OK;
}
LLCDResult LLConvexDecompositionVHACD::initThread()
{
return LLCD_OK;
}
LLCDResult LLConvexDecompositionVHACD::quitThread()
{
return LLCD_OK;
}
LLCDResult LLConvexDecompositionVHACD::quitSystem()
{
deleteSingleton();
return LLCD_OK;
}
LLConvexDecompositionVHACD::LLConvexDecompositionVHACD()
{
//Create our vhacd instance and setup default parameters
mVHACD = VHACD::CreateVHACD();
mVHACDParameters.m_callback = &mVHACDCallback;
mVHACDParameters.m_logger = &mVHACDLogger;
mDecompStages[0].mName = "Analyze";
mDecompStages[0].mDescription = nullptr;
LLCDParam param;
param.mName = "Fill Mode";
param.mDescription = nullptr;
param.mType = LLCDParam::LLCD_ENUM;
param.mDetails.mEnumValues.mNumEnums = 3;
static LLCDParam::LLCDEnumItem fill_enums[3];
fill_enums[(size_t)VHACD::FillMode::FLOOD_FILL].mName = "Flood";
fill_enums[(size_t)VHACD::FillMode::FLOOD_FILL].mValue = (int)VHACD::FillMode::FLOOD_FILL;
fill_enums[(size_t)VHACD::FillMode::SURFACE_ONLY].mName = "Surface Only";
fill_enums[(size_t)VHACD::FillMode::SURFACE_ONLY].mValue = (int)VHACD::FillMode::SURFACE_ONLY;
fill_enums[(size_t)VHACD::FillMode::RAYCAST_FILL].mName = "Raycast";
fill_enums[(size_t)VHACD::FillMode::RAYCAST_FILL].mValue = (int)VHACD::FillMode::RAYCAST_FILL;
param.mDetails.mEnumValues.mEnumsArray = fill_enums;
param.mDefault.mIntOrEnumValue = (int)VHACD::FillMode::FLOOD_FILL;
param.mStage = 0;
param.mReserved = -1;
mDecompParams.push_back(param);
enum EVoxelQualityLevels
{
E_LOW_QUALITY = 0,
E_NORMAL_QUALITY,
E_HIGH_QUALITY,
E_VERY_HIGH_QUALITY,
E_ULTRA_QUALITY,
E_MAX_QUALITY,
E_NUM_QUALITY_LEVELS
};
param.mName = "Voxel Resolution";
param.mDescription = nullptr;
param.mType = LLCDParam::LLCD_ENUM;
param.mDetails.mEnumValues.mNumEnums = E_NUM_QUALITY_LEVELS;
static LLCDParam::LLCDEnumItem voxel_quality_enums[E_NUM_QUALITY_LEVELS];
voxel_quality_enums[E_LOW_QUALITY].mName = "Low";
voxel_quality_enums[E_LOW_QUALITY].mValue = 200000;
voxel_quality_enums[E_NORMAL_QUALITY].mName = "Normal";
voxel_quality_enums[E_NORMAL_QUALITY].mValue = 400000;
voxel_quality_enums[E_HIGH_QUALITY].mName = "High";
voxel_quality_enums[E_HIGH_QUALITY].mValue = 800000;
voxel_quality_enums[E_VERY_HIGH_QUALITY].mName = "Very High";
voxel_quality_enums[E_VERY_HIGH_QUALITY].mValue = 1200000;
voxel_quality_enums[E_ULTRA_QUALITY].mName = "Ultra";
voxel_quality_enums[E_ULTRA_QUALITY].mValue = 1600000;
voxel_quality_enums[E_MAX_QUALITY].mName = "Maximum";
voxel_quality_enums[E_MAX_QUALITY].mValue = 2000000;
param.mDetails.mEnumValues.mEnumsArray = voxel_quality_enums;
param.mDefault.mIntOrEnumValue = 400000;
param.mStage = 0;
param.mReserved = -1;
mDecompParams.push_back(param);
param.mName = "Num Hulls";
param.mDescription = nullptr;
param.mType = LLCDParam::LLCD_FLOAT;
param.mDetails.mRange.mLow.mFloat = 1.f;
param.mDetails.mRange.mHigh.mFloat = MAX_HULLS;
param.mDetails.mRange.mDelta.mFloat = 1.f;
param.mDefault.mFloat = 8.f;
param.mStage = 0;
param.mReserved = -1;
mDecompParams.push_back(param);
param.mName = "Num Vertices";
param.mDescription = nullptr;
param.mType = LLCDParam::LLCD_FLOAT;
param.mDetails.mRange.mLow.mFloat = 3.f;
param.mDetails.mRange.mHigh.mFloat = MAX_VERTICES_PER_HULL;
param.mDetails.mRange.mDelta.mFloat = 1.f;
param.mDefault.mFloat = 32.f;
param.mStage = 0;
param.mReserved = -1;
mDecompParams.push_back(param);
param.mName = "Error Tolerance";
param.mDescription = nullptr;
param.mType = LLCDParam::LLCD_FLOAT;
param.mDetails.mRange.mLow.mFloat = 0.0001f;
param.mDetails.mRange.mHigh.mFloat = 99.f;
param.mDetails.mRange.mDelta.mFloat = 0.001f;
param.mDefault.mFloat = 1.f;
param.mStage = 0;
param.mReserved = -1;
mDecompParams.push_back(param);
for (const LLCDParam& param : mDecompParams)
{
const char* const name = param.mName;
switch (param.mType)
{
case LLCDParam::LLCD_FLOAT:
{
setParam(name, param.mDefault.mFloat);
break;
}
case LLCDParam::LLCD_ENUM:
case LLCDParam::LLCD_INTEGER:
{
setParam(name, param.mDefault.mIntOrEnumValue);
break;
}
case LLCDParam::LLCD_BOOLEAN:
{
setParam(name, (param.mDefault.mBool != 0));
break;
}
case LLCDParam::LLCD_INVALID:
default:
{
break;
}
}
}
}
LLConvexDecompositionVHACD::~LLConvexDecompositionVHACD()
{
mBoundDecomp = nullptr;
mDecompData.clear();
mVHACD->Release();
}
void LLConvexDecompositionVHACD::genDecomposition(int& decomp)
{
int new_decomp_id = static_cast<int>(mDecompData.size()) + 1;
mDecompData[new_decomp_id] = LLDecompData();
decomp = new_decomp_id;
}
void LLConvexDecompositionVHACD::deleteDecomposition(int decomp)
{
auto iter = mDecompData.find(decomp);
if (iter != mDecompData.end())
{
if (mBoundDecomp == &iter->second)
{
mBoundDecomp = nullptr;
}
mDecompData.erase(iter);
}
}
void LLConvexDecompositionVHACD::bindDecomposition(int decomp)
{
auto iter = mDecompData.find(decomp);
if (iter != mDecompData.end())
{
mBoundDecomp = &iter->second;
}
else
{
LL_WARNS() << "Failed to bind unknown decomposition: " << decomp << LL_ENDL;
mBoundDecomp = nullptr;
}
}
LLCDResult LLConvexDecompositionVHACD::setParam(const char* name, float val)
{
if (name == std::string("Num Hulls"))
{
mVHACDParameters.m_maxConvexHulls = llclamp(ll_round(val), 1, MAX_HULLS);
}
else if (name == std::string("Num Vertices"))
{
mVHACDParameters.m_maxNumVerticesPerCH = llclamp(ll_round(val), 3, MAX_VERTICES_PER_HULL);
}
else if (name == std::string("Error Tolerance"))
{
mVHACDParameters.m_minimumVolumePercentErrorAllowed = val;
}
return LLCD_OK;
}
LLCDResult LLConvexDecompositionVHACD::setParam(const char* name, bool val)
{
return LLCD_OK;
}
LLCDResult LLConvexDecompositionVHACD::setParam(const char* name, int val)
{
if (name == std::string("Fill Mode"))
{
mVHACDParameters.m_fillMode = (VHACD::FillMode)val;
}
else if (name == std::string("Voxel Resolution"))
{
mVHACDParameters.m_resolution = val;
}
return LLCD_OK;
}
LLCDResult LLConvexDecompositionVHACD::setMeshData( const LLCDMeshData* data, bool vertex_based )
{
if (!mBoundDecomp)
{
return LLCD_NULL_PTR;
}
return mBoundDecomp->mSourceMesh.from(data, vertex_based);
}
LLCDResult LLConvexDecompositionVHACD::registerCallback(int stage, llcdCallbackFunc callback )
{
if (stage == 0)
{
mVHACDCallback.setCallbackFunc(callback);
return LLCD_OK;
}
else
{
return LLCD_INVALID_STAGE;
}
}
LLCDResult LLConvexDecompositionVHACD::executeStage(int stage)
{
if (!mBoundDecomp)
{
return LLCD_NULL_PTR;
}
if (stage != 0)
{
return LLCD_INVALID_STAGE;
}
mBoundDecomp->mDecomposedHulls.clear();
const auto& decomp_mesh = mBoundDecomp->mSourceMesh;
if (!mVHACD->Compute((const double* const)decomp_mesh.mVertices.data(), static_cast<uint32_t>(decomp_mesh.mVertices.size()), (const uint32_t* const)decomp_mesh.mIndices.data(), static_cast<uint32_t>(decomp_mesh.mIndices.size()), mVHACDParameters))
{
return LLCD_INVALID_HULL_DATA;
}
uint32_t num_nulls = mVHACD->GetNConvexHulls();
if (num_nulls == 0)
{
return LLCD_INVALID_HULL_DATA;
}
for (uint32_t i = 0; num_nulls > i; ++i)
{
VHACD::IVHACD::ConvexHull ch;
if (!mVHACD->GetConvexHull(i, ch))
continue;
LLConvexMesh out_mesh;
out_mesh.setVertices(ch.m_points);
out_mesh.setIndices(ch.m_triangles);
mBoundDecomp->mDecomposedHulls.push_back(std::move(out_mesh));
}
mVHACD->Clean();
return LLCD_OK;
}
LLCDResult LLConvexDecompositionVHACD::buildSingleHull()
{
LL_INFOS() << "Building single hull mesh" << LL_ENDL;
if (!mBoundDecomp || mBoundDecomp->mSourceMesh.mVertices.empty())
{
return LLCD_NULL_PTR;
}
mBoundDecomp->mSingleHullMesh.clear();
VHACD::QuickHull quickhull;
uint32_t num_tris = quickhull.ComputeConvexHull(mBoundDecomp->mSourceMesh.mVertices, MAX_VERTICES_PER_HULL);
if (num_tris > 0)
{
mBoundDecomp->mSingleHullMesh.setVertices(quickhull.GetVertices());
mBoundDecomp->mSingleHullMesh.setIndices(quickhull.GetIndices());
return LLCD_OK;
}
return LLCD_INVALID_MESH_DATA;
}
int LLConvexDecompositionVHACD::getNumHullsFromStage(int stage)
{
if (!mBoundDecomp || stage != 0)
{
return 0;
}
return narrow(mBoundDecomp->mDecomposedHulls.size());
}
LLCDResult LLConvexDecompositionVHACD::getSingleHull( LLCDHull* hullOut )
{
memset( hullOut, 0, sizeof(LLCDHull) );
if (!mBoundDecomp)
{
return LLCD_NULL_PTR;
}
if (mBoundDecomp->mSingleHullMesh.vertices.empty())
{
return LLCD_INVALID_HULL_DATA;
}
mBoundDecomp->mSingleHullMesh.to(hullOut);
return LLCD_OK;
}
LLCDResult LLConvexDecompositionVHACD::getHullFromStage( int stage, int hull, LLCDHull* hullOut )
{
memset( hullOut, 0, sizeof(LLCDHull) );
if (!mBoundDecomp)
{
return LLCD_NULL_PTR;
}
if (stage != 0)
{
return LLCD_INVALID_STAGE;
}
if (mBoundDecomp->mDecomposedHulls.empty() || mBoundDecomp->mDecomposedHulls.size() <= hull)
{
return LLCD_REQUEST_OUT_OF_RANGE;
}
mBoundDecomp->mDecomposedHulls[hull].to(hullOut);
return LLCD_OK;
}
LLCDResult LLConvexDecompositionVHACD::getMeshFromStage( int stage, int hull, LLCDMeshData* meshDataOut )
{
memset( meshDataOut, 0, sizeof(LLCDMeshData));
if (!mBoundDecomp)
{
return LLCD_NULL_PTR;
}
if (stage != 0)
{
return LLCD_INVALID_STAGE;
}
if (mBoundDecomp->mDecomposedHulls.empty() || mBoundDecomp->mDecomposedHulls.size() <= hull)
{
return LLCD_REQUEST_OUT_OF_RANGE;
}
mBoundDecomp->mDecomposedHulls[hull].to(meshDataOut);
return LLCD_OK;
}
LLCDResult LLConvexDecompositionVHACD::getMeshFromHull( LLCDHull* hullIn, LLCDMeshData* meshOut )
{
memset(meshOut, 0, sizeof(LLCDMeshData));
LLVHACDMesh inMesh(hullIn);
VHACD::QuickHull quickhull;
uint32_t num_tris = quickhull.ComputeConvexHull(inMesh.mVertices, MAX_VERTICES_PER_HULL);
if (num_tris > 0)
{
mMeshFromHullData.setVertices(quickhull.GetVertices());
mMeshFromHullData.setIndices(quickhull.GetIndices());
mMeshFromHullData.to(meshOut);
return LLCD_OK;
}
return LLCD_INVALID_HULL_DATA;
}
LLCDResult LLConvexDecompositionVHACD::generateSingleHullMeshFromMesh(LLCDMeshData* meshIn, LLCDMeshData* meshOut)
{
memset( meshOut, 0, sizeof(LLCDMeshData) );
LLVHACDMesh inMesh(meshIn, true);
VHACD::QuickHull quickhull;
uint32_t num_tris = quickhull.ComputeConvexHull(inMesh.mVertices, MAX_VERTICES_PER_HULL);
if (num_tris > 0)
{
mSingleHullMeshFromMeshData.setVertices(quickhull.GetVertices());
mSingleHullMeshFromMeshData.setIndices(quickhull.GetIndices());
mSingleHullMeshFromMeshData.to(meshOut);
return LLCD_OK;
}
return LLCD_INVALID_MESH_DATA;
}
void LLConvexDecompositionVHACD::loadMeshData(const char* fileIn, LLCDMeshData** meshDataOut)
{
static LLCDMeshData meshData;
memset( &meshData, 0, sizeof(LLCDMeshData) );
*meshDataOut = &meshData;
}

View File

@ -0,0 +1,339 @@
/**
* @file llconvexdecompositionvhacd.h
* @author rye@alchemyviewer.org
* @brief A VHACD based implementation of LLConvexDecomposition
*
* $LicenseInfo:firstyear=2025&license=viewerlgpl$
* Second Life Viewer Source Code
* Copyright (C) 2025, Linden Research, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation;
* version 2.1 of the License only.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
#ifndef LL_CONVEX_DECOMP_UTIL_VHACD_H
#define LL_CONVEX_DECOMP_UTIL_VHACD_H
#include "llconvexdecomposition.h"
#include "llsingleton.h"
#include "llmath.h"
#include <vector>
#include "VHACD.h"
class LLDecompDataVHACD;
class LLConvexDecompositionVHACD : public LLSimpleton<LLConvexDecompositionVHACD>, public LLConvexDecomposition
{
class VHACDCallback : public VHACD::IVHACD::IUserCallback
{
public:
void Update(const double overallProgress, const double stageProgress, const char* const stage, const char* operation) override
{
std::string out_msg = llformat("Stage: %s Operation: %s", stage, operation);
if (mCurrentStage != stage && mCurrentOperation != operation)
{
mCurrentStage = stage;
mCurrentOperation = operation;
LL_INFOS("VHACD") << out_msg << LL_ENDL;
}
if(mCallbackFunc)
{
mCallbackFunc(out_msg.c_str(), ll_round(static_cast<F32>(stageProgress)), ll_round(static_cast<F32>(overallProgress)));
}
}
void setCallbackFunc(llcdCallbackFunc func)
{
mCallbackFunc = func;
}
private:
std::string mCurrentStage;
std::string mCurrentOperation;
llcdCallbackFunc mCallbackFunc = nullptr;
};
class VHACDLogger : public VHACD::IVHACD::IUserLogger
{
void Log(const char* const msg) override
{
LL_INFOS("VHACD") << msg << LL_ENDL;
}
};
public:
LLConvexDecompositionVHACD();
virtual ~LLConvexDecompositionVHACD();
static bool isFunctional();
static LLConvexDecomposition* getInstance();
static LLCDResult initSystem();
static LLCDResult initThread();
static LLCDResult quitThread();
static LLCDResult quitSystem();
void genDecomposition(int& decomp);
void deleteDecomposition(int decomp);
void bindDecomposition(int decomp);
// Sets *paramsOut to the address of the LLCDParam array and returns
// the length of the array
int getParameters(const LLCDParam** paramsOut)
{
*paramsOut = mDecompParams.data();
return narrow(mDecompParams.size());
}
int getStages(const LLCDStageData** stagesOut)
{
*stagesOut = mDecompStages.data();
return narrow(mDecompStages.size());
}
// Set a parameter by name. Returns false if out of bounds or unsupported parameter
LLCDResult setParam(const char* name, float val);
LLCDResult setParam(const char* name, int val);
LLCDResult setParam(const char* name, bool val);
LLCDResult setMeshData( const LLCDMeshData* data, bool vertex_based );
LLCDResult registerCallback(int stage, llcdCallbackFunc callback );
LLCDResult executeStage(int stage);
LLCDResult buildSingleHull();
int getNumHullsFromStage(int stage);
LLCDResult getHullFromStage( int stage, int hull, LLCDHull* hullOut );
LLCDResult getSingleHull( LLCDHull* hullOut ) ;
// TODO: Implement lock of some kind to disallow this call if data not yet ready
LLCDResult getMeshFromStage( int stage, int hull, LLCDMeshData* meshDataOut);
LLCDResult getMeshFromHull( LLCDHull* hullIn, LLCDMeshData* meshOut );
// For visualizing convex hull shapes in the viewer physics shape display
LLCDResult generateSingleHullMeshFromMesh( LLCDMeshData* meshIn, LLCDMeshData* meshOut);
/// Debug
void loadMeshData(const char* fileIn, LLCDMeshData** meshDataOut);
private:
std::vector<LLCDParam> mDecompParams;
std::array<LLCDStageData, 1> mDecompStages;
struct LLVHACDMesh
{
using vertex_type = VHACD::Vertex;
using index_type = VHACD::Triangle;
using vertex_array_type = std::vector<vertex_type>;
using index_array_type = std::vector<index_type>;
LLVHACDMesh() = default;
LLVHACDMesh(const LLCDHull* hullIn)
{
if (hullIn)
{
from(hullIn);
}
};
LLVHACDMesh(const LLCDMeshData* meshIn, bool vertex_based)
{
if (meshIn)
{
from(meshIn, vertex_based);
}
};
void clear()
{
mVertices.clear();
mIndices.clear();
}
void setVertices(const float* data, int num_vertices, int vertex_stride_bytes)
{
vertex_array_type vertices;
vertices.reserve(num_vertices);
const int stride = vertex_stride_bytes / sizeof(float);
for (int i = 0; i < num_vertices; ++i)
{
vertices.emplace_back(data[i * stride + 0],
data[i * stride + 1],
data[i * stride + 2]);
}
mVertices = std::move(vertices);
}
void setIndices(const void* data, int num_indices, int index_stride_bytes, LLCDMeshData::IndexType type)
{
index_array_type indices;
indices.reserve(num_indices);
if (type == LLCDMeshData::INT_16)
{
const U16* index_data = static_cast<const U16*>(data);
const int stride = index_stride_bytes / sizeof(U16);
for (int i = 0; i < num_indices; ++i)
{
indices.emplace_back(index_data[i * stride + 0],
index_data[i * stride + 1],
index_data[i * stride + 2]);
}
}
else
{
const U32* index_data = static_cast<const U32*>(data);
const int stride = index_stride_bytes / sizeof(U32);
for (int i = 0; i < num_indices; ++i)
{
indices.emplace_back(index_data[i * stride + 0],
index_data[i * stride + 1],
index_data[i * stride + 2]);
}
}
mIndices = std::move(indices);
}
LLCDResult from(const LLCDHull* hullIn)
{
clear();
if (!hullIn || !hullIn->mVertexBase || (hullIn->mNumVertices < 3) || (hullIn->mVertexStrideBytes != 12 && hullIn->mVertexStrideBytes != 16))
{
return LLCD_INVALID_HULL_DATA;
}
setVertices(hullIn->mVertexBase, hullIn->mNumVertices, hullIn->mVertexStrideBytes);
return LLCD_OK;
}
LLCDResult from(const LLCDMeshData* meshIn, bool vertex_based)
{
clear();
if (!meshIn || !meshIn->mVertexBase || (meshIn->mNumVertices < 3) || (meshIn->mVertexStrideBytes != 12 && meshIn->mVertexStrideBytes != 16))
{
return LLCD_INVALID_MESH_DATA;
}
if (!vertex_based && ((meshIn->mNumTriangles < 1) || !meshIn->mIndexBase))
{
return LLCD_INVALID_MESH_DATA;
}
setVertices(meshIn->mVertexBase, meshIn->mNumVertices, meshIn->mVertexStrideBytes);
if(!vertex_based)
{
setIndices(meshIn->mIndexBase, meshIn->mNumTriangles, meshIn->mIndexStrideBytes, meshIn->mIndexType);
}
return LLCD_OK;
}
vertex_array_type mVertices;
index_array_type mIndices;
};
struct LLConvexMesh
{
using vertex_type = glm::vec3;
using index_type = glm::u32vec3;
using vertex_array_type = std::vector<vertex_type>;
using index_array_type = std::vector<index_type>;
LLConvexMesh() = default;
void clear()
{
vertices.clear();
indices.clear();
}
void setVertices(const std::vector<VHACD::Vertex>& in_vertices)
{
vertices.clear();
vertices.reserve(in_vertices.size());
for (const auto& vertex : in_vertices)
{
vertices.emplace_back(narrow(vertex.mX), narrow(vertex.mY), narrow(vertex.mZ));
}
}
void setIndices(const std::vector<VHACD::Triangle>& in_indices)
{
indices.clear();
indices.reserve(in_indices.size());
for (const auto& triangle : in_indices)
{
indices.emplace_back(narrow(triangle.mI0), narrow(triangle.mI1), narrow(triangle.mI2));
}
}
void to(LLCDHull* meshOut) const
{
meshOut->mVertexBase = (float*)vertices.data();
meshOut->mVertexStrideBytes = sizeof(vertex_type);
meshOut->mNumVertices = (int)vertices.size();
}
void to(LLCDMeshData* meshOut) const
{
meshOut->mVertexBase = (float*)vertices.data();
meshOut->mVertexStrideBytes = sizeof(vertex_type);
meshOut->mNumVertices = (int)vertices.size();
meshOut->mIndexType = LLCDMeshData::INT_32;
meshOut->mIndexBase = indices.data();
meshOut->mIndexStrideBytes = sizeof(index_type);
meshOut->mNumTriangles = (int)indices.size();
}
vertex_array_type vertices;
index_array_type indices;
};
struct LLDecompData
{
LLVHACDMesh mSourceMesh;
LLConvexMesh mSingleHullMesh;
std::vector<LLConvexMesh> mDecomposedHulls;
};
std::unordered_map<int, LLDecompData> mDecompData;
LLDecompData* mBoundDecomp = nullptr;
VHACD::IVHACD* mVHACD = nullptr;
VHACDCallback mVHACDCallback;
VHACDLogger mVHACDLogger;
VHACD::IVHACD::Parameters mVHACDParameters;
LLConvexMesh mMeshFromHullData;
LLConvexMesh mSingleHullMeshFromMeshData;
};
#endif //LL_CONVEX_DECOMP_UTIL_VHACD_H

View File

@ -0,0 +1,83 @@
/**
* @file llpathinglib.cpp
* @author prep@lindenlab.com
* @brief LLPathingLib core creation methods
*
* $LicenseInfo:firstyear=2012&license=lgpl$
* Second Life Viewer Source Code
* Copyright (C) 2011, Linden Research, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation;
* version 2.1 of the License only.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
#include "linden_common.h"
#include "LLPathingLibStubImpl.h"
#include "llpathinglib.h"
//=============================================================================
/*static */bool LLPathingLib::s_isInitialized = false;
//=============================================================================
/*static*/bool LLPathingLib::isFunctional()
{
return false;
}
/*static*/LLPathingLib* LLPathingLib::getInstance()
{
if ( !s_isInitialized )
{
return NULL;
}
else
{
return LLPathingLibImpl::getInstance();
}
}
//=============================================================================
/*static */LLPathingLib::LLPLResult LLPathingLib::initSystem()
{
if ( LLPathingLibImpl::initSystem() == LLPL_OK )
{
s_isInitialized = true;
return LLPL_OK;
}
return LLPL_UNKOWN_ERROR;
}
//=============================================================================
/*static */LLPathingLib::LLPLResult LLPathingLib::quitSystem()
{
LLPLResult quitResult = LLPL_UNKOWN_ERROR;
if (s_isInitialized)
{
quitResult = LLPathingLibImpl::quitSystem();
s_isInitialized = false;
}
return quitResult;
}
//=============================================================================

View File

@ -0,0 +1,187 @@
/**
* @file llpathinglib.cpp
* @author prep@lindenlab.com
* @brief LLPathingLib interface definition
*
* $LicenseInfo:firstyear=2012&license=lgpl$
* Second Life Viewer Source Code
* Copyright (C) 2011, Linden Research, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation;
* version 2.1 of the License only.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
#ifndef LL_PATHING_LIBRARY
#define LL_PATHING_LIBRARY
#include "llpreprocessor.h"
#include "llsd.h"
#include "v3dmath.h"
#include "v4math.h"
#include "v4color.h"
#include "v4coloru.h"
#include "llphysicsextensions.h"
typedef int bool32;
#if defined(_WIN32) || defined(_WIN64)
#define LLCD_CALL __cdecl
#else
#define LLCD_CALL
#endif
class LLRender;
//=============================================================================
class LLPathingLib
{
public:
enum LLShapeType
{
LLST_WalkableObjects = 0,
LLST_ObstacleObjects,
LLST_MaterialPhantoms,
LLST_ExclusionPhantoms,
LLST_MaxShapeTypes = LLST_ExclusionPhantoms+1,
LLST_None = LLST_MaxShapeTypes+2,
LLST_SimpleBox = LLST_None+1,
LLST_SimpleCapsule = LLST_SimpleBox+1,
};
enum LLShapeTypeFlag
{
LLSTB_WalkableObjects = 0x1 << 1,
LLSTB_ObstacleObjects = 0x1 << 2,
LLSTB_MaterialPhantoms = 0x1 << 3,
LLSTB_ExclusionPhantoms = 0x1 << 4,
LLSTB_None = 0x1 << 5
};
enum LLPLPathBookEnd
{
LLPL_START = 0,
LLPL_END,
};
enum LLPLResult
{
LLPL_OK = 0,
LLPL_NOTSET,
LLPL_ERROR,
LLPL_NO_NAVMESH,
LLPL_UNKOWN_ERROR,
LLPL_NO_PATH,
LLPL_PATH_GENERATED_OK,
LLPL_NOT_IMPLEMENTED,
};
enum LLPLCharacterType
{
LLPL_CHARACTER_TYPE_A = 4,
LLPL_CHARACTER_TYPE_B = 3,
LLPL_CHARACTER_TYPE_C = 2,
LLPL_CHARACTER_TYPE_D = 1,
LLPL_CHARACTER_TYPE_NONE = 0
};
struct PathingPacket
{
PathingPacket() : mHasPointA(false), mHasPointB(false), mCharacterWidth(0.0f), mCharacterType(LLPL_CHARACTER_TYPE_NONE) {}
bool mHasPointA;
LLVector3 mStartPointA;
LLVector3 mEndPointA;
bool mHasPointB;
LLVector3 mStartPointB;
LLVector3 mEndPointB;
F32 mCharacterWidth;
LLPLCharacterType mCharacterType;
};
struct NavMeshColors
{
LLColor4U mWalkable;
LLColor4U mObstacle;
LLColor4U mMaterial;
LLColor4U mExclusion;
LLColor4U mConnectedEdge;
LLColor4U mBoundaryEdge;
LLColor4 mHeatColorBase;
LLColor4 mHeatColorMax;
LLColor4U mFaceColor;
LLColor4U mStarValid;
LLColor4U mStarInvalid;
LLColor4U mTestPath;
LLColor4U mWaterColor;
};
public:
//Ctor
LLPathingLib() {}
virtual ~LLPathingLib() {}
/// @returns false if this is the stub
static bool isFunctional();
// Obtain a pointer to the actual implementation
static LLPathingLib* getInstance();
static LLPathingLib::LLPLResult initSystem();
static LLPathingLib::LLPLResult quitSystem();
//Extract and store navmesh data from the llsd datablock sent down by the server
virtual LLPLResult extractNavMeshSrcFromLLSD( const LLSD::Binary& dataBlock, int dir ) = 0;
//Stitch any stored navmeshes together
virtual void processNavMeshData( ) = 0;
//Method used to generate and visualize a path on the viewers navmesh
virtual LLPLResult generatePath( const PathingPacket& pathingPacket ) = 0;
//Set the material type for the heatmap type
virtual void setNavMeshMaterialType( LLPLCharacterType materialType ) = 0;
//Set the various navmesh colors
virtual void setNavMeshColors( const NavMeshColors& color ) = 0;
//The entry method to rendering the client side navmesh
virtual void renderNavMesh() = 0;
//The entry method to rendering the client side navmesh edges
virtual void renderNavMeshEdges() = 0;
//The entry method to render the client navmesh shapes VBO
virtual void renderNavMeshShapesVBO( U32 shapeRenderFlags ) = 0;
//The entry method to render the clients designated path
virtual void renderPath() = 0;
//The entry method to render the capsule bookends for the clients designated path
virtual void renderPathBookend( LLRender& gl, LLPathingLib::LLPLPathBookEnd type ) = 0;
//Renders all of the generated simple shapes (using their default transforms)
virtual void renderSimpleShapes( LLRender& gl, F32 regionsWaterHeight ) = 0;
//Method called from second life to create a capsule from properties of a character
virtual void createPhysicsCapsuleRep( F32 length, F32 radius, BOOL horizontal, const LLUUID& id ) = 0;
//Removes any cached physics capsule using a list of cached uuids
virtual void cleanupPhysicsCapsuleRepResiduals() = 0;
//Renders a selected uuids physics rep
virtual void renderSimpleShapeCapsuleID( LLRender& gl, const LLUUID& id, const LLVector3& pos, const LLQuaternion& rot ) = 0;
//Method to delete any vbo's that are currently being managed by the pathing library
virtual void cleanupVBOManager( ) = 0;
//Method to cleanup any allocations within the implementation
virtual void cleanupResidual( ) = 0;
private:
static bool s_isInitialized;
};
#endif //LL_PATHING_LIBRARY

View File

@ -0,0 +1,78 @@
/**
* @file llphysicsextensions.cpp
* @author nyx@lindenlab.com
* @brief LLPhysicsExtensions core initialization methods
*
* $LicenseInfo:firstyear=2012&license=lgpl$
* Second Life Viewer Source Code
* Copyright (C) 2011, Linden Research, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation;
* version 2.1 of the License only.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
#include "linden_common.h"
#include "llphysicsextensions.h"
#include "LLPhysicsExtensionsStubImpl.h"
//disable the undefined symbol optimization
//#pragma warning (disable : 4221)
//=============================================================================
/*static */bool LLPhysicsExtensions::s_isInitialized = false;
/*static*/bool LLPhysicsExtensions::isFunctional()
{
return false;
}
//=============================================================================
/*static*/LLPhysicsExtensions* LLPhysicsExtensions::getInstance()
{
if ( !s_isInitialized )
{
return NULL;
}
else
{
return LLPhysicsExtensionsImpl::getInstance();
}
}
//=============================================================================
/*static */bool LLPhysicsExtensions::initSystem()
{
bool result = LLPhysicsExtensionsImpl::initSystem();
if ( result )
{
s_isInitialized = true;
}
return result;
}
//=============================================================================
/*static */bool LLPhysicsExtensions::quitSystem()
{
return LLPhysicsExtensionsImpl::quitSystem();
}
//=============================================================================

View File

@ -0,0 +1,59 @@
/**
* @file llphysicsextensions.h
* @author nyx@lindenlab.com
* @brief LLPhysicsExtensions core shared initialization
* routines
*
* $LicenseInfo:firstyear=2012&license=lgpl$
* Second Life Viewer Source Code
* Copyright (C) 2011, Linden Research, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation;
* version 2.1 of the License only.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
#ifndef LL_PHYSICS_EXTENSIONS
#define LL_PHYSICS_EXTENSIONS
#include "llpreprocessor.h"
#include "llsd.h"
#include "v3dmath.h"
#define LLPHYSICSEXTENSIONS_VERSION "1.0"
typedef int bool32;
class LLPhysicsExtensions
{
public:
// Obtain a pointer to the actual implementation
static LLPhysicsExtensions* getInstance();
/// @returns false if this is the stub
static bool isFunctional();
static bool initSystem();
static bool quitSystem();
private:
static bool s_isInitialized;
};
#endif //LL_PATHING_LIBRARY

View File

@ -982,14 +982,18 @@ void LLPluginProcessParent::poll(F64 timeout)
}
}
// Remove instances in the done state from the sInstances map.
mapInstances_t::iterator itClean = sInstances.begin();
while (itClean != sInstances.end())
if (sInstancesMutex)
{
if ((*itClean).second->isDone())
itClean = sInstances.erase(itClean);
else
++itClean;
// Remove instances in the done state from the sInstances map.
LLCoros::LockType lock(*sInstancesMutex);
mapInstances_t::iterator itClean = sInstances.begin();
while (itClean != sInstances.end())
{
if ((*itClean).second->isDone())
itClean = sInstances.erase(itClean);
else
++itClean;
}
}
}

View File

@ -7,21 +7,21 @@
* $LicenseInfo:firstyear=2010&license=viewerlgpl$
* Second Life Viewer Source Code
* Copyright (C) 2010, Linden Research, Inc.
*
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation;
* version 2.1 of the License only.
*
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
*
* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*
@ -38,51 +38,51 @@
void LLCocoaPlugin::setupCocoa()
{
static bool inited = false;
if(!inited)
{
createAutoReleasePool();
// The following prevents the Cocoa command line parser from trying to open 'unknown' arguements as documents.
// ie. running './secondlife -set Language fr' would cause a pop-up saying can't open document 'fr'
// when init'ing the Cocoa App window.
[[NSUserDefaults standardUserDefaults] setObject:@"NO" forKey:@"NSTreatUnknownArgumentsAsOpen"];
// This is a bit of voodoo taken from the Apple sample code "CarbonCocoa_PictureCursor":
// http://developer.apple.com/samplecode/CarbonCocoa_PictureCursor/index.html
// Needed for Carbon based applications which call into Cocoa
NSApplicationLoad();
static bool inited = false;
if(!inited)
{
createAutoReleasePool();
// The following prevents the Cocoa command line parser from trying to open 'unknown' arguements as documents.
// ie. running './secondlife -set Language fr' would cause a pop-up saying can't open document 'fr'
// when init'ing the Cocoa App window.
[[NSUserDefaults standardUserDefaults] setObject:@"NO" forKey:@"NSTreatUnknownArgumentsAsOpen"];
// This is a bit of voodoo taken from the Apple sample code "CarbonCocoa_PictureCursor":
// http://developer.apple.com/samplecode/CarbonCocoa_PictureCursor/index.html
// Needed for Carbon based applications which call into Cocoa
NSApplicationLoad();
// Must first call [[[NSWindow alloc] init] release] to get the NSWindow machinery set up so that NSCursor can use a window to cache the cursor image
[[[NSWindow alloc] init] release];
// Must first call [[[NSWindow alloc] init] release] to get the NSWindow machinery set up so that NSCursor can use a window to cache the cursor image
[[[NSWindow alloc] init] release];
mPluginWindow = [NSApp mainWindow];
deleteAutoReleasePool();
inited = true;
}
deleteAutoReleasePool();
inited = true;
}
}
static NSAutoreleasePool *sPool = NULL;
void LLCocoaPlugin::createAutoReleasePool()
{
if(!sPool)
{
sPool = [[NSAutoreleasePool alloc] init];
}
if(!sPool)
{
sPool = [[NSAutoreleasePool alloc] init];
}
}
void LLCocoaPlugin::deleteAutoReleasePool()
{
if(sPool)
{
[sPool release];
sPool = NULL;
}
if(sPool)
{
[sPool release];
sPool = NULL;
}
}
LLCocoaPlugin::LLCocoaPlugin():mHackState(0)
@ -110,12 +110,12 @@ void LLCocoaPlugin::setupGroup()
// {
// // Start out with a window layer that's way out in front (fixes the problem with the menubar not getting hidden on first switch to fullscreen youtube)
// SetWindowGroupName(layer_group, CFSTR("SLPlugin Layer"));
// SetWindowGroupLevel(layer_group, kCGOverlayWindowLevel);
// SetWindowGroupLevel(layer_group, kCGOverlayWindowLevel);
// }
}
void LLCocoaPlugin::updateWindows()
void LLCocoaPlugin::updateWindows()
{
// NSArray* window_list = [NSApp orderedWindows];
// NSWindow* current_window = [window_list objectAtIndex:0];
@ -123,38 +123,38 @@ void LLCocoaPlugin::updateWindows()
// bool this_is_front_process = false;
// bool parent_is_front_process = false;
//
//
//
// // Check for a change in this process's frontmost window.
// if ( current_window != mFrontWindow )
// {
// // and figure out whether this process or its parent are currently frontmost
// if ( current_window == parent_window ) parent_is_front_process = true;
// if ( current_window == mPluginWindow ) this_is_front_process = true;
//
//
// if (current_window != NULL && mFrontWindow == NULL)
// {
// // Opening the first window
//
//
// if(mHackState == 0)
// {
// // Next time through the event loop, lower the window group layer
// mHackState = 1;
// }
//
//
// if(parent_is_front_process)
// {
// // Bring this process's windows to the front.
// [mPluginWindow makeKeyAndOrderFront:NSApp];
// [mPluginWindow setOrderedIndex:0];
// }
//
//
// [NSApp activateIgnoringOtherApps:YES];
// }
//
//
// else if (( current_window == NULL) && (mFrontWindow != NULL))
// {
// // Closing the last window
//
//
// if(this_is_front_process)
// {
// // Try to bring this process's parent to the front
@ -171,7 +171,7 @@ void LLCocoaPlugin::updateWindows()
//// }
// mHackState = 2;
// }
//
//
// mFrontWindow = [window_list objectAtIndex:0];
// }
}

View File

@ -65,11 +65,20 @@ target_link_libraries(llprimitive
llxml
llcharacter
llrender
llphysicsextensions_impl
ll::colladadom
ll::glm
)
if (HAVOK OR HAVOK_TPV)
target_link_libraries(llprimitive
llphysicsextensions_impl
)
else()
target_link_libraries(llprimitive
llphysicsextensionsos
)
endif ()
#add unit tests
if (LL_TESTS)
INCLUDE(LLAddBuildTest)

View File

@ -923,3 +923,34 @@ void LLGLTFMaterial::updateTextureTracking()
// setTEGLTFMaterialOverride is responsible for tracking
// for material overrides editor will set it
}
void LLGLTFMaterial::convertTextureTransformToPBR(
F32 tex_scale_s,
F32 tex_scale_t,
F32 tex_offset_s,
F32 tex_offset_t,
F32 tex_rotation,
LLVector2& pbr_scale,
LLVector2& pbr_offset,
F32& pbr_rotation)
{
pbr_scale.set(tex_scale_s, tex_scale_t);
pbr_rotation = -(tex_rotation) / 2.f;
const F32 adjusted_offset_s = tex_offset_s;
const F32 adjusted_offset_t = -tex_offset_t;
F32 center_adjust_s = 0.5f * (1.0f - tex_scale_s);
F32 center_adjust_t = 0.5f * (1.0f - tex_scale_t);
if (pbr_rotation != 0.0f)
{
const F32 c = cosf(pbr_rotation);
const F32 s = sinf(pbr_rotation);
const F32 tmp_s = center_adjust_s * c - center_adjust_t * s;
const F32 tmp_t = center_adjust_s * s + center_adjust_t * c;
center_adjust_s = tmp_s;
center_adjust_t = tmp_t;
}
pbr_offset.set(adjusted_offset_s + center_adjust_s,
adjusted_offset_t + center_adjust_t);
}

View File

@ -214,6 +214,14 @@ public:
bool hasLocalTextures() { return !mTrackingIdToLocalTexture.empty(); }
virtual bool replaceLocalTexture(const LLUUID& tracking_id, const LLUUID &old_id, const LLUUID& new_id);
virtual void updateTextureTracking();
// Convert legacy TE transform values to PBR transform values.
static void convertTextureTransformToPBR(F32 tex_scale_s, F32 tex_scale_t,
F32 tex_offset_s, F32 tex_offset_t,
F32 tex_rotation,
LLVector2& pbr_scale,
LLVector2& pbr_offset,
F32& pbr_rotation);
protected:
static LLVector2 vec2FromJson(const std::map<std::string, tinygltf::Value>& object, const char* key, const LLVector2& default_value);
static F32 floatFromJson(const std::map<std::string, tinygltf::Value>& object, const char* key, const F32 default_value);

View File

@ -1296,10 +1296,10 @@ LLModel::weight_list& LLModel::getJointInfluences(const LLVector3& pos)
}
void LLModel::setConvexHullDecomposition(
const LLModel::convex_hull_decomposition& decomp)
const LLModel::convex_hull_decomposition& decomp, const std::vector<LLModel::PhysicsMesh>& decomp_mesh)
{
mPhysics.mHull = decomp;
mPhysics.mMesh.clear();
mPhysics.mMesh = decomp_mesh;
updateHullCenters();
}

View File

@ -305,7 +305,8 @@ public:
S32 mDecompID;
void setConvexHullDecomposition(
const convex_hull_decomposition& decomp);
const convex_hull_decomposition& decomp,
const std::vector<LLModel::PhysicsMesh>& decomp_mesh);
void updateHullCenters();
LLVector3 mCenterOfHullCenters;

View File

@ -86,7 +86,10 @@ void LLCubeMap::initGL()
#endif
mImages[i]->setTarget(mTargets[i], LLTexUnit::TT_CUBE_MAP);
mRawImages[i] = new LLImageRaw(RESOLUTION, RESOLUTION, 4);
mImages[i]->createGLTexture(0, mRawImages[i], texname);
if (!mImages[i]->createGLTexture(0, mRawImages[i], texname))
{
LL_WARNS() << "Failed to create GL texture for environment cubemap face " << i << LL_ENDL;
}
gGL.getTexUnit(0)->bindManual(LLTexUnit::TT_CUBE_MAP, texname);
mImages[i]->setAddressMode(LLTexUnit::TAM_CLAMP);
@ -203,7 +206,10 @@ void LLCubeMap::initEnvironmentMap(const std::vector<LLPointer<LLImageRaw> >& ra
mImages[i] = new LLImageGL(resolution, resolution, components, true);
mImages[i]->setTarget(mTargets[i], LLTexUnit::TT_CUBE_MAP);
mRawImages[i] = rawimages[i];
mImages[i]->createGLTexture(0, mRawImages[i], texname);
if (!mImages[i]->createGLTexture(0, mRawImages[i], texname))
{
LL_WARNS() << "Failed to create GL texture for environment cubemap face " << i << LL_ENDL;
}
gGL.getTexUnit(0)->bindManual(LLTexUnit::TT_CUBE_MAP, texname);
mImages[i]->setAddressMode(LLTexUnit::TAM_CLAMP);

View File

@ -57,6 +57,7 @@ S32 LLGLSLShader::sIndexedTextureChannels = 0;
U32 LLGLSLShader::sMaxGLTFMaterials = 0;
U32 LLGLSLShader::sMaxGLTFNodes = 0;
bool LLGLSLShader::sProfileEnabled = false;
bool LLGLSLShader::sCanProfile = true;
std::set<LLGLSLShader*> LLGLSLShader::sInstances;
LLGLSLShader::defines_map_t LLGLSLShader::sGlobalDefines;
U64 LLGLSLShader::sTotalTimeElapsed = 0;
@ -267,7 +268,7 @@ void LLGLSLShader::placeProfileQuery(bool for_runtime)
bool LLGLSLShader::readProfileQuery(bool for_runtime, bool force_read)
{
if (sProfileEnabled || for_runtime)
if ((sProfileEnabled || for_runtime) && sCanProfile)
{
if (!mProfilePending)
{

View File

@ -160,6 +160,7 @@ public:
static std::set<LLGLSLShader*> sInstances;
static bool sProfileEnabled;
static bool sCanProfile;
LLGLSLShader();
~LLGLSLShader();

View File

@ -1097,6 +1097,8 @@ void sub_image_lines(U32 target, S32 miplevel, S32 x_offset, S32 y_offset, S32 w
// full width texture, do 32 lines at a time
for (U32 y_pos = y_offset; y_pos < y_offset_end; y_pos += batch_size)
{
// If this keeps crashing, pass down data_size, looks like it is using
// imageraw->getData(); for data, but goes way over allocated size limit
glTexSubImage2D(target, miplevel, x_offset, y_pos, width, batch_size, pixformat, pixtype, src);
src += line_width * batch_size;
}
@ -1106,6 +1108,8 @@ void sub_image_lines(U32 target, S32 miplevel, S32 x_offset, S32 y_offset, S32 w
// partial width or strange height
for (U32 y_pos = y_offset; y_pos < y_offset_end; y_pos += 1)
{
// If this keeps crashing, pass down data_size, looks like it is using
// imageraw->getData(); for data, but goes way over allocated size limit
glTexSubImage2D(target, miplevel, x_offset, y_pos, width, 1, pixformat, pixtype, src);
src += line_width;
}
@ -1546,6 +1550,7 @@ bool LLImageGL::createGLTexture(S32 discard_level, const LLImageRaw* imageraw, S
llassert(mCurrentDiscardLevel >= 0);
discard_level = mCurrentDiscardLevel;
}
discard_level = llmin(discard_level, MAX_DISCARD_LEVEL);
// Actual image width/height = raw image width/height * 2^discard_level
S32 raw_w = imageraw->getWidth() ;
@ -1644,6 +1649,7 @@ bool LLImageGL::createGLTexture(S32 discard_level, const U8* data_in, bool data_
discard_level = mCurrentDiscardLevel;
}
discard_level = llclamp(discard_level, 0, (S32)mMaxDiscardLevel);
discard_level = llmin(discard_level, MAX_DISCARD_LEVEL);
if (main_thread // <--- always force creation of new_texname when not on main thread ...
&& !defer_copy // <--- ... or defer copy is set

View File

@ -1048,6 +1048,7 @@ void LLShaderMgr::clearShaderCache()
LL_INFOS("ShaderMgr") << "Removing shader cache at " << shader_cache << LL_ENDL;
const std::string mask = "*";
gDirUtilp->deleteFilesInDir(shader_cache, mask);
LLFile::rmdir(shader_cache);
mShaderBinaryCache.clear();
}

View File

@ -494,6 +494,7 @@ void LLAccordionCtrl::arrangeMultiple()
void LLAccordionCtrl::arrange()
{
LL_PROFILE_ZONE_SCOPED;
updateNoTabsHelpTextVisibility();
if (mAccordionTabs.empty())

View File

@ -248,10 +248,22 @@ void LLAccordionCtrlTab::LLAccordionCtrlTabHeader::draw()
void LLAccordionCtrlTab::LLAccordionCtrlTabHeader::reshape(S32 width, S32 height, bool called_from_parent /* = true */)
{
S32 header_height = mHeaderTextbox->getTextPixelHeight();
LLRect old_header_rect = mHeaderTextbox->getRect();
LLRect textboxRect(HEADER_TEXT_LEFT_OFFSET, (height + header_height) / 2, width, (height - header_height) / 2);
mHeaderTextbox->reshape(textboxRect.getWidth(), textboxRect.getHeight());
mHeaderTextbox->setRect(textboxRect);
if (old_header_rect.getHeight() != textboxRect.getHeight()
|| old_header_rect.mLeft != textboxRect.mLeft
|| old_header_rect.mTop != textboxRect.mTop
|| old_header_rect.getWidth() > textboxRect.getWidth() // reducing header's width
|| (old_header_rect.getWidth() < textboxRect.getWidth() && old_header_rect.getWidth() < mHeaderTextbox->getTextPixelWidth()))
{
// Expensive text reflow
// Update if position or height changes
// Update if width reduces
// But do not update if text already fits and width increases (arguably LLTextBox::reshape should be smarter, not Accordion)
mHeaderTextbox->reshape(textboxRect.getWidth(), textboxRect.getHeight());
mHeaderTextbox->setRect(textboxRect);
}
if (mHeaderTextbox->getTextPixelWidth() > mHeaderTextbox->getRect().getWidth())
{
@ -416,8 +428,11 @@ void LLAccordionCtrlTab::reshape(S32 width, S32 height, bool called_from_parent
LLRect headerRect;
headerRect.setLeftTopAndSize(0, height, width, HEADER_HEIGHT);
mHeader->setRect(headerRect);
mHeader->reshape(headerRect.getWidth(), headerRect.getHeight());
if (mHeader->getRect() != headerRect)
{
mHeader->setRect(headerRect);
mHeader->reshape(headerRect.getWidth(), headerRect.getHeight());
}
if (!mDisplayChildren)
return;
@ -464,7 +479,34 @@ void LLAccordionCtrlTab::onUpdateScrollToChild(const LLUICtrl *cntrl)
// Translate to parent coordinatess to check if we are in visible rectangle
rect.translate(getRect().mLeft, getRect().mBottom);
if (!getRect().contains(rect))
bool needs_to_scroll = false;
const LLRect &acc_rect = getRect();
if (!acc_rect.contains(rect))
{
if (acc_rect.mTop < rect.mBottom || acc_rect.mBottom > rect.mTop)
{
// Content fully not in view
needs_to_scroll = true;
}
else if (acc_rect.getHeight() >= rect.getHeight())
{
// Content can be displayed fully, but only partially in view
needs_to_scroll = true;
}
else if (acc_rect.mTop <= rect.mTop || acc_rect.mBottom >= rect.mBottom)
{
// Intersects, but too big to be displayed fully
S32 covered_height = acc_rect.mTop > rect.mTop ? rect.mTop - acc_rect.mBottom : acc_rect.mTop - rect.mBottom;
constexpr F32 covered_ratio = 0.7f;
if (covered_height < covered_ratio * acc_rect.getHeight())
{
// Try to show bigger portion of the content
needs_to_scroll = true;
}
}
// else too big and in the middle of the view as is
}
if (needs_to_scroll)
{
// for accordition's scroll, height is in pixels
// Back to local coords and calculate position for scroller
@ -932,7 +974,7 @@ void LLAccordionCtrlTab::adjustContainerPanel(const LLRect& child_rect)
show_hide_scrollbar(child_rect);
updateLayout(child_rect);
}
else
else if (mContainerPanel->getRect() != child_rect)
{
mContainerPanel->reshape(child_rect.getWidth(), child_rect.getHeight());
mContainerPanel->setRect(child_rect);

View File

@ -390,14 +390,17 @@ void LLEmojiDictionary::loadEmojis()
continue;
}
std::string category;
std::list<std::string> categories = loadCategories(sd);
if (categories.empty())
{
LL_WARNS() << "Skipping invalid emoji descriptor (no categories)" << LL_ENDL;
continue;
// Should already have a localization for "other symbols"
category = "other symbols";
}
else
{
category = categories.front();
}
std::string category = categories.front();
if (std::find(mSkipCategories.begin(), mSkipCategories.end(), category) != mSkipCategories.end())
{

View File

@ -117,7 +117,17 @@ void LLEmojiHelper::showHelper(LLUICtrl* hostctrl_p, S32 local_x, S32 local_y, c
S32 top = floater_y - HELPER_FLOATER_OFFSET_Y + rect.getHeight();
rect.setLeftTopAndSize(left, top, rect.getWidth(), rect.getHeight());
pHelperFloater->setRect(rect);
// Hack: Trying to open floater, search for a match,
// and hide floater immediately if no match found,
// instead of checking prior to opening
//
// Supress sounds in case floater won't be shown.
// Todo: add some kind of shouldShow(short_code)
U8 sound_flags = pHelperFloater->getSoundFlags();
pHelperFloater->setSoundFlags(LLView::SILENT);
pHelperFloater->openFloater(LLSD().with("hint", short_code));
pHelperFloater->setSoundFlags(sound_flags);
}
void LLEmojiHelper::hideHelper(const LLUICtrl* ctrl_p, bool strict)

View File

@ -2274,7 +2274,7 @@ void LLFloater::drawConeToOwner(F32 &context_cone_opacity,
LLRect local_rect = getLocalRect();
gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE);
LLGLEnable(GL_CULL_FACE);
LLGLEnable cull_face(GL_CULL_FACE);
gGL.begin(LLRender::TRIANGLE_STRIP);
{
gGL.color4f(0.f, 0.f, 0.f, contex_cone_in_alpha * context_cone_opacity);

View File

@ -1510,6 +1510,7 @@ bool LLFolderView::handleRightMouseDown( S32 x, S32 y, MASK mask )
&& ( count > 0 && (hasVisibleChildren()) ))) && // show menu only if selected items are visible
!hide_folder_menu)
{
LL_INFOS("Inventory") << "Opening inventory menu from path: " << getPathname() << LL_ENDL;
if (mCallbackRegistrar)
{
mCallbackRegistrar->pushScope();

View File

@ -1884,10 +1884,18 @@ void LLFolderViewFolder::updateHasFavorites(bool new_childs_value)
void LLFolderViewFolder::onIdleUpdateFavorites(void* data)
{
LLFolderViewFolder* self = reinterpret_cast<LLFolderViewFolder*>(data);
if (self->mFavoritesDirtyFlags == FAVORITE_CLEANUP)
{
// parent or child already processed the update, clean the callback
self->mFavoritesDirtyFlags = 0;
gIdleCallbacks.deleteFunction(&LLFolderViewFolder::onIdleUpdateFavorites, data);
return;
}
if (self->mFavoritesDirtyFlags == 0)
{
// already processed either on previous run or by a different callback
gIdleCallbacks.deleteFunction(&LLFolderViewFolder::onIdleUpdateFavorites, self);
llassert(false); // should not happen, everything that sets to 0 should clean callback
gIdleCallbacks.deleteFunction(&LLFolderViewFolder::onIdleUpdateFavorites, data);
return;
}
@ -1915,7 +1923,7 @@ void LLFolderViewFolder::onIdleUpdateFavorites(void* data)
// Parent will remove onIdleUpdateFavorites later, don't remove now,
// We are inside gIdleCallbacks. Removing 'self' callback is safe,
// but removing 'parent' can invalidate following iterator
parent->mFavoritesDirtyFlags = 0;
parent->mFavoritesDirtyFlags = FAVORITE_CLEANUP;
}
parent = parent->getParentFolder();
}
@ -1981,7 +1989,7 @@ void LLFolderViewFolder::onIdleUpdateFavorites(void* data)
// Parent will remove onIdleUpdateFavorites later, don't remove now,
// We are inside gIdleCallbacks. Removing 'self' callback is safe,
// but removing 'parent' can invalidate following iterator
parent->mFavoritesDirtyFlags = 0;
parent->mFavoritesDirtyFlags = FAVORITE_CLEANUP;
}
parent = parent->getParentFolder();
}
@ -1992,7 +2000,7 @@ void LLFolderViewFolder::onIdleUpdateFavorites(void* data)
// Parent will remove onIdleUpdateFavorites later, don't remove now.
// We are inside gIdleCallbacks. Removing 'self' callback is safe,
// but removing 'parent' can invalidate following iterator
parent->mFavoritesDirtyFlags = 0;
parent->mFavoritesDirtyFlags = FAVORITE_CLEANUP;
}
parent = parent->getParentFolder();
}
@ -2106,10 +2114,14 @@ void LLFolderViewFolder::setOpen(bool openitem)
{
// navigateToFolder can destroy this view
// delay it in case setOpen was called from click or key processing
doOnIdleOneTime([this]()
{
getViewModelItem()->navigateToFolder();
});
LLPointer<LLFolderViewModelItem> view_model_item = mViewModelItem;
doOnIdleOneTime([view_model_item]()
{
if (view_model_item.notNull())
{
view_model_item.get()->navigateToFolder();
}
});
}
else
{

View File

@ -421,6 +421,7 @@ private:
constexpr static S32 FAVORITE_ADDED = 1;
constexpr static S32 FAVORITE_REMOVED = 2;
constexpr static S32 FAVORITE_CLEANUP = 4;
S32 mFavoritesDirtyFlags { 0 };
public:

View File

@ -185,6 +185,7 @@ LLTextBase::LLTextBase(const LLTextBase::Params &p)
mURLClickSignal(NULL),
mIsFriendSignal(NULL),
mIsObjectBlockedSignal(NULL),
mIsObjectReachableSignal(NULL),
mMaxTextByteLength( p.max_text_length ),
mFont(p.font),
mFontShadow(p.font_shadow),
@ -290,6 +291,7 @@ LLTextBase::~LLTextBase()
delete mURLClickSignal;
delete mIsFriendSignal;
delete mIsObjectBlockedSignal;
delete mIsObjectReachableSignal;
}
void LLTextBase::initFromParams(const LLTextBase::Params& p)
@ -1036,8 +1038,37 @@ S32 LLTextBase::insertStringNoUndo(S32 pos, const LLWString &wstr, LLTextBase::s
{
LLStyleSP emoji_style;
LLEmojiDictionary* ed = LLEmojiDictionary::instanceExists() ? LLEmojiDictionary::getInstance() : NULL;
LLTextSegment* segmentp = nullptr;
segment_vec_t::iterator seg_iter;
if (segments && segments->size() > 0)
{
seg_iter = segments->begin();
segmentp = *seg_iter;
}
for (S32 text_kitty = 0, text_len = static_cast<S32>(wstr.size()); text_kitty < text_len; text_kitty++)
{
if (segmentp)
{
if (segmentp->getEnd() <= pos + text_kitty)
{
seg_iter++;
if (seg_iter != segments->end())
{
segmentp = *seg_iter;
}
else
{
segmentp = nullptr;
}
}
if (segmentp && !segmentp->getPermitsEmoji())
{
// Some segments, like LLInlineViewSegment do not permit splitting
// and should not be interrupted by emoji segments
continue;
}
}
llwchar code = wstr[text_kitty];
bool isEmoji = ed ? ed->isEmoji(code) : LLStringOps::isEmoji(code);
if (isEmoji)
@ -1446,6 +1477,8 @@ void LLTextBase::reshape(S32 width, S32 height, bool called_from_parent)
// up-to-date mVisibleTextRect
updateRects();
// Todo: This might be wrong. updateRects already sets needsReflow conditionaly.
// Reflow is expensive and doing it at any twith can be too much.
needsReflow();
}
}
@ -2281,6 +2314,15 @@ void LLTextBase::createUrlContextMenu(S32 x, S32 y, const std::string &in_url)
unblockButton->setVisible(is_blocked);
}
}
if (mIsObjectReachableSignal)
{
bool is_reachable = *(*mIsObjectReachableSignal)(LLUUID(LLUrlAction::getObjectId(url)));
if (LLView* zoom_btn = menu->getChild<LLView>("zoom_in"))
{
zoom_btn->setEnabled(is_reachable);
}
}
menu->show(x, y);
LLMenuGL::showPopup(this, menu, x, y);
}
@ -3387,6 +3429,15 @@ boost::signals2::connection LLTextBase::setIsObjectBlockedCallback(const is_bloc
return mIsObjectBlockedSignal->connect(cb);
}
boost::signals2::connection LLTextBase::setIsObjectReachableCallback(const is_obj_reachable_signal_t::slot_type& cb)
{
if (!mIsObjectReachableSignal)
{
mIsObjectReachableSignal = new is_obj_reachable_signal_t();
}
return mIsObjectReachableSignal->connect(cb);
}
//
// LLTextSegment
//
@ -3426,6 +3477,7 @@ S32 LLTextSegment::getNumChars(S32 num_pixels, S32 segment_offset, S32 line_offs
void LLTextSegment::updateLayout(const LLTextBase& editor) {}
F32 LLTextSegment::draw(S32 start, S32 end, S32 selection_start, S32 selection_end, const LLRectf& draw_rect) { return draw_rect.mLeft; }
bool LLTextSegment::canEdit() const { return false; }
bool LLTextSegment::getPermitsEmoji() const { return true; }
void LLTextSegment::unlinkFromDocument(LLTextBase*) {}
void LLTextSegment::linkToDocument(LLTextBase*) {}
const LLUIColor& LLTextSegment::getColor() const { static const LLUIColor white = LLUIColorTable::instance().getColor("White", LLColor4::white); return white; }

View File

@ -87,6 +87,7 @@ public:
virtual void updateLayout(const class LLTextBase& editor);
virtual F32 draw(S32 start, S32 end, S32 selection_start, S32 selection_end, const LLRectf& draw_rect);
virtual bool canEdit() const;
virtual bool getPermitsEmoji() const;
virtual void unlinkFromDocument(class LLTextBase* editor);
virtual void linkToDocument(class LLTextBase* editor);
@ -255,6 +256,7 @@ public:
/*virtual*/ void updateLayout(const class LLTextBase& editor);
/*virtual*/ F32 draw(S32 start, S32 end, S32 selection_start, S32 selection_end, const LLRectf& draw_rect);
/*virtual*/ bool canEdit() const { return false; }
/*virtual*/ bool getPermitsEmoji() const { return false; }
/*virtual*/ void unlinkFromDocument(class LLTextBase* editor);
/*virtual*/ void linkToDocument(class LLTextBase* editor);
@ -325,6 +327,7 @@ public:
typedef boost::signals2::signal<bool (const LLUUID& user_id)> is_friend_signal_t;
typedef boost::signals2::signal<bool (const LLUUID& blocked_id, const std::string from)> is_blocked_signal_t;
typedef boost::signals2::signal<bool (const LLUUID& obj_id)> is_obj_reachable_signal_t;
struct LineSpacingParams : public LLInitParam::ChoiceBlock<LineSpacingParams>
{
@ -535,6 +538,7 @@ public:
boost::signals2::connection setURLClickedCallback(const commit_signal_t::slot_type& cb);
boost::signals2::connection setIsFriendCallback(const is_friend_signal_t::slot_type& cb);
boost::signals2::connection setIsObjectBlockedCallback(const is_blocked_signal_t::slot_type& cb);
boost::signals2::connection setIsObjectReachableCallback(const is_obj_reachable_signal_t::slot_type& cb);
void setWordWrap(bool wrap);
LLScrollContainer* getScrollContainer() const { return mScroller; }
@ -783,6 +787,7 @@ protected:
// Used to check if user with given ID is avatar's friend
is_friend_signal_t* mIsFriendSignal;
is_blocked_signal_t* mIsObjectBlockedSignal;
is_obj_reachable_signal_t* mIsObjectReachableSignal;
LLUIString mLabel; // text label that is visible when no user text provided
};

View File

@ -1054,7 +1054,7 @@ bool LLToolBar::handleDragAndDrop(S32 x, S32 y, MASK mask, bool drop,
// if drop is set, it's time to call the callback to get the operation done
if (handled && drop)
{
handled = mHandleDropCallback(cargo_data, x, y, this);
handled = mHandleDropCallback(cargo_data, cargo_type, x, y, this);
}
// We accept only single tool drop on toolbars

View File

@ -41,7 +41,7 @@ class LLIconCtrl;
typedef boost::function<void (S32 x, S32 y, LLToolBarButton* button)> tool_startdrag_callback_t;
typedef boost::function<bool (S32 x, S32 y, const LLUUID& uuid, LLAssetType::EType type)> tool_handledrag_callback_t;
typedef boost::function<bool (void* data, S32 x, S32 y, LLToolBar* toolbar)> tool_handledrop_callback_t;
typedef boost::function<bool (void* data, EDragAndDropType cargo_type, S32 x, S32 y, LLToolBar* toolbar)> tool_handledrop_callback_t;
class LLToolBarButton : public LLButton
{

View File

@ -34,8 +34,6 @@ LLViewerEventRecorder::LLViewerEventRecorder() {
logEvents = false;
// Remove any previous event log file
std::string old_log_ui_events_to_llsd_file = gDirUtilp->getExpandedFilename(LL_PATH_LOGS, "SecondLife_Events_log.old");
LLFile::remove(old_log_ui_events_to_llsd_file, ENOENT);
mLogFilename = gDirUtilp->getExpandedFilename(LL_PATH_LOGS, "SecondLife_Events_log.llsd");
LLFile::rename(mLogFilename, old_log_ui_events_to_llsd_file, ENOENT);

View File

@ -374,8 +374,6 @@ void LLWebRTCImpl::terminate()
mSignalingThread->BlockingCall([this]() { mPeerConnectionFactory = nullptr; });
mPeerConnections.clear();
mWorkerThread->BlockingCall(
[this]()
{
@ -386,6 +384,14 @@ void LLWebRTCImpl::terminate()
mDeviceModule = nullptr;
mTaskQueueFactory = nullptr;
});
// In case peer connections still somehow have jobs in workers,
// only clear connections up after clearing workers.
mNetworkThread = nullptr;
mWorkerThread = nullptr;
mSignalingThread = nullptr;
mPeerConnections.clear();
webrtc::LogMessage::RemoveLogToStream(mLogSink);
}
@ -566,14 +572,20 @@ void LLWebRTCImpl::workerDeployDevices()
void LLWebRTCImpl::setCaptureDevice(const std::string &id)
{
mRecordingDevice = id;
deployDevices();
if (mRecordingDevice != id)
{
mRecordingDevice = id;
deployDevices();
}
}
void LLWebRTCImpl::setRenderDevice(const std::string &id)
{
mPlayoutDevice = id;
deployDevices();
if (mPlayoutDevice != id)
{
mPlayoutDevice = id;
deployDevices();
}
}
// updateDevices needs to happen on the worker thread.
@ -635,6 +647,13 @@ void LLWebRTCImpl::OnDevicesUpdated()
void LLWebRTCImpl::setTuningMode(bool enable)
{
mTuningMode = enable;
if (!mTuningMode
&& !mMute
&& mPeerCustomProcessor
&& mPeerCustomProcessor->getGain() != mGain)
{
mPeerCustomProcessor->setGain(mGain);
}
mWorkerThread->PostTask(
[this]
{
@ -766,6 +785,7 @@ void LLWebRTCImpl::freePeerConnection(LLWebRTCPeerConnectionInterface* peer_conn
std::find(mPeerConnections.begin(), mPeerConnections.end(), peer_connection);
if (it != mPeerConnections.end())
{
// Todo: make sure conection had no jobs in workers
mPeerConnections.erase(it);
if (mPeerConnections.empty())
{
@ -785,7 +805,8 @@ LLWebRTCPeerConnectionImpl::LLWebRTCPeerConnectionImpl() :
mWebRTCImpl(nullptr),
mPeerConnection(nullptr),
mMute(MUTE_INITIAL),
mAnswerReceived(false)
mAnswerReceived(false),
mPendingJobs(0)
{
}
@ -793,6 +814,10 @@ LLWebRTCPeerConnectionImpl::~LLWebRTCPeerConnectionImpl()
{
mSignalingObserverList.clear();
mDataObserverList.clear();
if (mPendingJobs > 0)
{
RTC_LOG(LS_ERROR) << __FUNCTION__ << "Destroying a connection that has " << std::to_string(mPendingJobs) << " unfinished jobs that might cause workers to crash";
}
}
//
@ -804,8 +829,10 @@ void LLWebRTCPeerConnectionImpl::init(LLWebRTCImpl * webrtc_impl)
mWebRTCImpl = webrtc_impl;
mPeerConnectionFactory = mWebRTCImpl->getPeerConnectionFactory();
}
void LLWebRTCPeerConnectionImpl::terminate()
{
mPendingJobs++;
mWebRTCImpl->PostSignalingTask(
[this]()
{
@ -848,7 +875,9 @@ void LLWebRTCPeerConnectionImpl::terminate()
observer->OnPeerConnectionClosed();
}
}
mPendingJobs--;
});
mPeerConnectionFactory.release();
}
void LLWebRTCPeerConnectionImpl::setSignalingObserver(LLWebRTCSignalingObserver *observer) { mSignalingObserverList.emplace_back(observer); }
@ -869,6 +898,7 @@ bool LLWebRTCPeerConnectionImpl::initializeConnection(const LLWebRTCPeerConnecti
RTC_DCHECK(!mPeerConnection);
mAnswerReceived = false;
mPendingJobs++;
mWebRTCImpl->PostSignalingTask(
[this,options]()
{
@ -890,6 +920,13 @@ bool LLWebRTCPeerConnectionImpl::initializeConnection(const LLWebRTCPeerConnecti
config.set_max_port(60100);
webrtc::PeerConnectionDependencies pc_dependencies(this);
if (mPeerConnectionFactory == nullptr)
{
RTC_LOG(LS_ERROR) << __FUNCTION__ << "Error creating peer connection, factory doesn't exist";
// Too early?
mPendingJobs--;
return;
}
auto error_or_peer_connection = mPeerConnectionFactory->CreatePeerConnectionOrError(config, std::move(pc_dependencies));
if (error_or_peer_connection.ok())
{
@ -902,6 +939,7 @@ bool LLWebRTCPeerConnectionImpl::initializeConnection(const LLWebRTCPeerConnecti
{
observer->OnRenegotiationNeeded();
}
mPendingJobs--;
return;
}
@ -964,6 +1002,7 @@ bool LLWebRTCPeerConnectionImpl::initializeConnection(const LLWebRTCPeerConnecti
webrtc::PeerConnectionInterface::RTCOfferAnswerOptions offerOptions;
mPeerConnection->CreateOffer(this, offerOptions);
mPendingJobs--;
});
return true;
@ -1006,6 +1045,7 @@ void LLWebRTCPeerConnectionImpl::AnswerAvailable(const std::string &sdp)
{
RTC_LOG(LS_INFO) << __FUNCTION__ << " Remote SDP: " << sdp;
mPendingJobs++;
mWebRTCImpl->PostSignalingTask(
[this, sdp]()
{
@ -1015,6 +1055,7 @@ void LLWebRTCPeerConnectionImpl::AnswerAvailable(const std::string &sdp)
mPeerConnection->SetRemoteDescription(webrtc::CreateSessionDescription(webrtc::SdpType::kAnswer, sdp),
webrtc::scoped_refptr<webrtc::SetRemoteDescriptionObserverInterface>(this));
}
mPendingJobs--;
});
}
@ -1037,6 +1078,7 @@ void LLWebRTCPeerConnectionImpl::setMute(bool mute)
mMute = new_state;
mPendingJobs++;
mWebRTCImpl->PostSignalingTask(
[this, force_reset, enable]()
{
@ -1060,6 +1102,7 @@ void LLWebRTCPeerConnectionImpl::setMute(bool mute)
track->set_enabled(enable);
}
}
mPendingJobs--;
}
});
}
@ -1081,6 +1124,7 @@ void LLWebRTCPeerConnectionImpl::resetMute()
void LLWebRTCPeerConnectionImpl::setReceiveVolume(float volume)
{
mPendingJobs++;
mWebRTCImpl->PostSignalingTask(
[this, volume]()
{
@ -1099,11 +1143,13 @@ void LLWebRTCPeerConnectionImpl::setReceiveVolume(float volume)
}
}
}
mPendingJobs--;
});
}
void LLWebRTCPeerConnectionImpl::setSendVolume(float volume)
{
mPendingJobs++;
mWebRTCImpl->PostSignalingTask(
[this, volume]()
{
@ -1114,6 +1160,7 @@ void LLWebRTCPeerConnectionImpl::setSendVolume(float volume)
track->GetSource()->SetVolume(volume*5.0);
}
}
mPendingJobs--;
});
}
@ -1190,11 +1237,13 @@ void LLWebRTCPeerConnectionImpl::OnConnectionChange(webrtc::PeerConnectionInterf
{
case webrtc::PeerConnectionInterface::PeerConnectionState::kConnected:
{
mPendingJobs++;
mWebRTCImpl->PostWorkerTask([this]() {
for (auto &observer : mSignalingObserverList)
{
observer->OnAudioEstablished(this);
}
mPendingJobs--;
});
break;
}
@ -1452,11 +1501,13 @@ void LLWebRTCPeerConnectionImpl::sendData(const std::string& data, bool binary)
{
webrtc::CopyOnWriteBuffer cowBuffer(data.data(), data.length());
webrtc::DataBuffer buffer(cowBuffer, binary);
mPendingJobs++;
mWebRTCImpl->PostNetworkTask([this, buffer]() {
if (mDataChannel)
{
mDataChannel->Send(buffer);
}
mPendingJobs--;
});
}
}

View File

@ -422,6 +422,9 @@ class LLWebRTCImpl : public LLWebRTCDeviceInterface, public webrtc::AudioDeviceO
~LLWebRTCImpl()
{
delete mLogSink;
// Explicit cleanup for the sake of debugging and crash stacks
mPeerCustomProcessor = nullptr;
}
void init();
@ -669,6 +672,8 @@ class LLWebRTCPeerConnectionImpl : public LLWebRTCPeerConnectionInterface,
// data
std::vector<LLWebRTCDataObserver *> mDataObserverList;
webrtc::scoped_refptr<webrtc::DataChannelInterface> mDataChannel;
std::atomic<int> mPendingJobs;
};
}

View File

@ -114,7 +114,7 @@ attributedStringInfo getSegments(NSAttributedString *str)
- (unsigned long)getVramSize
{
CGLRendererInfoObj info = 0;
GLint vram_megabytes = 0;
GLint vram_megabytes = 0;
int num_renderers = 0;
CGLError the_err = CGLQueryRendererInfo (CGDisplayIDToOpenGLDisplayMask(kCGDirectMainDisplay), &info, &num_renderers);
if(0 == the_err)
@ -132,29 +132,29 @@ attributedStringInfo getSegments(NSAttributedString *str)
vram_megabytes = 256;
}
return (unsigned long)vram_megabytes; // return value is in megabytes.
return (unsigned long)vram_megabytes; // return value is in megabytes.
}
- (void)viewDidMoveToWindow
{
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(windowResized:) name:NSWindowDidResizeNotification
object:[self window]];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(windowResized:) name:NSWindowDidResizeNotification
object:[self window]];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(windowWillMiniaturize:) name:NSWindowWillMiniaturizeNotification
object:[self window]];
selector:@selector(windowWillMiniaturize:) name:NSWindowWillMiniaturizeNotification
object:[self window]];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(windowDidDeminiaturize:) name:NSWindowDidDeminiaturizeNotification
object:[self window]];
selector:@selector(windowDidDeminiaturize:) name:NSWindowDidDeminiaturizeNotification
object:[self window]];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(windowDidBecomeKey:) name:NSWindowDidBecomeKeyNotification
object:[self window]];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(windowDidChangeScreen:) name:NSWindowDidChangeScreenNotification
object:[self window]];
selector:@selector(windowDidBecomeKey:) name:NSWindowDidBecomeKeyNotification
object:[self window]];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(windowDidChangeScreen:) name:NSWindowDidChangeScreenNotification
object:[self window]];
NSRect wnd_rect = [[self window] frame];
@ -188,28 +188,28 @@ attributedStringInfo getSegments(NSAttributedString *str)
-(void)windowDidChangeScreen:(NSNotification *)notification;
{
callWindowDidChangeScreen();
callWindowDidChangeScreen();
}
- (void)dealloc
{
[[NSNotificationCenter defaultCenter] removeObserver:self];
[super dealloc];
[[NSNotificationCenter defaultCenter] removeObserver:self];
[super dealloc];
}
- (id) init
{
return [self initWithFrame:[self bounds] withSamples:2 andVsync:TRUE];
return [self initWithFrame:[self bounds] withSamples:2 andVsync:TRUE];
}
- (id) initWithSamples:(NSUInteger)samples
{
return [self initWithFrame:[self bounds] withSamples:samples andVsync:TRUE];
return [self initWithFrame:[self bounds] withSamples:samples andVsync:TRUE];
}
- (id) initWithSamples:(NSUInteger)samples andVsync:(BOOL)vsync
{
return [self initWithFrame:[self bounds] withSamples:samples andVsync:vsync];
return [self initWithFrame:[self bounds] withSamples:samples andVsync:vsync];
}
#if LL_DARWIN
@ -221,90 +221,90 @@ attributedStringInfo getSegments(NSAttributedString *str)
- (id) initWithFrame:(NSRect)frame withSamples:(NSUInteger)samples andVsync:(BOOL)vsync
{
[self registerForDraggedTypes:[NSArray arrayWithObject:NSPasteboardTypeURL]];
[self initWithFrame:frame];
[self initWithFrame:frame];
// Initialize with a default "safe" pixel format that will work with versions dating back to OS X 10.6.
// Any specialized pixel formats, i.e. a core profile pixel format, should be initialized through rebuildContextWithFormat.
// 10.7 and 10.8 don't really care if we're defining a profile or not. If we don't explicitly request a core or legacy profile, it'll always assume a legacy profile (for compatibility reasons).
NSOpenGLPixelFormatAttribute attrs[] = {
// Initialize with a default "safe" pixel format that will work with versions dating back to OS X 10.6.
// Any specialized pixel formats, i.e. a core profile pixel format, should be initialized through rebuildContextWithFormat.
// 10.7 and 10.8 don't really care if we're defining a profile or not. If we don't explicitly request a core or legacy profile, it'll always assume a legacy profile (for compatibility reasons).
NSOpenGLPixelFormatAttribute attrs[] = {
NSOpenGLPFANoRecovery,
NSOpenGLPFADoubleBuffer,
NSOpenGLPFAClosestPolicy,
NSOpenGLPFAAccelerated,
NSOpenGLPFASampleBuffers, 0,
NSOpenGLPFASamples, 0,
NSOpenGLPFAStencilSize, 8,
NSOpenGLPFADepthSize, 24,
NSOpenGLPFAAlphaSize, 8,
NSOpenGLPFAColorSize, 24,
NSOpenGLPFAOpenGLProfile, NSOpenGLProfileVersion4_1Core,
0
NSOpenGLPFADoubleBuffer,
NSOpenGLPFAClosestPolicy,
NSOpenGLPFAAccelerated,
NSOpenGLPFASampleBuffers, 0,
NSOpenGLPFASamples, 0,
NSOpenGLPFAStencilSize, 8,
NSOpenGLPFADepthSize, 24,
NSOpenGLPFAAlphaSize, 8,
NSOpenGLPFAColorSize, 24,
NSOpenGLPFAOpenGLProfile, NSOpenGLProfileVersion4_1Core,
0
};
NSOpenGLPixelFormat *pixelFormat = [[[NSOpenGLPixelFormat alloc] initWithAttributes:attrs] autorelease];
NSOpenGLPixelFormat *pixelFormat = [[[NSOpenGLPixelFormat alloc] initWithAttributes:attrs] autorelease];
if (pixelFormat == nil)
{
NSLog(@"Failed to create pixel format!", nil);
return nil;
}
if (pixelFormat == nil)
{
NSLog(@"Failed to create pixel format!", nil);
return nil;
}
NSOpenGLContext *glContext = [[NSOpenGLContext alloc] initWithFormat:pixelFormat shareContext:nil];
NSOpenGLContext *glContext = [[NSOpenGLContext alloc] initWithFormat:pixelFormat shareContext:nil];
if (glContext == nil)
{
NSLog(@"Failed to create OpenGL context!", nil);
return nil;
}
if (glContext == nil)
{
NSLog(@"Failed to create OpenGL context!", nil);
return nil;
}
[self setPixelFormat:pixelFormat];
[self setPixelFormat:pixelFormat];
//for retina support
[self setWantsBestResolutionOpenGLSurface:gHiDPISupport];
//for retina support
[self setWantsBestResolutionOpenGLSurface:gHiDPISupport];
[self setOpenGLContext:glContext];
[self setOpenGLContext:glContext];
[glContext setView:self];
[glContext setView:self];
[glContext makeCurrentContext];
[glContext makeCurrentContext];
if (vsync)
{
GLint value = 1;
if (vsync)
{
GLint value = 1;
[glContext setValues:&value forParameter:NSOpenGLContextParameterSwapInterval];
} else {
// supress this error after move to Xcode 7:
// error: null passed to a callee that requires a non-null argument [-Werror,-Wnonnull]
// Tried using ObjC 'nonnull' keyword as per SO article but didn't build
GLint swapInterval=0;
} else {
// supress this error after move to Xcode 7:
// error: null passed to a callee that requires a non-null argument [-Werror,-Wnonnull]
// Tried using ObjC 'nonnull' keyword as per SO article but didn't build
GLint swapInterval=0;
[glContext setValues:&swapInterval forParameter:NSOpenGLContextParameterSwapInterval];
}
}
return self;
return self;
}
- (BOOL) rebuildContext
{
return [self rebuildContextWithFormat:[self pixelFormat]];
return [self rebuildContextWithFormat:[self pixelFormat]];
}
- (BOOL) rebuildContextWithFormat:(NSOpenGLPixelFormat *)format
{
NSOpenGLContext *ctx = [self openGLContext];
NSOpenGLContext *ctx = [self openGLContext];
[ctx clearDrawable];
[ctx initWithFormat:format shareContext:nil];
[ctx clearDrawable];
[ctx initWithFormat:format shareContext:nil];
if (ctx == nil)
{
NSLog(@"Failed to create OpenGL context!", nil);
return false;
}
if (ctx == nil)
{
NSLog(@"Failed to create OpenGL context!", nil);
return false;
}
[self setOpenGLContext:ctx];
[ctx setView:self];
[ctx makeCurrentContext];
return true;
[self setOpenGLContext:ctx];
[ctx setView:self];
[ctx makeCurrentContext];
return true;
}
#if LL_DARWIN
@ -313,14 +313,14 @@ attributedStringInfo getSegments(NSAttributedString *str)
- (CGLContextObj)getCGLContextObj
{
NSOpenGLContext *ctx = [self openGLContext];
return (CGLContextObj)[ctx CGLContextObj];
NSOpenGLContext *ctx = [self openGLContext];
return (CGLContextObj)[ctx CGLContextObj];
}
- (CGLPixelFormatObj*)getCGLPixelFormatObj
{
NSOpenGLPixelFormat *fmt = [self pixelFormat];
return (CGLPixelFormatObj*)[fmt CGLPixelFormatObj];
NSOpenGLPixelFormat *fmt = [self pixelFormat];
return (CGLPixelFormatObj*)[fmt CGLPixelFormatObj];
}
// Various events can be intercepted by our view, thus not reaching our window.
@ -365,29 +365,29 @@ attributedStringInfo getSegments(NSAttributedString *str)
- (void) rightMouseDown:(NSEvent *)theEvent
{
callRightMouseDown(mMousePos, [theEvent modifierFlags]);
callRightMouseDown(mMousePos, [theEvent modifierFlags]);
}
- (void) rightMouseUp:(NSEvent *)theEvent
{
callRightMouseUp(mMousePos, [theEvent modifierFlags]);
callRightMouseUp(mMousePos, [theEvent modifierFlags]);
}
- (void)mouseMoved:(NSEvent *)theEvent
{
NSPoint dev_delta = [self convertPointToBacking:NSMakePoint([theEvent deltaX], [theEvent deltaY])];
float mouseDeltas[] = {
float(dev_delta.x),
float(dev_delta.y)
};
float mouseDeltas[] = {
float(dev_delta.x),
float(dev_delta.y)
};
callDeltaUpdate(mouseDeltas, 0);
callDeltaUpdate(mouseDeltas, 0);
NSPoint mPoint = [self convertPointToBacking:[theEvent locationInWindow]];
mMousePos[0] = mPoint.x;
mMousePos[1] = mPoint.y;
callMouseMoved(mMousePos, 0);
mMousePos[0] = mPoint.x;
mMousePos[1] = mPoint.y;
callMouseMoved(mMousePos, 0);
}
// NSWindow doesn't trigger mouseMoved when the mouse is being clicked and dragged.
@ -395,23 +395,23 @@ attributedStringInfo getSegments(NSAttributedString *str)
- (void) mouseDragged:(NSEvent *)theEvent
{
// Trust the deltas supplied by NSEvent.
// The old CoreGraphics APIs we previously relied on are now flagged as obsolete.
// NSEvent isn't obsolete, and provides us with the correct deltas.
// Trust the deltas supplied by NSEvent.
// The old CoreGraphics APIs we previously relied on are now flagged as obsolete.
// NSEvent isn't obsolete, and provides us with the correct deltas.
NSPoint dev_delta = [self convertPointToBacking:NSMakePoint([theEvent deltaX], [theEvent deltaY])];
float mouseDeltas[] = {
float(dev_delta.x),
float(dev_delta.y)
};
float mouseDeltas[] = {
float(dev_delta.x),
float(dev_delta.y)
};
callDeltaUpdate(mouseDeltas, 0);
callDeltaUpdate(mouseDeltas, 0);
NSPoint mPoint = [self convertPointToBacking:[theEvent locationInWindow]];
mMousePos[0] = mPoint.x;
mMousePos[1] = mPoint.y;
callMouseDragged(mMousePos, 0);
NSPoint mPoint = [self convertPointToBacking:[theEvent locationInWindow]];
mMousePos[0] = mPoint.x;
mMousePos[1] = mPoint.y;
callMouseDragged(mMousePos, 0);
}
- (void) otherMouseDown:(NSEvent *)theEvent
@ -426,29 +426,29 @@ attributedStringInfo getSegments(NSAttributedString *str)
- (void) rightMouseDragged:(NSEvent *)theEvent
{
[self mouseDragged:theEvent];
[self mouseDragged:theEvent];
}
- (void) otherMouseDragged:(NSEvent *)theEvent
{
[self mouseDragged:theEvent];
[self mouseDragged:theEvent];
}
- (void) scrollWheel:(NSEvent *)theEvent
{
callScrollMoved(-[theEvent deltaX], -[theEvent deltaY]);
callScrollMoved(-[theEvent deltaX], -[theEvent deltaY]);
}
- (void) mouseExited:(NSEvent *)theEvent
{
callMouseExit();
callMouseExit();
}
- (void) keyUp:(NSEvent *)theEvent
{
NativeKeyEventData eventData = extractKeyDataFromKeyEvent(theEvent);
eventData.mKeyEvent = NativeKeyEventData::KEYUP;
callKeyUp(&eventData, [theEvent keyCode], [theEvent modifierFlags]);
callKeyUp(&eventData, [theEvent keyCode], [theEvent modifierFlags]);
}
- (void) keyDown:(NSEvent *)theEvent
@ -462,7 +462,7 @@ attributedStringInfo getSegments(NSAttributedString *str)
// Because flagsChange event handler misses event when other window is activated,
// e.g. OS Window for upload something or Input Window...
// mModifiers instance variable is for insertText: or insertText:replacementRange: (by Pell Smit)
mModifiers = [theEvent modifierFlags];
mModifiers = [theEvent modifierFlags];
NSString *str_no_modifiers = [theEvent charactersIgnoringModifiers];
unichar ch = 0;
if (str_no_modifiers.length)
@ -490,8 +490,8 @@ attributedStringInfo getSegments(NSAttributedString *str)
{
NativeKeyEventData eventData = extractKeyDataFromModifierEvent(theEvent);
mModifiers = [theEvent modifierFlags];
callModifier([theEvent modifierFlags]);
mModifiers = [theEvent modifierFlags];
callModifier([theEvent modifierFlags]);
NSInteger mask = 0;
switch([theEvent keyCode])
@ -532,69 +532,69 @@ attributedStringInfo getSegments(NSAttributedString *str)
- (BOOL) acceptsFirstResponder
{
return YES;
return YES;
}
- (NSDragOperation) draggingEntered:(id<NSDraggingInfo>)sender
{
NSPasteboard *pboard;
NSPasteboard *pboard;
NSDragOperation sourceDragMask;
sourceDragMask = [sender draggingSourceOperationMask];
sourceDragMask = [sender draggingSourceOperationMask];
pboard = [sender draggingPasteboard];
pboard = [sender draggingPasteboard];
if ([[pboard types] containsObject:NSPasteboardTypeURL])
{
if (sourceDragMask & NSDragOperationLink) {
NSURL *fileUrl = [[pboard readObjectsForClasses:[NSArray arrayWithObject:[NSURL class]] options:[NSDictionary dictionary]] objectAtIndex:0];
mLastDraggedUrl = [[fileUrl absoluteString] UTF8String];
{
if (sourceDragMask & NSDragOperationLink) {
NSURL *fileUrl = [[pboard readObjectsForClasses:[NSArray arrayWithObject:[NSURL class]] options:[NSDictionary dictionary]] objectAtIndex:0];
mLastDraggedUrl = [[fileUrl absoluteString] UTF8String];
return NSDragOperationLink;
}
}
return NSDragOperationNone;
}
return NSDragOperationNone;
}
- (NSDragOperation)draggingUpdated:(id <NSDraggingInfo>)sender
{
callHandleDragUpdated(mLastDraggedUrl);
callHandleDragUpdated(mLastDraggedUrl);
return NSDragOperationLink;
return NSDragOperationLink;
}
- (void) draggingExited:(id<NSDraggingInfo>)sender
{
callHandleDragExited(mLastDraggedUrl);
callHandleDragExited(mLastDraggedUrl);
}
- (BOOL)prepareForDragOperation:(id < NSDraggingInfo >)sender
{
return YES;
return YES;
}
- (BOOL) performDragOperation:(id<NSDraggingInfo>)sender
{
callHandleDragDropped(mLastDraggedUrl);
return true;
callHandleDragDropped(mLastDraggedUrl);
return true;
}
- (BOOL)hasMarkedText
{
return mHasMarkedText;
return mHasMarkedText;
}
- (NSRange)markedRange
{
int range[2];
getPreeditMarkedRange(&range[0], &range[1]);
return NSMakeRange(range[0], range[1]);
int range[2];
getPreeditMarkedRange(&range[0], &range[1]);
return NSMakeRange(range[0], range[1]);
}
- (NSRange)selectedRange
{
int range[2];
getPreeditSelectionRange(&range[0], &range[1]);
return NSMakeRange(range[0], range[1]);
int range[2];
getPreeditSelectionRange(&range[0], &range[1]);
return NSMakeRange(range[0], range[1]);
}
- (void)setMarkedText:(id)aString selectedRange:(NSRange)selectedRange replacementRange:(NSRange)replacementRange
@ -680,21 +680,21 @@ attributedStringInfo getSegments(NSAttributedString *str)
- (void)unmarkText
{
[[self inputContext] discardMarkedText];
resetPreedit();
mHasMarkedText = FALSE;
[[self inputContext] discardMarkedText];
resetPreedit();
mHasMarkedText = FALSE;
}
// We don't support attributed strings.
- (NSArray *)validAttributesForMarkedText
{
return [NSArray array];
return [NSArray array];
}
// See above.
- (NSAttributedString *)attributedSubstringForProposedRange:(NSRange)aRange actualRange:(NSRangePointer)actualRange
{
return nil;
return nil;
}
- (void)insertText:(id)insertString
@ -707,9 +707,9 @@ attributedStringInfo getSegments(NSAttributedString *str)
- (void)insertText:(id)aString replacementRange:(NSRange)replacementRange
{
// SL-19801 Special workaround for system emoji picker
if ([aString length] == 2)
{
// SL-19801 Special workaround for system emoji picker
if ([aString length] == 2)
{
@try
{
uint32_t b0 = [aString characterAtIndex:0];
@ -727,7 +727,7 @@ attributedStringInfo getSegments(NSAttributedString *str)
NSLog(@"Encountered an unsupported attributed character. Exception: %@ String: %@", e.name, aString);
return;
}
}
}
@try
{
@ -760,36 +760,36 @@ attributedStringInfo getSegments(NSAttributedString *str)
if (!(mModifiers & NSEventModifierFlagCommand) &&
!(mModifiers & NSEventModifierFlagShift) &&
!(mModifiers & NSEventModifierFlagOption))
{
callUnicodeCallback(13, 0);
} else {
callUnicodeCallback(13, mModifiers);
}
{
callUnicodeCallback(13, 0);
} else {
callUnicodeCallback(13, mModifiers);
}
}
- (NSUInteger)characterIndexForPoint:(NSPoint)aPoint
{
return NSNotFound;
return NSNotFound;
}
- (NSRect)firstRectForCharacterRange:(NSRange)aRange actualRange:(NSRangePointer)actualRange
{
float pos[4] = {0, 0, 0, 0};
getPreeditLocation(pos, mMarkedTextLength);
return NSMakeRect(pos[0], pos[1], pos[2], pos[3]);
float pos[4] = {0, 0, 0, 0};
getPreeditLocation(pos, mMarkedTextLength);
return NSMakeRect(pos[0], pos[1], pos[2], pos[3]);
}
- (void)doCommandBySelector:(SEL)aSelector
{
if (aSelector == @selector(insertNewline:))
{
[self insertNewline:self];
}
if (aSelector == @selector(insertNewline:))
{
[self insertNewline:self];
}
}
- (BOOL)drawsVerticallyForCharacterAtIndex:(NSUInteger)charIndex
{
return NO;
return NO;
}
- (void) allowMarkedTextInput:(bool)allowed
@ -824,7 +824,7 @@ attributedStringInfo getSegments(NSAttributedString *str)
- (void) setGLView:(LLOpenGLView *)view
{
glview = view;
glview = view;
}
- (void)keyDown:(NSEvent *)theEvent
@ -875,24 +875,24 @@ attributedStringInfo getSegments(NSAttributedString *str)
- (id) init
{
return self;
return self;
}
- (BOOL) becomeFirstResponder
{
callFocus();
return true;
callFocus();
return true;
}
- (BOOL) resignFirstResponder
{
callFocusLost();
return true;
callFocusLost();
return true;
}
- (void) close
{
callQuitHandler();
callQuitHandler();
}
@end

View File

@ -95,6 +95,7 @@ public:
#if LL_WINDOWS
virtual bool getCursorDelta(LLCoordCommon* delta) = 0;
#endif
virtual bool isWrapMouse() const = 0;
virtual void showCursor() = 0;
virtual void hideCursor() = 0;
virtual bool isCursorHidden() = 0;

View File

@ -68,7 +68,13 @@ void LLWindowCallbacks::handleMouseLeave(LLWindow *window)
return;
}
bool LLWindowCallbacks::handleCloseRequest(LLWindow *window)
bool LLWindowCallbacks::handleCloseRequest(LLWindow *window, bool from_user)
{
//allow the window to close
return true;
}
bool LLWindowCallbacks::handleSessionExit(LLWindow* window)
{
//allow the window to close
return true;

View File

@ -42,7 +42,8 @@ public:
virtual bool handleMouseUp(LLWindow *window, LLCoordGL pos, MASK mask);
virtual void handleMouseLeave(LLWindow *window);
// return true to allow window to close, which will then cause handleQuit to be called
virtual bool handleCloseRequest(LLWindow *window);
virtual bool handleCloseRequest(LLWindow *window, bool from_user);
virtual bool handleSessionExit(LLWindow* window);
// window is about to be destroyed, clean up your business
virtual void handleQuit(LLWindow *window);
virtual bool handleRightMouseDown(LLWindow *window, LLCoordGL pos, MASK mask);

View File

@ -63,6 +63,7 @@ public:
#if LL_WINDOWS
/*virtual*/ bool getCursorDelta(LLCoordCommon* delta) override { return false; }
#endif
/*virtual*/ bool isWrapMouse() const override { return true; }
/*virtual*/ void showCursor() override {}
/*virtual*/ void hideCursor() override {}
/*virtual*/ void showCursorFromMouseMove() override {}

View File

@ -41,15 +41,15 @@
int createNSApp(int argc, const char *argv[])
{
return NSApplicationMain(argc, argv);
return NSApplicationMain(argc, argv);
}
void setupCocoa()
{
static bool inited = false;
if(!inited)
{
static bool inited = false;
if(!inited)
{
@autoreleasepool {
// The following prevents the Cocoa command line parser from trying to open 'unknown' arguements as documents.
// ie. running './secondlife -set Language fr' would cause a pop-up saying can't open document 'fr'
@ -57,8 +57,8 @@ void setupCocoa()
[[NSUserDefaults standardUserDefaults] setObject:@"NO" forKey:@"NSTreatUnknownArgumentsAsOpen"];
}
inited = true;
}
inited = true;
}
}
bool copyToPBoard(const unsigned short *str, unsigned int len)
@ -66,7 +66,7 @@ bool copyToPBoard(const unsigned short *str, unsigned int len)
@autoreleasepool {
NSPasteboard *pboard = [NSPasteboard generalPasteboard];
[pboard clearContents];
NSArray *contentsToPaste = [[[NSArray alloc] initWithObjects:[NSString stringWithCharacters:str length:len], nil] autorelease];
return [pboard writeObjects:contentsToPaste];
}
@ -74,8 +74,8 @@ bool copyToPBoard(const unsigned short *str, unsigned int len)
bool pasteBoardAvailable()
{
NSArray *classArray = [NSArray arrayWithObject:[NSString class]];
return [[NSPasteboard generalPasteboard] canReadObjectForClasses:classArray options:[NSDictionary dictionary]];
NSArray *classArray = [NSArray arrayWithObject:[NSString class]];
return [[NSPasteboard generalPasteboard] canReadObjectForClasses:classArray options:[NSDictionary dictionary]];
}
unsigned short *copyFromPBoard()
@ -111,55 +111,55 @@ CursorRef createImageCursor(const char *fullpath, int hotspotX, int hotspotY)
hotSpot:NSMakePoint(hotspotX, hotspotY)
] retain];
}
return (CursorRef)cursor;
return (CursorRef)cursor;
}
void setArrowCursor()
{
NSCursor *cursor = [NSCursor arrowCursor];
[NSCursor unhide];
[cursor set];
NSCursor *cursor = [NSCursor arrowCursor];
[NSCursor unhide];
[cursor set];
}
void setIBeamCursor()
{
NSCursor *cursor = [NSCursor IBeamCursor];
[cursor set];
NSCursor *cursor = [NSCursor IBeamCursor];
[cursor set];
}
void setPointingHandCursor()
{
NSCursor *cursor = [NSCursor pointingHandCursor];
[cursor set];
NSCursor *cursor = [NSCursor pointingHandCursor];
[cursor set];
}
void setCopyCursor()
{
NSCursor *cursor = [NSCursor dragCopyCursor];
[cursor set];
NSCursor *cursor = [NSCursor dragCopyCursor];
[cursor set];
}
void setCrossCursor()
{
NSCursor *cursor = [NSCursor crosshairCursor];
[cursor set];
NSCursor *cursor = [NSCursor crosshairCursor];
[cursor set];
}
void setNotAllowedCursor()
{
NSCursor *cursor = [NSCursor operationNotAllowedCursor];
[cursor set];
NSCursor *cursor = [NSCursor operationNotAllowedCursor];
[cursor set];
}
void hideNSCursor()
{
[NSCursor hide];
[NSCursor hide];
}
void showNSCursor()
{
[NSCursor unhide];
[NSCursor unhide];
}
#if LL_DARWIN
@ -179,42 +179,42 @@ bool isCGCursorVisible()
void hideNSCursorTillMove(bool hide)
{
[NSCursor setHiddenUntilMouseMoves:hide];
[NSCursor setHiddenUntilMouseMoves:hide];
}
// This is currently unused, since we want all our cursors to persist for the life of the app, but I've included it for completeness.
OSErr releaseImageCursor(CursorRef ref)
{
if( ref != NULL )
{
if( ref != NULL )
{
@autoreleasepool {
NSCursor *cursor = (NSCursor*)ref;
[cursor autorelease];
}
}
else
{
return paramErr;
}
return noErr;
}
else
{
return paramErr;
}
return noErr;
}
OSErr setImageCursor(CursorRef ref)
{
if( ref != NULL )
{
if( ref != NULL )
{
@autoreleasepool {
NSCursor *cursor = (NSCursor*)ref;
[cursor set];
}
}
else
{
return paramErr;
}
return noErr;
}
else
{
return paramErr;
}
return noErr;
}
// Now for some unholy juggling between generic pointers and casting them to Obj-C objects!
@ -222,46 +222,46 @@ OSErr setImageCursor(CursorRef ref)
NSWindowRef createNSWindow(int x, int y, int width, int height)
{
LLNSWindow *window = [[LLNSWindow alloc]initWithContentRect:NSMakeRect(x, y, width, height)
LLNSWindow *window = [[LLNSWindow alloc]initWithContentRect:NSMakeRect(x, y, width, height)
styleMask:NSWindowStyleMaskTitled | NSWindowStyleMaskResizable | NSWindowStyleMaskClosable | NSWindowStyleMaskMiniaturizable
backing:NSBackingStoreBuffered defer:NO];
[window makeKeyAndOrderFront:nil];
[window setAcceptsMouseMovedEvents:TRUE];
[window makeKeyAndOrderFront:nil];
[window setAcceptsMouseMovedEvents:TRUE];
[window setRestorable:FALSE]; // Viewer manages state from own settings
return window;
return window;
}
GLViewRef createOpenGLView(NSWindowRef window, unsigned int samples, bool vsync)
{
LLOpenGLView *glview = [[LLOpenGLView alloc]initWithFrame:[(LLNSWindow*)window frame] withSamples:samples andVsync:vsync];
[(LLNSWindow*)window setContentView:glview];
return glview;
LLOpenGLView *glview = [[LLOpenGLView alloc]initWithFrame:[(LLNSWindow*)window frame] withSamples:samples andVsync:vsync];
[(LLNSWindow*)window setContentView:glview];
return glview;
}
void glSwapBuffers(void* context)
{
[(NSOpenGLContext*)context flushBuffer];
[(NSOpenGLContext*)context flushBuffer];
}
CGLContextObj getCGLContextObj(GLViewRef view)
{
return [(LLOpenGLView *)view getCGLContextObj];
return [(LLOpenGLView *)view getCGLContextObj];
}
CGLPixelFormatObj* getCGLPixelFormatObj(NSWindowRef window)
{
LLOpenGLView *glview = [(LLNSWindow*)window contentView];
return [glview getCGLPixelFormatObj];
LLOpenGLView *glview = [(LLNSWindow*)window contentView];
return [glview getCGLPixelFormatObj];
}
unsigned long getVramSize(GLViewRef view)
{
return [(LLOpenGLView *)view getVramSize];
return [(LLOpenGLView *)view getVramSize];
}
float getDeviceUnitSize(GLViewRef view)
{
return [(LLOpenGLView*)view convertSizeToBacking:NSMakeSize(1, 1)].width;
return [(LLOpenGLView*)view convertSizeToBacking:NSMakeSize(1, 1)].width;
}
CGRect getContentViewRect(NSWindowRef window)
@ -276,48 +276,48 @@ CGRect getBackingViewRect(NSWindowRef window, GLViewRef view)
void getWindowSize(NSWindowRef window, float* size)
{
NSRect frame = [(LLNSWindow*)window frame];
size[0] = frame.origin.x;
size[1] = frame.origin.y;
size[2] = frame.size.width;
size[3] = frame.size.height;
NSRect frame = [(LLNSWindow*)window frame];
size[0] = frame.origin.x;
size[1] = frame.origin.y;
size[2] = frame.size.width;
size[3] = frame.size.height;
}
void setWindowSize(NSWindowRef window, int width, int height)
{
NSRect frame = [(LLNSWindow*)window frame];
frame.size.width = width;
frame.size.height = height;
[(LLNSWindow*)window setFrame:frame display:TRUE];
NSRect frame = [(LLNSWindow*)window frame];
frame.size.width = width;
frame.size.height = height;
[(LLNSWindow*)window setFrame:frame display:TRUE];
}
void setWindowPos(NSWindowRef window, float* pos)
{
NSPoint point;
point.x = pos[0];
point.y = pos[1];
[(LLNSWindow*)window setFrameOrigin:point];
NSPoint point;
point.x = pos[0];
point.y = pos[1];
[(LLNSWindow*)window setFrameOrigin:point];
}
void getCursorPos(NSWindowRef window, float* pos)
{
NSPoint mLoc;
mLoc = [(LLNSWindow*)window mouseLocationOutsideOfEventStream];
pos[0] = mLoc.x;
pos[1] = mLoc.y;
NSPoint mLoc;
mLoc = [(LLNSWindow*)window mouseLocationOutsideOfEventStream];
pos[0] = mLoc.x;
pos[1] = mLoc.y;
}
void makeWindowOrderFront(NSWindowRef window)
{
[(LLNSWindow*)window makeKeyAndOrderFront:nil];
[(LLNSWindow*)window makeKeyAndOrderFront:nil];
}
void convertScreenToWindow(NSWindowRef window, float *coord)
{
NSRect point = NSMakeRect(coord[0], coord[1], 0,0);
point = [(LLNSWindow*)window convertRectFromScreen:point];
coord[0] = point.origin.x;
coord[1] = point.origin.y;
point = [(LLNSWindow*)window convertRectFromScreen:point];
coord[0] = point.origin.x;
coord[1] = point.origin.y;
}
void convertRectToScreen(NSWindowRef window, float *coord)
@ -325,21 +325,21 @@ void convertRectToScreen(NSWindowRef window, float *coord)
NSRect rect = NSMakeRect(coord[0], coord[1], coord[2], coord[3]);;
rect = [(LLNSWindow*)window convertRectToScreen:rect];
coord[0] = rect.origin.x;
coord[1] = rect.origin.y;
coord[2] = rect.size.width;
coord[3] = rect.size.height;
coord[0] = rect.origin.x;
coord[1] = rect.origin.y;
coord[2] = rect.size.width;
coord[3] = rect.size.height;
}
void convertRectFromScreen(NSWindowRef window, float *coord)
{
NSRect point = NSMakeRect(coord[0], coord[1], coord[2], coord[3]);
point = [(LLNSWindow*)window convertRectFromScreen:point];
coord[0] = point.origin.x;
coord[1] = point.origin.y;
coord[2] = point.size.width;
coord[3] = point.size.height;
NSRect point = NSMakeRect(coord[0], coord[1], coord[2], coord[3]);
point = [(LLNSWindow*)window convertRectFromScreen:point];
coord[0] = point.origin.x;
coord[1] = point.origin.y;
coord[2] = point.size.width;
coord[3] = point.size.height;
}
void convertWindowToScreen(NSWindowRef window, float *coord)
@ -353,24 +353,24 @@ void convertWindowToScreen(NSWindowRef window, float *coord)
void closeWindow(NSWindowRef window)
{
[(LLNSWindow*)window close];
[(LLNSWindow*)window release];
[(LLNSWindow*)window close];
[(LLNSWindow*)window release];
}
void removeGLView(GLViewRef view)
{
[(LLOpenGLView*)view clearGLContext];
[(LLOpenGLView*)view removeFromSuperview];
[(LLOpenGLView*)view clearGLContext];
[(LLOpenGLView*)view removeFromSuperview];
}
void setupInputWindow(NSWindowRef window, GLViewRef glview)
{
[[(LLAppDelegate*)[NSApp delegate] inputView] setGLView:(LLOpenGLView*)glview];
[[(LLAppDelegate*)[NSApp delegate] inputView] setGLView:(LLOpenGLView*)glview];
}
void commitCurrentPreedit(GLViewRef glView)
{
[(LLOpenGLView*)glView commitCurrentPreedit];
[(LLOpenGLView*)glView commitCurrentPreedit];
}
void allowDirectMarkedTextInput(bool allow, GLViewRef glView)
@ -380,20 +380,20 @@ void allowDirectMarkedTextInput(bool allow, GLViewRef glView)
NSWindowRef getMainAppWindow()
{
LLNSWindow *winRef = [(LLAppDelegate*)[[NSApplication sharedApplication] delegate] window];
[winRef setAcceptsMouseMovedEvents:TRUE];
return winRef;
LLNSWindow *winRef = [(LLAppDelegate*)[[NSApplication sharedApplication] delegate] window];
[winRef setAcceptsMouseMovedEvents:TRUE];
return winRef;
}
void makeFirstResponder(NSWindowRef window, GLViewRef view)
{
[(LLNSWindow*)window makeFirstResponder:(LLOpenGLView*)view];
[(LLNSWindow*)window makeFirstResponder:(LLOpenGLView*)view];
}
void requestUserAttention()
{
[[NSApplication sharedApplication] requestUserAttention:NSInformationalRequest];
[[NSApplication sharedApplication] requestUserAttention:NSInformationalRequest];
}
long showAlert(std::string text, std::string title, int type)
@ -401,7 +401,7 @@ long showAlert(std::string text, std::string title, int type)
long ret = 0;
@autoreleasepool {
NSAlert *alert = [[[NSAlert alloc] init] autorelease];
[alert setMessageText:[NSString stringWithCString:title.c_str() encoding:[NSString defaultCStringEncoding]]];
[alert setInformativeText:[NSString stringWithCString:text.c_str() encoding:[NSString defaultCStringEncoding]]];
if (type == 0)
@ -418,7 +418,7 @@ long showAlert(std::string text, std::string title, int type)
}
ret = [alert runModal];
}
if (ret == NSAlertFirstButtonReturn)
{
if (type == 1)
@ -438,7 +438,7 @@ long showAlert(std::string text, std::string title, int type)
ret = 1;
}
}
return ret;
}
@ -451,5 +451,5 @@ long showAlert(std::string text, std::string title, int type)
unsigned int getModifiers()
{
return [NSEvent modifierFlags];
return [NSEvent modifierFlags];
}

View File

@ -610,7 +610,7 @@ void callQuitHandler()
{
if (gWindowImplementation && gWindowImplementation->getCallbacks())
{
if(gWindowImplementation->getCallbacks()->handleCloseRequest(gWindowImplementation))
if(gWindowImplementation->getCallbacks()->handleCloseRequest(gWindowImplementation, true))
{
gWindowImplementation->getCallbacks()->handleQuit(gWindowImplementation);
}

View File

@ -63,6 +63,7 @@ public:
bool switchContext(bool fullscreen, const LLCoordScreen &size, bool enable_vsync, const LLCoordScreen * const posp = NULL) override;
bool setCursorPosition(LLCoordWindow position) override;
bool getCursorPosition(LLCoordWindow *position) override;
bool isWrapMouse() const override { return !mCursorDecoupled; };
void showCursor() override;
void hideCursor() override;
void showCursorFromMouseMove() override;

View File

@ -1881,7 +1881,7 @@ void LLWindowSDL::gatherInput()
{
// *FIX: More informative dialog?
LL_INFOS() << "Could not recreate context after resize! Quitting..." << LL_ENDL;
if(mCallbacks->handleCloseRequest(this))
if(mCallbacks->handleCloseRequest(this, false))
{
// Get the app to initiate cleanup.
mCallbacks->handleQuit(this);
@ -1931,7 +1931,7 @@ void LLWindowSDL::gatherInput()
break;
case SDL_QUIT:
if(mCallbacks->handleCloseRequest(this))
if(mCallbacks->handleCloseRequest(this, true))
{
// Get the app to initiate cleanup.
mCallbacks->handleQuit(this);

View File

@ -68,6 +68,7 @@ public:
/*virtual*/ bool switchContext(bool fullscreen, const LLCoordScreen &size, bool disable_vsync, const LLCoordScreen * const posp = NULL);
/*virtual*/ bool setCursorPosition(LLCoordWindow position);
/*virtual*/ bool getCursorPosition(LLCoordWindow *position);
/*virtual*/ bool isWrapMouse() const override { return true; }
/*virtual*/ void showCursor();
/*virtual*/ void hideCursor();
/*virtual*/ void showCursorFromMouseMove();

View File

@ -450,6 +450,7 @@ LLWindowWin32::LLWindowWin32(LLWindowCallbacks* callbacks,
F32 max_gl_version)
:
LLWindow(callbacks, fullscreen, flags),
mAbsoluteCursorPosition(false),
mMaxGLVersion(max_gl_version),
mMaxCores(max_cores)
{
@ -2464,10 +2465,13 @@ LRESULT CALLBACK LLWindowWin32::mainWindowProc(HWND h_wnd, UINT u_msg, WPARAM w_
case WM_CLOSE:
{
LL_PROFILE_ZONE_NAMED_CATEGORY_WIN32("mwp - WM_CLOSE");
// todo: WM_CLOSE can be caused by user and by task manager,
// distinguish these cases.
// For now assume it is always user.
window_imp->post([=]()
{
// Will the app allow the window to close?
if (window_imp->mCallbacks->handleCloseRequest(window_imp))
if (window_imp->mCallbacks->handleCloseRequest(window_imp, true))
{
// Get the app to initiate cleanup.
window_imp->mCallbacks->handleQuit(window_imp);
@ -2485,6 +2489,50 @@ LRESULT CALLBACK LLWindowWin32::mainWindowProc(HWND h_wnd, UINT u_msg, WPARAM w_
}
return 0;
}
case WM_QUERYENDSESSION:
{
// Generally means that OS is going to shut down or user is going to log off.
// Can use ShutdownBlockReasonCreate here.
LL_INFOS("Window") << "Received WM_QUERYENDSESSION with wParam: " << (U32)w_param << " lParam: " << (U32)l_param << LL_ENDL;
return TRUE; // 1 = ok to end session. 0 no longer works by itself, use ShutdownBlockReasonCreate
}
case WM_ENDSESSION:
{
// OS session is shutting down, initiate cleanup.
// Comes after WM_QUERYENDSESSION
LL_PROFILE_ZONE_NAMED_CATEGORY_WIN32("mwp - WM_ENDSESSION");
LL_INFOS("Window") << "Received WM_ENDSESSION with wParam: " << (U32)w_param << " lParam: " << (U32)l_param << LL_ENDL;
unsigned int end_session_flags = (U32)w_param;
if (end_session_flags == 0)
{
// session is not actually ending
return 0;
}
if ((end_session_flags & ENDSESSION_CLOSEAPP)
|| (end_session_flags & ENDSESSION_CRITICAL)
|| (end_session_flags & ENDSESSION_LOGOFF))
{
window_imp->post([=]()
{
// Check if app needs cleanup or can be closed immediately.
if (window_imp->mCallbacks->handleSessionExit(window_imp))
{
// Get the app to initiate cleanup.
window_imp->mCallbacks->handleQuit(window_imp);
// The app is responsible for calling destroyWindow when done with GL
}
});
// Give app a second to finish up. That's not enough for a clean exit,
// but better than nothing.
// Todo: sync this better, some kind of waitForResult? Can't wait forever,
// but can potentially use ShutdownBlockReasonCreate for a bigger delay.
ms_sleep(1000);
}
// Don't need to post quit or destroy window,
// if session is ending OS is going to take care of it.
return 0;
}
case WM_COMMAND:
{
LL_PROFILE_ZONE_NAMED_CATEGORY_WIN32("mwp - WM_COMMAND");
@ -3109,6 +3157,7 @@ LRESULT CALLBACK LLWindowWin32::mainWindowProc(HWND h_wnd, UINT u_msg, WPARAM w_
prev_absolute_x = absolute_x;
prev_absolute_y = absolute_y;
window_imp->mAbsoluteCursorPosition = true;
}
else
{
@ -3125,6 +3174,7 @@ LRESULT CALLBACK LLWindowWin32::mainWindowProc(HWND h_wnd, UINT u_msg, WPARAM w_
window_imp->mRawMouseDelta.mX += (S32)round((F32)raw->data.mouse.lLastX * (F32)speed / DEFAULT_SPEED);
window_imp->mRawMouseDelta.mY -= (S32)round((F32)raw->data.mouse.lLastY * (F32)speed / DEFAULT_SPEED);
}
window_imp->mAbsoluteCursorPosition = false;
}
}
}

View File

@ -70,6 +70,7 @@ public:
/*virtual*/ bool setCursorPosition(LLCoordWindow position);
/*virtual*/ bool getCursorPosition(LLCoordWindow *position);
/*virtual*/ bool getCursorDelta(LLCoordCommon* delta);
/*virtual*/ bool isWrapMouse() const override { return !mAbsoluteCursorPosition; };
/*virtual*/ void showCursor();
/*virtual*/ void hideCursor();
/*virtual*/ void showCursorFromMouseMove();
@ -195,6 +196,7 @@ protected:
HCURSOR mCursor[ UI_CURSOR_COUNT ]; // Array of all mouse cursors
LLCoordWindow mCursorPosition; // mouse cursor position, should only be mutated on main thread
bool mAbsoluteCursorPosition; // true if last position was received in absolute coordinates.
LLMutex mRawMouseMutex;
RAWINPUTDEVICE mRawMouse;
LLCoordWindow mLastCursorPosition; // mouse cursor position from previous frame

View File

@ -50,7 +50,7 @@ include(VulkanGltf)
include(ZLIBNG)
include(LLPrimitive)
if (NOT HAVOK_TPV)
if (HAVOK)
# When using HAVOK_TPV, the library is precompiled, so no need for this
# Stub and probably havok lib itself is a hack, autobuild loads a 3p that really is a source tarball
@ -76,7 +76,7 @@ if (NOT HAVOK_TPV)
target_compile_options( llphysicsextensions PRIVATE -Wno-unused-local-typedef)
endif (DARWIN)
endif()
endif (NOT HAVOK_TPV)
endif ()
set(viewer_SOURCE_FILES
gltfscenemanager.cpp
@ -1714,10 +1714,6 @@ if (WINDOWS)
list(APPEND viewer_SOURCE_FILES ${viewer_INSTALLER_FILES})
endif (WINDOWS)
if (HAVOK OR HAVOK_TPV)
set(LLSTARTUP_COMPILE_FLAGS "${LLSTARTUP_COMPILE_FLAGS} -DLL_HAVOK")
endif (HAVOK OR HAVOK_TPV)
if( DEFINED LLSTARTUP_COMPILE_FLAGS )
# progress view disables/enables icons based on available packages
set_source_files_properties(llprogressview.cpp PROPERTIES COMPILE_FLAGS "${LLSTARTUP_COMPILE_FLAGS}")
@ -1956,13 +1952,30 @@ elseif (DARWIN)
PROPERTIES
RESOURCE SecondLife.xib
LINK_FLAGS_RELEASE "${LINK_FLAGS_RELEASE} -Xlinker -dead_strip"
# arch specific flags for universal builds: https://stackoverflow.com/a/77942065
XCODE_ATTRIBUTE_OTHER_CFLAGS[arch=x86_64] "$(inherited) -DLLPHYSICSEXTENSIONS_USE_FULL"
XCODE_ATTRIBUTE_OTHER_CFLAGS[arch=arm64] "$(inherited) -DLLPHYSICSEXTENSIONS_USE_STUB"
# only generate the .MAP file for llphysicsextensions_tpv on x86_64
XCODE_ATTRIBUTE_OTHER_LDFLAGS[arch=x86_64] "$(inherited) -L${CMAKE_CURRENT_BINARY_DIR}/llphysicsextensions/$<IF:$<BOOL:${LL_GENERATOR_IS_MULTI_CONFIG}>,$<CONFIG>,${CMAKE_CFG_INTDIR}>/ -lllphysicsextensions -Xlinker -map -Xlinker ${CMAKE_CURRENT_BINARY_DIR}/${VIEWER_BINARY_NAME}.MAP"
XCODE_ATTRIBUTE_OTHER_LDFLAGS[arch=arm64] "$(inherited) -L${CMAKE_CURRENT_BINARY_DIR}/llphysicsextensionsstub/$<IF:$<BOOL:${LL_GENERATOR_IS_MULTI_CONFIG}>,$<CONFIG>,${CMAKE_CFG_INTDIR}>/ -lllphysicsextensionsstub"
)
if(HAVOK)
set_target_properties(${VIEWER_BINARY_NAME}
PROPERTIES
# arch specific flags for universal builds: https://stackoverflow.com/a/77942065
XCODE_ATTRIBUTE_OTHER_CFLAGS[arch=x86_64] "$(inherited) -DLLPHYSICSEXTENSIONS_USE_FULL -DLL_HAVOK=1"
XCODE_ATTRIBUTE_OTHER_CFLAGS[arch=arm64] "$(inherited) -DLLPHYSICSEXTENSIONS_USE_STUB"
# only generate the .MAP file for llphysicsextensions_tpv on x86_64
XCODE_ATTRIBUTE_OTHER_LDFLAGS[arch=x86_64] "$(inherited) -L${CMAKE_CURRENT_BINARY_DIR}/llphysicsextensions/$<IF:$<BOOL:${LL_GENERATOR_IS_MULTI_CONFIG}>,$<CONFIG>,${CMAKE_CFG_INTDIR}>/ -lllphysicsextensions -Xlinker -map -Xlinker ${CMAKE_CURRENT_BINARY_DIR}/${VIEWER_BINARY_NAME}.MAP"
XCODE_ATTRIBUTE_OTHER_LDFLAGS[arch=arm64] "$(inherited) -L${CMAKE_BINARY_DIR}/llphysicsextensionsos/$<IF:$<BOOL:${LL_GENERATOR_IS_MULTI_CONFIG}>,$<CONFIG>,${CMAKE_CFG_INTDIR}>/ -lllphysicsextensionsos"
)
elseif(HAVOK_TPV)
set_target_properties(${VIEWER_BINARY_NAME}
PROPERTIES
# arch specific flags for universal builds: https://stackoverflow.com/a/77942065
XCODE_ATTRIBUTE_OTHER_CFLAGS[arch=x86_64] "$(inherited) -DLLPHYSICSEXTENSIONS_USE_FULL -DLL_HAVOK=1"
XCODE_ATTRIBUTE_OTHER_CFLAGS[arch=arm64] "$(inherited) -DLLPHYSICSEXTENSIONS_USE_STUB"
# only generate the .MAP file for llphysicsextensions_tpv on x86_64
XCODE_ATTRIBUTE_OTHER_LDFLAGS[arch=x86_64] "$(inherited) -L${ARCH_PREBUILT_DIRS}/ -lllphysicsextensions_tpv"
XCODE_ATTRIBUTE_OTHER_LDFLAGS[arch=arm64] "$(inherited) -L${CMAKE_BINARY_DIR}/llphysicsextensionsos/$<IF:$<BOOL:${LL_GENERATOR_IS_MULTI_CONFIG}>,$<CONFIG>,${CMAKE_CFG_INTDIR}>/ -lllphysicsextensionsos"
)
else()
target_link_libraries(${VIEWER_BINARY_NAME} llphysicsextensionsos)
endif()
else (WINDOWS)
# Linux
set_target_properties(${VIEWER_BINARY_NAME}
@ -2039,6 +2052,10 @@ if( TARGET ll::nvapi )
target_link_libraries(${VIEWER_BINARY_NAME} ll::nvapi )
endif()
if ( TARGET llconvexdecomposition )
target_link_libraries(${VIEWER_BINARY_NAME} llconvexdecomposition )
endif ()
set(ARTWORK_DIR ${CMAKE_CURRENT_SOURCE_DIR} CACHE PATH
"Path to artwork files.")

View File

@ -1 +1 @@
7.2.2
7.2.3

View File

@ -2469,16 +2469,16 @@
<key>Value</key>
<integer>0</integer>
</map>
<key>DisableMouseWarp</key>
<key>MouseWarpMode</key>
<map>
<key>Comment</key>
<string>Disable warping of the mouse to the center of the screen during alt-zoom and mouse look. Useful with certain input devices, mouse sharing programs like Synergy, or running under Parallels.</string>
<string>Controls warping of the mouse to the center of the screen during alt-zoom and mouse look. Useful with certain input devices, mouse sharing programs like Synergy, or running under Parallels. 0 - automatic, 1 - on, 2 - off</string>
<key>Persist</key>
<integer>1</integer>
<key>Type</key>
<string>Boolean</string>
<string>S32</string>
<key>Value</key>
<integer>0</integer>
<integer>1</integer>
</map>
<key>DisableExternalBrowser</key>
<map>
@ -4380,7 +4380,7 @@
<key>Type</key>
<string>F32</string>
<key>Value</key>
<real>40.0</real>
<real>90.0</real>
</map>
<key>LogMessages</key>
<map>
@ -7863,7 +7863,7 @@
<key>RenderMaxOpenGLVersion</key>
<map>
<key>Comment</key>
<string>Maximum OpenGL version to attempt use (minimum 3.1 maximum 4.6). Requires restart.</string>
<string>Maximum OpenGL version to attempt use (minimum 3.1 maximum 4.6). Requires restart. Windows only.</string>
<key>Persist</key>
<integer>1</integer>
<key>Type</key>
@ -9111,7 +9111,7 @@
<key>RenderQualityPerformance</key>
<map>
<key>Comment</key>
<string>Which graphics settings you've chosen</string>
<string>Which graphics settings you've chosen. Don't use this setting to change quality directly from debug settings.</string>
<key>Persist</key>
<integer>1</integer>
<key>Type</key>
@ -9119,7 +9119,18 @@
<key>Value</key>
<integer>1</integer>
</map>
<key>DebugQualityPerformance</key>
<map>
<key>Comment</key>
<string>Allows to change performance quality directly from debug settings.</string>
<key>Persist</key>
<integer>1</integer>
<key>Type</key>
<string>U32</string>
<key>Value</key>
<integer>1</integer>
</map>
<key>RenderReflectionDetail</key>
<map>
<key>Comment</key>
@ -10330,13 +10341,13 @@
<key>SceneLoadRearMaxRadiusFraction</key>
<map>
<key>Comment</key>
<string>a percentage of draw distance beyond which all objects outside of view frustum will be unloaded, regardless of pixel threshold</string>
<string>a fraction of draw distance beyond which all objects outside of view frustum will be unloaded, regardless of pixel threshold</string>
<key>Persist</key>
<integer>1</integer>
<key>Type</key>
<string>F32</string>
<key>Value</key>
<real>75.0</real>
<real>0.75</real>
</map>
<key>SceneLoadRearPixelThreshold</key>
<map>
@ -13036,9 +13047,9 @@
<key>Use24HourClock</key>
<map>
<key>Comment</key>
<string>12 vs 24. At the moment only for region restart schedule floater</string>
<string>12 vs 24. At the moment coverage is partial</string>
<key>Persist</key>
<integer>0</integer>
<integer>1</integer>
<key>Type</key>
<string>Boolean</string>
<key>Value</key>

View File

@ -158,6 +158,11 @@ bool Buffer::prep(Asset& asset)
{
std::string dir = gDirUtilp->getDirName(asset.mFilename);
std::string bin_file = dir + gDirUtilp->getDirDelimiter() + mUri;
if (!gDirUtilp->fileExists(bin_file))
{
// Characters might be escaped in the URI
bin_file = dir + gDirUtilp->getDirDelimiter() + LLURI::unescape(mUri);
}
llifstream file(bin_file.c_str(), std::ios::binary);
if (!file.is_open())

View File

@ -412,17 +412,14 @@ void LLGLTFLoader::processNodeHierarchy(S32 node_idx, std::map<std::string, S32>
// Process this node's mesh if it has one
if (node.mMesh >= 0 && node.mMesh < mGLTFAsset.mMeshes.size())
{
LLMatrix4 transformation;
material_map mats;
LLModel* pModel = new LLModel(volume_params, 0.f);
const LL::GLTF::Mesh& mesh = mGLTFAsset.mMeshes[node.mMesh];
// Get base mesh name and track usage
std::string base_name = getLodlessLabel(mesh);
// Get base node name and track usage
// Potentially multiple nodes can reuse the same mesh and Collada used
// node name instead of mesh name, so for consistency use node name if
// avaliable, node index otherwise.
std::string base_name = getLodlessLabel(node);
if (base_name.empty())
{
base_name = "mesh_" + std::to_string(node.mMesh);
base_name = "node_" + std::to_string(node_idx);
}
S32 instance_count = mesh_name_counts[base_name]++;
@ -433,6 +430,12 @@ void LLGLTFLoader::processNodeHierarchy(S32 node_idx, std::map<std::string, S32>
base_name = base_name + "_copy_" + std::to_string(instance_count);
}
LLMatrix4 transformation;
material_map mats;
LLModel* pModel = new LLModel(volume_params, 0.f);
const LL::GLTF::Mesh& mesh = mGLTFAsset.mMeshes[node.mMesh];
if (populateModelFromMesh(pModel, base_name, mesh, node, mats) &&
(LLModel::NO_ERRORS == pModel->getStatus()) &&
validate_model(pModel))
@ -652,6 +655,14 @@ std::string LLGLTFLoader::processTexture(S32 texture_index, const std::string& t
filename = filename.substr(pos + 1);
}
std::string dir = gDirUtilp->getDirName(mFilename);
std::string full_path = dir + gDirUtilp->getDirDelimiter() + filename;
if (!gDirUtilp->fileExists(full_path) && filename.find("data:") == std::string::npos)
{
// Uri might be escaped
filename = LLURI::unescape(filename);
}
LL_INFOS("GLTF_IMPORT") << "Found texture: " << filename << " for material: " << material_name << LL_ENDL;
LLSD args;
@ -1810,13 +1821,13 @@ size_t LLGLTFLoader::getSuffixPosition(const std::string &label)
return -1;
}
std::string LLGLTFLoader::getLodlessLabel(const LL::GLTF::Mesh& mesh)
std::string LLGLTFLoader::getLodlessLabel(const LL::GLTF::Node& node)
{
size_t ext_pos = getSuffixPosition(mesh.mName);
size_t ext_pos = getSuffixPosition(node.mName);
if (ext_pos != -1)
{
return mesh.mName.substr(0, ext_pos);
return node.mName.substr(0, ext_pos);
}
return mesh.mName;
return node.mName;
}

View File

@ -170,7 +170,7 @@ private:
void notifyUnsupportedExtension(bool unsupported);
static size_t getSuffixPosition(const std::string& label);
static std::string getLodlessLabel(const LL::GLTF::Mesh& mesh);
static std::string getLodlessLabel(const LL::GLTF::Node& mesh);
// bool mPreprocessGLTF;

View File

@ -167,7 +167,7 @@ std::map<S32, std::string> LLTeleportRequest::sTeleportStatusName = { { kPending
class LLTeleportRequestViaLandmark : public LLTeleportRequest
{
public:
LLTeleportRequestViaLandmark(const LLUUID &pLandmarkId);
LLTeleportRequestViaLandmark(const LLUUID &pLandmarkId, bool log = true);
virtual ~LLTeleportRequestViaLandmark();
virtual void toOstream(std::ostream& os) const;
@ -179,6 +179,7 @@ public:
protected:
inline const LLUUID &getLandmarkId() const {return mLandmarkId;};
bool mLogOnDestruction = true;
private:
LLUUID mLandmarkId;
@ -5008,16 +5009,25 @@ void LLTeleportRequest::toOstream(std::ostream& os) const
//-----------------------------------------------------------------------------
// LLTeleportRequestViaLandmark
//-----------------------------------------------------------------------------
LLTeleportRequestViaLandmark::LLTeleportRequestViaLandmark(const LLUUID &pLandmarkId)
: LLTeleportRequest(),
mLandmarkId(pLandmarkId)
LLTeleportRequestViaLandmark::LLTeleportRequestViaLandmark(const LLUUID &pLandmarkId, bool log)
: LLTeleportRequest()
, mLandmarkId(pLandmarkId)
, mLogOnDestruction(true)
{
LL_INFOS("Teleport") << "LLTeleportRequestViaLandmark created, " << *this << LL_ENDL;
if (log)
{
// Workaround to not log twice for LLTeleportRequestViaLure, besides this wouldn't have logged fully.
LL_INFOS("Teleport") << "LLTeleportRequestViaLandmark created, " << *this << LL_ENDL;
}
}
LLTeleportRequestViaLandmark::~LLTeleportRequestViaLandmark()
{
LL_INFOS("Teleport") << "~LLTeleportRequestViaLandmark, " << *this << LL_ENDL;
if (mLogOnDestruction)
{
// Workaround to not crash on toOstream for derived classes and to not log twice.
LL_INFOS("Teleport") << "~LLTeleportRequestViaLandmark, " << *this << LL_ENDL;
}
}
void LLTeleportRequestViaLandmark::toOstream(std::ostream& os) const
@ -5047,16 +5057,20 @@ void LLTeleportRequestViaLandmark::restartTeleport()
// LLTeleportRequestViaLure
//-----------------------------------------------------------------------------
LLTeleportRequestViaLure::LLTeleportRequestViaLure(const LLUUID &pLureId, bool pIsLureGodLike)
: LLTeleportRequestViaLandmark(pLureId),
LLTeleportRequestViaLure::LLTeleportRequestViaLure(const LLUUID& pLureId, bool pIsLureGodLike)
: LLTeleportRequestViaLandmark(pLureId, false),
mIsLureGodLike(pIsLureGodLike)
{
LL_INFOS("Teleport") << "LLTeleportRequestViaLure created" << LL_ENDL;
LL_INFOS("Teleport") << "LLTeleportRequestViaLure created: " << *this << LL_ENDL;
}
LLTeleportRequestViaLure::~LLTeleportRequestViaLure()
{
LL_INFOS("Teleport") << "~LLTeleportRequestViaLure" << LL_ENDL;
if (mLogOnDestruction)
{
LL_INFOS("Teleport") << "~LLTeleportRequestViaLure: " << *this << LL_ENDL;
mLogOnDestruction = false;
}
}
void LLTeleportRequestViaLure::toOstream(std::ostream& os) const

View File

@ -538,6 +538,27 @@ LLInventoryItem* LLAgentWearables::getWearableInventoryItem(LLWearableType::ETyp
return item;
}
const S32 LLAgentWearables::getWearableIdxFromItem(const LLViewerInventoryItem* item) const
{
if (!item) return -1;
if (!item->isWearableType()) return -1;
LLWearableType::EType type = item->getWearableType();
U32 wearable_count = getWearableCount(type);
if (0 == wearable_count) return -1;
const LLUUID& asset_id = item->getAssetUUID();
for (U32 i = 0; i < wearable_count; ++i)
{
const LLViewerWearable* wearable = getViewerWearable(type, i);
if (!wearable) continue;
if (wearable->getAssetID() != asset_id) continue;
return i;
}
return -1;
}
const LLViewerWearable* LLAgentWearables::getWearableFromItemID(const LLUUID& item_id) const
{
const LLUUID& base_item_id = gInventory.getLinkedItemID(item_id);
@ -1471,7 +1492,7 @@ bool LLAgentWearables::moveWearable(const LLViewerInventoryItem* item, bool clos
LLWearableType::EType type = item->getWearableType();
U32 wearable_count = getWearableCount(type);
if (0 == wearable_count) return false;
if (wearable_count < 2) return false;
const LLUUID& asset_id = item->getAssetUUID();

View File

@ -87,6 +87,7 @@ public:
public:
const LLUUID getWearableItemID(LLWearableType::EType type, U32 index /*= 0*/) const;
const LLUUID getWearableAssetID(LLWearableType::EType type, U32 index /*= 0*/) const;
const S32 getWearableIdxFromItem(const LLViewerInventoryItem* item) const;
const LLViewerWearable* getWearableFromItemID(const LLUUID& item_id) const;
LLViewerWearable* getWearableFromItemID(const LLUUID& item_id);
LLViewerWearable* getWearableFromAssetID(const LLUUID& asset_id);

View File

@ -57,42 +57,42 @@
- (void) applicationDidFinishLaunching:(NSNotification *)notification
{
// Call constructViewer() first so our logging subsystem is in place. This
// risks missing crashes in the LLAppViewerMacOSX constructor, but for
// present purposes it's more important to get the startup sequence
// properly logged.
// Someday I would like to modify the logging system so that calls before
// it's initialized are cached in a std::ostringstream and then, once it's
// initialized, "played back" into whatever handlers have been set up.
constructViewer();
// Call constructViewer() first so our logging subsystem is in place. This
// risks missing crashes in the LLAppViewerMacOSX constructor, but for
// present purposes it's more important to get the startup sequence
// properly logged.
// Someday I would like to modify the logging system so that calls before
// it's initialized are cached in a std::ostringstream and then, once it's
// initialized, "played back" into whatever handlers have been set up.
constructViewer();
#if defined(LL_BUGSPLAT)
infos("bugsplat setup");
// Engage BugsplatStartupManager *before* calling initViewer() to handle
// any crashes during initialization.
// https://www.bugsplat.com/docs/platforms/os-x#initialization
[BugsplatStartupManager sharedManager].autoSubmitCrashReport = YES;
[BugsplatStartupManager sharedManager].askUserDetails = NO;
[BugsplatStartupManager sharedManager].delegate = self;
[[BugsplatStartupManager sharedManager] start];
// Engage BugsplatStartupManager *before* calling initViewer() to handle
// any crashes during initialization.
// https://www.bugsplat.com/docs/platforms/os-x#initialization
[BugsplatStartupManager sharedManager].autoSubmitCrashReport = YES;
[BugsplatStartupManager sharedManager].askUserDetails = NO;
[BugsplatStartupManager sharedManager].delegate = self;
[[BugsplatStartupManager sharedManager] start];
#endif
infos("post-bugsplat setup");
frameTimer = nil;
frameTimer = nil;
[self languageUpdated];
[self languageUpdated];
if (initViewer())
{
// Set up recurring calls to oneFrame (repeating timer with timeout 0)
// until applicationShouldTerminate.
frameTimer = [NSTimer scheduledTimerWithTimeInterval:0.0 target:self
selector:@selector(oneFrame) userInfo:nil repeats:YES];
} else {
exit(0);
}
if (initViewer())
{
// Set up recurring calls to oneFrame (repeating timer with timeout 0)
// until applicationShouldTerminate.
frameTimer = [NSTimer scheduledTimerWithTimeInterval:0.0 target:self
selector:@selector(oneFrame) userInfo:nil repeats:YES];
} else {
exit(0);
}
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(languageUpdated) name:@"NSTextInputContextKeyboardSelectionDidChangeNotification" object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(languageUpdated) name:@"NSTextInputContextKeyboardSelectionDidChangeNotification" object:nil];
// [[NSAppleEventManager sharedAppleEventManager] setEventHandler:self andSelector:@selector(handleGetURLEvent:withReplyEvent:) forEventClass:kInternetEventClass andEventID:kAEGetURL];
}
@ -110,74 +110,74 @@
- (void) applicationDidBecomeActive:(NSNotification *)notification
{
callWindowFocus();
callWindowFocus();
}
- (void) applicationDidResignActive:(NSNotification *)notification
{
callWindowUnfocus();
callWindowUnfocus();
}
- (void) applicationDidHide:(NSNotification *)notification
{
callWindowHide();
callWindowHide();
}
- (void) applicationDidUnhide:(NSNotification *)notification
{
callWindowUnhide();
callWindowUnhide();
}
- (NSApplicationTerminateReply) applicationShouldTerminate:(NSApplication *)sender
{
// run one frame to assess state
if (!pumpMainLoop())
{
// pumpMainLoop() returns true when done, false if it wants to be
// called again. Since it returned false, do not yet cancel
// frameTimer.
handleQuit();
[[NSApplication sharedApplication] stopModal];
return NSTerminateCancel;
} else {
// pumpMainLoop() returned true: it's done. Okay, done with frameTimer.
[frameTimer release];
cleanupViewer();
return NSTerminateNow;
}
// run one frame to assess state
if (!pumpMainLoop())
{
// pumpMainLoop() returns true when done, false if it wants to be
// called again. Since it returned false, do not yet cancel
// frameTimer.
handleQuit();
[[NSApplication sharedApplication] stopModal];
return NSTerminateCancel;
} else {
// pumpMainLoop() returned true: it's done. Okay, done with frameTimer.
[frameTimer release];
cleanupViewer();
return NSTerminateNow;
}
}
- (void) oneFrame
{
bool appExiting = pumpMainLoop();
if (appExiting)
{
// Once pumpMainLoop() reports that we're done, cancel frameTimer:
// stop the repetitive calls.
[frameTimer release];
[[NSApplication sharedApplication] terminate:self];
}
bool appExiting = pumpMainLoop();
if (appExiting)
{
// Once pumpMainLoop() reports that we're done, cancel frameTimer:
// stop the repetitive calls.
[frameTimer release];
[[NSApplication sharedApplication] terminate:self];
}
}
- (void) showInputWindow:(bool)show withEvent:(NSEvent*)textEvent
{
if (![self romanScript])
{
if (show)
{
NSLog(@"Showing input window.");
[inputWindow makeKeyAndOrderFront:inputWindow];
if (![self romanScript])
{
if (show)
{
NSLog(@"Showing input window.");
[inputWindow makeKeyAndOrderFront:inputWindow];
if (textEvent != nil)
{
[[inputView inputContext] discardMarkedText];
[[inputView inputContext] handleEvent:textEvent];
}
} else {
NSLog(@"Hiding input window.");
[inputWindow orderOut:inputWindow];
[window makeKeyAndOrderFront:window];
}
}
} else {
NSLog(@"Hiding input window.");
[inputWindow orderOut:inputWindow];
[window makeKeyAndOrderFront:window];
}
}
}
// This will get called multiple times by NSNotificationCenter.
@ -187,15 +187,15 @@
- (void) languageUpdated
{
TISInputSourceRef currentInput = TISCopyCurrentKeyboardInputSource();
CFArrayRef languages = (CFArrayRef)TISGetInputSourceProperty(currentInput, kTISPropertyInputSourceLanguages);
TISInputSourceRef currentInput = TISCopyCurrentKeyboardInputSource();
CFArrayRef languages = (CFArrayRef)TISGetInputSourceProperty(currentInput, kTISPropertyInputSourceLanguages);
#if 0 // In the event of ever needing to add new language sources, change this to 1 and watch the terminal for "languages:"
NSLog(@"languages: %@", TISGetInputSourceProperty(currentInput, kTISPropertyInputSourceLanguages));
NSLog(@"languages: %@", TISGetInputSourceProperty(currentInput, kTISPropertyInputSourceLanguages));
#endif
// Typically the language we want is going to be the very first result in the array.
currentInputLanguage = (NSString*)CFArrayGetValueAtIndex(languages, 0);
// Typically the language we want is going to be the very first result in the array.
currentInputLanguage = (NSString*)CFArrayGetValueAtIndex(languages, 0);
}
- (bool) romanScript
@ -209,7 +209,7 @@
return false;
}
}
return true;
}
@ -313,11 +313,11 @@ struct AttachmentInfo
// We "happen to know" that info[0].basename is "SecondLife.old" -- due to
// the fact that BugsplatMac only notices a crash during the viewer run
// following the crash.
// following the crash.
// The Bugsplat service doesn't respect the MIME type above when returning
// the log data to a browser, so take this opportunity to rename the file
// from <base>.old to <base>_log.txt
info[0].basename =
info[0].basename =
boost::filesystem::path(info[0].pathname).stem().string() + "_log.txt";
infos("attachmentsForBugsplatStartupManager attaching log " + info[0].basename);
@ -373,7 +373,7 @@ struct AttachmentInfo
{
[super sendEvent:event];
if ([event type] == NSEventTypeKeyUp && ([event modifierFlags] & NSEventModifierFlagCommand))
{
{
[[self keyWindow] sendEvent:event];
}
}

View File

@ -1500,6 +1500,27 @@ void wear_on_avatar_cb(const LLUUID& inv_item, bool do_replace = false)
}
}
bool needs_to_replace(LLViewerInventoryItem* item_to_wear, bool & first_for_object, std::vector<bool>& first_for_type, bool replace)
{
bool res = false;
LLAssetType::EType type = item_to_wear->getType();
if (type == LLAssetType::AT_OBJECT)
{
res = first_for_object && replace;
first_for_object = false;
}
else if (type == LLAssetType::AT_CLOTHING)
{
LLWearableType::EType wtype = item_to_wear->getWearableType();
if (wtype >= 0 && wtype < LLWearableType::WT_COUNT)
{
res = first_for_type[wtype] && replace;
first_for_type[wtype] = false;
}
}
return res;
}
void LLAppearanceMgr::wearItemsOnAvatar(const uuid_vec_t& item_ids_to_wear,
bool do_update,
bool replace,
@ -1508,7 +1529,8 @@ void LLAppearanceMgr::wearItemsOnAvatar(const uuid_vec_t& item_ids_to_wear,
LL_DEBUGS("UIUsage") << "wearItemsOnAvatar" << LL_ENDL;
LLUIUsage::instance().logCommand("Avatar.WearItem");
bool first = true;
bool first_for_object = true;
std::vector<bool> first_for_type(LLWearableType::WT_COUNT, true);
LLInventoryObject::const_object_list_t items_to_link;
@ -1516,9 +1538,6 @@ void LLAppearanceMgr::wearItemsOnAvatar(const uuid_vec_t& item_ids_to_wear,
it != item_ids_to_wear.end();
++it)
{
replace = first && replace;
first = false;
const LLUUID& item_id_to_wear = *it;
if (item_id_to_wear.isNull())
@ -1537,8 +1556,9 @@ void LLAppearanceMgr::wearItemsOnAvatar(const uuid_vec_t& item_ids_to_wear,
if (gInventory.isObjectDescendentOf(item_to_wear->getUUID(), gInventory.getLibraryRootFolderID()))
{
LL_DEBUGS("Avatar") << "inventory item in library, will copy and wear "
<< item_to_wear->getName() << " id " << item_id_to_wear << LL_ENDL;
LLPointer<LLInventoryCallback> cb = new LLBoostFuncInventoryCallback(boost::bind(wear_on_avatar_cb,_1,replace));
<< item_to_wear->getName() << " id " << item_id_to_wear << LL_ENDL;
bool replace_item = needs_to_replace(item_to_wear, first_for_object, first_for_type, replace);
LLPointer<LLInventoryCallback> cb = new LLBoostFuncInventoryCallback(boost::bind(wear_on_avatar_cb, _1, replace_item));
copy_inventory_item(gAgent.getID(), item_to_wear->getPermissions().getOwner(),
item_to_wear->getUUID(), LLUUID::null, std::string(), cb);
continue;
@ -1576,7 +1596,8 @@ void LLAppearanceMgr::wearItemsOnAvatar(const uuid_vec_t& item_ids_to_wear,
}
LLWearableType::EType type = item_to_wear->getWearableType();
S32 wearable_count = gAgentWearables.getWearableCount(type);
if ((replace && wearable_count != 0) || !gAgentWearables.canAddWearable(type))
bool replace_item = needs_to_replace(item_to_wear, first_for_object, first_for_type, replace);
if ((replace_item && wearable_count != 0) || !gAgentWearables.canAddWearable(type))
{
LLUUID item_id = gAgentWearables.getWearableItemID(item_to_wear->getWearableType(),
wearable_count-1);
@ -1605,7 +1626,13 @@ void LLAppearanceMgr::wearItemsOnAvatar(const uuid_vec_t& item_ids_to_wear,
case LLAssetType::AT_OBJECT:
{
rez_attachment(item_to_wear, NULL, replace);
// Note that this will replace only first attachment regardless of attachment point,
// so if user is wearing two items over other two on different attachment points,
// only one will be replaced.
// Unfortunately we have no way to determine attachment point from inventory item.
// We might want to forbid wearing multiple objects with replace option in future.
bool replace_item = needs_to_replace(item_to_wear, first_for_object, first_for_type, replace);
rez_attachment(item_to_wear, NULL, replace_item);
}
break;
@ -4218,37 +4245,54 @@ bool LLAppearanceMgr::moveWearable(LLViewerInventoryItem* item, bool closer_to_b
if (item->getType() != LLAssetType::AT_CLOTHING) return false;
if (!gInventory.isObjectDescendentOf(item->getUUID(), getCOF())) return false;
S32 pos = gAgentWearables.getWearableIdxFromItem(item);
if (pos < 0) return false; // Not found
U32 count = gAgentWearables.getWearableCount(item->getWearableType());
if (count < 2) return false; // Nothing to swap with
if (closer_to_body)
{
if (pos == 0) return false; // already first
}
else
{
if (pos == count - 1) return false; // already last
}
U32 old_pos = (U32)pos;
U32 swap_with = closer_to_body ? old_pos - 1 : old_pos + 1;
LLUUID swap_item_id = gAgentWearables.getWearableItemID(item->getWearableType(), swap_with);
// Find link item from item id.
LLInventoryModel::cat_array_t cats;
LLInventoryModel::item_array_t items;
LLFindWearablesOfType filter_wearables_of_type(item->getWearableType());
gInventory.collectDescendentsIf(getCOF(), cats, items, true, filter_wearables_of_type);
if (items.empty()) return false;
// We assume that the items have valid descriptions.
std::sort(items.begin(), items.end(), WearablesOrderComparator(item->getWearableType()));
LLViewerInventoryItem* swap_item = nullptr;
for (auto iter : items)
{
if (iter->getLinkedUUID() == swap_item_id)
{
swap_item = iter.get();
break;
}
}
if (!swap_item)
{
return false;
}
if (closer_to_body && items.front() == item) return false;
if (!closer_to_body && items.back() == item) return false;
// Description is supposed to hold sort index, but user could have changed
// order rapidly and there might be a state mismatch between description
// and gAgentWearables, trust gAgentWearables over description.
// Generate new description.
std::string new_desc = build_order_string(item->getWearableType(), old_pos);
swap_item->setDescription(new_desc);
new_desc = build_order_string(item->getWearableType(), swap_with);
item->setDescription(new_desc);
LLInventoryModel::item_array_t::iterator it = std::find(items.begin(), items.end(), item);
if (items.end() == it) return false;
//swapping descriptions
closer_to_body ? --it : ++it;
LLViewerInventoryItem* swap_item = *it;
if (!swap_item) return false;
std::string tmp = swap_item->getActualDescription();
swap_item->setDescription(item->getActualDescription());
item->setDescription(tmp);
// LL_DEBUGS("Inventory") << "swap, item "
// << ll_pretty_print_sd(item->asLLSD())
// << " swap_item "
// << ll_pretty_print_sd(swap_item->asLLSD()) << LL_ENDL;
// FIXME switch to use AISv3 where supported.
//items need to be updated on a dataserver
item->setComplete(true);
item->updateServer(false);
gInventory.updateItem(item);

View File

@ -1265,6 +1265,7 @@ bool LLAppViewer::init()
LLViewerCamera::createInstance();
LL::GLTFSceneManager::createInstance();
gSavedSettings.setU32("DebugQualityPerformance", gSavedSettings.getU32("RenderQualityPerformance"));
#if LL_WINDOWS
if (!mSecondInstance)
@ -2397,7 +2398,6 @@ void LLAppViewer::initLoggingAndGetLastDuration()
if (gDirUtilp->fileExists(user_data_path_cef_log))
{
std::string user_data_path_cef_old = gDirUtilp->getExpandedFilename(LL_PATH_LOGS, "cef.old");
LLFile::remove(user_data_path_cef_old, ENOENT);
LLFile::rename(user_data_path_cef_log, user_data_path_cef_old);
}
}
@ -3944,8 +3944,15 @@ void LLAppViewer::processMarkerFiles()
else if (marker_is_same_version)
{
// the file existed, is ours, and matched our version, so we can report on what it says
LL_INFOS("MarkerFile") << "Exec marker '"<< mMarkerFileName << "' found; last exec crashed" << LL_ENDL;
LL_INFOS("MarkerFile") << "Exec marker '"<< mMarkerFileName << "' found; last exec crashed or froze" << LL_ENDL;
#if LL_WINDOWS && LL_BUGSPLAT
// bugsplat will set correct state in bugsplatSendLog
// Might be more accurate to rename this one into 'unknown'
gLastExecEvent = LAST_EXEC_FROZE;
#else
gLastExecEvent = LAST_EXEC_OTHER_CRASH;
#endif // LL_WINDOWS
}
else
{
@ -4194,7 +4201,7 @@ void LLAppViewer::earlyExit(const std::string& name, const LLSD& substitutions)
// case where we need the viewer to exit without any need for notifications
void LLAppViewer::earlyExitNoNotify()
{
LL_WARNS() << "app_early_exit with no notification: " << LL_ENDL;
LL_WARNS() << "app_early_exit with no notification." << LL_ENDL;
gDoDisconnect = true;
finish_early_exit( LLSD(), LLSD() );
}
@ -4473,6 +4480,7 @@ void LLAppViewer::purgeCache()
LLAppViewer::getTextureCache()->purgeCache(LL_PATH_CACHE);
LLVOCache::getInstance()->removeCache(LL_PATH_CACHE);
LLViewerShaderMgr::instance()->clearShaderCache();
purgeCefStaleCaches();
gDirUtilp->deleteFilesInDir(gDirUtilp->getExpandedFilename(LL_PATH_CACHE, ""), "*");
}
@ -5407,6 +5415,12 @@ void LLAppViewer::createErrorMarker(eLastExecEvent error_code) const
}
}
bool LLAppViewer::errorMarkerExists() const
{
std::string error_marker_file = gDirUtilp->getExpandedFilename(LL_PATH_LOGS, ERROR_MARKER_FILE_NAME);
return LLAPRFile::isExist(error_marker_file, NULL, LL_APR_RB);
}
void LLAppViewer::outOfMemorySoftQuit()
{
if (!mQuitRequested)
@ -5538,7 +5552,10 @@ void LLAppViewer::idleNetwork()
add(LLStatViewer::NUM_NEW_OBJECTS, gObjectList.mNumNewObjects);
// Retransmit unacknowledged packets.
gXferManager->retransmitUnackedPackets();
if (gXferManager)
{
gXferManager->retransmitUnackedPackets();
}
gAssetStorage->checkForTimeouts();
gViewerThrottle.setBufferLoadRate(gMessageSystem->getBufferLoadRate());
gViewerThrottle.updateDynamicThrottle();

View File

@ -149,6 +149,12 @@ public:
std::string getWindowTitle() const; // The window display name.
void forceDisconnect(const std::string& msg); // Force disconnection, with a message to the user.
// sendSimpleLogoutRequest does not create a marker file.
// Meant for lost network case, and for forced shutdowns,
// to at least attempt to remove the ghost from the world.
void sendSimpleLogoutRequest();
void badNetworkHandler(); // Cause a crash state due to bad network packet.
bool hasSavedFinalSnapshot() { return mSavedFinalSnapshot; }
@ -245,6 +251,7 @@ public:
// Writes an error code into the error_marker file for use on next startup.
void createErrorMarker(eLastExecEvent error_code) const;
bool errorMarkerExists() const;
// Attempt a 'soft' quit with disconnect and saving of settings/cache.
// Intended to be thread safe.
@ -311,10 +318,6 @@ private:
void sendLogoutRequest();
void disconnectViewer();
// Does not create a marker file. For lost network case,
// to at least attempt to remove the ghost from the world.
void sendSimpleLogoutRequest();
// *FIX: the app viewer class should be some sort of singleton, no?
// Perhaps its child class is the singleton and this should be an abstract base.
static LLAppViewer* sInstance;

View File

@ -5,27 +5,27 @@
* $LicenseInfo:firstyear=2007&license=viewerlgpl$
* Second Life Viewer Source Code
* Copyright (C) 2010, Linden Research, Inc.
*
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation;
* version 2.1 of the License only.
*
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
*
* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
*/
#if !defined LL_DARWIN
#error "Use only with macOS"
#error "Use only with macOS"
#endif
#import <Cocoa/Cocoa.h>

View File

@ -172,6 +172,22 @@ namespace
<< '/' << loc.mV[1]
<< '/' << loc.mV[2])));
}
LLAppViewer* app = LLAppViewer::instance();
if (!app->isSecondInstance() && !app->errorMarkerExists())
{
// If marker doesn't exist, create a marker with 'other' code for next launch
// otherwise don't override existing file
// Any unmarked crashes will be considered as freezes
if (app->logoutRequestSent())
{
app->createErrorMarker(LAST_EXEC_LOGOUT_CRASH);
}
else
{
app->createErrorMarker(LAST_EXEC_OTHER_CRASH);
}
}
} // MDSCB_EXCEPTIONCODE
return false;

View File

@ -215,7 +215,8 @@ public:
LLUUID obj_id = mObjectData["object_id"];
if (obj_id.notNull())
{
return nullptr != gObjectList.findObject(mAvatarID);
LLViewerObject* object = gObjectList.findObject(obj_id);
return object && object->isReachable();
}
return false;
}
@ -441,6 +442,7 @@ public:
time_t current_time = time_corrected();
time_t message_time = (time_t)(current_time - LLFrameTimer::getElapsedSeconds() + mTime);
// Report abuse shouldn't use AM/PM, use 24-hour time
time_string = "[" + LLTrans::getString("TimeMonth") + "]/["
+ LLTrans::getString("TimeDay") + "]/["
+ LLTrans::getString("TimeYear") + "] ["
@ -1117,7 +1119,11 @@ LLChatHistory::LLChatHistory(const LLChatHistory::Params& p)
mEditor = LLUICtrlFactory::create<LLTextEditor>(editor_params, this);
mEditor->setIsFriendCallback(LLAvatarActions::isFriend);
mEditor->setIsObjectBlockedCallback(boost::bind(&LLMuteList::isMuted, LLMuteList::getInstance(), _1, _2, 0));
mEditor->setIsObjectReachableCallback([](const LLUUID& obj_id)
{
LLViewerObject* object = gObjectList.findObject(obj_id);
return object && object->isReachable();
});
}
LLSD LLChatHistory::getValue() const

View File

@ -118,11 +118,21 @@ const std::string LLConversation::createTimestamp(const U64Seconds& utc_time)
LLSD substitution;
substitution["datetime"] = (S32)utc_time.value();
timeStr = "["+LLTrans::getString ("TimeMonth")+"]/["
+LLTrans::getString ("TimeDay")+"]/["
+LLTrans::getString ("TimeYear")+"] ["
+LLTrans::getString ("TimeHour")+"]:["
+LLTrans::getString ("TimeMin")+"]";
static bool use_24h = gSavedSettings.getBOOL("Use24HourClock");
timeStr = "[" + LLTrans::getString("TimeMonth") + "]/["
+ LLTrans::getString("TimeDay") + "]/["
+ LLTrans::getString("TimeYear") + "] [";
if (use_24h)
{
timeStr += LLTrans::getString("TimeHour") + "]:["
+ LLTrans::getString("TimeMin") + "]";
}
else
{
timeStr += LLTrans::getString("TimeHour12") + "]:["
+ LLTrans::getString("TimeMin") + "] ["
+ LLTrans::getString("TimeAMPM") + "]";
}
LLStringUtil::format (timeStr, substitution);

View File

@ -802,7 +802,10 @@ void LLBumpImageList::onSourceStandardLoaded( bool success, LLViewerFetchedTextu
}
src_vi->setExplicitFormat(GL_RGBA, GL_RGBA);
{
src_vi->createGLTexture(src_vi->getDiscardLevel(), nrm_image);
if (!src_vi->createGLTexture(src_vi->getDiscardLevel(), nrm_image))
{
LL_WARNS() << "Failed to create bump image texture for image " << src_vi->getID() << LL_ENDL;
}
}
}
}
@ -896,7 +899,10 @@ void LLBumpImageList::onSourceUpdated(LLViewerTexture* src, EBumpEffect bump_cod
LLImageGL* src_img = src->getGLTexture();
LLImageGL* dst_img = bump->getGLTexture();
dst_img->setSize(src->getWidth(), src->getHeight(), 4, 0);
if (!dst_img->setSize(src->getWidth(), src->getHeight(), 4, 0))
{
LL_WARNS() << "Failed to setSize for image " << bump->getID() << LL_ENDL;
}
dst_img->setUseMipMaps(true);
dst_img->setDiscardLevel(0);
dst_img->createGLTexture();

Some files were not shown because too many files have changed in this diff Show More