summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.github/workflows/build.yaml3
-rw-r--r--indra/llcommon/llleap.cpp11
-rwxr-xr-xindra/llcorehttp/llhttpconstants.cpp2
-rwxr-xr-xindra/llcorehttp/llhttpconstants.h2
-rw-r--r--indra/llimagej2coj/llimagej2coj.cpp18
-rw-r--r--indra/llinventory/llpermissions.cpp1
-rw-r--r--indra/llmath/llvolume.cpp28
-rw-r--r--indra/llmessage/llcoproceduremanager.cpp33
-rw-r--r--indra/llprimitive/llgltfmaterial.cpp31
-rw-r--r--indra/llprimitive/llgltfmaterial.h8
-rw-r--r--indra/llrender/llimagegl.cpp6
-rw-r--r--indra/llrender/llshadermgr.cpp1
-rw-r--r--indra/llui/llaccordionctrl.cpp8
-rw-r--r--indra/llui/llaccordionctrltab.cpp25
-rw-r--r--indra/llui/llemojidictionary.cpp11
-rw-r--r--indra/llui/llfolderview.cpp1
-rw-r--r--indra/llui/llfolderviewitem.cpp12
-rw-r--r--indra/llui/lltextbase.cpp22
-rw-r--r--indra/llui/lltextbase.h3
-rw-r--r--indra/llui/lltoolbar.cpp2
-rw-r--r--indra/llui/lltoolbar.h2
-rw-r--r--indra/llwebrtc/llwebrtc.cpp51
-rw-r--r--indra/llwebrtc/llwebrtc_impl.h5
-rw-r--r--indra/llwindow/llwindowcallbacks.cpp8
-rw-r--r--indra/llwindow/llwindowcallbacks.h3
-rw-r--r--indra/llwindow/llwindowmacosx.cpp2
-rw-r--r--indra/llwindow/llwindowsdl.cpp4
-rw-r--r--indra/llwindow/llwindowwin32.cpp49
-rw-r--r--indra/newview/app_settings/settings.xml27
-rw-r--r--indra/newview/gltf/accessor.cpp5
-rw-r--r--indra/newview/gltf/llgltfloader.cpp37
-rw-r--r--indra/newview/gltf/llgltfloader.h2
-rw-r--r--indra/newview/llagentwearables.cpp23
-rw-r--r--indra/newview/llagentwearables.h1
-rw-r--r--indra/newview/llappearancemgr.cpp106
-rw-r--r--indra/newview/llappviewer.cpp24
-rw-r--r--indra/newview/llappviewer.h11
-rw-r--r--indra/newview/llappviewerwin32.cpp9
-rw-r--r--indra/newview/llchathistory.cpp10
-rw-r--r--indra/newview/llconversationlog.cpp20
-rw-r--r--indra/newview/lleventpoll.cpp64
-rw-r--r--indra/newview/lleventpoll.h25
-rw-r--r--indra/newview/llfetchedgltfmaterial.cpp8
-rw-r--r--indra/newview/llfetchedgltfmaterial.h1
-rw-r--r--indra/newview/llfloaterdisplayname.cpp22
-rw-r--r--indra/newview/llfloaterfixedenvironment.cpp13
-rw-r--r--indra/newview/llfloaterimsessiontab.cpp15
-rw-r--r--indra/newview/llfloaterinspect.cpp3
-rw-r--r--indra/newview/llfloaterland.cpp3
-rw-r--r--indra/newview/llfloaterpreference.cpp12
-rw-r--r--indra/newview/llfloaterpreference.h2
-rw-r--r--indra/newview/llfloatertoybox.cpp2
-rw-r--r--indra/newview/llfloaterurlentry.cpp10
-rw-r--r--indra/newview/llgltfmateriallist.cpp4
-rw-r--r--indra/newview/llimview.cpp4
-rw-r--r--indra/newview/llinventoryitemslist.cpp85
-rw-r--r--indra/newview/llinventoryitemslist.h13
-rw-r--r--indra/newview/lllogchat.cpp48
-rw-r--r--indra/newview/lllogininstance.cpp2
-rw-r--r--indra/newview/llmeshrepository.cpp537
-rw-r--r--indra/newview/llmeshrepository.h32
-rw-r--r--indra/newview/llmodelpreview.cpp10
-rw-r--r--indra/newview/llmodelpreview.h2
-rw-r--r--indra/newview/llnotificationlistitem.cpp21
-rw-r--r--indra/newview/lloutfitgallery.cpp1
-rw-r--r--indra/newview/lloutfitslist.cpp8
-rw-r--r--indra/newview/llpanellandmarkinfo.cpp4
-rw-r--r--indra/newview/llpanelprofile.cpp2
-rw-r--r--indra/newview/llpanelteleporthistory.cpp15
-rw-r--r--indra/newview/llpanelvoicedevicesettings.cpp29
-rw-r--r--indra/newview/llscriptfloater.cpp34
-rw-r--r--indra/newview/llselectmgr.cpp103
-rw-r--r--indra/newview/llsettingspicker.cpp6
-rw-r--r--indra/newview/llsidepaneliteminfo.cpp3
-rw-r--r--indra/newview/llstartup.cpp21
-rw-r--r--indra/newview/lltexturectrl.cpp20
-rw-r--r--indra/newview/lltexturefetch.cpp38
-rw-r--r--indra/newview/lltoastgroupnotifypanel.cpp19
-rw-r--r--indra/newview/lltoolbarview.cpp29
-rw-r--r--indra/newview/lltoolbarview.h2
-rw-r--r--indra/newview/lltooldraganddrop.cpp215
-rw-r--r--indra/newview/llviewercontrol.cpp18
-rw-r--r--indra/newview/llviewerdisplay.cpp5
-rw-r--r--indra/newview/llviewerinventory.cpp4
-rw-r--r--indra/newview/llviewermenu.cpp2
-rw-r--r--indra/newview/llviewerobject.cpp130
-rw-r--r--indra/newview/llviewerobject.h4
-rw-r--r--indra/newview/llviewertexture.cpp2
-rw-r--r--indra/newview/llviewerwindow.cpp37
-rw-r--r--indra/newview/llviewerwindow.h3
-rw-r--r--indra/newview/llvocache.cpp2
-rw-r--r--indra/newview/llvoicewebrtc.cpp14
-rw-r--r--indra/newview/llvovolume.cpp4
-rw-r--r--indra/newview/llworldmap.cpp18
-rw-r--r--indra/newview/pipeline.cpp25
-rw-r--r--indra/newview/skins/default/xui/da/sidepanel_item_info.xml3
-rw-r--r--indra/newview/skins/default/xui/de/sidepanel_item_info.xml3
-rw-r--r--indra/newview/skins/default/xui/de/strings.xml4
-rw-r--r--indra/newview/skins/default/xui/en/floater_about_land.xml3
-rw-r--r--indra/newview/skins/default/xui/en/floater_inspect.xml4
-rw-r--r--indra/newview/skins/default/xui/en/notifications.xml2
-rw-r--r--indra/newview/skins/default/xui/en/panel_group_roles.xml1179
-rw-r--r--indra/newview/skins/default/xui/en/panel_landmark_info.xml4
-rw-r--r--indra/newview/skins/default/xui/en/panel_place_profile.xml4
-rw-r--r--indra/newview/skins/default/xui/en/panel_preferences_general.xml31
-rw-r--r--indra/newview/skins/default/xui/en/panel_profile_firstlife.xml1
-rw-r--r--indra/newview/skins/default/xui/en/panel_profile_secondlife.xml3
-rw-r--r--indra/newview/skins/default/xui/en/panel_sound_devices.xml4
-rw-r--r--indra/newview/skins/default/xui/en/sidepanel_item_info.xml4
-rw-r--r--indra/newview/tests/llworldmap_test.cpp6
110 files changed, 2393 insertions, 1244 deletions
diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml
index 8f89db0923..5a5628ede2 100644
--- a/.github/workflows/build.yaml
+++ b/.github/workflows/build.yaml
@@ -1,6 +1,7 @@
name: Build
on:
+ workflow_dispatch:
pull_request:
push:
branches: ["main", "release/*", "project/*"]
@@ -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 }}"
diff --git a/indra/llcommon/llleap.cpp b/indra/llcommon/llleap.cpp
index 662a2511cd..ada6b9519e 100644
--- a/indra/llcommon/llleap.cpp
+++ b/indra/llcommon/llleap.cpp
@@ -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;
diff --git a/indra/llcorehttp/llhttpconstants.cpp b/indra/llcorehttp/llhttpconstants.cpp
index 40d6c7506c..7a671543d9 100755
--- a/indra/llcorehttp/llhttpconstants.cpp
+++ b/indra/llcorehttp/llhttpconstants.cpp
@@ -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");
diff --git a/indra/llcorehttp/llhttpconstants.h b/indra/llcorehttp/llhttpconstants.h
index 583f9fbcb7..71c1dfa173 100755
--- a/indra/llcorehttp/llhttpconstants.h
+++ b/indra/llcorehttp/llhttpconstants.h
@@ -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
diff --git a/indra/llimagej2coj/llimagej2coj.cpp b/indra/llimagej2coj/llimagej2coj.cpp
index 5fa53657a0..7cfadb889d 100644
--- a/indra/llimagej2coj/llimagej2coj.cpp
+++ b/indra/llimagej2coj/llimagej2coj.cpp
@@ -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;
diff --git a/indra/llinventory/llpermissions.cpp b/indra/llinventory/llpermissions.cpp
index ebf7445c65..b17e22e465 100644
--- a/indra/llinventory/llpermissions.cpp
+++ b/indra/llinventory/llpermissions.cpp
@@ -774,6 +774,7 @@ void LLPermissions::importLLSD(const LLSD& sd_perm)
}
}
+ fixOwnership();
fix();
}
diff --git a/indra/llmath/llvolume.cpp b/indra/llmath/llvolume.cpp
index 7c60253618..b3cb278d59 100644
--- a/indra/llmath/llvolume.cpp
+++ b/indra/llmath/llvolume.cpp
@@ -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;
}
diff --git a/indra/llmessage/llcoproceduremanager.cpp b/indra/llmessage/llcoproceduremanager.cpp
index 563dd9459c..5c7b1c4235 100644
--- a/indra/llmessage/llcoproceduremanager.cpp
+++ b/indra/llmessage/llcoproceduremanager.cpp
@@ -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;
+ }
}
//-------------------------------------------------------------------------
diff --git a/indra/llprimitive/llgltfmaterial.cpp b/indra/llprimitive/llgltfmaterial.cpp
index cc4921416f..930222e3db 100644
--- a/indra/llprimitive/llgltfmaterial.cpp
+++ b/indra/llprimitive/llgltfmaterial.cpp
@@ -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);
+}
diff --git a/indra/llprimitive/llgltfmaterial.h b/indra/llprimitive/llgltfmaterial.h
index 10df4c8ee1..8d45cb6185 100644
--- a/indra/llprimitive/llgltfmaterial.h
+++ b/indra/llprimitive/llgltfmaterial.h
@@ -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);
diff --git a/indra/llrender/llimagegl.cpp b/indra/llrender/llimagegl.cpp
index 52738ec626..97ea6f67bd 100644
--- a/indra/llrender/llimagegl.cpp
+++ b/indra/llrender/llimagegl.cpp
@@ -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
diff --git a/indra/llrender/llshadermgr.cpp b/indra/llrender/llshadermgr.cpp
index 9cdd02f403..e9bbdeead5 100644
--- a/indra/llrender/llshadermgr.cpp
+++ b/indra/llrender/llshadermgr.cpp
@@ -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();
}
diff --git a/indra/llui/llaccordionctrl.cpp b/indra/llui/llaccordionctrl.cpp
index 495ba2f40f..1a64c2699d 100644
--- a/indra/llui/llaccordionctrl.cpp
+++ b/indra/llui/llaccordionctrl.cpp
@@ -303,8 +303,11 @@ void LLAccordionCtrl::ctrlSetLeftTopAndSize(LLView* panel, S32 left, S32 top, S3
return;
LLRect panel_rect = panel->getRect();
panel_rect.setLeftTopAndSize( left, top, width, height);
- panel->reshape( width, height, 1);
- panel->setRect(panel_rect);
+ if (panel->getRect() != panel_rect)
+ {
+ panel->reshape( width, height, 1);
+ panel->setRect(panel_rect);
+ }
}
void LLAccordionCtrl::ctrlShiftVertical(LLView* panel, S32 delta)
@@ -494,6 +497,7 @@ void LLAccordionCtrl::arrangeMultiple()
void LLAccordionCtrl::arrange()
{
+ LL_PROFILE_ZONE_SCOPED;
updateNoTabsHelpTextVisibility();
if (mAccordionTabs.empty())
diff --git a/indra/llui/llaccordionctrltab.cpp b/indra/llui/llaccordionctrltab.cpp
index ac66525030..bdf93348bb 100644
--- a/indra/llui/llaccordionctrltab.cpp
+++ b/indra/llui/llaccordionctrltab.cpp
@@ -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;
@@ -932,7 +947,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);
diff --git a/indra/llui/llemojidictionary.cpp b/indra/llui/llemojidictionary.cpp
index 925608e47e..16e6f0591a 100644
--- a/indra/llui/llemojidictionary.cpp
+++ b/indra/llui/llemojidictionary.cpp
@@ -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())
{
diff --git a/indra/llui/llfolderview.cpp b/indra/llui/llfolderview.cpp
index b664065532..db4ab8487e 100644
--- a/indra/llui/llfolderview.cpp
+++ b/indra/llui/llfolderview.cpp
@@ -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();
diff --git a/indra/llui/llfolderviewitem.cpp b/indra/llui/llfolderviewitem.cpp
index 9ca77dbe46..0f9fd35868 100644
--- a/indra/llui/llfolderviewitem.cpp
+++ b/indra/llui/llfolderviewitem.cpp
@@ -2106,10 +2106,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
{
diff --git a/indra/llui/lltextbase.cpp b/indra/llui/lltextbase.cpp
index 3fe0df1848..1de12896eb 100644
--- a/indra/llui/lltextbase.cpp
+++ b/indra/llui/lltextbase.cpp
@@ -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)
@@ -1446,6 +1448,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 +2285,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 +3400,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
//
diff --git a/indra/llui/lltextbase.h b/indra/llui/lltextbase.h
index 8ca653acb9..07cd9a1ee5 100644
--- a/indra/llui/lltextbase.h
+++ b/indra/llui/lltextbase.h
@@ -325,6 +325,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 +536,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 +785,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
};
diff --git a/indra/llui/lltoolbar.cpp b/indra/llui/lltoolbar.cpp
index 5955a28fa3..56ab6e9bae 100644
--- a/indra/llui/lltoolbar.cpp
+++ b/indra/llui/lltoolbar.cpp
@@ -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
diff --git a/indra/llui/lltoolbar.h b/indra/llui/lltoolbar.h
index 5556406fbd..a3f044c256 100644
--- a/indra/llui/lltoolbar.h
+++ b/indra/llui/lltoolbar.h
@@ -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
{
diff --git a/indra/llwebrtc/llwebrtc.cpp b/indra/llwebrtc/llwebrtc.cpp
index 828896f620..f93ad07c1f 100644
--- a/indra/llwebrtc/llwebrtc.cpp
+++ b/indra/llwebrtc/llwebrtc.cpp
@@ -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);
}
@@ -635,6 +641,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 +779,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 +799,8 @@ LLWebRTCPeerConnectionImpl::LLWebRTCPeerConnectionImpl() :
mWebRTCImpl(nullptr),
mPeerConnection(nullptr),
mMute(MUTE_INITIAL),
- mAnswerReceived(false)
+ mAnswerReceived(false),
+ mPendingJobs(0)
{
}
@@ -793,6 +808,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 +823,10 @@ void LLWebRTCPeerConnectionImpl::init(LLWebRTCImpl * webrtc_impl)
mWebRTCImpl = webrtc_impl;
mPeerConnectionFactory = mWebRTCImpl->getPeerConnectionFactory();
}
+
void LLWebRTCPeerConnectionImpl::terminate()
{
+ mPendingJobs++;
mWebRTCImpl->PostSignalingTask(
[this]()
{
@@ -848,7 +869,9 @@ void LLWebRTCPeerConnectionImpl::terminate()
observer->OnPeerConnectionClosed();
}
}
+ mPendingJobs--;
});
+ mPeerConnectionFactory.release();
}
void LLWebRTCPeerConnectionImpl::setSignalingObserver(LLWebRTCSignalingObserver *observer) { mSignalingObserverList.emplace_back(observer); }
@@ -869,6 +892,7 @@ bool LLWebRTCPeerConnectionImpl::initializeConnection(const LLWebRTCPeerConnecti
RTC_DCHECK(!mPeerConnection);
mAnswerReceived = false;
+ mPendingJobs++;
mWebRTCImpl->PostSignalingTask(
[this,options]()
{
@@ -890,6 +914,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 +933,7 @@ bool LLWebRTCPeerConnectionImpl::initializeConnection(const LLWebRTCPeerConnecti
{
observer->OnRenegotiationNeeded();
}
+ mPendingJobs--;
return;
}
@@ -964,6 +996,7 @@ bool LLWebRTCPeerConnectionImpl::initializeConnection(const LLWebRTCPeerConnecti
webrtc::PeerConnectionInterface::RTCOfferAnswerOptions offerOptions;
mPeerConnection->CreateOffer(this, offerOptions);
+ mPendingJobs--;
});
return true;
@@ -1006,6 +1039,7 @@ void LLWebRTCPeerConnectionImpl::AnswerAvailable(const std::string &sdp)
{
RTC_LOG(LS_INFO) << __FUNCTION__ << " Remote SDP: " << sdp;
+ mPendingJobs++;
mWebRTCImpl->PostSignalingTask(
[this, sdp]()
{
@@ -1015,6 +1049,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 +1072,7 @@ void LLWebRTCPeerConnectionImpl::setMute(bool mute)
mMute = new_state;
+ mPendingJobs++;
mWebRTCImpl->PostSignalingTask(
[this, force_reset, enable]()
{
@@ -1060,6 +1096,7 @@ void LLWebRTCPeerConnectionImpl::setMute(bool mute)
track->set_enabled(enable);
}
}
+ mPendingJobs--;
}
});
}
@@ -1081,6 +1118,7 @@ void LLWebRTCPeerConnectionImpl::resetMute()
void LLWebRTCPeerConnectionImpl::setReceiveVolume(float volume)
{
+ mPendingJobs++;
mWebRTCImpl->PostSignalingTask(
[this, volume]()
{
@@ -1099,11 +1137,13 @@ void LLWebRTCPeerConnectionImpl::setReceiveVolume(float volume)
}
}
}
+ mPendingJobs--;
});
}
void LLWebRTCPeerConnectionImpl::setSendVolume(float volume)
{
+ mPendingJobs++;
mWebRTCImpl->PostSignalingTask(
[this, volume]()
{
@@ -1114,6 +1154,7 @@ void LLWebRTCPeerConnectionImpl::setSendVolume(float volume)
track->GetSource()->SetVolume(volume*5.0);
}
}
+ mPendingJobs--;
});
}
@@ -1190,11 +1231,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 +1495,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--;
});
}
}
diff --git a/indra/llwebrtc/llwebrtc_impl.h b/indra/llwebrtc/llwebrtc_impl.h
index df06cb88fa..01cfb17ced 100644
--- a/indra/llwebrtc/llwebrtc_impl.h
+++ b/indra/llwebrtc/llwebrtc_impl.h
@@ -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;
};
}
diff --git a/indra/llwindow/llwindowcallbacks.cpp b/indra/llwindow/llwindowcallbacks.cpp
index 195f68e08b..7331f50ba0 100644
--- a/indra/llwindow/llwindowcallbacks.cpp
+++ b/indra/llwindow/llwindowcallbacks.cpp
@@ -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;
diff --git a/indra/llwindow/llwindowcallbacks.h b/indra/llwindow/llwindowcallbacks.h
index d812f93524..59dcdd3ade 100644
--- a/indra/llwindow/llwindowcallbacks.h
+++ b/indra/llwindow/llwindowcallbacks.h
@@ -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);
diff --git a/indra/llwindow/llwindowmacosx.cpp b/indra/llwindow/llwindowmacosx.cpp
index 030bd5ee7e..e37fe11671 100644
--- a/indra/llwindow/llwindowmacosx.cpp
+++ b/indra/llwindow/llwindowmacosx.cpp
@@ -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);
}
diff --git a/indra/llwindow/llwindowsdl.cpp b/indra/llwindow/llwindowsdl.cpp
index 7433ad6bd2..05be319c0b 100644
--- a/indra/llwindow/llwindowsdl.cpp
+++ b/indra/llwindow/llwindowsdl.cpp
@@ -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);
diff --git a/indra/llwindow/llwindowwin32.cpp b/indra/llwindow/llwindowwin32.cpp
index fc7d8cec72..23d5866710 100644
--- a/indra/llwindow/llwindowwin32.cpp
+++ b/indra/llwindow/llwindowwin32.cpp
@@ -2464,10 +2464,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 +2488,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");
diff --git a/indra/newview/app_settings/settings.xml b/indra/newview/app_settings/settings.xml
index d64f82d303..ac79223738 100644
--- a/indra/newview/app_settings/settings.xml
+++ b/indra/newview/app_settings/settings.xml
@@ -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>
diff --git a/indra/newview/gltf/accessor.cpp b/indra/newview/gltf/accessor.cpp
index 03f7331893..f0ad3fa594 100644
--- a/indra/newview/gltf/accessor.cpp
+++ b/indra/newview/gltf/accessor.cpp
@@ -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())
diff --git a/indra/newview/gltf/llgltfloader.cpp b/indra/newview/gltf/llgltfloader.cpp
index dd1d327683..4f8f80129d 100644
--- a/indra/newview/gltf/llgltfloader.cpp
+++ b/indra/newview/gltf/llgltfloader.cpp
@@ -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;
}
diff --git a/indra/newview/gltf/llgltfloader.h b/indra/newview/gltf/llgltfloader.h
index e8b91996c7..7aa1a94c20 100644
--- a/indra/newview/gltf/llgltfloader.h
+++ b/indra/newview/gltf/llgltfloader.h
@@ -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;
diff --git a/indra/newview/llagentwearables.cpp b/indra/newview/llagentwearables.cpp
index 25f5cbd78f..a075b6f004 100644
--- a/indra/newview/llagentwearables.cpp
+++ b/indra/newview/llagentwearables.cpp
@@ -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();
diff --git a/indra/newview/llagentwearables.h b/indra/newview/llagentwearables.h
index 3b8ff93c76..1e118ffa98 100644
--- a/indra/newview/llagentwearables.h
+++ b/indra/newview/llagentwearables.h
@@ -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);
diff --git a/indra/newview/llappearancemgr.cpp b/indra/newview/llappearancemgr.cpp
index 6fa23727b1..0d57b33a51 100644
--- a/indra/newview/llappearancemgr.cpp
+++ b/indra/newview/llappearancemgr.cpp
@@ -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()));
-
- if (closer_to_body && items.front() == item) return false;
- if (!closer_to_body && items.back() == item) return false;
-
- 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);
+ LLViewerInventoryItem* swap_item = nullptr;
+ for (auto iter : items)
+ {
+ if (iter->getLinkedUUID() == swap_item_id)
+ {
+ swap_item = iter.get();
+ break;
+ }
+ }
+ if (!swap_item)
+ {
+ return false;
+ }
- // LL_DEBUGS("Inventory") << "swap, item "
- // << ll_pretty_print_sd(item->asLLSD())
- // << " swap_item "
- // << ll_pretty_print_sd(swap_item->asLLSD()) << LL_ENDL;
+ // 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);
- // FIXME switch to use AISv3 where supported.
- //items need to be updated on a dataserver
item->setComplete(true);
item->updateServer(false);
gInventory.updateItem(item);
diff --git a/indra/newview/llappviewer.cpp b/indra/newview/llappviewer.cpp
index 63d364eaa8..eadc5e7e63 100644
--- a/indra/newview/llappviewer.cpp
+++ b/indra/newview/llappviewer.cpp
@@ -1265,6 +1265,7 @@ bool LLAppViewer::init()
LLViewerCamera::createInstance();
LL::GLTFSceneManager::createInstance();
+ gSavedSettings.setU32("DebugQualityPerformance", gSavedSettings.getU32("RenderQualityPerformance"));
#if LL_WINDOWS
if (!mSecondInstance)
@@ -3944,8 +3945,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 +4202,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 +4481,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 +5416,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 +5553,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();
diff --git a/indra/newview/llappviewer.h b/indra/newview/llappviewer.h
index 3afde6b9f5..4f2583cb16 100644
--- a/indra/newview/llappviewer.h
+++ b/indra/newview/llappviewer.h
@@ -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;
diff --git a/indra/newview/llappviewerwin32.cpp b/indra/newview/llappviewerwin32.cpp
index 6386d0636a..a951338138 100644
--- a/indra/newview/llappviewerwin32.cpp
+++ b/indra/newview/llappviewerwin32.cpp
@@ -172,6 +172,15 @@ 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
+ app->createErrorMarker(LAST_EXEC_OTHER_CRASH);
+ }
} // MDSCB_EXCEPTIONCODE
return false;
diff --git a/indra/newview/llchathistory.cpp b/indra/newview/llchathistory.cpp
index 1988e2072b..c1af09ebc7 100644
--- a/indra/newview/llchathistory.cpp
+++ b/indra/newview/llchathistory.cpp
@@ -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
diff --git a/indra/newview/llconversationlog.cpp b/indra/newview/llconversationlog.cpp
index cea68c1779..65a068e08d 100644
--- a/indra/newview/llconversationlog.cpp
+++ b/indra/newview/llconversationlog.cpp
@@ -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);
diff --git a/indra/newview/lleventpoll.cpp b/indra/newview/lleventpoll.cpp
index c6fea1ba82..86c58a3497 100644
--- a/indra/newview/lleventpoll.cpp
+++ b/indra/newview/lleventpoll.cpp
@@ -54,13 +54,6 @@ namespace Details
void stop();
private:
- // We will wait RETRY_SECONDS + (errorCount * RETRY_SECONDS_INC) before retrying after an error.
- // This means we attempt to recover relatively quickly but back off giving more time to recover
- // until we finally give up after MAX_EVENT_POLL_HTTP_ERRORS attempts.
- static const F32 EVENT_POLL_ERROR_RETRY_SECONDS;
- static const F32 EVENT_POLL_ERROR_RETRY_SECONDS_INC;
- static const S32 MAX_EVENT_POLL_HTTP_ERRORS;
-
void eventPollCoro(std::string url);
void handleMessage(const LLSD &content);
@@ -76,9 +69,13 @@ namespace Details
};
- const F32 LLEventPollImpl::EVENT_POLL_ERROR_RETRY_SECONDS = 15.f; // ~ half of a normal timeout.
- const F32 LLEventPollImpl::EVENT_POLL_ERROR_RETRY_SECONDS_INC = 5.f; // ~ half of a normal timeout.
- const S32 LLEventPollImpl::MAX_EVENT_POLL_HTTP_ERRORS = 10; // ~5 minutes, by the above rules.
+ // We will wait RETRY_SECONDS + (errorCount * RETRY_SECONDS_INC) before retrying after an error.
+ // This means we attempt to recover relatively quickly but back off giving more time to recover
+ // until we finally give up after MAX_EVENT_POLL_HTTP_ERRORS attempts.
+ constexpr F32 EVENT_POLL_ERROR_RETRY_SECONDS = 15.f; // ~ half of a normal timeout.
+ constexpr F32 EVENT_POLL_ERROR_RETRY_SECONDS_INC = 5.f; // ~ half of a normal timeout.
+ constexpr S32 MAX_EVENT_POLL_HTTP_ERRORS = 10; // ~5 minutes, by the above rules.
+ constexpr F64 MIN_SECONDS_PASSED = 10.0; // Minimum time we expect the server to hold the request.
int LLEventPollImpl::sNextCounter = 1;
@@ -151,11 +148,17 @@ namespace Details
LLSD acknowledge;
int errorCount = 0;
int counter = mCounter; // saved on the stack for logging.
+ LLTimer message_time;
LL_DEBUGS("LLEventPollImpl") << " <" << counter << "> entering coroutine." << LL_ENDL;
mAdapter = httpAdapter;
+ // This is a loop with its own waitToRetry implementation,
+ // so disable retries.
+ LLCore::HttpOptions::ptr_t httpOpts(new LLCore::HttpOptions);
+ httpOpts->setRetries(0);
+
LL::WorkQueue::ptr_t main_queue = nullptr;
// HACK -- grab the mainloop workqueue to move execution of the handler
@@ -172,11 +175,13 @@ namespace Details
request["ack"] = acknowledge;
request["done"] = mDone;
+ message_time.reset();
+
// LL_DEBUGS("LLEventPollImpl::eventPollCoro") << "<" << counter << "> request = "
// << LLSDXMLStreamer(request) << LL_ENDL;
LL_DEBUGS("LLEventPollImpl") << " <" << counter << "> posting and yielding." << LL_ENDL;
- LLSD result = httpAdapter->postAndSuspend(mHttpRequest, url, request);
+ LLSD result = httpAdapter->postAndSuspend(mHttpRequest, url, request, httpOpts);
// LL_DEBUGS("LLEventPollImpl::eventPollCoro") << "<" << counter << "> result = "
// << LLSDXMLStreamer(result) << LL_ENDL;
@@ -194,11 +199,30 @@ namespace Details
if (!status)
{
- if (status == LLCore::HttpStatus(LLCore::HttpStatus::EXT_CURL_EASY, CURLE_OPERATION_TIMEDOUT))
- { // A standard timeout response we get this when there are no events.
- LL_DEBUGS("LLEventPollImpl") << "All is very quiet on target server. It may have gone idle?" << LL_ENDL;
- errorCount = 0;
- continue;
+ if (status == LLCore::HttpStatus(LLCore::HttpStatus::EXT_CURL_EASY, CURLE_OPERATION_TIMEDOUT) // A standard timeout, no events.
+ || status == LLCore::HttpStatus(HTTP_BAD_GATEWAY) // An expected 'No events' case.
+ || status == LLCore::HttpStatus(HTTP_INTERNAL_ERROR)
+ || status == LLCore::HttpStatus(HTTP_SERVICE_UNAVAILABLE)
+ || status == LLCore::HttpStatus(HTTP_GATEWAY_TIME_OUT))
+ {
+ if (message_time.getElapsedSeconds() < MIN_SECONDS_PASSED)
+ {
+ // Server is supposed to hold request for 20 to 30 seconds.
+ // If it didn't hold the request at least for 10s, treat as an error.
+ LL_WARNS("LLEventPollImpl") << "Response arrived too early, status: " << status.toTerseString()
+ << ", time passed: " << message_time.getElapsedSeconds() << LL_ENDL;
+ }
+ else
+ {
+ // Timeout, expected and means 'no events'. Request is to be re-issued immediately.
+ // Current definition of a timeout is any of :
+ // - libcurl easy 28 status code
+ // - Linden 499 special http status code
+ // - RFC - standard 502 - 504 http status codes
+ LL_DEBUGS("LLEventPollImpl") << "No events, from: " << mSenderIp <<" status: " << (S32)status.getStatus() << LL_ENDL;
+ errorCount = 0;
+ continue;
+ }
}
else if ((status == LLCore::HttpStatus(LLCore::HttpStatus::LLCORE, LLCore::HE_OP_CANCELED)) ||
(status == LLCore::HttpStatus(HTTP_NOT_FOUND)))
@@ -206,13 +230,13 @@ namespace Details
// some cases the server gets ahead of the viewer and will
// return a 404 error (Not Found) before the cancel event
// comes back in the queue
- LL_WARNS("LLEventPollImpl") << "Canceling coroutine" << LL_ENDL;
+ LL_WARNS("LLEventPollImpl") << "<" << counter << "> Canceling coroutine, status: " << status.toTerseString() << LL_ENDL;
break;
}
else if (!status.isHttpStatus())
{
/// Some LLCore or LIBCurl error was returned. This is unlikely to be recoverable
- LL_WARNS("LLEventPollImpl") << "Critical error from poll request returned from libraries. Canceling coroutine." << LL_ENDL;
+ LL_WARNS("LLEventPollImpl") << "<" << counter << "> Critical error from poll request returned from libraries. Canceling coroutine." << LL_ENDL;
break;
}
LL_WARNS("LLEventPollImpl") << "<" << counter << "> Error result from LLCoreHttpUtil::HttpCoroHandler. Code "
@@ -255,6 +279,10 @@ namespace Details
LL_WARNS("LLEventPollImpl") << "< " << counter << "> Forcing disconnect due to stalled main region event poll." << LL_ENDL;
LLAppViewer::instance()->forceDisconnect(LLTrans::getString("AgentLostConnection"));
}
+ else
+ {
+ LL_WARNS("LLEventPollImpl") << "< " << counter << "> Stopping event poll for " << mSenderIp << " due to failures." << LL_ENDL;
+ }
break;
}
}
diff --git a/indra/newview/lleventpoll.h b/indra/newview/lleventpoll.h
index bb407b3799..ea186aa803 100644
--- a/indra/newview/lleventpoll.h
+++ b/indra/newview/lleventpoll.h
@@ -40,7 +40,30 @@ namespace Details
class LLEventPoll
- ///< implements the viewer side of server-to-viewer pushed events.
+ ///< Implements the viewer side of server-to-viewer pushed events.
+ ///
+ /// This class implements the sole consumer of the EventQueueGet capability
+ /// and delivers data, including llsd-encoded llmessage payloads, from
+ /// simulator to viewer.
+ ///
+ /// https://wiki.secondlife.com/wiki/EventQueueGet
+ /// The wiki page is neither complete nor entirely correct. Request timeouts
+ /// became the de facto method of returning an empty event set to the viewer.
+ /// But the timeout behavior was never defined. It was simply whatever
+ /// behavior a given grid implementation implemented.
+ ///
+ /// In SL's case, the path may include reverse proxies, http caches, http and
+ /// socks proxies, transparent hijacking, and other horrors. A pitfall for
+ /// implementors.
+ ///
+ /// Current definition of a timeout is any of :
+ /// - libcurl easy 28 status code
+ /// - Linden 499 special http status code
+ /// - RFC - standard 502 - 504 http status codes
+ /// If requests are failing too quickly with the above errors, they are treated
+ /// as actual errors and not an empty payload. These will count towards a final
+ /// error declaration and can lead to disconnection from a simulator or the
+ /// entire grid.
{
public:
LLEventPoll(const std::string& pollURL, const LLHost& sender);
diff --git a/indra/newview/llfetchedgltfmaterial.cpp b/indra/newview/llfetchedgltfmaterial.cpp
index 558fc92018..a05f725673 100644
--- a/indra/newview/llfetchedgltfmaterial.cpp
+++ b/indra/newview/llfetchedgltfmaterial.cpp
@@ -222,6 +222,14 @@ void LLFetchedGLTFMaterial::updateTextureTracking()
}
}
+void LLFetchedGLTFMaterial::clearFetchedTextures()
+{
+ mBaseColorTexture = nullptr;
+ mNormalTexture = nullptr;
+ mMetallicRoughnessTexture = nullptr;
+ mEmissiveTexture = nullptr;
+}
+
void LLFetchedGLTFMaterial::materialBegin()
{
llassert(!mFetching);
diff --git a/indra/newview/llfetchedgltfmaterial.h b/indra/newview/llfetchedgltfmaterial.h
index 4a33b9f05f..074e3fef41 100644
--- a/indra/newview/llfetchedgltfmaterial.h
+++ b/indra/newview/llfetchedgltfmaterial.h
@@ -67,6 +67,7 @@ public:
LLPointer<LLViewerFetchedTexture> mNormalTexture;
LLPointer<LLViewerFetchedTexture> mMetallicRoughnessTexture;
LLPointer<LLViewerFetchedTexture> mEmissiveTexture;
+ void clearFetchedTextures();
std::set<LLTextureEntry*> mTextureEntires;
diff --git a/indra/newview/llfloaterdisplayname.cpp b/indra/newview/llfloaterdisplayname.cpp
index 236aadfbc1..4843a48e66 100644
--- a/indra/newview/llfloaterdisplayname.cpp
+++ b/indra/newview/llfloaterdisplayname.cpp
@@ -56,6 +56,7 @@ private:
void onCacheSetName(bool success,
const std::string& reason,
const LLSD& content);
+ bool mIsLockedOut = false;
};
LLFloaterDisplayName::LLFloaterDisplayName(const LLSD& key) :
@@ -72,8 +73,8 @@ void LLFloaterDisplayName::onOpen(const LLSD& key)
LLAvatarNameCache::get(gAgent.getID(), &av_name);
F64 now_secs = LLDate::now().secondsSinceEpoch();
-
- if (now_secs < av_name.mNextUpdate)
+ mIsLockedOut = now_secs < av_name.mNextUpdate;
+ if (mIsLockedOut)
{
// ...can't update until some time in the future
F64 next_update_local_secs =
@@ -167,18 +168,19 @@ void LLFloaterDisplayName::onReset()
}
getChild<LLUICtrl>("display_name_editor")->setValue(av_name.getUserName());
- if (getChild<LLUICtrl>("display_name_editor")->getEnabled())
+ if (mIsLockedOut)
{
- // UI is enabled, fill the first field
- getChild<LLUICtrl>("display_name_confirm")->clear();
- getChild<LLUICtrl>("display_name_confirm")->setFocus(true);
+ // UI is disabled.
+ // We should allow resetting even if user already
+ // set a display name, enable save button
+ getChild<LLUICtrl>("display_name_confirm")->setValue(av_name.getUserName());
+ getChild<LLUICtrl>("save_btn")->setEnabled(true);
}
else
{
- // UI is disabled, looks like we should allow resetting
- // even if user already set a display name, enable save button
- getChild<LLUICtrl>("display_name_confirm")->setValue(av_name.getUserName());
- getChild<LLUICtrl>("save_btn")->setEnabled(true);
+ // UI is enabled, focus on the confirm field
+ getChild<LLUICtrl>("display_name_confirm")->clear();
+ getChild<LLUICtrl>("display_name_confirm")->setFocus(true);
}
}
diff --git a/indra/newview/llfloaterfixedenvironment.cpp b/indra/newview/llfloaterfixedenvironment.cpp
index d28c987414..1825797159 100644
--- a/indra/newview/llfloaterfixedenvironment.cpp
+++ b/indra/newview/llfloaterfixedenvironment.cpp
@@ -134,12 +134,15 @@ void LLFloaterFixedEnvironment::onClose(bool app_quitting)
{
doCloseInventoryFloater(app_quitting);
- LLEnvironment::instance().setSelectedEnvironment(LLEnvironment::ENV_LOCAL);
- LLEnvironment::instance().setCurrentEnvironmentSelection(LLEnvironment::ENV_LOCAL);
- LLEnvironment::instance().clearEnvironment(LLEnvironment::ENV_EDIT);
+ if (!app_quitting)
+ {
+ LLEnvironment::instance().setSelectedEnvironment(LLEnvironment::ENV_LOCAL);
+ LLEnvironment::instance().setCurrentEnvironmentSelection(LLEnvironment::ENV_LOCAL);
+ LLEnvironment::instance().clearEnvironment(LLEnvironment::ENV_EDIT);
- mSettings.reset();
- syncronizeTabs();
+ mSettings.reset();
+ syncronizeTabs();
+ }
}
void LLFloaterFixedEnvironment::refresh()
diff --git a/indra/newview/llfloaterimsessiontab.cpp b/indra/newview/llfloaterimsessiontab.cpp
index e03422780a..65c13797ac 100644
--- a/indra/newview/llfloaterimsessiontab.cpp
+++ b/indra/newview/llfloaterimsessiontab.cpp
@@ -618,8 +618,19 @@ void LLFloaterIMSessionTab::deleteAllChildren()
std::string LLFloaterIMSessionTab::appendTime()
{
- std::string timeStr = "[" + LLTrans::getString("TimeHour") + "]:"
- "[" + LLTrans::getString("TimeMin") + "]";
+ std::string timeStr;
+ static bool use_24h = gSavedSettings.getBOOL("Use24HourClock");
+ if (use_24h)
+ {
+ timeStr = "[" + LLTrans::getString("TimeHour") + "]:"
+ "[" + LLTrans::getString("TimeMin") + "]";
+ }
+ else
+ {
+ timeStr = "[" + LLTrans::getString("TimeHour12") + "]:"
+ "[" + LLTrans::getString("TimeMin") + "] ["
+ + LLTrans::getString("TimeAMPM") + "]";
+ }
LLSD substitution;
substitution["datetime"] = (S32)time_corrected();
diff --git a/indra/newview/llfloaterinspect.cpp b/indra/newview/llfloaterinspect.cpp
index 4f993ca0e1..c0fe7ad896 100644
--- a/indra/newview/llfloaterinspect.cpp
+++ b/indra/newview/llfloaterinspect.cpp
@@ -220,7 +220,8 @@ void LLFloaterInspect::refresh()
}
time_t timestamp = (time_t) (obj->mCreationDate/1000000);
- std::string timeStr = getString("timeStamp");
+ static bool use_24h = gSavedSettings.getBOOL("Use24HourClock");
+ std::string timeStr = use_24h ? getString("timeStamp") : getString("timeStampAMPM");
LLSD substitution;
substitution["datetime"] = (S32) timestamp;
LLStringUtil::format (timeStr, substitution);
diff --git a/indra/newview/llfloaterland.cpp b/indra/newview/llfloaterland.cpp
index 52a3e78d04..5c5219bcdd 100644
--- a/indra/newview/llfloaterland.cpp
+++ b/indra/newview/llfloaterland.cpp
@@ -733,7 +733,8 @@ void LLPanelLandGeneral::refresh()
// Display claim date
time_t claim_date = parcel->getClaimDate();
- std::string claim_date_str = getString("time_stamp_template");
+ static bool use_24h = gSavedSettings.getBOOL("Use24HourClock");
+ std::string claim_date_str = use_24h ? getString("time_stamp_template") : getString("time_stamp_template_ampm");
LLSD substitution;
substitution["datetime"] = (S32) claim_date;
LLStringUtil::format (claim_date_str, substitution);
diff --git a/indra/newview/llfloaterpreference.cpp b/indra/newview/llfloaterpreference.cpp
index 291f22d78f..5a39d41478 100644
--- a/indra/newview/llfloaterpreference.cpp
+++ b/indra/newview/llfloaterpreference.cpp
@@ -475,6 +475,8 @@ bool LLFloaterPreference::postBuild()
getChild<LLUICtrl>("log_path_string")->setEnabled(false); // make it read-only but selectable
getChild<LLComboBox>("language_combobox")->setCommitCallback(boost::bind(&LLFloaterPreference::onLanguageChange, this));
+ mTimeFormatCombobox = getChild<LLComboBox>("time_format_combobox");
+ mTimeFormatCombobox->setCommitCallback(boost::bind(&LLFloaterPreference::onTimeFormatChange, this));
getChild<LLComboBox>("FriendIMOptions")->setCommitCallback(boost::bind(&LLFloaterPreference::onNotificationsChange, this,"FriendIMOptions"));
getChild<LLComboBox>("NonFriendIMOptions")->setCommitCallback(boost::bind(&LLFloaterPreference::onNotificationsChange, this,"NonFriendIMOptions"));
@@ -1103,6 +1105,13 @@ void LLFloaterPreference::onLanguageChange()
}
}
+void LLFloaterPreference::onTimeFormatChange()
+{
+ std::string val = mTimeFormatCombobox->getValue();
+ gSavedSettings.setBOOL("Use24HourClock", val == "1");
+ onLanguageChange();
+}
+
void LLFloaterPreference::onNotificationsChange(const std::string& OptionName)
{
mNotificationOptions[OptionName] = getChild<LLComboBox>(OptionName)->getSelectedItemLabel();
@@ -1318,6 +1327,8 @@ void LLFloaterPreference::refresh()
advanced->refresh();
}
updateClickActionViews();
+
+ mTimeFormatCombobox->selectByValue(gSavedSettings.getBOOL("Use24HourClock") ? "1" : "0");
}
void LLFloaterPreference::onCommitWindowedMode()
@@ -1356,6 +1367,7 @@ void LLFloaterPreference::onChangeQuality(const LLSD& data)
}
mLastQualityLevel = level;
LLFeatureManager::getInstance()->setGraphicsLevel(level, true);
+ gSavedSettings.setU32("DebugQualityPerformance", level);
refreshEnabledGraphics();
refresh();
}
diff --git a/indra/newview/llfloaterpreference.h b/indra/newview/llfloaterpreference.h
index a784d502ef..a2aa3ee8de 100644
--- a/indra/newview/llfloaterpreference.h
+++ b/indra/newview/llfloaterpreference.h
@@ -117,6 +117,7 @@ protected:
void onClickClearCache(); // Clear viewer texture cache, file cache on next startup
void onClickBrowserClearCache(); // Clear web history and caches as well as viewer caches above
void onLanguageChange();
+ void onTimeFormatChange();
void onNotificationsChange(const std::string& OptionName);
void onNameTagOpacityChange(const LLSD& newvalue);
@@ -235,6 +236,7 @@ private:
LLButton* mDeleteTranscriptsBtn = nullptr;
LLButton* mEnablePopupBtn = nullptr;
LLButton* mDisablePopupBtn = nullptr;
+ LLComboBox* mTimeFormatCombobox = nullptr;
std::unique_ptr< ll::prefs::SearchData > mSearchData;
bool mSearchDataDirty;
diff --git a/indra/newview/llfloatertoybox.cpp b/indra/newview/llfloatertoybox.cpp
index f6257dbd3d..5e3ec366d5 100644
--- a/indra/newview/llfloatertoybox.cpp
+++ b/indra/newview/llfloatertoybox.cpp
@@ -63,7 +63,7 @@ bool LLFloaterToybox::postBuild()
mToolBar->setStartDragCallback(boost::bind(LLToolBarView::startDragTool,_1,_2,_3));
mToolBar->setHandleDragCallback(boost::bind(LLToolBarView::handleDragTool,_1,_2,_3,_4));
- mToolBar->setHandleDropCallback(boost::bind(LLToolBarView::handleDropTool,_1,_2,_3,_4));
+ mToolBar->setHandleDropCallback(boost::bind(LLToolBarView::handleDropTool,_1,_2,_3,_4,_5));
mToolBar->setButtonEnterCallback(boost::bind(&LLFloaterToybox::onToolBarButtonEnter,this,_1));
//
diff --git a/indra/newview/llfloaterurlentry.cpp b/indra/newview/llfloaterurlentry.cpp
index 7651b2528f..2f1857ec61 100644
--- a/indra/newview/llfloaterurlentry.cpp
+++ b/indra/newview/llfloaterurlentry.cpp
@@ -242,6 +242,16 @@ void LLFloaterURLEntry::getMediaTypeCoro(std::string url, LLHandle<LLFloater> pa
resolvedMimeType = mimeType;
}
}
+ else if (resultHeaders.has(HTTP_IN_HEADER_X_CONTENT_TYPE_OPTIONS))
+ {
+ const std::string& val = resultHeaders[HTTP_IN_HEADER_X_CONTENT_TYPE_OPTIONS];
+ if (val == HTTP_NOSNIFF)
+ {
+ // Doesn't permit 'sniffing' mime type, default to either html or plain
+ // If this doesn't work user will have to choose something manually.
+ resolvedMimeType = HTTP_CONTENT_TEXT_HTML;
+ }
+ }
floaterUrlEntry->headerFetchComplete(status.getType(), resolvedMimeType);
diff --git a/indra/newview/llgltfmateriallist.cpp b/indra/newview/llgltfmateriallist.cpp
index 8da835ed7d..3e4aadc381 100644
--- a/indra/newview/llgltfmateriallist.cpp
+++ b/indra/newview/llgltfmateriallist.cpp
@@ -359,6 +359,7 @@ void LLGLTFMaterialList::queueApply(const LLViewerObject* obj, S32 side, const L
{
if (asset_id.isNull() || override_json.empty())
{
+ // If there is no asset, there can't be an override
queueApply(obj, side, asset_id);
}
else
@@ -371,6 +372,7 @@ void LLGLTFMaterialList::queueApply(const LLViewerObject* obj, S32 side, const L
{
if (asset_id.isNull() || material_override == nullptr)
{
+ // If there is no asset, there can't be an override
queueApply(obj, side, asset_id);
}
else
@@ -470,7 +472,7 @@ void LLGLTFMaterialList::flushUpdatesOnce(std::shared_ptr<CallbackHolder> callba
{
data[i]["gltf_json"] = e.override_data->asJSON();
}
- if (!e.override_json.empty())
+ else if (!e.override_json.empty())
{
data[i]["gltf_json"] = e.override_json;
}
diff --git a/indra/newview/llimview.cpp b/indra/newview/llimview.cpp
index c1e80ba4f1..bda4afcc04 100644
--- a/indra/newview/llimview.cpp
+++ b/indra/newview/llimview.cpp
@@ -809,6 +809,10 @@ void LLIMModel::LLIMSession::initVoiceChannel(const LLSD& voiceChannelInfo)
{
if (mVoiceChannel)
{
+ if (!voiceChannelInfo.isMap())
+ {
+ LL_WARNS() << "initVoiceChannel called without voiceChannelInfo" << LL_ENDL;
+ }
if (mVoiceChannel->isThisVoiceChannel(voiceChannelInfo))
{
return;
diff --git a/indra/newview/llinventoryitemslist.cpp b/indra/newview/llinventoryitemslist.cpp
index cfa37cc3ee..73cc953692 100644
--- a/indra/newview/llinventoryitemslist.cpp
+++ b/indra/newview/llinventoryitemslist.cpp
@@ -40,6 +40,10 @@
#include "llinventorymodel.h"
#include "llviewerinventory.h"
+bool LLInventoryItemsList::sListIdleRegistered = false;
+LLInventoryItemsList::all_list_t LLInventoryItemsList::sAllLists;
+LLInventoryItemsList::all_list_t::iterator LLInventoryItemsList::sAllListIter;
+
LLInventoryItemsList::Params::Params()
{}
@@ -55,13 +59,39 @@ LLInventoryItemsList::LLInventoryItemsList(const LLInventoryItemsList::Params& p
setNoFilteredItemsMsg(LLTrans::getString("InventoryNoMatchingItems"));
- gIdleCallbacks.addFunction(idle, this);
+ sAllLists.push_back(this);
+ sAllListIter = sAllLists.begin();
+
+ if (!sListIdleRegistered)
+ {
+ sAllListIter = sAllLists.begin();
+ gIdleCallbacks.addFunction(idle, nullptr);
+
+ LLEventPumps::instance().obtain("LLApp").listen(
+ "LLInventoryItemsList",
+ [](const LLSD& stat)
+ {
+ std::string status(stat["status"]);
+ if (status != "running")
+ {
+ // viewer is starting shutdown
+ gIdleCallbacks.deleteFunction(idle, nullptr);
+ }
+ return false;
+ });
+ sListIdleRegistered = true;
+ }
}
// virtual
LLInventoryItemsList::~LLInventoryItemsList()
{
- gIdleCallbacks.deleteFunction(idle, this);
+ auto it = std::find(sAllLists.begin(), sAllLists.end(), this);
+ if (it != sAllLists.end())
+ {
+ sAllLists.erase(it);
+ sAllListIter = sAllLists.begin();
+ }
}
void LLInventoryItemsList::refreshList(const LLInventoryModel::item_array_t item_array)
@@ -111,25 +141,55 @@ void LLInventoryItemsList::updateSelection()
mSelectTheseIDs.clear();
}
-void LLInventoryItemsList::doIdle()
+bool LLInventoryItemsList::doIdle()
{
- if (mRefreshState == REFRESH_COMPLETE) return;
+ if (mRefreshState == REFRESH_COMPLETE) return true; // done
if (isInVisibleChain() || mForceRefresh || !getFilterSubString().empty())
{
refresh();
mRefreshCompleteSignal(this, LLSD());
+ return false; // keep going
}
+ return true; // done
}
//static
void LLInventoryItemsList::idle(void* user_data)
{
- LLInventoryItemsList* self = static_cast<LLInventoryItemsList*>(user_data);
- if ( self )
- { // Do the real idle
- self->doIdle();
+ if (sAllLists.empty())
+ return;
+
+ LL_PROFILE_ZONE_SCOPED;
+
+ using namespace std::chrono;
+ auto start = steady_clock::now();
+ const milliseconds time_limit = milliseconds(3);
+ const auto end_time = start + time_limit;
+ S32 max_update_count = 50;
+
+ if (sAllListIter == sAllLists.end())
+ {
+ sAllListIter = sAllLists.begin();
+ }
+
+ S32 updated = 0;
+ while (steady_clock::now() < end_time
+ && updated < max_update_count
+ && sAllListIter != sAllLists.end())
+ {
+ LLInventoryItemsList* list = *sAllListIter;
+ // Refresh is split into multiple separate parts,
+ // so keep repeating it while there is time, until done.
+ // Todo: refresh() split is pointless now?
+ // Or still useful for large folders?
+ if (list->doIdle())
+ {
+ // Item is done
+ ++sAllListIter;
+ updated++;
+ }
}
}
@@ -141,6 +201,7 @@ void LLInventoryItemsList::refresh()
{
case REFRESH_ALL:
{
+ LL_PROFILE_ZONE_NAMED("items_refresh_all");
mAddedItems.clear();
mRemovedItems.clear();
computeDifference(getIDs(), mAddedItems, mRemovedItems);
@@ -163,6 +224,7 @@ void LLInventoryItemsList::refresh()
}
case REFRESH_LIST_ERASE:
{
+ LL_PROFILE_ZONE_NAMED("items_refresh_erase");
uuid_vec_t::const_iterator it = mRemovedItems.begin();
for (; mRemovedItems.end() != it; ++it)
{
@@ -175,6 +237,7 @@ void LLInventoryItemsList::refresh()
}
case REFRESH_LIST_APPEND:
{
+ LL_PROFILE_ZONE_NAMED("items_refresh_append");
static const unsigned ADD_LIMIT = 25; // Note: affects perfomance
unsigned int nadded = 0;
@@ -239,6 +302,7 @@ void LLInventoryItemsList::refresh()
}
case REFRESH_LIST_SORT:
{
+ LL_PROFILE_ZONE_NAMED("items_refresh_sort");
// Filter, sort, rearrange and notify parent about shape changes
filterItems(true, true);
@@ -255,7 +319,10 @@ void LLInventoryItemsList::refresh()
break;
}
default:
- break;
+ {
+ mRefreshState = REFRESH_COMPLETE;
+ break;
+ }
}
setForceRefresh(mRefreshState != REFRESH_COMPLETE);
diff --git a/indra/newview/llinventoryitemslist.h b/indra/newview/llinventoryitemslist.h
index 9ebeb5e52b..b20c27eec8 100644
--- a/indra/newview/llinventoryitemslist.h
+++ b/indra/newview/llinventoryitemslist.h
@@ -59,7 +59,10 @@ public:
* Sets the flag indicating that the list needs to be refreshed even if it is
* not currently visible.
*/
- void setForceRefresh(bool force_refresh) { mForceRefresh = force_refresh; }
+ void setForceRefresh(bool force_refresh)
+ {
+ mForceRefresh = force_refresh;
+ }
/**
* If refreshes when invisible.
@@ -76,7 +79,7 @@ public:
* This is needed for example to filter items of the list hidden by closed
* accordion tab.
*/
- virtual void doIdle(); // Real idle routine
+ bool doIdle(); // Real idle routine
static void idle(void* user_data); // static glue to doIdle()
protected:
@@ -126,6 +129,12 @@ private:
bool mForceRefresh;
commit_signal_t mRefreshCompleteSignal;
+
+ // Update synchronization
+ typedef std::vector<LLInventoryItemsList*> all_list_t;
+ static all_list_t sAllLists;
+ static all_list_t::iterator sAllListIter;
+ static bool sListIdleRegistered;
};
#endif //LL_LLINVENTORYITEMSLIST_H
diff --git a/indra/newview/lllogchat.cpp b/indra/newview/lllogchat.cpp
index fa1f650113..30ea255e24 100644
--- a/indra/newview/lllogchat.cpp
+++ b/indra/newview/lllogchat.cpp
@@ -80,8 +80,8 @@ const static std::string MULTI_LINE_PREFIX(" ");
*
* Note: "You" was used as an avatar names in viewers of previous versions
*/
-const static boost::regex TIMESTAMP_AND_STUFF("^(\\[\\d{4}/\\d{1,2}/\\d{1,2}\\s+\\d{1,2}:\\d{2}\\]\\s+|\\[\\d{1,2}:\\d{2}\\]\\s+)?(.*)$");
-const static boost::regex TIMESTAMP("^(\\[\\d{4}/\\d{1,2}/\\d{1,2}\\s+\\d{1,2}:\\d{2}\\]|\\[\\d{1,2}:\\d{2}\\]).*");
+const static boost::regex TIMESTAMP_AND_STUFF("^(\\[\\d{4}/\\d{1,2}/\\d{1,2}\\s+\\d{1,2}:\\d{2}\\s[AaPp][Mm]\\]\\s+|\\[\\d{4}/\\d{1,2}/\\d{1,2}\\s+\\d{1,2}:\\d{2}\\]\\s+|\\[\\d{1,2}:\\d{2}\\s[AaPp][Mm]\\]\\s+|\\[\\d{1,2}:\\d{2}\\]\\s+)?(.*)$");
+const static boost::regex TIMESTAMP("^(\\[\\d{4}/\\d{1,2}/\\d{1,2}\\s+\\d{1,2}:\\d{2}(\\s[AaPp][Mm])?\\]|\\[\\d{1,2}:\\d{2}(\\s[AaPp][Mm])?\\]).*");
/**
* Regular expression suitable to match names like
@@ -152,6 +152,10 @@ public:
void checkAndCutOffDate(std::string& time_str)
{
+ if (time_str.size() < 10) // not enough space for a date
+ {
+ return;
+ }
// Cuts off the "%Y/%m/%d" from string for todays timestamps.
// Assume that passed string has at least "%H:%M" time format.
date log_date(not_a_date_time);
@@ -168,20 +172,12 @@ public:
if ( days_alive == zero_days )
{
- // Yep, today's so strip "%Y/%m/%d" info
- ptime stripped_time(not_a_date_time);
-
- mTimeStream.str(LLStringUtil::null);
- mTimeStream << time_str;
- mTimeStream >> stripped_time;
- mTimeStream.clear();
-
- time_str.clear();
-
- mTimeStream.str(LLStringUtil::null);
- mTimeStream << stripped_time;
- mTimeStream >> time_str;
- mTimeStream.clear();
+ size_t pos = time_str.find_first_of(' ');
+ if (pos != std::string::npos)
+ {
+ time_str.erase(0, pos + 1);
+ LLStringUtil::trim(time_str);
+ }
}
LL_DEBUGS("LLChatLogParser")
@@ -310,16 +306,22 @@ std::string LLLogChat::timestamp2LogString(U32 timestamp, bool withdate)
std::string timeStr;
if (withdate)
{
- timeStr = "[" + LLTrans::getString ("TimeYear") + "]/["
- + LLTrans::getString ("TimeMonth") + "]/["
- + LLTrans::getString ("TimeDay") + "] ["
- + LLTrans::getString ("TimeHour") + "]:["
- + LLTrans::getString ("TimeMin") + "]";
+ timeStr = "[" + LLTrans::getString("TimeYear") + "]/["
+ + LLTrans::getString("TimeMonth") + "]/["
+ + LLTrans::getString("TimeDay") + "] ";
+ }
+
+ static bool use_24h = gSavedSettings.getBOOL("Use24HourClock");
+ if (use_24h)
+ {
+ timeStr += "[" + LLTrans::getString("TimeHour") + "]:["
+ + LLTrans::getString("TimeMin") + "]";
}
else
{
- timeStr = "[" + LLTrans::getString("TimeHour") + "]:["
- + LLTrans::getString ("TimeMin")+"]";
+ timeStr += "[" + LLTrans::getString("TimeHour12") + "]:["
+ + LLTrans::getString("TimeMin") + "] ["
+ + LLTrans::getString("TimeAMPM") + "]";
}
LLSD substitution;
diff --git a/indra/newview/lllogininstance.cpp b/indra/newview/lllogininstance.cpp
index 4bffe7feac..41cec4f074 100644
--- a/indra/newview/lllogininstance.cpp
+++ b/indra/newview/lllogininstance.cpp
@@ -62,7 +62,7 @@
const S32 LOGIN_MAX_RETRIES = 0; // Viewer should not autmatically retry login
const F32 LOGIN_SRV_TIMEOUT_MIN = 10;
-const F32 LOGIN_SRV_TIMEOUT_MAX = 120;
+const F32 LOGIN_SRV_TIMEOUT_MAX = 180;
const F32 LOGIN_DNS_TIMEOUT_FACTOR = 0.9; // make DNS wait shorter then retry time
class LLLoginInstance::Disposable {
diff --git a/indra/newview/llmeshrepository.cpp b/indra/newview/llmeshrepository.cpp
index 1f6a046919..413f02b723 100644
--- a/indra/newview/llmeshrepository.cpp
+++ b/indra/newview/llmeshrepository.cpp
@@ -2577,7 +2577,7 @@ EMeshProcessingResult LLMeshRepoThread::physicsShapeReceived(const LLUUID& mesh_
return MESH_OK;
}
-LLMeshUploadThread::LLMeshUploadThread(LLMeshUploadThread::instance_list& data, const LLMeshUploadThread::lod_sources_map_t& sources_list,
+LLMeshUploadThread::LLMeshUploadThread(LLMeshUploadThread::instance_list_t& data, const LLMeshUploadThread::lod_sources_map_t& sources_list,
LLVector3& scale, bool upload_textures,
bool upload_skin, bool upload_joints, bool lock_scale_if_joint_position,
const std::string & upload_url, LLUUID destination_folder_id, bool do_upload,
@@ -2659,7 +2659,7 @@ void LLMeshUploadThread::DecompRequest::completed()
void LLMeshUploadThread::preStart()
{
//build map of LLModel refs to instances for callbacks
- for (instance_list::iterator iter = mInstanceList.begin(); iter != mInstanceList.end(); ++iter)
+ for (instance_list_t::iterator iter = mInstanceList.begin(); iter != mInstanceList.end(); ++iter)
{
mInstance[iter->mModel].push_back(*iter);
}
@@ -2708,6 +2708,179 @@ LLSD llsd_from_file(std::string filename)
return result;
}
+void LLMeshUploadThread::packModelIntance(
+ LLModel* model,
+ LLMeshUploadThread::instance_list_t& instance_list,
+ std::string& model_name,
+ LLSD& res,
+ S32& mesh_num,
+ S32& texture_num,
+ S32& instance_num,
+ std::unordered_set<LLViewerTexture* >& textures,
+ std::unordered_map<LLViewerTexture*, S32> texture_index,
+ std::unordered_map<LLModel*, S32>& mesh_index,
+ std::vector<std::string>& texture_list_dest,
+ bool include_textures
+ )
+{
+ LLMeshUploadData data;
+ data.mBaseModel = model;
+
+ LLModelInstance& first_instance = *(instance_list.begin());
+ for (S32 i = 0; i < 5; i++)
+ {
+ data.mModel[i] = first_instance.mLOD[i];
+ }
+
+ if (mesh_index.find(data.mBaseModel) == mesh_index.end())
+ {
+ // Have not seen this model before - create a new mesh_list entry for it.
+ if (model_name.empty())
+ {
+ model_name = data.mBaseModel->getName();
+ }
+
+ std::stringstream ostr;
+
+ LLModel::Decomposition& decomp =
+ data.mModel[LLModel::LOD_PHYSICS].notNull() ?
+ data.mModel[LLModel::LOD_PHYSICS]->mPhysics :
+ data.mBaseModel->mPhysics;
+
+ decomp.mBaseHull = mHullMap[data.mBaseModel];
+
+ LLSD mesh_header = LLModel::writeModel(
+ ostr,
+ data.mModel[LLModel::LOD_PHYSICS],
+ data.mModel[LLModel::LOD_HIGH],
+ data.mModel[LLModel::LOD_MEDIUM],
+ data.mModel[LLModel::LOD_LOW],
+ data.mModel[LLModel::LOD_IMPOSTOR],
+ decomp,
+ mUploadSkin,
+ mUploadJoints,
+ mLockScaleIfJointPosition,
+ LLModel::WRITE_BINARY,
+ false,
+ data.mBaseModel->mSubmodelID);
+
+ data.mAssetData = ostr.str();
+ std::string str = ostr.str();
+
+ res["mesh_list"][mesh_num] = LLSD::Binary(str.begin(), str.end());
+ mesh_index[data.mBaseModel] = mesh_num;
+ mesh_num++;
+ }
+
+ // For all instances that use this model
+ for (instance_list_t::iterator instance_iter = instance_list.begin();
+ instance_iter != instance_list.end();
+ ++instance_iter)
+ {
+ LLModelInstance& instance = *instance_iter;
+
+ LLSD instance_entry;
+
+ for (S32 i = 0; i < 5; i++)
+ {
+ data.mModel[i] = instance.mLOD[i];
+ }
+
+ LLVector3 pos, scale;
+ LLQuaternion rot;
+ LLMatrix4 transformation = instance.mTransform;
+ decomposeMeshMatrix(transformation, pos, rot, scale);
+ instance_entry["position"] = ll_sd_from_vector3(pos);
+ instance_entry["rotation"] = ll_sd_from_quaternion(rot);
+ instance_entry["scale"] = ll_sd_from_vector3(scale);
+
+ instance_entry["material"] = LL_MCODE_WOOD;
+ if (model->mSubmodelID)
+ {
+ // Should it really be different?
+ instance_entry["physics_shape_type"] = (U8)(LLViewerObject::PHYSICS_SHAPE_NONE);
+ }
+ else
+ {
+ instance_entry["physics_shape_type"] = data.mModel[LLModel::LOD_PHYSICS].notNull() ? (U8)(LLViewerObject::PHYSICS_SHAPE_PRIM) : (U8)(LLViewerObject::PHYSICS_SHAPE_CONVEX_HULL);
+ }
+ instance_entry["mesh"] = mesh_index[data.mBaseModel];
+ instance_entry["mesh_name"] = instance.mLabel;
+
+ instance_entry["face_list"] = LLSD::emptyArray();
+
+ // We want to be able to allow more than 8 materials...
+ //
+ S32 end = llmin((S32)data.mBaseModel->mMaterialList.size(), instance.mModel->getNumVolumeFaces());
+
+ for (S32 face_num = 0; face_num < end; face_num++)
+ {
+ // multiple faces can reuse the same material
+ LLImportMaterial& material = instance.mMaterial[data.mBaseModel->mMaterialList[face_num]];
+ LLSD face_entry = LLSD::emptyMap();
+
+ LLViewerFetchedTexture* texture = NULL;
+
+ if (material.mDiffuseMapFilename.size())
+ {
+ texture = FindViewerTexture(material);
+ }
+
+ if ((texture != NULL) &&
+ (textures.find(texture) == textures.end()))
+ {
+ textures.insert(texture);
+ }
+
+ std::stringstream texture_str;
+ if (texture != NULL && include_textures && mUploadTextures)
+ {
+ if (texture->hasSavedRawImage())
+ {
+ LLImageDataLock lock(texture->getSavedRawImage());
+
+ LLPointer<LLImageJ2C> upload_file =
+ LLViewerTextureList::convertToUploadFile(texture->getSavedRawImage());
+
+ if (!upload_file.isNull() && upload_file->getDataSize() && !upload_file->isBufferInvalid())
+ {
+ texture_str.write((const char*)upload_file->getData(), upload_file->getDataSize());
+ }
+ }
+ }
+
+ if (texture != NULL &&
+ mUploadTextures &&
+ texture_index.find(texture) == texture_index.end())
+ {
+ texture_index[texture] = texture_num;
+ std::string str = texture_str.str();
+ res["texture_list"][texture_num] = LLSD::Binary(str.begin(), str.end());
+ // store indexes for error handling;
+ texture_list_dest.push_back(material.mDiffuseMapFilename);
+ texture_num++;
+ }
+
+ // Subset of TextureEntry fields.
+ if (texture != NULL && mUploadTextures)
+ {
+ face_entry["image"] = texture_index[texture];
+ face_entry["scales"] = 1.0;
+ face_entry["scalet"] = 1.0;
+ face_entry["offsets"] = 0.0;
+ face_entry["offsett"] = 0.0;
+ face_entry["imagerot"] = 0.0;
+ }
+ face_entry["diffuse_color"] = ll_sd_from_color4(material.mDiffuseColor);
+ face_entry["fullbright"] = material.mFullbright;
+ instance_entry["face_list"][face_num] = face_entry;
+ }
+
+ res["instance_list"][instance_num] = instance_entry;
+ instance_num++;
+ }
+}
+
void LLMeshUploadThread::wholeModelToLLSD(LLSD& dest, std::vector<std::string>& texture_list_dest, bool include_textures)
{
LLSD result;
@@ -2741,6 +2914,7 @@ void LLMeshUploadThread::wholeModelToLLSD(LLSD& dest, std::vector<std::string>&
}
S32 mesh_num = 0;
S32 texture_num = 0;
+ S32 instance_num = 0;
std::unordered_set<LLViewerTexture* > textures;
std::unordered_map<LLViewerTexture*,S32> texture_index;
@@ -2748,7 +2922,34 @@ void LLMeshUploadThread::wholeModelToLLSD(LLSD& dest, std::vector<std::string>&
std::unordered_map<LLModel*,S32> mesh_index;
std::string model_name;
- S32 instance_num = 0;
+ // If server gets a m1, m2, m3, m4 list, m1 becomes the root
+ // and the rest go as m4, m3, m2
+ // to counter that mInstance is sorted as m4, m3, m2, m1
+ // and we grab m1 from the end and send it first
+ LLModel* root_model = nullptr;
+ for (instance_map_t::reverse_iterator iter = mInstance.rbegin(); iter != mInstance.rend(); ++iter)
+ {
+ if (iter->first->mSubmodelID)
+ {
+ // Submodel can't be root
+ continue;
+ }
+ root_model = iter->first;
+ packModelIntance(
+ iter->first,
+ iter->second,
+ model_name,
+ res,
+ mesh_num,
+ texture_num,
+ instance_num,
+ textures,
+ texture_index,
+ mesh_index,
+ texture_list_dest,
+ include_textures);
+ break;
+ }
// Handle models, ignore submodels for now.
// Probably should pre-sort by mSubmodelID instead of running twice.
@@ -2756,319 +2957,53 @@ void LLMeshUploadThread::wholeModelToLLSD(LLSD& dest, std::vector<std::string>&
// deterministic order.
for (auto& iter : mInstance)
{
- LLMeshUploadData data;
- data.mBaseModel = iter.first;
-
- if (data.mBaseModel->mSubmodelID)
+ if (iter.first->mSubmodelID)
{
// These are handled below to insure correct parenting order on creation
// due to map walking being based on model address (aka random)
continue;
}
-
- LLModelInstance& first_instance = *(iter.second.begin());
- for (S32 i = 0; i < 5; i++)
- {
- data.mModel[i] = first_instance.mLOD[i];
- }
-
- if (mesh_index.find(data.mBaseModel) == mesh_index.end())
- {
- // Have not seen this model before - create a new mesh_list entry for it.
- if (model_name.empty())
- {
- model_name = data.mBaseModel->getName();
- }
-
- std::stringstream ostr;
-
- LLModel::Decomposition& decomp =
- data.mModel[LLModel::LOD_PHYSICS].notNull() ?
- data.mModel[LLModel::LOD_PHYSICS]->mPhysics :
- data.mBaseModel->mPhysics;
-
- decomp.mBaseHull = mHullMap[data.mBaseModel];
-
- LLSD mesh_header = LLModel::writeModel(
- ostr,
- data.mModel[LLModel::LOD_PHYSICS],
- data.mModel[LLModel::LOD_HIGH],
- data.mModel[LLModel::LOD_MEDIUM],
- data.mModel[LLModel::LOD_LOW],
- data.mModel[LLModel::LOD_IMPOSTOR],
- decomp,
- mUploadSkin,
- mUploadJoints,
- mLockScaleIfJointPosition,
- LLModel::WRITE_BINARY,
- false,
- data.mBaseModel->mSubmodelID);
-
- data.mAssetData = ostr.str();
- std::string str = ostr.str();
-
- res["mesh_list"][mesh_num] = LLSD::Binary(str.begin(),str.end());
- mesh_index[data.mBaseModel] = mesh_num;
- mesh_num++;
- }
-
- // For all instances that use this model
- for (instance_list::iterator instance_iter = iter.second.begin();
- instance_iter != iter.second.end();
- ++instance_iter)
+ if (root_model == iter.first)
{
-
- LLModelInstance& instance = *instance_iter;
-
- LLSD instance_entry;
-
- for (S32 i = 0; i < 5; i++)
- {
- data.mModel[i] = instance.mLOD[i];
- }
-
- LLVector3 pos, scale;
- LLQuaternion rot;
- LLMatrix4 transformation = instance.mTransform;
- decomposeMeshMatrix(transformation,pos,rot,scale);
- instance_entry["position"] = ll_sd_from_vector3(pos);
- instance_entry["rotation"] = ll_sd_from_quaternion(rot);
- instance_entry["scale"] = ll_sd_from_vector3(scale);
-
- instance_entry["material"] = LL_MCODE_WOOD;
- instance_entry["physics_shape_type"] = data.mModel[LLModel::LOD_PHYSICS].notNull() ? (U8)(LLViewerObject::PHYSICS_SHAPE_PRIM) : (U8)(LLViewerObject::PHYSICS_SHAPE_CONVEX_HULL);
- instance_entry["mesh"] = mesh_index[data.mBaseModel];
- instance_entry["mesh_name"] = instance.mLabel;
-
- instance_entry["face_list"] = LLSD::emptyArray();
-
- // We want to be able to allow more than 8 materials...
- //
- S32 end = llmin((S32)data.mBaseModel->mMaterialList.size(), instance.mModel->getNumVolumeFaces()) ;
-
- for (S32 face_num = 0; face_num < end; face_num++)
- {
- // multiple faces can reuse the same material
- LLImportMaterial& material = instance.mMaterial[data.mBaseModel->mMaterialList[face_num]];
- LLSD face_entry = LLSD::emptyMap();
-
- LLViewerFetchedTexture *texture = NULL;
-
- if (material.mDiffuseMapFilename.size())
- {
- texture = FindViewerTexture(material);
- }
-
- if ((texture != NULL) &&
- (textures.find(texture) == textures.end()))
- {
- textures.insert(texture);
- }
-
- std::stringstream texture_str;
- if (texture != NULL && include_textures && mUploadTextures)
- {
- if (texture->hasSavedRawImage())
- {
- LLImageDataLock lock(texture->getSavedRawImage());
-
- LLPointer<LLImageJ2C> upload_file =
- LLViewerTextureList::convertToUploadFile(texture->getSavedRawImage());
-
- if (!upload_file.isNull() && upload_file->getDataSize() && !upload_file->isBufferInvalid())
- {
- texture_str.write((const char*) upload_file->getData(), upload_file->getDataSize());
- }
- }
- }
-
- if (texture != NULL &&
- mUploadTextures &&
- texture_index.find(texture) == texture_index.end())
- {
- texture_index[texture] = texture_num;
- std::string str = texture_str.str();
- res["texture_list"][texture_num] = LLSD::Binary(str.begin(),str.end());
- // store indexes for error handling;
- texture_list_dest.push_back(material.mDiffuseMapFilename);
- texture_num++;
- }
-
- // Subset of TextureEntry fields.
- if (texture != NULL && mUploadTextures)
- {
- face_entry["image"] = texture_index[texture];
- face_entry["scales"] = 1.0;
- face_entry["scalet"] = 1.0;
- face_entry["offsets"] = 0.0;
- face_entry["offsett"] = 0.0;
- face_entry["imagerot"] = 0.0;
- }
- face_entry["diffuse_color"] = ll_sd_from_color4(material.mDiffuseColor);
- face_entry["fullbright"] = material.mFullbright;
- instance_entry["face_list"][face_num] = face_entry;
- }
-
- res["instance_list"][instance_num] = instance_entry;
- instance_num++;
+ // Reached root, root was already packed and is last non-submodel
+ break;
}
+ packModelIntance(
+ iter.first,
+ iter.second,
+ model_name,
+ res,
+ mesh_num,
+ texture_num,
+ instance_num,
+ textures,
+ texture_index,
+ mesh_index,
+ texture_list_dest,
+ include_textures);
}
// Now handle the submodels.
for (auto& iter : mInstance)
{
- LLMeshUploadData data;
- data.mBaseModel = iter.first;
-
- if (!data.mBaseModel->mSubmodelID)
+ if (!iter.first->mSubmodelID)
{
// These were handled above already...
- //
continue;
}
-
- LLModelInstance& first_instance = *(iter.second.begin());
- for (S32 i = 0; i < 5; i++)
- {
- data.mModel[i] = first_instance.mLOD[i];
- }
-
- if (mesh_index.find(data.mBaseModel) == mesh_index.end())
- {
- // Have not seen this model before - create a new mesh_list entry for it.
- if (model_name.empty())
- {
- model_name = data.mBaseModel->getName();
- }
-
- std::stringstream ostr;
-
- LLModel::Decomposition& decomp =
- data.mModel[LLModel::LOD_PHYSICS].notNull() ?
- data.mModel[LLModel::LOD_PHYSICS]->mPhysics :
- data.mBaseModel->mPhysics;
-
- decomp.mBaseHull = mHullMap[data.mBaseModel];
-
- LLSD mesh_header = LLModel::writeModel(
- ostr,
- data.mModel[LLModel::LOD_PHYSICS],
- data.mModel[LLModel::LOD_HIGH],
- data.mModel[LLModel::LOD_MEDIUM],
- data.mModel[LLModel::LOD_LOW],
- data.mModel[LLModel::LOD_IMPOSTOR],
- decomp,
- mUploadSkin,
- mUploadJoints,
- mLockScaleIfJointPosition,
- LLModel::WRITE_BINARY,
- false,
- data.mBaseModel->mSubmodelID);
-
- data.mAssetData = ostr.str();
- std::string str = ostr.str();
-
- res["mesh_list"][mesh_num] = LLSD::Binary(str.begin(),str.end());
- mesh_index[data.mBaseModel] = mesh_num;
- mesh_num++;
- }
-
- // For all instances that use this model
- for (instance_list::iterator instance_iter = iter.second.begin();
- instance_iter != iter.second.end();
- ++instance_iter)
- {
-
- LLModelInstance& instance = *instance_iter;
-
- LLSD instance_entry;
-
- for (S32 i = 0; i < 5; i++)
- {
- data.mModel[i] = instance.mLOD[i];
- }
-
- LLVector3 pos, scale;
- LLQuaternion rot;
- LLMatrix4 transformation = instance.mTransform;
- decomposeMeshMatrix(transformation,pos,rot,scale);
- instance_entry["position"] = ll_sd_from_vector3(pos);
- instance_entry["rotation"] = ll_sd_from_quaternion(rot);
- instance_entry["scale"] = ll_sd_from_vector3(scale);
-
- instance_entry["material"] = LL_MCODE_WOOD;
- instance_entry["physics_shape_type"] = (U8)(LLViewerObject::PHYSICS_SHAPE_NONE);
- instance_entry["mesh"] = mesh_index[data.mBaseModel];
-
- instance_entry["face_list"] = LLSD::emptyArray();
-
- // We want to be able to allow more than 8 materials...
- //
- S32 end = llmin((S32)instance.mMaterial.size(), instance.mModel->getNumVolumeFaces()) ;
-
- for (S32 face_num = 0; face_num < end; face_num++)
- {
- LLImportMaterial& material = instance.mMaterial[data.mBaseModel->mMaterialList[face_num]];
- LLSD face_entry = LLSD::emptyMap();
-
- LLViewerFetchedTexture *texture = NULL;
-
- if (material.mDiffuseMapFilename.size())
- {
- texture = FindViewerTexture(material);
- }
-
- if ((texture != NULL) &&
- (textures.find(texture) == textures.end()))
- {
- textures.insert(texture);
- }
-
- std::stringstream texture_str;
- if (texture != NULL && include_textures && mUploadTextures)
- {
- if (texture->hasSavedRawImage())
- {
- LLImageDataLock lock(texture->getSavedRawImage());
-
- LLPointer<LLImageJ2C> upload_file =
- LLViewerTextureList::convertToUploadFile(texture->getSavedRawImage());
-
- if (!upload_file.isNull() && upload_file->getDataSize())
- {
- texture_str.write((const char*) upload_file->getData(), upload_file->getDataSize());
- }
- }
- }
-
- if (texture != NULL &&
- mUploadTextures &&
- texture_index.find(texture) == texture_index.end())
- {
- texture_index[texture] = texture_num;
- std::string str = texture_str.str();
- res["texture_list"][texture_num] = LLSD::Binary(str.begin(),str.end());
- texture_num++;
- }
-
- // Subset of TextureEntry fields.
- if (texture != NULL && mUploadTextures)
- {
- face_entry["image"] = texture_index[texture];
- face_entry["scales"] = 1.0;
- face_entry["scalet"] = 1.0;
- face_entry["offsets"] = 0.0;
- face_entry["offsett"] = 0.0;
- face_entry["imagerot"] = 0.0;
- }
- face_entry["diffuse_color"] = ll_sd_from_color4(material.mDiffuseColor);
- face_entry["fullbright"] = material.mFullbright;
- instance_entry["face_list"][face_num] = face_entry;
- }
-
- res["instance_list"][instance_num] = instance_entry;
- instance_num++;
- }
+ packModelIntance(
+ iter.first,
+ iter.second,
+ model_name,
+ res,
+ mesh_num,
+ texture_num,
+ instance_num,
+ textures,
+ texture_index,
+ mesh_index,
+ texture_list_dest,
+ include_textures);
}
if (model_name.empty()) model_name = "mesh model";
@@ -3084,7 +3019,7 @@ void LLMeshUploadThread::generateHulls()
{
bool has_valid_requests = false ;
- for (instance_map::iterator iter = mInstance.begin(); iter != mInstance.end(); ++iter)
+ for (instance_map_t::iterator iter = mInstance.begin(); iter != mInstance.end(); ++iter)
{
LLMeshUploadData data;
data.mBaseModel = iter->first;
diff --git a/indra/newview/llmeshrepository.h b/indra/newview/llmeshrepository.h
index cf05807164..01b51e753e 100644
--- a/indra/newview/llmeshrepository.h
+++ b/indra/newview/llmeshrepository.h
@@ -671,11 +671,11 @@ public:
LLPointer<DecompRequest> mFinalDecomp;
volatile bool mPhysicsComplete;
- typedef std::map<LLPointer<LLModel>, std::vector<LLVector3> > hull_map;
- hull_map mHullMap;
+ typedef std::map<LLPointer<LLModel>, std::vector<LLVector3> > hull_map_t;
+ hull_map_t mHullMap;
- typedef std::vector<LLModelInstance> instance_list;
- instance_list mInstanceList;
+ typedef std::vector<LLModelInstance> instance_list_t;
+ instance_list_t mInstanceList;
// Upload should happen in deterministic order, so sort instances by model name.
struct LLUploadModelInstanceLess
@@ -689,11 +689,11 @@ public:
}
// Note: probably can sort by mBaseModel->mSubmodelID here as well to avoid
// running over the list twice in wholeModelToLLSD.
- return a->mLabel < b->mLabel;
+ return a->mLabel > b->mLabel;
}
};
- typedef std::map<LLPointer<LLModel>, instance_list, LLUploadModelInstanceLess> instance_map;
- instance_map mInstance;
+ typedef std::map<LLPointer<LLModel>, instance_list_t, LLUploadModelInstanceLess> instance_map_t;
+ instance_map_t mInstance;
typedef std::map<std::string, std::string> lod_sources_map_t;
lod_sources_map_t mLodSources;
@@ -712,7 +712,7 @@ public:
std::string mWholeModelUploadURL;
LLUUID mDestinationFolderId;
- LLMeshUploadThread(instance_list& data, const lod_sources_map_t& sources_list,
+ LLMeshUploadThread(instance_list_t& data, const lod_sources_map_t& sources_list,
LLVector3& scale, bool upload_textures,
bool upload_skin, bool upload_joints, bool lock_scale_if_joint_position,
const std::string & upload_url,
@@ -748,6 +748,22 @@ public:
static LLViewerFetchedTexture* FindViewerTexture(const LLImportMaterial& material);
+protected:
+ void packModelIntance(
+ LLModel* model,
+ LLMeshUploadThread::instance_list_t& instance_list,
+ std::string& model_name,
+ LLSD& res,
+ S32& mesh_num,
+ S32& texture_num,
+ S32& instance_num,
+ std::unordered_set<LLViewerTexture* > &textures,
+ std::unordered_map<LLViewerTexture*, S32> texture_index,
+ std::unordered_map<LLModel*, S32>& mesh_index,
+ std::vector<std::string>& texture_list_dest,
+ bool include_textures
+ );
+
private:
LLHandle<LLWholeModelFeeObserver> mFeeObserverHandle;
LLHandle<LLWholeModelUploadObserver> mUploadObserverHandle;
diff --git a/indra/newview/llmodelpreview.cpp b/indra/newview/llmodelpreview.cpp
index d71f368232..6b1fbdce4c 100644
--- a/indra/newview/llmodelpreview.cpp
+++ b/indra/newview/llmodelpreview.cpp
@@ -576,7 +576,7 @@ void LLModelPreview::rebuildUploadData()
for (U32 model_ind = 0; model_ind < mModel[lod].size(); ++model_ind)
{
bool found_model = false;
- for (LLMeshUploadThread::instance_list::iterator iter = mUploadData.begin(); iter != mUploadData.end(); ++iter)
+ for (LLMeshUploadThread::instance_list_t::iterator iter = mUploadData.begin(); iter != mUploadData.end(); ++iter)
{
LLModelInstance& instance = *iter;
if (instance.mLOD[lod] == mModel[lod][model_ind])
@@ -2209,7 +2209,7 @@ void LLModelPreview::updateStatusMessages()
total_submeshes[i] = 0;
}
- for (LLMeshUploadThread::instance_list::iterator iter = mUploadData.begin(); iter != mUploadData.end(); ++iter)
+ for (LLMeshUploadThread::instance_list_t::iterator iter = mUploadData.begin(); iter != mUploadData.end(); ++iter)
{
LLModelInstance& instance = *iter;
@@ -3542,7 +3542,7 @@ bool LLModelPreview::render()
if (!show_skin_weight)
{
- for (LLMeshUploadThread::instance_list::iterator iter = mUploadData.begin(); iter != mUploadData.end(); ++iter)
+ for (LLMeshUploadThread::instance_list_t::iterator iter = mUploadData.begin(); iter != mUploadData.end(); ++iter)
{
LLModelInstance& instance = *iter;
@@ -3628,7 +3628,7 @@ bool LLModelPreview::render()
gGL.blendFunc(LLRender::BF_SOURCE_ALPHA, LLRender::BF_ONE_MINUS_SOURCE_ALPHA);
- for (LLMeshUploadThread::instance_list::iterator iter = mUploadData.begin(); iter != mUploadData.end(); ++iter)
+ for (LLMeshUploadThread::instance_list_t::iterator iter = mUploadData.begin(); iter != mUploadData.end(); ++iter)
{
LLModelInstance& instance = *iter;
@@ -3750,7 +3750,7 @@ bool LLModelPreview::render()
gGL.diffuseColor4f(1.f, 0.f, 0.f, 1.f);
const LLVector4a scale(0.5f);
- for (LLMeshUploadThread::instance_list::iterator iter = mUploadData.begin(); iter != mUploadData.end(); ++iter)
+ for (LLMeshUploadThread::instance_list_t::iterator iter = mUploadData.begin(); iter != mUploadData.end(); ++iter)
{
LLModelInstance& instance = *iter;
diff --git a/indra/newview/llmodelpreview.h b/indra/newview/llmodelpreview.h
index 7b3b699b33..92ac2d1faf 100644
--- a/indra/newview/llmodelpreview.h
+++ b/indra/newview/llmodelpreview.h
@@ -319,7 +319,7 @@ protected:
// Amount of triangles in original(base) model
U32 mMaxTriangleLimit;
- LLMeshUploadThread::instance_list mUploadData;
+ LLMeshUploadThread::instance_list_t mUploadData;
std::set<LLViewerFetchedTexture * > mTextureSet;
LLLoadedCallbackEntry::source_callback_list_t mCallbackTextureList;
diff --git a/indra/newview/llnotificationlistitem.cpp b/indra/newview/llnotificationlistitem.cpp
index 5b8b28ebe6..9a33bcb1b9 100644
--- a/indra/newview/llnotificationlistitem.cpp
+++ b/indra/newview/llnotificationlistitem.cpp
@@ -38,6 +38,7 @@
#include "lluicolortable.h"
#include "message.h"
#include "llnotificationsutil.h"
+#include "llviewercontrol.h"
#include <boost/regex.hpp>
LLNotificationListItem::LLNotificationListItem(const Params& p) : LLPanel(p),
@@ -133,10 +134,22 @@ std::string LLNotificationListItem::buildNotificationDate(const LLDate& time_sta
default:
timeStr = "[" + LLTrans::getString("TimeMonth") + "]/["
+LLTrans::getString("TimeDay")+"]/["
- +LLTrans::getString("TimeYear")+"] ["
- +LLTrans::getString("TimeHour")+"]:["
- +LLTrans::getString("TimeMin")+"] ["
- +LLTrans::getString("TimeTimezone")+"]";
+ +LLTrans::getString("TimeYear")+"] [";
+
+ static bool use_24h = gSavedSettings.getBOOL("Use24HourClock");
+ if (use_24h)
+ {
+ timeStr += LLTrans::getString("TimeHour") + "]:["
+ + LLTrans::getString("TimeMin") + "] ["
+ + LLTrans::getString("TimeTimezone") + "]";
+ }
+ else
+ {
+ timeStr += LLTrans::getString("TimeHour12") + "]:["
+ + LLTrans::getString("TimeMin") + "] ["
+ + LLTrans::getString("TimeAMPM") + "] ["
+ + LLTrans::getString("TimeTimezone") + "]";
+ }
break;
}
LLSD substitution;
diff --git a/indra/newview/lloutfitgallery.cpp b/indra/newview/lloutfitgallery.cpp
index 3adee9fa16..8589afae06 100644
--- a/indra/newview/lloutfitgallery.cpp
+++ b/indra/newview/lloutfitgallery.cpp
@@ -818,6 +818,7 @@ void LLOutfitGallery::getCurrentCategories(uuid_vec_t& vcur)
void LLOutfitGallery::updateAddedCategory(LLUUID cat_id)
{
+ LL_PROFILE_ZONE_SCOPED;
LLViewerInventoryCategory *cat = gInventory.getCategory(cat_id);
if (!cat) return;
diff --git a/indra/newview/lloutfitslist.cpp b/indra/newview/lloutfitslist.cpp
index 4e594af432..58cd9fab83 100644
--- a/indra/newview/lloutfitslist.cpp
+++ b/indra/newview/lloutfitslist.cpp
@@ -181,6 +181,7 @@ void LLOutfitsList::onOpen(const LLSD& info)
void LLOutfitsList::updateAddedCategory(LLUUID cat_id)
{
+ LL_PROFILE_ZONE_SCOPED;
LLViewerInventoryCategory *cat = gInventory.getCategory(cat_id);
if (!cat) return;
@@ -251,7 +252,9 @@ void LLOutfitsList::updateAddedCategory(LLUUID cat_id)
if (AISAPI::isAvailable() && LLInventoryModelBackgroundFetch::instance().folderFetchActive())
{
- // for reliability just fetch it whole, linked items included
+ // For reliability just fetch it whole, linked items included
+ // Todo: list is not warrantied to exist once callback arrives
+ // Fix it!
LLInventoryModelBackgroundFetch::instance().fetchFolderAndLinks(cat_id, [cat_id, list]
{
if (list)
@@ -1059,6 +1062,7 @@ void LLOutfitListBase::onIdle(void* userdata)
void LLOutfitListBase::onIdleRefreshList()
{
+ LL_PROFILE_ZONE_SCOPED;
if (LLAppViewer::instance()->quitRequested())
{
mRefreshListState.CategoryUUID.setNull();
@@ -1072,7 +1076,7 @@ void LLOutfitListBase::onIdleRefreshList()
return;
}
- const F64 MAX_TIME = 0.05f;
+ const F64 MAX_TIME = 0.005f;
F64 curent_time = LLTimer::getTotalSeconds();
const F64 end_time = curent_time + MAX_TIME;
diff --git a/indra/newview/llpanellandmarkinfo.cpp b/indra/newview/llpanellandmarkinfo.cpp
index 41373cd7f5..7596c0eba8 100644
--- a/indra/newview/llpanellandmarkinfo.cpp
+++ b/indra/newview/llpanellandmarkinfo.cpp
@@ -41,6 +41,7 @@
#include "lllandmarkactions.h"
#include "llparcel.h"
#include "llslurl.h"
+#include "llviewercontrol.h"
#include "llviewerinventory.h"
#include "llviewerparcelmgr.h"
#include "llviewerregion.h"
@@ -326,7 +327,8 @@ void LLPanelLandmarkInfo::displayItemInfo(const LLInventoryItem* pItem)
}
else
{
- std::string timeStr = getString("acquired_date");
+ static bool use_24h = gSavedSettings.getBOOL("Use24HourClock");
+ std::string timeStr = use_24h ? getString("acquired_date") : getString("acquired_date_ampm");
LLSD substitution;
substitution["datetime"] = (S32) time_utc;
LLStringUtil::format (timeStr, substitution);
diff --git a/indra/newview/llpanelprofile.cpp b/indra/newview/llpanelprofile.cpp
index b77ee2f439..6ea9cd2b92 100644
--- a/indra/newview/llpanelprofile.cpp
+++ b/indra/newview/llpanelprofile.cpp
@@ -859,7 +859,7 @@ void LLPanelProfileSecondLife::resetData()
resetLoading();
// Set default image and 1:1 dimensions for it
- mSecondLifePic->setValue("Generic_Person_Large");
+ mSecondLifePic->setValue(LLUUID());
LLRect imageRect = mSecondLifePicLayout->getRect();
mSecondLifePicLayout->reshape(imageRect.getWidth(), imageRect.getWidth());
diff --git a/indra/newview/llpanelteleporthistory.cpp b/indra/newview/llpanelteleporthistory.cpp
index 902412d359..bfdfa68e01 100644
--- a/indra/newview/llpanelteleporthistory.cpp
+++ b/indra/newview/llpanelteleporthistory.cpp
@@ -42,6 +42,7 @@
#include "llnotificationsutil.h"
#include "lltextbox.h"
#include "lltoggleablemenu.h"
+#include "llviewercontrol.h"
#include "llviewermenu.h"
#include "lllandmarkactions.h"
#include "llclipboard.h"
@@ -215,8 +216,18 @@ std::string LLTeleportHistoryFlatItem::getTimestamp()
// Only show timestamp for today and yesterday
if(time_diff < seconds_today + seconds_in_day)
{
- timestamp = "[" + LLTrans::getString("TimeHour12")+"]:["
- + LLTrans::getString("TimeMin")+"] ["+ LLTrans::getString("TimeAMPM")+"]";
+ static bool use_24h = gSavedSettings.getBOOL("Use24HourClock");
+ if (use_24h)
+ {
+ timestamp = "[" + LLTrans::getString("TimeHour") + "]:["
+ + LLTrans::getString("TimeMin") + "]";
+ }
+ else
+ {
+ timestamp = "[" + LLTrans::getString("TimeHour12") + "]:["
+ + LLTrans::getString("TimeMin") + "] [" + LLTrans::getString("TimeAMPM") + "]";
+ }
+
LLSD substitution;
substitution["datetime"] = (S32) date.secondsSinceEpoch();
LLStringUtil::format(timestamp, substitution);
diff --git a/indra/newview/llpanelvoicedevicesettings.cpp b/indra/newview/llpanelvoicedevicesettings.cpp
index 60877494e7..d8d6bcf5fd 100644
--- a/indra/newview/llpanelvoicedevicesettings.cpp
+++ b/indra/newview/llpanelvoicedevicesettings.cpp
@@ -240,13 +240,38 @@ void LLPanelVoiceDeviceSettings::refresh()
if(mCtrlInputDevices)
{
mCtrlInputDevices->removeall();
- mCtrlInputDevices->add(getLocalizedDeviceName(mInputDevice), mInputDevice, ADD_BOTTOM);
+ auto it = mLocalizedDeviceNames.find(mInputDevice);
+ if (it != mLocalizedDeviceNames.end())
+ {
+ mCtrlInputDevices->add(getLocalizedDeviceName(mInputDevice), mInputDevice, ADD_BOTTOM);
+ }
+ else
+ {
+ // Display name generaly doesn't match value.
+ // Value is an id so it's not nessesary readable,
+ // might not even be valid (disconnected usb).
+ // Until we get the data, don't change the device,
+ // otherwise box might override the control.
+ // But show a readable placeholder.
+ // Combo is disabled so it's safe to show
+ // a placeholder.
+ mCtrlInputDevices->add(getString("device_not_loaded"), mInputDevice, ADD_BOTTOM);
+ }
mCtrlInputDevices->setValue(mInputDevice);
}
if(mCtrlOutputDevices)
{
mCtrlOutputDevices->removeall();
- mCtrlOutputDevices->add(getLocalizedDeviceName(mOutputDevice), mOutputDevice, ADD_BOTTOM);
+ auto it = mLocalizedDeviceNames.find(mOutputDevice);
+ if (it != mLocalizedDeviceNames.end())
+ {
+ mCtrlOutputDevices->add(getLocalizedDeviceName(mOutputDevice), mOutputDevice, ADD_BOTTOM);
+ }
+ else
+ {
+ // Don't change the device, only the label
+ mCtrlOutputDevices->add(getString("device_not_loaded"), mOutputDevice, ADD_BOTTOM);
+ }
mCtrlOutputDevices->setValue(mOutputDevice);
}
}
diff --git a/indra/newview/llscriptfloater.cpp b/indra/newview/llscriptfloater.cpp
index 22a9dd0027..2fdec14f6d 100644
--- a/indra/newview/llscriptfloater.cpp
+++ b/indra/newview/llscriptfloater.cpp
@@ -109,25 +109,27 @@ bool LLScriptFloater::toggle(const LLUUID& notification_id)
LLScriptFloater* LLScriptFloater::show(const LLUUID& notification_id)
{
LLScriptFloater* floater = LLFloaterReg::getTypedInstance<LLScriptFloater>("script_floater", notification_id);
- floater->setNotificationId(notification_id);
- floater->createForm(notification_id);
-
- //LLDialog(LLGiveInventory and LLLoadURL) should no longer steal focus (see EXT-5445)
- floater->setAutoFocus(false);
-
- if(LLScriptFloaterManager::OBJ_SCRIPT == LLScriptFloaterManager::getObjectType(notification_id))
+ if (floater)
{
- floater->setSavePosition(true);
- floater->restorePosition();
- }
- else
- {
- floater->dockToChiclet(true);
- }
+ floater->setNotificationId(notification_id);
+ floater->createForm(notification_id);
- //LLDialog(LLGiveInventory and LLLoadURL) should no longer steal focus (see EXT-5445)
- LLFloaterReg::showTypedInstance<LLScriptFloater>("script_floater", notification_id, false);
+ //LLDialog(LLGiveInventory and LLLoadURL) should no longer steal focus (see EXT-5445)
+ floater->setAutoFocus(false);
+ if (LLScriptFloaterManager::OBJ_SCRIPT == LLScriptFloaterManager::getObjectType(notification_id))
+ {
+ floater->setSavePosition(true);
+ floater->restorePosition();
+ }
+ else
+ {
+ floater->dockToChiclet(true);
+ }
+
+ //LLDialog(LLGiveInventory and LLLoadURL) should no longer steal focus (see EXT-5445)
+ LLFloaterReg::showTypedInstance<LLScriptFloater>("script_floater", notification_id, false);
+ }
return floater;
}
diff --git a/indra/newview/llselectmgr.cpp b/indra/newview/llselectmgr.cpp
index 01fd5ae63c..be4b449e2a 100644
--- a/indra/newview/llselectmgr.cpp
+++ b/indra/newview/llselectmgr.cpp
@@ -1996,9 +1996,96 @@ bool LLSelectMgr::selectionSetGLTFMaterial(const LLUUID& mat_id)
asset_id = BLANK_MATERIAL_ASSET_ID;
}
}
+
+ // If this face already has the target material ID, do nothing.
+ // This prevents re-sending the same ID on OK, which can cause the server
+ // to drop overrides when queueApply is invoked with the OLD id.
+ if (objectp->getRenderMaterialID(te) == asset_id)
+ {
+ return true;
+ }
+
+ // Preserve existing texture transforms when switching to PBR material
+ LLTextureEntry* tep = objectp->getTE(te);
+ bool should_preserve_transforms = false;
+ LLGLTFMaterial* preserved_override = nullptr;
+
+ if (tep && asset_id.notNull())
+ {
+ // Only preserve transforms from existing GLTF material override
+ // Do not fall back to texture entry transforms when switching between PBR materials
+ LLGLTFMaterial* existing_override = tep->getGLTFMaterialOverride();
+ if (existing_override)
+ {
+ // Check if existing override has non-default transforms
+ const LLGLTFMaterial::TextureTransform& existing_transform = existing_override->mTextureTransform[0];
+ const LLGLTFMaterial::TextureTransform& default_transform = LLGLTFMaterial::TextureTransform();
+
+ if (existing_transform.mScale != default_transform.mScale ||
+ existing_transform.mOffset != default_transform.mOffset ||
+ existing_transform.mRotation != default_transform.mRotation)
+ {
+ // Preserve non-default transforms from current PBR material
+ preserved_override = new LLGLTFMaterial();
+ for (U32 i = 0; i < LLGLTFMaterial::GLTF_TEXTURE_INFO_COUNT; ++i)
+ {
+ preserved_override->mTextureTransform[i].mScale = existing_transform.mScale;
+ preserved_override->mTextureTransform[i].mOffset = existing_transform.mOffset;
+ preserved_override->mTextureTransform[i].mRotation = existing_transform.mRotation;
+ }
+ should_preserve_transforms = true;
+ }
+ // If existing override has default transforms, don't preserve anything
+ }
+ else
+ {
+ // No existing PBR material override - check texture entry transforms
+ // This handles the case of switching from Blinn-Phong to PBR material
+ F32 existing_scale_s, existing_scale_t, existing_offset_s, existing_offset_t, existing_rotation;
+ tep->getScale(&existing_scale_s, &existing_scale_t);
+ tep->getOffset(&existing_offset_s, &existing_offset_t);
+ existing_rotation = tep->getRotation();
+
+ const LLGLTFMaterial::TextureTransform& default_transform = LLGLTFMaterial::TextureTransform();
+ if (existing_scale_s != default_transform.mScale.mV[0] || existing_scale_t != default_transform.mScale.mV[1] ||
+ existing_offset_s != default_transform.mOffset.mV[0] || existing_offset_t != default_transform.mOffset.mV[1] ||
+ existing_rotation != default_transform.mRotation)
+ {
+ // Preserve non-default transforms from texture entry
+ preserved_override = new LLGLTFMaterial();
+ for (U32 i = 0; i < LLGLTFMaterial::GLTF_TEXTURE_INFO_COUNT; ++i)
+ {
+ LLVector2 pbr_scale, pbr_offset;
+ F32 pbr_rotation;
+ LLGLTFMaterial::convertTextureTransformToPBR(
+ existing_scale_s, existing_scale_t,
+ existing_offset_s, existing_offset_t,
+ existing_rotation,
+ pbr_scale, pbr_offset, pbr_rotation);
+ preserved_override->mTextureTransform[i].mScale = pbr_scale;
+ preserved_override->mTextureTransform[i].mOffset = pbr_offset;
+ preserved_override->mTextureTransform[i].mRotation = pbr_rotation;
+ }
+ should_preserve_transforms = true;
+ }
+ }
+ }
+
objectp->clearTEWaterExclusion(te);
// Blank out most override data on the object and send to server
objectp->setRenderMaterialID(te, asset_id);
+ if (should_preserve_transforms && preserved_override)
+ {
+ // Apply material with preserved transforms
+ LLGLTFMaterialList::queueApply(objectp, te, asset_id, preserved_override);
+ // Update local state
+ objectp->setRenderMaterialID(te, asset_id, false, true);
+ tep->setGLTFMaterialOverride(preserved_override);
+ }
+ else
+ {
+ objectp->setRenderMaterialID(te, asset_id);
+ }
return true;
}
@@ -2265,23 +2352,9 @@ void LLSelectMgr::selectionRevertGLTFMaterials()
//blank override out
LLGLTFMaterialList::queueApply(objectp, te, asset_id);
}
- if (old_asset_id != asset_id)
- {
- // Restore overrides and base material
- // Note: might not work reliably if asset is already there, might
- // have a server sided problem where servers applies override
- // first then resets it by adding asset, in which case need
- // to create a server ticket and chain asset then override
- // application.
- LLGLTFMaterialList::queueApply(objectp, te, asset_id, material);
- }
else
{
- // Enqueue override update to server
- // Note: this is suboptimal, better to send asset id as well
- // but there seems to be a server problem with queueApply
- // that ignores override in some cases
- LLGLTFMaterialList::queueModify(objectp, te, material);
+ LLGLTFMaterialList::queueApply(objectp, te, asset_id, material);
}
}
return true;
diff --git a/indra/newview/llsettingspicker.cpp b/indra/newview/llsettingspicker.cpp
index 619882dc5e..5fc39b0dd8 100644
--- a/indra/newview/llsettingspicker.cpp
+++ b/indra/newview/llsettingspicker.cpp
@@ -152,7 +152,11 @@ void LLFloaterSettingsPicker::onClose(bool app_quitting)
owner->setFocus(true);
}
mSettingItemID.setNull();
- mInventoryPanel->getRootFolder()->clearSelection();
+ mInventoryPanel->clearSelection();
+ if (mInventoryPanel->getRootFolder())
+ {
+ mInventoryPanel->getRootFolder()->clearSelection();
+ }
}
void LLFloaterSettingsPicker::setValue(const LLSD& value)
diff --git a/indra/newview/llsidepaneliteminfo.cpp b/indra/newview/llsidepaneliteminfo.cpp
index 3d4b4fb9c1..6d50f216f5 100644
--- a/indra/newview/llsidepaneliteminfo.cpp
+++ b/indra/newview/llsidepaneliteminfo.cpp
@@ -487,7 +487,8 @@ void LLSidepanelItemInfo::refreshFromItem(LLViewerInventoryItem* item)
}
else
{
- std::string timeStr = getString("acquiredDate");
+ static bool use_24h = gSavedSettings.getBOOL("Use24HourClock");
+ std::string timeStr = use_24h ? getString("acquiredDate") : getString("acquiredDateAMPM");
LLSD substitution;
substitution["datetime"] = (S32) time_utc;
LLStringUtil::format (timeStr, substitution);
diff --git a/indra/newview/llstartup.cpp b/indra/newview/llstartup.cpp
index bf7c58a3cb..3786a9b160 100644
--- a/indra/newview/llstartup.cpp
+++ b/indra/newview/llstartup.cpp
@@ -439,7 +439,26 @@ bool idle_startup()
system = osString.substr (begIdx, endIdx - begIdx);
system += "Locale";
- LLStringUtil::setLocale (LLTrans::getString(system));
+ std::string locale = LLTrans::getString(system);
+ if (locale != LLStringUtil::getLocale()) // is there a reason to do this on repeat?
+ {
+ LLStringUtil::setLocale(locale);
+
+ // Not all locales have AMPM, test it
+ if (LLStringOps::sAM.empty()) // Might already be overriden from LLAppViewer::init()
+ {
+ LLDate datetime(0.0);
+ std::string val = datetime.toHTTPDateString("%p");
+ if (val.empty())
+ {
+ LL_DEBUGS("InitInfo") << "Current locale \"" << locale << "\" "
+ << "doesn't support AM/PM time format" << LL_ENDL;
+ // fallback to declarations in strings.xml
+ LLStringOps::sAM = LLTrans::getString("dateTimeAM");
+ LLStringOps::sPM = LLTrans::getString("dateTimePM");
+ }
+ }
+ }
//note: Removing this line will cause incorrect button size in the login screen. -- bao.
gTextureList.updateImages(0.01f) ;
diff --git a/indra/newview/lltexturectrl.cpp b/indra/newview/lltexturectrl.cpp
index 5507ef517a..b2f2509e80 100644
--- a/indra/newview/lltexturectrl.cpp
+++ b/indra/newview/lltexturectrl.cpp
@@ -1012,6 +1012,8 @@ void LLFloaterTexturePicker::onBtnSetToDefault(void* userdata)
{
self->setImageID( self->getDefaultImageAssetID() );
self->setTentative(false);
+ // Deselect in case inventory has a selected item with the same id
+ self->mInventoryPanel->getRootFolder()->clearSelection();
}
self->commitIfImmediateSet();
}
@@ -1023,6 +1025,8 @@ void LLFloaterTexturePicker::onBtnBlank(void* userdata)
self->setCanApply(true, true);
self->setImageID( self->getBlankImageAssetID() );
self->setTentative(false);
+ // Deselect in case inventory has a selected item with the same id
+ self->mInventoryPanel->getRootFolder()->clearSelection();
self->commitIfImmediateSet();
}
@@ -1034,6 +1038,8 @@ void LLFloaterTexturePicker::onBtnNone(void* userdata)
self->setCanApply(true, true);
self->setImageID( LLUUID::null );
self->setTentative(false);
+ // Deselect in case inventory has a selected item with null id
+ self->mInventoryPanel->getRootFolder()->clearSelection();
self->commitIfImmediateSet();
}
@@ -1698,10 +1704,16 @@ LLTextureCtrl::LLTextureCtrl(const LLTextureCtrl::Params& p)
mDefaultImageName(p.default_image_name),
mFallbackImage(p.fallback_image)
{
-
- // Default of defaults is white image for diff tex
- //
- setBlankImageAssetID(IMG_WHITE);
+ if (mInventoryPickType == PICK_MATERIAL)
+ {
+ setBlankImageAssetID(BLANK_MATERIAL_ASSET_ID);
+ }
+ else
+ {
+ // Default of defaults is white image for diff tex
+ //
+ setBlankImageAssetID(IMG_WHITE);
+ }
setAllowNoTexture(p.allow_no_texture);
setCanApplyImmediately(p.can_apply_immediately);
diff --git a/indra/newview/lltexturefetch.cpp b/indra/newview/lltexturefetch.cpp
index 93b5806acf..f7cb0ee7ed 100644
--- a/indra/newview/lltexturefetch.cpp
+++ b/indra/newview/lltexturefetch.cpp
@@ -1295,10 +1295,19 @@ bool LLTextureFetchWorker::doWork(S32 param)
else
{
mCanUseCapability = false;
- mRegionRetryAttempt++;
- mRegionRetryTimer.setTimerExpirySec(CAP_MISSING_EXPIRATION_DELAY);
- // ex: waiting for caps
- LL_INFOS_ONCE(LOG_TXT) << "Texture not available via HTTP: empty URL." << LL_ENDL;
+ if (gDisconnected)
+ {
+ // We lost connection or are shutting down.
+ mCanUseHTTP = false;
+ return true; // abort
+ }
+ else
+ {
+ // Ex: waiting for caps
+ mRegionRetryAttempt++;
+ mRegionRetryTimer.setTimerExpirySec(CAP_MISSING_EXPIRATION_DELAY);
+ LL_INFOS_ONCE(LOG_TXT) << "Texture not available via HTTP: empty URL." << LL_ENDL;
+ }
}
}
else
@@ -1694,10 +1703,10 @@ bool LLTextureFetchWorker::doWork(S32 param)
mHttpReplyOffset = 0;
mLoadedDiscard = mRequestedDiscard;
- if (mLoadedDiscard < 0)
+ if (mLoadedDiscard < 0 || (mLoadedDiscard > MAX_DISCARD_LEVEL && mFormattedImage->getCodec() == IMG_CODEC_J2C))
{
LL_WARNS(LOG_TXT) << mID << " mLoadedDiscard is " << mLoadedDiscard
- << ", should be >=0" << LL_ENDL;
+ << ", should be >=0 and <=" << MAX_DISCARD_LEVEL << LL_ENDL;
}
setState(DECODE_IMAGE);
if (mWriteToCacheState != NOT_WRITE)
@@ -1759,14 +1768,27 @@ bool LLTextureFetchWorker::doWork(S32 param)
LL_DEBUGS(LOG_TXT) << mID << " DECODE_IMAGE abort: mLoadedDiscard < 0" << LL_ENDL;
return true;
}
+
+ llassert_always(mFormattedImage.notNull());
+ S32 discard = mHaveAllData && mFormattedImage->getCodec() != IMG_CODEC_J2C ? 0 : mLoadedDiscard;
+ if (discard > MAX_DISCARD_LEVEL) // only warn for j2c
+ {
+ // We encode j2c with fixed amount of discard levels,
+ // Trying to decode beyound that will fail.
+ LL_WARNS(LOG_TXT) << "Decode entered with invalid discard. ID = " << mID << LL_ENDL;
+
+ //abort, don't decode
+ setState(DONE);
+ LL_DEBUGS(LOG_TXT) << mID << " DECODE_IMAGE abort: mLoadedDiscard > MAX_DISCARD_LEVEL" << LL_ENDL;
+ return true;
+ }
+
mDecodeTimer.reset();
mRawImage = NULL;
mAuxImage = NULL;
- llassert_always(mFormattedImage.notNull());
// if we have the entire image data (and the image is not J2C), decode the full res image
// DO NOT decode a higher res j2c than was requested. This is a waste of time and memory.
- S32 discard = mHaveAllData && mFormattedImage->getCodec() != IMG_CODEC_J2C ? 0 : mLoadedDiscard;
mDecoded = false;
setState(DECODE_IMAGE_UPDATE);
LL_DEBUGS(LOG_TXT) << mID << ": Decoding. Bytes: " << mFormattedImage->getDataSize() << " Discard: " << discard
diff --git a/indra/newview/lltoastgroupnotifypanel.cpp b/indra/newview/lltoastgroupnotifypanel.cpp
index 3c3440d41a..95653dc19b 100644
--- a/indra/newview/lltoastgroupnotifypanel.cpp
+++ b/indra/newview/lltoastgroupnotifypanel.cpp
@@ -87,10 +87,21 @@ LLToastGroupNotifyPanel::LLToastGroupNotifyPanel(const LLNotificationPtr& notifi
std::string timeStr = "[" + LLTrans::getString("TimeWeek") + "], ["
+ LLTrans::getString("TimeMonth") + "]/["
+ LLTrans::getString("TimeDay") + "]/["
- + LLTrans::getString("TimeYear") + "] ["
- + LLTrans::getString("TimeHour") + "]:["
- + LLTrans::getString("TimeMin") + "] ["
- + LLTrans::getString("TimeTimezone") + "]";
+ + LLTrans::getString("TimeYear") + "] [";
+ static bool use_24h = gSavedSettings.getBOOL("Use24HourClock");
+ if (use_24h)
+ {
+ timeStr += LLTrans::getString("TimeHour") + "]:["
+ + LLTrans::getString("TimeMin") + "] ["
+ + LLTrans::getString("TimeTimezone") + "]";
+ }
+ else
+ {
+ timeStr += LLTrans::getString("TimeHour12") + "]:["
+ + LLTrans::getString("TimeMin") + "] ["
+ + LLTrans::getString("TimeAMPM") + "] ["
+ + LLTrans::getString("TimeTimezone") + "]";
+ }
const LLDate timeStamp = notification->getDate();
LLDate notice_date = timeStamp.notNull() ? timeStamp : payload["received_time"].asDate();
diff --git a/indra/newview/lltoolbarview.cpp b/indra/newview/lltoolbarview.cpp
index 0063e0b7fd..c1ec5fa183 100644
--- a/indra/newview/lltoolbarview.cpp
+++ b/indra/newview/lltoolbarview.cpp
@@ -110,7 +110,7 @@ bool LLToolBarView::postBuild()
{
mToolbars[i]->setStartDragCallback(boost::bind(LLToolBarView::startDragTool,_1,_2,_3));
mToolbars[i]->setHandleDragCallback(boost::bind(LLToolBarView::handleDragTool,_1,_2,_3,_4));
- mToolbars[i]->setHandleDropCallback(boost::bind(LLToolBarView::handleDropTool,_1,_2,_3,_4));
+ mToolbars[i]->setHandleDropCallback(boost::bind(LLToolBarView::handleDropTool,_1,_2,_3,_4,_5));
mToolbars[i]->setButtonAddCallback(boost::bind(LLToolBarView::onToolBarButtonAdded,_1));
mToolbars[i]->setButtonRemoveCallback(boost::bind(LLToolBarView::onToolBarButtonRemoved,_1));
}
@@ -624,8 +624,14 @@ bool LLToolBarView::handleDragTool( S32 x, S32 y, const LLUUID& uuid, LLAssetTyp
return false;
}
-bool LLToolBarView::handleDropTool( void* cargo_data, S32 x, S32 y, LLToolBar* toolbar)
+bool LLToolBarView::handleDropTool( void* cargo_data, EDragAndDropType cargo_type, S32 x, S32 y, LLToolBar* toolbar)
{
+ if (cargo_type == DAD_PERSON)
+ {
+ // DAD_PERSON means that cargo_data contains an uuid, not an LLInventoryObject
+ resetDragTool(NULL);
+ return false;
+ }
bool handled = false;
LLInventoryObject* inv_item = static_cast<LLInventoryObject*>(cargo_data);
@@ -647,15 +653,18 @@ bool LLToolBarView::handleDropTool( void* cargo_data, S32 x, S32 y, LLToolBar* t
if (old_toolbar_loc != LLToolBarEnums::TOOLBAR_NONE)
{
llassert(gToolBarView->mDragToolbarButton);
- old_toolbar = gToolBarView->mDragToolbarButton->getParentByType<LLToolBar>();
- if (old_toolbar->isReadOnly() && toolbar->isReadOnly())
- {
- // do nothing
- }
- else
+ if (gToolBarView->mDragToolbarButton)
{
- int old_rank = LLToolBar::RANK_NONE;
- gToolBarView->removeCommand(command_id, old_rank);
+ old_toolbar = gToolBarView->mDragToolbarButton->getParentByType<LLToolBar>();
+ if (old_toolbar->isReadOnly() && toolbar->isReadOnly())
+ {
+ // do nothing
+ }
+ else
+ {
+ int old_rank = LLToolBar::RANK_NONE;
+ gToolBarView->removeCommand(command_id, old_rank);
+ }
}
}
diff --git a/indra/newview/lltoolbarview.h b/indra/newview/lltoolbarview.h
index 7cecd81052..7212f099b7 100644
--- a/indra/newview/lltoolbarview.h
+++ b/indra/newview/lltoolbarview.h
@@ -92,7 +92,7 @@ public:
static void startDragTool(S32 x, S32 y, LLToolBarButton* toolbarButton);
static bool handleDragTool(S32 x, S32 y, const LLUUID& uuid, LLAssetType::EType type);
- static bool handleDropTool(void* cargo_data, S32 x, S32 y, LLToolBar* toolbar);
+ static bool handleDropTool(void* cargo_data, EDragAndDropType cargo_type, S32 x, S32 y, LLToolBar* toolbar);
static void resetDragTool(LLToolBarButton* toolbarButton);
LLInventoryObject* getDragItem();
LLView* getBottomToolbar() { return mBottomToolbarPanel; }
diff --git a/indra/newview/lltooldraganddrop.cpp b/indra/newview/lltooldraganddrop.cpp
index b4a5955be3..d0c0bdb5ce 100644
--- a/indra/newview/lltooldraganddrop.cpp
+++ b/indra/newview/lltooldraganddrop.cpp
@@ -29,6 +29,8 @@
// library headers
#include "llnotificationsutil.h"
+#include <vector>
+#include <tuple>
// project headers
#include "llagent.h"
#include "llagentcamera.h"
@@ -1297,7 +1299,89 @@ void LLToolDragAndDrop::dropMaterialOneFace(LLViewerObject* hit_obj,
asset_id = BLANK_MATERIAL_ASSET_ID;
}
- hit_obj->setRenderMaterialID(hit_face, asset_id);
+ // Preserve existing texture transforms when switching to PBR material
+ LLTextureEntry* tep = hit_obj->getTE(hit_face);
+ F32 existing_scale_s = LLGLTFMaterial::TextureTransform().mScale.mV[0];
+ F32 existing_scale_t = LLGLTFMaterial::TextureTransform().mScale.mV[1];
+ F32 existing_offset_s = LLGLTFMaterial::TextureTransform().mOffset.mV[0];
+ F32 existing_offset_t = LLGLTFMaterial::TextureTransform().mOffset.mV[1];
+ F32 existing_rotation = LLGLTFMaterial::TextureTransform().mRotation;
+ bool should_preserve_transforms = false;
+ LLGLTFMaterial* preserved_override = nullptr;
+
+ if (tep && asset_id.notNull())
+ {
+ // Only preserve transforms from existing GLTF material override
+ // Do not fall back to texture entry transforms when switching between PBR materials
+ LLGLTFMaterial* existing_override = tep->getGLTFMaterialOverride();
+ if (existing_override)
+ {
+ // Check if existing override has non-default transforms
+ const LLGLTFMaterial::TextureTransform& existing_transform = existing_override->mTextureTransform[0];
+ const LLGLTFMaterial::TextureTransform& default_transform = LLGLTFMaterial::TextureTransform();
+
+ if (existing_transform.mScale != default_transform.mScale ||
+ existing_transform.mOffset != default_transform.mOffset ||
+ existing_transform.mRotation != default_transform.mRotation)
+ {
+ // Preserve non-default transforms from current PBR material
+ preserved_override = new LLGLTFMaterial();
+ for (U32 i = 0; i < LLGLTFMaterial::GLTF_TEXTURE_INFO_COUNT; ++i)
+ {
+ preserved_override->mTextureTransform[i].mScale = existing_transform.mScale;
+ preserved_override->mTextureTransform[i].mOffset = existing_transform.mOffset;
+ preserved_override->mTextureTransform[i].mRotation = existing_transform.mRotation;
+ }
+ should_preserve_transforms = true;
+ }
+ // If existing override has default transforms, don't preserve anything
+ }
+ else
+ {
+ // No existing PBR material override - check texture entry transforms
+ // This handles the case of switching from Blinn-Phong to PBR material
+ F32 existing_scale_s, existing_scale_t, existing_offset_s, existing_offset_t, existing_rotation;
+ tep->getScale(&existing_scale_s, &existing_scale_t);
+ tep->getOffset(&existing_offset_s, &existing_offset_t);
+ existing_rotation = tep->getRotation();
+
+ const LLGLTFMaterial::TextureTransform& default_transform = LLGLTFMaterial::TextureTransform();
+ if (existing_scale_s != default_transform.mScale.mV[0] || existing_scale_t != default_transform.mScale.mV[1] ||
+ existing_offset_s != default_transform.mOffset.mV[0] || existing_offset_t != default_transform.mOffset.mV[1] ||
+ existing_rotation != default_transform.mRotation)
+ {
+ // Preserve non-default transforms from texture entry
+ preserved_override = new LLGLTFMaterial();
+ for (U32 i = 0; i < LLGLTFMaterial::GLTF_TEXTURE_INFO_COUNT; ++i)
+ {
+ LLVector2 pbr_scale, pbr_offset;
+ F32 pbr_rotation;
+ LLGLTFMaterial::convertTextureTransformToPBR(
+ existing_scale_s, existing_scale_t,
+ existing_offset_s, existing_offset_t,
+ existing_rotation,
+ pbr_scale, pbr_offset, pbr_rotation);
+ preserved_override->mTextureTransform[i].mScale = pbr_scale;
+ preserved_override->mTextureTransform[i].mOffset = pbr_offset;
+ preserved_override->mTextureTransform[i].mRotation = pbr_rotation;
+ }
+ should_preserve_transforms = true;
+ }
+ }
+ }
+
+ if (should_preserve_transforms && preserved_override)
+ {
+ // Apply material with preserved transforms
+ LLGLTFMaterialList::queueApply(hit_obj, hit_face, asset_id, preserved_override);
+ // Update local state
+ hit_obj->setRenderMaterialID(hit_face, asset_id, false, true);
+ tep->setGLTFMaterialOverride(preserved_override);
+ }
+ else
+ {
+ hit_obj->setRenderMaterialID(hit_face, asset_id);
+ }
dialog_refresh_all();
@@ -1333,7 +1417,134 @@ void LLToolDragAndDrop::dropMaterialAllFaces(LLViewerObject* hit_obj,
asset_id = BLANK_MATERIAL_ASSET_ID;
}
- hit_obj->setRenderMaterialIDs(asset_id);
+ // Preserve existing texture transforms when switching to PBR material for all faces
+ std::vector<std::pair<bool, LLGLTFMaterial*>> preserved_transforms(hit_obj->getNumTEs());
+
+ if (asset_id.notNull())
+ {
+ for (S32 te = 0; te < hit_obj->getNumTEs(); ++te)
+ {
+ LLTextureEntry* tep = hit_obj->getTE(te);
+ if (!tep) continue;
+
+ bool should_preserve = false;
+ LLGLTFMaterial* preserved_override = nullptr;
+
+ // Only preserve transforms from existing GLTF material override
+ // Do not fall back to texture entry transforms when switching between PBR materials
+ LLGLTFMaterial* existing_override = tep->getGLTFMaterialOverride();
+ if (existing_override)
+ {
+ // Check if existing override has non-default transforms
+ const LLGLTFMaterial::TextureTransform& existing_transform = existing_override->mTextureTransform[0];
+ const LLGLTFMaterial::TextureTransform& default_transform = LLGLTFMaterial::TextureTransform();
+
+ if (existing_transform.mScale != default_transform.mScale ||
+ existing_transform.mOffset != default_transform.mOffset ||
+ existing_transform.mRotation != default_transform.mRotation)
+ {
+ // Preserve non-default transforms from current PBR material
+ preserved_override = new LLGLTFMaterial();
+ for (U32 i = 0; i < LLGLTFMaterial::GLTF_TEXTURE_INFO_COUNT; ++i)
+ {
+ preserved_override->mTextureTransform[i].mScale = existing_transform.mScale;
+ preserved_override->mTextureTransform[i].mOffset = existing_transform.mOffset;
+ preserved_override->mTextureTransform[i].mRotation = existing_transform.mRotation;
+ }
+ should_preserve = true;
+ }
+ else
+ {
+ // Existing override has default transforms, fall back to texture entry
+ F32 existing_scale_s, existing_scale_t, existing_offset_s, existing_offset_t, existing_rotation;
+ tep->getScale(&existing_scale_s, &existing_scale_t);
+ tep->getOffset(&existing_offset_s, &existing_offset_t);
+ existing_rotation = tep->getRotation();
+
+ if (existing_scale_s != default_transform.mScale.mV[0] || existing_scale_t != default_transform.mScale.mV[1] ||
+ existing_offset_s != default_transform.mOffset.mV[0] || existing_offset_t != default_transform.mOffset.mV[1] ||
+ existing_rotation != default_transform.mRotation)
+ {
+ // Preserve non-default transforms from texture entry
+ preserved_override = new LLGLTFMaterial();
+ for (U32 i = 0; i < LLGLTFMaterial::GLTF_TEXTURE_INFO_COUNT; ++i)
+ {
+ LLVector2 pbr_scale, pbr_offset;
+ F32 pbr_rotation;
+ LLGLTFMaterial::convertTextureTransformToPBR(
+ existing_scale_s, existing_scale_t,
+ existing_offset_s, existing_offset_t,
+ existing_rotation,
+ pbr_scale, pbr_offset, pbr_rotation);
+ preserved_override->mTextureTransform[i].mScale = pbr_scale;
+ preserved_override->mTextureTransform[i].mOffset = pbr_offset;
+ preserved_override->mTextureTransform[i].mRotation = pbr_rotation;
+ }
+ should_preserve = true;
+ }
+ }
+ }
+ else
+ {
+ // No existing PBR material override - check texture entry transforms
+ // This handles the case of switching from Blinn-Phong to PBR material
+ F32 existing_scale_s, existing_scale_t, existing_offset_s, existing_offset_t, existing_rotation;
+ tep->getScale(&existing_scale_s, &existing_scale_t);
+ tep->getOffset(&existing_offset_s, &existing_offset_t);
+ existing_rotation = tep->getRotation();
+
+ const LLGLTFMaterial::TextureTransform& default_transform = LLGLTFMaterial::TextureTransform();
+ if (existing_scale_s != default_transform.mScale.mV[0] || existing_scale_t != default_transform.mScale.mV[1] ||
+ existing_offset_s != default_transform.mOffset.mV[0] || existing_offset_t != default_transform.mOffset.mV[1] ||
+ existing_rotation != default_transform.mRotation)
+ {
+ // Preserve non-default transforms from texture entry
+ preserved_override = new LLGLTFMaterial();
+ for (U32 i = 0; i < LLGLTFMaterial::GLTF_TEXTURE_INFO_COUNT; ++i)
+ {
+ LLVector2 pbr_scale, pbr_offset;
+ F32 pbr_rotation;
+ LLGLTFMaterial::convertTextureTransformToPBR(
+ existing_scale_s, existing_scale_t,
+ existing_offset_s, existing_offset_t,
+ existing_rotation,
+ pbr_scale, pbr_offset, pbr_rotation);
+ preserved_override->mTextureTransform[i].mScale = pbr_scale;
+ preserved_override->mTextureTransform[i].mOffset = pbr_offset;
+ preserved_override->mTextureTransform[i].mRotation = pbr_rotation;
+ }
+ should_preserve = true;
+ }
+ }
+
+ preserved_transforms[te] = std::make_pair(should_preserve, preserved_override);
+ }
+ }
+
+ // Apply materials with preserved transforms
+ if (asset_id.notNull())
+ {
+ for (S32 te = 0; te < hit_obj->getNumTEs(); ++te)
+ {
+ LLGLTFMaterial* preserved_override = preserved_transforms[te].second;
+ if (preserved_override)
+ {
+ // Apply material with preserved transforms
+ LLGLTFMaterialList::queueApply(hit_obj, te, asset_id, preserved_override);
+ // Update local state
+ hit_obj->setRenderMaterialID(te, asset_id, false, true);
+ hit_obj->getTE(te)->setGLTFMaterialOverride(preserved_override);
+ }
+ else
+ {
+ hit_obj->setRenderMaterialID(te, asset_id, false, true);
+ }
+ }
+ }
+ else
+ {
+ hit_obj->setRenderMaterialIDs(asset_id);
+ }
dialog_refresh_all();
// send the update to the simulator
hit_obj->sendTEUpdate();
diff --git a/indra/newview/llviewercontrol.cpp b/indra/newview/llviewercontrol.cpp
index d39805f4c7..75cc76fee6 100644
--- a/indra/newview/llviewercontrol.cpp
+++ b/indra/newview/llviewercontrol.cpp
@@ -68,6 +68,8 @@
#include "llrender.h"
#include "llnavigationbar.h"
#include "llnotificationsutil.h"
+#include "llfloaterpreference.h"
+#include "llfloaterreg.h"
#include "llfloatertools.h"
#include "llpaneloutfitsinventory.h"
#include "llpanellogin.h"
@@ -149,6 +151,21 @@ static bool handleDebugAvatarJointsChanged(const LLSD& newvalue)
return true;
}
+static bool handleDebugQualityPerformanceChanged(const LLSD& newvalue)
+{
+ // control was set directly or after adjusting Preference setting, no need to update
+ if (gSavedSettings.getU32("RenderQualityPerformance") != gSavedSettings.getU32("DebugQualityPerformance"))
+ {
+ LLFloaterPreference* instance = LLFloaterReg::getTypedInstance<LLFloaterPreference>("preferences");
+ if (instance)
+ {
+ gSavedSettings.setU32("RenderQualityPerformance", newvalue.asInteger());
+ instance->onChangeQuality(newvalue);
+ }
+ }
+ return true;
+}
+
static bool handleAvatarHoverOffsetChanged(const LLSD& newvalue)
{
if (isAgentAvatarValid())
@@ -946,6 +963,7 @@ void settings_setup_listeners()
setting_setup_signal_listener(gSavedSettings, "SpellCheckDictionary", handleSpellCheckChanged);
setting_setup_signal_listener(gSavedSettings, "LoginLocation", handleLoginLocationChanged);
setting_setup_signal_listener(gSavedSettings, "DebugAvatarJoints", handleDebugAvatarJointsChanged);
+ setting_setup_signal_listener(gSavedSettings, "DebugQualityPerformance", handleDebugQualityPerformanceChanged);
setting_setup_signal_listener(gSavedSettings, "TargetFPS", handleTargetFPSChanged);
setting_setup_signal_listener(gSavedSettings, "AutoTuneFPS", handleAutoTuneFPSChanged);
diff --git a/indra/newview/llviewerdisplay.cpp b/indra/newview/llviewerdisplay.cpp
index 7a93536c00..4c408ec17d 100644
--- a/indra/newview/llviewerdisplay.cpp
+++ b/indra/newview/llviewerdisplay.cpp
@@ -1524,6 +1524,11 @@ void render_ui(F32 zoom_factor, int subfield)
render_disconnected_background();
}
}
+ else
+ {
+ // Make sure particle effects disappear
+ LLHUDObject::renderAllForTimer();
+ }
if (render_ui)
{
diff --git a/indra/newview/llviewerinventory.cpp b/indra/newview/llviewerinventory.cpp
index 6155058f14..ec5381ddfc 100644
--- a/indra/newview/llviewerinventory.cpp
+++ b/indra/newview/llviewerinventory.cpp
@@ -419,7 +419,9 @@ void LLViewerInventoryItem::updateServer(bool is_new) const
<< LL_ENDL;
return;
}
- if(gAgent.getID() != mPermissions.getOwner())
+ LLUUID owner = mPermissions.getOwner();
+ if(gAgent.getID() != owner
+ && owner.notNull()) // incomplete?
{
// *FIX: deal with this better.
LL_WARNS(LOG_INV) << "LLViewerInventoryItem::updateServer() - for unowned item "
diff --git a/indra/newview/llviewermenu.cpp b/indra/newview/llviewermenu.cpp
index f7688b762f..34a3745222 100644
--- a/indra/newview/llviewermenu.cpp
+++ b/indra/newview/llviewermenu.cpp
@@ -6516,7 +6516,7 @@ bool handle_zoom_to_object(const LLUUID& object_id)
LLViewerObject* object = gObjectList.findObject(object_id);
- if (object)
+ if (object && object->isReachable())
{
gAgentCamera.setFocusOnAvatar(false, ANIMATE);
diff --git a/indra/newview/llviewerobject.cpp b/indra/newview/llviewerobject.cpp
index 9e77b40a45..ed4856df11 100644
--- a/indra/newview/llviewerobject.cpp
+++ b/indra/newview/llviewerobject.cpp
@@ -5090,11 +5090,10 @@ void LLViewerObject::setNumTEs(const U8 num_tes)
if (base_material && override_material)
{
tep->setGLTFMaterialOverride(new LLGLTFMaterial(*override_material));
-
- LLGLTFMaterial* render_material = new LLFetchedGLTFMaterial();
- *render_material = *base_material;
- render_material->applyOverride(*override_material);
- tep->setGLTFRenderMaterial(render_material);
+ }
+ if (base_material)
+ {
+ initRenderMaterial(i);
}
}
}
@@ -5270,6 +5269,9 @@ void LLViewerObject::updateTEMaterialTextures(U8 te)
});
}
getTE(te)->setGLTFMaterial(mat);
+ initRenderMaterial(te);
+ mat = (LLFetchedGLTFMaterial*) getTE(te)->getGLTFRenderMaterial();
+ llassert(mat == nullptr || dynamic_cast<LLFetchedGLTFMaterial*>(getTE(te)->getGLTFRenderMaterial()) != nullptr);
}
else if (mat_id.isNull() && mat != nullptr)
{
@@ -5659,6 +5661,42 @@ S32 LLViewerObject::setTEMaterialParams(const U8 te, const LLMaterialPtr pMateri
return retval;
}
+// Set render material if there are overrides or if the base material is has a
+// baked texture. Otherwise, set it to null.
+// If you are setting the material override and not sending an update message,
+// you should probably call this function.
+S32 LLViewerObject::initRenderMaterial(U8 te)
+{
+ LL_PROFILE_ZONE_SCOPED;
+
+ LLTextureEntry* tep = getTE(te);
+ if (!tep) { return 0; }
+ const LLFetchedGLTFMaterial* base_material = static_cast<LLFetchedGLTFMaterial*>(tep->getGLTFMaterial());
+ llassert(base_material);
+ if (!base_material) { return 0; }
+ const LLGLTFMaterial* override_material = tep->getGLTFMaterialOverride();
+ LLFetchedGLTFMaterial* render_material = nullptr;
+ bool need_render_material = override_material;
+ if (!need_render_material)
+ {
+ for (const LLUUID& texture_id : base_material->mTextureId)
+ {
+ if (LLAvatarAppearanceDefines::LLAvatarAppearanceDictionary::isBakedImageId(texture_id))
+ {
+ need_render_material = true;
+ break;
+ }
+ }
+ }
+ if (need_render_material)
+ {
+ render_material = new LLFetchedGLTFMaterial(*base_material);
+ if (override_material) { render_material->applyOverride(*override_material); }
+ render_material->clearFetchedTextures();
+ }
+ return tep->setGLTFRenderMaterial(render_material);
+}
+
S32 LLViewerObject::setTEGLTFMaterialOverride(U8 te, LLGLTFMaterial* override_mat)
{
LL_PROFILE_ZONE_SCOPED;
@@ -5692,22 +5730,13 @@ S32 LLViewerObject::setTEGLTFMaterialOverride(U8 te, LLGLTFMaterial* override_ma
if (retval)
{
+ retval = initRenderMaterial(te) | retval;
if (override_mat)
{
- LLFetchedGLTFMaterial* render_mat = new LLFetchedGLTFMaterial(*src_mat);
- render_mat->applyOverride(*override_mat);
- tep->setGLTFRenderMaterial(render_mat);
- retval = TEM_CHANGE_TEXTURE;
-
for (LLGLTFMaterial::local_tex_map_t::value_type &val : override_mat->mTrackingIdToLocalTexture)
{
LLLocalBitmapMgr::getInstance()->associateGLTFMaterial(val.first, override_mat);
}
-
- }
- else if (tep->setGLTFRenderMaterial(nullptr))
- {
- retval = TEM_CHANGE_TEXTURE;
}
}
@@ -7577,25 +7606,15 @@ void LLViewerObject::setRenderMaterialID(S32 te_in, const LLUUID& id, bool updat
// the overrides have not changed due to being only texture
// transforms. Re-apply the overrides to the render material here,
// if present.
- const LLGLTFMaterial* override_material = tep->getGLTFMaterialOverride();
- if (override_material)
+ // Also, sometimes, the material has baked textures, which requires
+ // a copy unique to this object.
+ // Currently, we do not deduplicate render materials.
+ new_material->onMaterialComplete([obj_id = getID(), te]()
{
- new_material->onMaterialComplete([obj_id = getID(), te]()
- {
- LLViewerObject* obj = gObjectList.findObject(obj_id);
- if (!obj) { return; }
- LLTextureEntry* tep = obj->getTE(te);
- if (!tep) { return; }
- const LLGLTFMaterial* new_material = tep->getGLTFMaterial();
- if (!new_material) { return; }
- const LLGLTFMaterial* override_material = tep->getGLTFMaterialOverride();
- if (!override_material) { return; }
- LLGLTFMaterial* render_material = new LLFetchedGLTFMaterial();
- *render_material = *new_material;
- render_material->applyOverride(*override_material);
- tep->setGLTFRenderMaterial(render_material);
- });
- }
+ LLViewerObject* obj = gObjectList.findObject(obj_id);
+ if (!obj) { return; }
+ obj->initRenderMaterial(te);
+ });
}
}
@@ -7717,6 +7736,51 @@ void LLViewerObject::clearTEWaterExclusion(const U8 te)
}
}
+bool LLViewerObject::isReachable()
+{
+ LLViewerRegion* agent_region = gAgent.getRegion();
+ LLViewerRegion* object_region = getRegion();
+
+ if (!agent_region || !object_region)
+ {
+ return false;
+ }
+ if (agent_region == object_region)
+ {
+ return true;
+ }
+
+ std::unordered_set<LLViewerRegion*> visited;
+ std::queue<LLViewerRegion*> pending;
+ visited.insert(agent_region);
+ pending.push(agent_region);
+
+ while (!pending.empty())
+ {
+ LLViewerRegion* current = pending.front();
+ pending.pop();
+
+ std::vector<LLViewerRegion*> neighbors;
+ current->getNeighboringRegions(neighbors);
+
+ for (LLViewerRegion* neighbor : neighbors)
+ {
+ if (!neighbor) continue;
+
+ if (neighbor == object_region)
+ {
+ return true;
+ }
+ // region's neighbors were not checked
+ if (visited.insert(neighbor).second)
+ {
+ pending.push(neighbor);
+ }
+ }
+ }
+ return false;
+}
+
class ObjectPhysicsProperties : public LLHTTPNode
{
public:
diff --git a/indra/newview/llviewerobject.h b/indra/newview/llviewerobject.h
index 2b52ea2076..a9c2db60e4 100644
--- a/indra/newview/llviewerobject.h
+++ b/indra/newview/llviewerobject.h
@@ -249,6 +249,9 @@ public:
// Accessor functions
LLViewerRegion* getRegion() const { return mRegionp; }
+ // Check if object is reachable from agent region by traversing loaded neighboring regions
+ bool isReachable();
+
bool isSelected() const { return mUserSelected; }
// Check whole linkset
bool isAnySelected() const;
@@ -389,6 +392,7 @@ public:
/*virtual*/ S32 setTEGlow(const U8 te, const F32 glow);
/*virtual*/ S32 setTEMaterialID(const U8 te, const LLMaterialID& pMaterialID);
/*virtual*/ S32 setTEMaterialParams(const U8 te, const LLMaterialPtr pMaterialParams);
+ S32 initRenderMaterial(const U8 te);
virtual S32 setTEGLTFMaterialOverride(U8 te, LLGLTFMaterial* mat);
// Used by Materials update functions to properly kick off rebuilds
diff --git a/indra/newview/llviewertexture.cpp b/indra/newview/llviewertexture.cpp
index 3142c9950b..6135e18840 100644
--- a/indra/newview/llviewertexture.cpp
+++ b/indra/newview/llviewertexture.cpp
@@ -2153,7 +2153,7 @@ bool LLViewerFetchedTexture::updateFetch()
}
const U32 override_tex_discard_level = gSavedSettings.getU32("TextureDiscardLevel");
- if (override_tex_discard_level != 0)
+ if (override_tex_discard_level != 0 && override_tex_discard_level <= MAX_DISCARD_LEVEL)
{
desired_discard = override_tex_discard_level;
}
diff --git a/indra/newview/llviewerwindow.cpp b/indra/newview/llviewerwindow.cpp
index 8abced7e8f..3849c286c5 100644
--- a/indra/newview/llviewerwindow.cpp
+++ b/indra/newview/llviewerwindow.cpp
@@ -1466,18 +1466,43 @@ void LLViewerWindow::handleMouseLeave(LLWindow *window)
LLToolTipMgr::instance().blockToolTips();
}
-bool LLViewerWindow::handleCloseRequest(LLWindow *window)
+bool LLViewerWindow::handleCloseRequest(LLWindow *window, bool from_user)
{
if (!LLApp::isExiting() && !LLApp::isStopped())
{
- // User has indicated they want to close, but we may need to ask
- // about modified documents.
- LLAppViewer::instance()->userQuit();
- // Don't quit immediately
+ if (from_user)
+ {
+ // User has indicated they want to close, but we may need to ask
+ // about modified documents.
+ LLAppViewer::instance()->userQuit();
+ // Don't quit immediately
+ }
+ else
+ {
+ // OS is asking us to quit, assume we have time and start cleanup
+ LLAppViewer::instance()->requestQuit();
+ }
}
return false;
}
+bool LLViewerWindow::handleSessionExit(LLWindow* window)
+{
+ if (!LLApp::isExiting() && !LLApp::isStopped())
+ {
+ // Viewer received WM_ENDSESSION and app will be killed soon if it doesn't respond
+ LLAppViewer* app = LLAppViewer::instance();
+ app->sendSimpleLogoutRequest();
+ app->earlyExitNoNotify();
+
+ // Not viewer's fault, remove marker files so
+ // that statistics won't consider this to be a crash
+ app->removeMarkerFiles();
+ return false;
+ }
+ return true;
+}
+
void LLViewerWindow::handleQuit(LLWindow *window)
{
if (gNonInteractive)
@@ -1897,7 +1922,7 @@ LLViewerWindow::LLViewerWindow(const Params& p)
p.ignore_pixel_depth,
0,
max_core_count,
- max_gl_version); //don't use window level anti-aliasing
+ max_gl_version); //don't use window level anti-aliasing, windows only
if (NULL == mWindow)
{
diff --git a/indra/newview/llviewerwindow.h b/indra/newview/llviewerwindow.h
index d55c2d3817..61aa84394c 100644
--- a/indra/newview/llviewerwindow.h
+++ b/indra/newview/llviewerwindow.h
@@ -197,7 +197,8 @@ public:
/*virtual*/ bool handleUnicodeChar(llwchar uni_char, MASK mask); // NOT going to handle extended
/*virtual*/ bool handleMouseDown(LLWindow *window, LLCoordGL pos, MASK mask);
/*virtual*/ bool handleMouseUp(LLWindow *window, LLCoordGL pos, MASK mask);
- /*virtual*/ bool handleCloseRequest(LLWindow *window);
+ /*virtual*/ bool handleCloseRequest(LLWindow *window, bool from_user);
+ /*virtual*/ bool handleSessionExit(LLWindow* window);
/*virtual*/ void handleQuit(LLWindow *window);
/*virtual*/ bool handleRightMouseDown(LLWindow *window, LLCoordGL pos, MASK mask);
/*virtual*/ bool handleRightMouseUp(LLWindow *window, LLCoordGL pos, MASK mask);
diff --git a/indra/newview/llvocache.cpp b/indra/newview/llvocache.cpp
index 52a6afc2d0..5d456b1a19 100644
--- a/indra/newview/llvocache.cpp
+++ b/indra/newview/llvocache.cpp
@@ -498,7 +498,7 @@ void LLVOCacheEntry::updateDebugSettings()
sNearRadius = MIN_RADIUS + ((clamped_min_radius - MIN_RADIUS) * adjust_factor);
// a percentage of draw distance beyond which all objects outside of view frustum will be unloaded, regardless of pixel threshold
- static LLCachedControl<F32> rear_max_radius_frac(gSavedSettings,"SceneLoadRearMaxRadiusFraction");
+ static LLCachedControl<F32> rear_max_radius_frac(gSavedSettings,"SceneLoadRearMaxRadiusFraction", .75f);
const F32 min_radius_plus_one = sNearRadius + 1.f;
const F32 max_radius = rear_max_radius_frac * draw_radius;
const F32 clamped_max_radius = llclamp(max_radius, min_radius_plus_one, draw_radius); // [sNearRadius, mDrawDistance]
diff --git a/indra/newview/llvoicewebrtc.cpp b/indra/newview/llvoicewebrtc.cpp
index 62bab7d24a..83f104ef01 100644
--- a/indra/newview/llvoicewebrtc.cpp
+++ b/indra/newview/llvoicewebrtc.cpp
@@ -291,9 +291,8 @@ void LLWebRTCVoiceClient::terminate()
LL_INFOS("Voice") << "Terminating WebRTC" << LL_ENDL;
mVoiceEnabled = false;
+ sShuttingDown = true; // so that coroutines won't post more work.
llwebrtc::terminate();
-
- sShuttingDown = true;
}
//---------------------------------------------------
@@ -2659,6 +2658,11 @@ void LLVoiceWebRTCConnection::breakVoiceConnectionCoro(connectionPtr_t connectio
void LLVoiceWebRTCSpatialConnection::requestVoiceConnection()
{
LL_PROFILE_ZONE_SCOPED_CATEGORY_VOICE;
+ if (LLWebRTCVoiceClient::isShuttingDown())
+ {
+ mOutstandingRequests--;
+ return;
+ }
LLViewerRegion *regionp = LLWorld::instance().getRegionFromID(mRegionID);
@@ -3266,6 +3270,12 @@ void LLVoiceWebRTCAdHocConnection::requestVoiceConnection()
{
LL_PROFILE_ZONE_SCOPED_CATEGORY_VOICE;
+ if (LLWebRTCVoiceClient::isShuttingDown())
+ {
+ mOutstandingRequests--;
+ return;
+ }
+
LLViewerRegion *regionp = LLWorld::instance().getRegionFromID(mRegionID);
LL_DEBUGS("Voice") << "Requesting voice connection." << LL_ENDL;
diff --git a/indra/newview/llvovolume.cpp b/indra/newview/llvovolume.cpp
index 80fc486a10..2370b948ef 100644
--- a/indra/newview/llvovolume.cpp
+++ b/indra/newview/llvovolume.cpp
@@ -6765,7 +6765,7 @@ U32 LLVolumeGeometryManager::genDrawInfo(LLSpatialGroup* group, U32 mask, LLFace
&& te->getShiny()
&& can_be_shiny)
{ //shiny
- if (tex->getPrimaryFormat() == GL_ALPHA)
+ if (tex && tex->getPrimaryFormat() == GL_ALPHA)
{ //invisiprim+shiny
if (!facep->getViewerObject()->isAttachment() && !facep->getViewerObject()->isRiggedMesh())
{
@@ -6805,7 +6805,7 @@ U32 LLVolumeGeometryManager::genDrawInfo(LLSpatialGroup* group, U32 mask, LLFace
}
else
{ //not alpha and not shiny
- if (!is_alpha && tex->getPrimaryFormat() == GL_ALPHA)
+ if (!is_alpha && tex && tex->getPrimaryFormat() == GL_ALPHA)
{ //invisiprim
if (!facep->getViewerObject()->isAttachment() && !facep->getViewerObject()->isRiggedMesh())
{
diff --git a/indra/newview/llworldmap.cpp b/indra/newview/llworldmap.cpp
index 7962c28e6d..153bee3aef 100644
--- a/indra/newview/llworldmap.cpp
+++ b/indra/newview/llworldmap.cpp
@@ -32,6 +32,7 @@
#include "message.h"
#include "lltracker.h"
#include "lluistring.h"
+#include "llviewercontrol.h"
#include "llviewertexturelist.h"
#include "lltrans.h"
#include "llgltexture.h"
@@ -492,9 +493,20 @@ bool LLWorldMap::insertItem(U32 x_world, U32 y_world, std::string& name, LLUUID&
case MAP_ITEM_MATURE_EVENT:
case MAP_ITEM_ADULT_EVENT:
{
- std::string timeStr = "["+ LLTrans::getString ("TimeHour")+"]:["
- +LLTrans::getString ("TimeMin")+"] ["
- +LLTrans::getString ("TimeAMPM")+"]";
+ std::string timeStr;
+
+ static bool use_24h = gSavedSettings.getBOOL("Use24HourClock");
+ if (use_24h)
+ {
+ timeStr = "[" + LLTrans::getString("TimeHour") + "]:["
+ + LLTrans::getString("TimeMin") + "]";
+ }
+ else
+ {
+ timeStr = "[" + LLTrans::getString("TimeHour12") + "]:["
+ + LLTrans::getString("TimeMin") + "] ["
+ + LLTrans::getString("TimeAMPM") + "]";
+ }
LLSD substitution;
substitution["datetime"] = (S32) extra;
LLStringUtil::format (timeStr, substitution);
diff --git a/indra/newview/pipeline.cpp b/indra/newview/pipeline.cpp
index 856dee3dd4..8b6680ee10 100644
--- a/indra/newview/pipeline.cpp
+++ b/indra/newview/pipeline.cpp
@@ -11524,21 +11524,24 @@ public:
}
};
-
+// Called from LLViewHighlightTransparent when "Highlight Transparent" is toggled
void LLPipeline::rebuildDrawInfo()
{
- for (LLWorld::region_list_t::const_iterator iter = LLWorld::getInstance()->getRegionList().begin();
- iter != LLWorld::getInstance()->getRegionList().end(); ++iter)
+ const U32 types_to_traverse[] =
{
- LLViewerRegion* region = *iter;
-
- LLOctreeDirty dirty;
-
- LLSpatialPartition* part = region->getSpatialPartition(LLViewerRegion::PARTITION_VOLUME);
- dirty.traverse(part->mOctree);
+ LLViewerRegion::PARTITION_VOLUME,
+ LLViewerRegion::PARTITION_BRIDGE,
+ LLViewerRegion::PARTITION_AVATAR
+ };
- part = region->getSpatialPartition(LLViewerRegion::PARTITION_BRIDGE);
- dirty.traverse(part->mOctree);
+ LLOctreeDirty dirty;
+ for (LLViewerRegion* region : LLWorld::getInstance()->getRegionList())
+ {
+ for (U32 type : types_to_traverse)
+ {
+ LLSpatialPartition* part = region->getSpatialPartition(type);
+ dirty.traverse(part->mOctree);
+ }
}
}
diff --git a/indra/newview/skins/default/xui/da/sidepanel_item_info.xml b/indra/newview/skins/default/xui/da/sidepanel_item_info.xml
index d52845160b..e5ad86b315 100644
--- a/indra/newview/skins/default/xui/da/sidepanel_item_info.xml
+++ b/indra/newview/skins/default/xui/da/sidepanel_item_info.xml
@@ -15,6 +15,9 @@
<panel.string name="acquiredDate">
[wkday,datetime,local] [mth,datetime,local] [day,datetime,local] [hour,datetime,local]:[min,datetime,local]:[second,datetime,local] [year,datetime,local]
</panel.string>
+ <panel.string name="acquiredDateAMPM">
+ [wkday,datetime,local] [mth,datetime,local] [day,datetime,local] [hour12,datetime,local]:[min,datetime,local]:[second,datetime,local] [ampm,datetime,local] [year,datetime,local]
+ </panel.string>
<panel.string name="origin_inventory">
(Beholdning)
</panel.string>
diff --git a/indra/newview/skins/default/xui/de/sidepanel_item_info.xml b/indra/newview/skins/default/xui/de/sidepanel_item_info.xml
index 168bb14248..3570ccacd2 100644
--- a/indra/newview/skins/default/xui/de/sidepanel_item_info.xml
+++ b/indra/newview/skins/default/xui/de/sidepanel_item_info.xml
@@ -21,6 +21,9 @@
<panel.string name="acquiredDate">
[wkday,datetime,local] [mth,datetime,local] [day,datetime,local] [hour,datetime,local]:[min,datetime,local]:[second,datetime,local] [year,datetime,local]
</panel.string>
+ <panel.string name="acquiredDateAMPM">
+ [wkday,datetime,local] [mth,datetime,local] [day,datetime,local] [hour12,datetime,local]:[min,datetime,local]:[second,datetime,local] [ampm,datetime,local] [year,datetime,local]
+ </panel.string>
<panel.string name="origin_inventory">
(Inventar)
</panel.string>
diff --git a/indra/newview/skins/default/xui/de/strings.xml b/indra/newview/skins/default/xui/de/strings.xml
index f0945234a6..d7b2ee57a8 100644
--- a/indra/newview/skins/default/xui/de/strings.xml
+++ b/indra/newview/skins/default/xui/de/strings.xml
@@ -5074,10 +5074,10 @@ Bitte überprüfen Sie http://status.secondlifegrid.net, um herauszufinden, ob e
[MDAY]
</string>
<string name="dateTimeAM">
- Uhr
+ AM
</string>
<string name="dateTimePM">
- Uhr
+ PM
</string>
<string name="LocalEstimateUSD">
[AMOUNT] US$
diff --git a/indra/newview/skins/default/xui/en/floater_about_land.xml b/indra/newview/skins/default/xui/en/floater_about_land.xml
index 508aba6ae1..c5b42b6dae 100644
--- a/indra/newview/skins/default/xui/en/floater_about_land.xml
+++ b/indra/newview/skins/default/xui/en/floater_about_land.xml
@@ -125,6 +125,9 @@
name="no_selection_text">
No parcel selected.
</panel.string>
+ <panel.string name="time_stamp_template_ampm">
+ [wkday,datetime,slt] [mth,datetime,slt] [day,datetime,slt] [hour12,datetime,slt]:[min,datetime,slt]:[second,datetime,slt] [ampm,datetime,slt] [year,datetime,slt]
+ </panel.string>
<panel.string name="time_stamp_template">
[wkday,datetime,slt] [mth,datetime,slt] [day,datetime,slt] [hour,datetime,slt]:[min,datetime,slt]:[second,datetime,slt] [year,datetime,slt]
</panel.string>
diff --git a/indra/newview/skins/default/xui/en/floater_inspect.xml b/indra/newview/skins/default/xui/en/floater_inspect.xml
index 9403d58441..a083683c23 100644
--- a/indra/newview/skins/default/xui/en/floater_inspect.xml
+++ b/indra/newview/skins/default/xui/en/floater_inspect.xml
@@ -12,6 +12,10 @@
title="INSPECT OBJECTS"
width="400">
<floater.string
+ name="timeStampAMPM">
+ [wkday,datetime,slt] [mth,datetime,slt] [day,datetime,slt] [hour12,datetime,slt]:[min,datetime,slt]:[second,datetime,slt] [ampm,datetime,slt] [year,datetime,slt]
+ </floater.string>
+ <floater.string
name="timeStamp">
[wkday,datetime,slt] [mth,datetime,slt] [day,datetime,slt] [hour,datetime,slt]:[min,datetime,slt]:[second,datetime,slt] [year,datetime,slt]
</floater.string>
diff --git a/indra/newview/skins/default/xui/en/notifications.xml b/indra/newview/skins/default/xui/en/notifications.xml
index 536008c85c..69df05f6c2 100644
--- a/indra/newview/skins/default/xui/en/notifications.xml
+++ b/indra/newview/skins/default/xui/en/notifications.xml
@@ -1670,7 +1670,7 @@ The new skin will appear after you restart [APP_NAME].
icon="alertmodal.tga"
name="ChangeLanguage"
type="alertmodal">
-Changing language will take effect after you restart [APP_NAME].
+Changing language or time format will take effect after you restart [APP_NAME].
</notification>
<notification
diff --git a/indra/newview/skins/default/xui/en/panel_group_roles.xml b/indra/newview/skins/default/xui/en/panel_group_roles.xml
index 02e135a2c7..868d54401e 100644
--- a/indra/newview/skins/default/xui/en/panel_group_roles.xml
+++ b/indra/newview/skins/default/xui/en/panel_group_roles.xml
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<panel
- height="750"
+ height="770"
label="Members &amp; Roles"
layout="topleft"
left="0"
@@ -18,9 +18,9 @@
<panel.string
name="help_text" />
<tab_container
- border="false"
+ border="false"
follows="left|top|right"
- height="552"
+ height="770"
halign="center"
layout="topleft"
left="0"
@@ -29,16 +29,10 @@
tab_position="top"
tab_height="22"
tab_min_width="90"
- top="0"
- width="304">
+ top="0">
<panel
border="false"
- follows="all"
- height="303"
label="MEMBERS"
- layout="topleft"
- left="0"
- right="-1"
help_topic="roles_members_tab"
name="members_sub_tab"
tool_tip="Members"
@@ -65,16 +59,16 @@ clicking on their names.
name="power_partial_icon" translate="false">
Checkbox_Off
</panel.string>
- <filter_editor
- layout="topleft"
- top="5"
- left="5"
- right="-5"
- height="22"
- search_button_visible="false"
- follows="left|top|right"
- label="Filter Members"
- name="filter_input" />
+ <filter_editor
+ layout="topleft"
+ top="5"
+ left="5"
+ right="-5"
+ height="22"
+ search_button_visible="false"
+ follows="left|top|right"
+ label="Filter Members"
+ name="filter_input" />
<name_list
column_padding="2"
draw_heading="true"
@@ -90,11 +84,11 @@ clicking on their names.
<name_list.columns
label="Member"
name="name"
- relative_width="0.44" />
+ relative_width="0.44" />
<name_list.columns
label="Donation"
name="donated"
- relative_width="0.2" />
+ relative_width="0.2" />
<name_list.columns
label="Status"
name="online"
@@ -118,25 +112,21 @@ clicking on their names.
left_pad="10"
name="member_eject"
width="100" />
- <button
- height="23"
- label="Ban Member(s)"
- follows="top|left"
- left_pad="10"
- name="member_ban"
- width="100" />
+ <button
+ height="23"
+ label="Ban Member(s)"
+ follows="top|left"
+ left_pad="10"
+ name="member_ban"
+ width="100" />
</panel>
<panel
border="false"
- height="303"
label="ROLES"
- layout="topleft"
- left="0"
- right="-1"
help_topic="roles_roles_tab"
name="roles_sub_tab"
class="panel_group_roles_subtab">
- <!-- <button
+ <!--<button
enabled="false"
height="20"
label="Show All"
@@ -145,411 +135,393 @@ clicking on their names.
right="-5"
name="show_all_button"
width="100" />-->
- <panel.string
- name="help_text">
- Roles have a title and an allowed list of Abilities
- that Members can perform. Members can belong to
- one or more Roles. A group can have up to 10 Roles,
- including the Everyone and Owner Roles.
- </panel.string>
- <panel.string
- name="cant_delete_role">
- The &apos;Everyone&apos; and &apos;Owners&apos; Roles are special and can't be deleted.
- </panel.string>
- <panel.string
- name="power_folder_icon" translate="false">
- Inv_FolderClosed
- </panel.string>
- <panel.string
- name="power_all_have_icon" translate="false">
- Checkbox_On
- </panel.string>
- <panel.string
- name="power_partial_icon" translate="false">
- Checkbox_Off
- </panel.string>
- <filter_editor
- layout="topleft"
- top="5"
- left="5"
- right="-5"
- height="22"
- search_button_visible="false"
- follows="left|top|right"
- label="Filter Roles"
- name="filter_input" />
- <scroll_list
- column_padding="0"
- draw_heading="true"
- draw_stripes="false"
- heading_height="23"
- height="132"
- layout="topleft"
- search_column="1"
- left="0"
- follows="left|top|right"
- right="-1"
- name="role_list"
- top_pad="2"
- width="310">
- <scroll_list.columns
- label="Role"
- name="name"
- relative_width="0.45" />
- <scroll_list.columns
- label="Title"
- name="title"
- relative_width="0.45" />
- <scroll_list.columns
- label="#"
- name="members"
- relative_width="0.15" />
- </scroll_list>
- <button
- follows="top|left"
- height="23"
- label="New Role"
- layout="topleft"
- left="0"
- name="role_create"
- width="100" />
- <button
- follows="top|left"
- height="23"
- label="Copy Role"
- layout="topleft"
- left_pad="10"
- name="role_copy"
- width="100" />
- <button
- height="23"
- follows="top|left"
- label="Delete Role"
- layout="topleft"
- left_pad="10"
- name="role_delete"
- width="100" />
- </panel>
+ <panel.string
+ name="help_text">
+ Roles have a title and an allowed list of Abilities
+that Members can perform. Members can belong to
+one or more Roles. A group can have up to 10 Roles,
+including the Everyone and Owner Roles.
+ </panel.string>
+ <panel.string
+ name="cant_delete_role">
+ The &apos;Everyone&apos; and &apos;Owners&apos; Roles are special and can't be deleted.
+ </panel.string>
+ <panel.string
+ name="power_folder_icon" translate="false">
+ Inv_FolderClosed
+ </panel.string>
+ <panel.string
+ name="power_all_have_icon" translate="false">
+ Checkbox_On
+ </panel.string>
+ <panel.string
+ name="power_partial_icon" translate="false">
+ Checkbox_Off
+ </panel.string>
+ <filter_editor
+ layout="topleft"
+ top="5"
+ left="5"
+ right="-5"
+ height="22"
+ search_button_visible="false"
+ follows="left|top|right"
+ label="Filter Roles"
+ name="filter_input" />
+ <scroll_list
+ column_padding="0"
+ draw_heading="true"
+ draw_stripes="false"
+ heading_height="23"
+ height="138"
+ layout="topleft"
+ search_column="1"
+ left="0"
+ follows="left|top|right"
+ right="-1"
+ name="role_list"
+ top_pad="2">
+ <scroll_list.columns
+ label="Role"
+ name="name"
+ relative_width="0.45" />
+ <scroll_list.columns
+ label="Title"
+ name="title"
+ relative_width="0.45" />
+ <scroll_list.columns
+ label="#"
+ name="members"
+ relative_width="0.15" />
+ </scroll_list>
+ <button
+ follows="top|left"
+ height="23"
+ label="New Role"
+ layout="topleft"
+ left="0"
+ name="role_create"
+ width="100" />
+ <button
+ follows="top|left"
+ height="23"
+ label="Copy Role"
+ layout="topleft"
+ left_pad="10"
+ name="role_copy"
+ width="100" />
+ <button
+ height="23"
+ follows="top|left"
+ label="Delete Role"
+ layout="topleft"
+ left_pad="10"
+ name="role_delete"
+ width="100" />
+ </panel>
+ <panel
+ border="false"
+ label="ABILITIES"
+ help_topic="roles_actions_tab"
+ name="actions_sub_tab"
+ class="panel_group_actions_subtab"
+ tool_tip="You can view an Ability&apos;s Description and which Roles and Members can execute the Ability.">
+ <panel.string
+ name="help_text">
+ Abilities allow Members in Roles to do specific
+things in this group. There&apos;s a broad variety of Abilities.
+ </panel.string>
+ <panel.string
+ name="power_folder_icon" translate="false">
+ Inv_FolderClosed
+ </panel.string>
+ <panel.string
+ name="power_all_have_icon" translate="false">
+ Checkbox_On
+ </panel.string>
+ <panel.string
+ name="power_partial_icon" translate="false">
+ Checkbox_Off
+ </panel.string>
+ <filter_editor
+ layout="topleft"
+ top="5"
+ left="5"
+ right="-5"
+ height="22"
+ search_button_visible="false"
+ follows="left|top|right"
+ label="Filter Abilities"
+ name="filter_input" />
+ <scroll_list
+ column_padding="0"
+ draw_stripes="true"
+ height="200"
+ follows="left|top|right"
+ layout="topleft"
+ left="0"
+ right="-1"
+ name="action_list"
+ search_column="2"
+ tool_tip="Select an Ability to view more details"
+ top_pad="5">
+ <scroll_list.columns
+ label=""
+ name="icon"
+ width="2" />
+ <scroll_list.columns
+ label=""
+ name="checkbox"
+ width="20" />
+ <scroll_list.columns
+ label=""
+ name="action" />
+ </scroll_list>
+ </panel>
+ <panel
+ border="false"
+ label="BANNED RESIDENTS"
+ help_topic="roles_banlist_tab"
+ name="banlist_sub_tab"
+ class="panel_group_banlist_subtab"
+ tool_tip="View the banned residents from this group.">
+ <panel.string
+ name="help_text">
+ Any resident on the ban list will be unable to join the group.
+ </panel.string>
+ <panel.string
+ name="ban_count_template">
+ Ban count: [COUNT]/[LIMIT]
+ </panel.string>
+ <name_list
+ column_padding="0"
+ draw_heading="true"
+ height="714"
+ follows="left|top|right"
+ layout="topleft"
+ left="0"
+ right="-1"
+ multi_select="true"
+ name="ban_list"
+ short_names="false"
+ top_pad="5">
+ <name_list.columns
+ label="Resident"
+ name="name"
+ font.name="SANSSERIF_SMALL"
+ font.style="NORMAL"
+ relative_width="0.7" />
+ <name_list.columns
+ label="Date Banned"
+ name="ban_date"
+ relative_width="0.3" />
+ </name_list>
+ <button
+ follows="top|left"
+ height="23"
+ label="Ban Resident(s)"
+ layout="topleft"
+ left="3"
+ name="ban_create"
+ tool_tip="Ban residents from your group"
+ width="120" />
+ <button
+ follows="top|left"
+ height="23"
+ label="Remove Ban(s)"
+ layout="topleft"
+ left_pad="5"
+ name="ban_delete"
+ tool_tip="Unban selected residents from your group"
+ width="120" />
+ <button
+ follows="top|left"
+ height="23"
+ width="23"
+ image_overlay="Refresh_Off"
+ layout="topleft"
+ left_pad="5"
+ name="ban_refresh"
+ tool_tip="Refresh the ban list" />
+ <text
+ type="string"
+ height="18"
+ left_pad="5"
+ follows="top|left"
+ layout="topleft"
+ name="ban_count"
+ width="100">
+ </text>
+ </panel>
+ </tab_container>
<panel
- border="false"
- height="303"
- label="ABILITIES"
+ height="350"
+ background_visible="false"
+ bg_alpha_color="FloaterUnfocusBorderColor"
layout="topleft"
+ follows="top|left|right"
left="0"
right="-1"
- help_topic="roles_actions_tab"
- name="actions_sub_tab"
- class="panel_group_actions_subtab"
- tool_tip="You can view an Ability&apos;s Description and which Roles and Members can execute the Ability."
- width="310">
- <panel.string
- name="help_text">
- Abilities allow Members in Roles to do specific
- things in this group. There&apos;s a broad variety of Abilities.
- </panel.string>
- <panel.string
- name="power_folder_icon" translate="false">
- Inv_FolderClosed
- </panel.string>
- <panel.string
- name="power_all_have_icon" translate="false">
- Checkbox_On
- </panel.string>
- <panel.string
- name="power_partial_icon" translate="false">
- Checkbox_Off
- </panel.string>
- <filter_editor
- layout="topleft"
- top="5"
- left="5"
- right="-5"
- height="22"
- search_button_visible="false"
- follows="left|top|right"
- label="Filter Abilities"
- name="filter_input" />
- <scroll_list
- column_padding="0"
- draw_stripes="true"
- height="200"
- follows="left|top|right"
- layout="topleft"
- left="0"
- right="-1"
- name="action_list"
- search_column="2"
- tool_tip="Select an Ability to view more details"
- top_pad="5"
- width="300">
- <scroll_list.columns
- label=""
- name="icon"
- width="2" />
- <scroll_list.columns
- label=""
- name="checkbox"
- width="20" />
- <scroll_list.columns
- label=""
- name="action" />
- </scroll_list>
+ mouse_opaque="false"
+ name="members_footer"
+ top="325"
+ visible="false">
+ <text
+ type="string"
+ height="16"
+ layout="topleft"
+ follows="left|top"
+ left="5"
+ top="8"
+ text_color="EmphasisColor"
+ name="static"
+ width="300">
+ Assigned Roles
+ </text>
+ <scroll_list
+ draw_stripes="true"
+ follows="left|top|right"
+ height="150"
+ layout="topleft"
+ left="0"
+ right="-1"
+ name="member_assigned_roles"
+ top_pad="0">
+ <scroll_list.columns
+ label=""
+ name="checkbox"
+ width="20" />
+ <scroll_list.columns
+ label=""
+ name="role"
+ width="270" />
+ </scroll_list>
+ <text
+ type="string"
+ height="16"
+ layout="topleft"
+ follows="left|top"
+ left="5"
+ top_pad="5"
+ text_color="EmphasisColor"
+ name="static2"
+ width="285">
+ Allowed Abilities
+ </text>
+ <scroll_list
+ draw_stripes="true"
+ follows="left|top|right"
+ height="150"
+ layout="topleft"
+ left="0"
+ right="-1"
+ name="member_allowed_actions"
+ search_column="2"
+ tool_tip="For details of each allowed ability see the abilities tab"
+ top_pad="0">
+ <scroll_list.columns
+ label=""
+ name="icon"
+ width="2" />
+ <scroll_list.columns
+ label=""
+ name="checkbox"
+ width="20" />
+ <scroll_list.columns
+ label=""
+ name="action"
+ width="270" />
+ </scroll_list>
</panel>
<panel
- border="false"
- height="303"
- label="BANNED RESIDENTS"
+ height="90"
+ background_visible="false"
+ bg_alpha_color="FloaterUnfocusBorderColor"
layout="topleft"
+ follows="top|left|right"
left="0"
right="-1"
- help_topic="roles_banlist_tab"
- name="banlist_sub_tab"
- class="panel_group_banlist_subtab"
- tool_tip="View the banned residents from this group."
- width="310">
- <panel.string
- name="help_text">
- Any resident on the ban list will be unable to join the group.
- </panel.string>
- <panel.string
- name="ban_count_template">
- Ban count: [COUNT]/[LIMIT]
- </panel.string>
- <name_list
- column_padding="0"
- draw_heading="true"
- height="400"
- follows="left|top|right"
- layout="topleft"
- left="0"
- right="-1"
- multi_select="true"
- name="ban_list"
- short_names="false"
- top_pad="5">
- <name_list.columns
- label="Resident"
- name="name"
- font.name="SANSSERIF_SMALL"
- font.style="NORMAL"
- relative_width="0.7" />
- <name_list.columns
- label="Date Banned"
- name="ban_date"
- relative_width="0.3" />
- </name_list>
- <button
- follows="top|left"
- height="23"
- label="Ban Resident(s)"
- layout="topleft"
- left="3"
- name="ban_create"
- tool_tip="Ban residents from your group"
- width="120" />
- <button
- follows="top|left"
- height="23"
- label="Remove Ban(s)"
- layout="topleft"
- left_pad="5"
- name="ban_delete"
- tool_tip="Unban selected residents from your group"
- width="120" />
- <button
- follows="top|left"
- height="23"
- width="23"
- image_overlay="Refresh_Off"
- layout="topleft"
- left_pad="5"
- name="ban_refresh"
- tool_tip="Refresh the ban list"
- />
- <text
- type="string"
- height="18"
- left_pad="5"
- follows="top|left"
- layout="topleft"
- name="ban_count"
- width="100">
- </text>
+ mouse_opaque="false"
+ name="members_header"
+ top_pad="3"
+ visible="false">
+ <text_editor
+ bg_readonly_color="Transparent"
+ text_readonly_color="EmphasisColor"
+ font="SansSerifSmall"
+ type="string"
+ enabled="false"
+ halign="left"
+ layout="topleft"
+ top_pad="0"
+ follows="left|top|right"
+ left="0"
+ right="-1"
+ height="90"
+ max_length="512"
+ name="member_action_description"
+ word_wrap="true">
+ This Ability is &apos;Eject Members from this Group&apos;. Only an Owner can eject another Owner.
+ </text_editor>
</panel>
- </tab_container>
- <panel
- height="350"
- background_visible="false"
- bg_alpha_color="FloaterUnfocusBorderColor"
- layout="topleft"
- follows="top|left|right"
- left="0"
- right="-1"
- width="313"
- mouse_opaque="false"
- name="members_footer"
- top="325"
- visible="false">
- <text
- type="string"
- height="16"
- layout="topleft"
- follows="left|top"
- left="5"
- top="8"
- text_color="EmphasisColor"
- name="static"
- width="300">
- Assigned Roles
- </text>
- <scroll_list
- draw_stripes="true"
- follows="left|top|right"
- height="150"
+ <panel
+ height="460"
+ background_visible="false"
+ bg_alpha_color="FloaterUnfocusBorderColor"
layout="topleft"
+ follows="top|left|right"
left="0"
right="-1"
- name="member_assigned_roles"
- top_pad="0">
- <scroll_list.columns
- label=""
- name="checkbox"
- width="20" />
- <scroll_list.columns
- label=""
- name="role"
- width="270" />
- </scroll_list>
- <text
- type="string"
- height="16"
- layout="topleft"
- follows="left|top"
- left="5"
- top_pad="5"
- text_color="EmphasisColor"
- name="static2"
- width="285">
- Allowed Abilities
- </text>
- <scroll_list
- draw_stripes="true"
+ mouse_opaque="false"
+ name="roles_footer"
+ top_delta="0"
+ top="215"
+ visible="false">
+ <text
+ type="string"
+ height="16"
+ layout="topleft"
+ follows="left|top"
+ left="5"
+ top="5"
+ name="static"
+ width="300">
+ Role Name
+ </text>
+ <line_editor
+ type="string"
+ height="20"
+ layout="topleft"
+ left="0"
follows="left|top|right"
- height="150"
- layout="topleft"
- left="0"
- right="-1"
- name="member_allowed_actions"
- search_column="2"
- tool_tip="For details of each allowed ability see the abilities tab"
- top_pad="0">
- <scroll_list.columns
- label=""
- name="icon"
- width="2" />
- <scroll_list.columns
- label=""
- name="checkbox"
- width="20" />
- <scroll_list.columns
- label=""
- name="action"
- width="270" />
- </scroll_list>
- </panel>
- <panel
- height="90"
- background_visible="false"
- bg_alpha_color="FloaterUnfocusBorderColor"
- layout="topleft"
- follows="top|left|right"
- left="0"
- right="-1"
- width="313"
- mouse_opaque="false"
- name="members_header"
- top_pad="3"
- visible="false">
- <text_editor
- bg_readonly_color="Transparent"
- text_readonly_color="EmphasisColor"
- font="SansSerifSmall"
- type="string"
- enabled="false"
- halign="left"
- layout="topleft"
- top_pad="0"
- follows="left|top|right"
- left="0"
- right="-1"
- height="90"
- max_length="512"
- name="member_action_description"
- word_wrap="true">
- This Ability is &apos;Eject Members from this Group&apos;. Only an Owner can eject another Owner.
- </text_editor>
- </panel>
- <panel
- height="460"
- background_visible="false"
- bg_alpha_color="FloaterUnfocusBorderColor"
- layout="topleft"
- follows="top|left|right"
- left="0"
- right="-1"
- width="313"
- mouse_opaque="false"
- name="roles_footer"
- top_delta="0"
- top="209"
- visible="false">
- <text
- type="string"
- height="16"
- layout="topleft"
- follows="left|top"
- left="5"
- top="5"
- name="static"
- width="300">
- Role Name
- </text>
- <line_editor
- type="string"
- height="20"
- layout="topleft"
- left="0"
- follows="left|top|right"
- right="-1"
- max_length_bytes="20"
- name="role_name"
- top_pad="0"
- width="300">
- </line_editor>
- <text
- type="string"
- height="16"
- layout="topleft"
- follows="left|top"
- left="5"
- name="static3"
- top_pad="5"
- width="300">
- Role Title
- </text>
- <line_editor
- type="string"
- height="20"
- layout="topleft"
- left="0"
- follows="left|top|right"
- right="-1"
- max_length_bytes="20"
- name="role_title"
- top_pad="0"
- width="300">
- </line_editor>
- <text
+ right="-1"
+ max_length_bytes="20"
+ name="role_name"
+ top_pad="0">
+ </line_editor>
+ <text
+ type="string"
+ height="16"
+ layout="topleft"
+ follows="left|top"
+ left="5"
+ name="static3"
+ top_pad="5"
+ width="300">
+ Role Title
+ </text>
+ <line_editor
+ type="string"
+ height="20"
+ layout="topleft"
+ left="0"
+ follows="left|top|right"
+ right="-1"
+ max_length_bytes="20"
+ name="role_title"
+ top_pad="0">
+ </line_editor>
+ <text
type="string"
height="16"
layout="topleft"
@@ -558,192 +530,185 @@ clicking on their names.
name="static2"
top_pad="5"
width="200">
- Description
- </text>
- <text_editor
- type="string"
- layout="topleft"
- left="0"
- follows="left|top|right"
- right="-1"
- max_length="295"
- height="35"
- name="role_description"
- top_pad="0"
- width="300"
- word_wrap="true">
- </text_editor>
- <text
- type="string"
- height="16"
- layout="topleft"
- follows="left|top"
- left="5"
- text_color="EmphasisColor"
- name="static4"
- top_pad="5"
- width="300">
- Assigned Members
- </text>
- <name_list
- draw_stripes="true"
- height="128"
- layout="topleft"
- left="0"
- follows="left|top|right"
- right="-1"
- name="role_assigned_members"
- top_pad="0"
- width="300" />
- <check_box
- height="15"
- label="Reveal members"
- left="5"
- layout="topleft"
- name="role_visible_in_list"
- tool_tip="Sets whether members of this role are visible in the General tab to people outside of the group."
- top_pad="4"
- width="300" />
- <text
- type="string"
- height="16"
- layout="topleft"
- follows="left|top"
- left="5"
- text_color="EmphasisColor"
- name="static5"
- top_pad="2"
- width="300">
- Allowed Abilities
- </text>
- <scroll_list
- draw_stripes="true"
- height="140"
- layout="topleft"
- left="0"
- follows="left|top|right"
- right="-1"
- name="role_allowed_actions"
- search_column="2"
- tool_tip="For details of each allowed ability see the abilities tab"
- top_pad="0"
- width="300">
- <scroll_list.columns
- label=""
- name="icon"
- width="2" />
- <scroll_list.columns
- label=""
- name="checkbox"
- width="20" />
- <scroll_list.columns
- label=""
- name="action" />
- </scroll_list>
- </panel>
- <panel
- height="90"
- background_visible="false"
- bg_alpha_color="FloaterUnfocusBorderColor"
- layout="topleft"
- follows="top|left|right"
- left="0"
- right="-1"
- width="313"
- mouse_opaque="false"
- name="roles_header"
- top_pad="3"
- visible="false">
- <text_editor
- bg_readonly_color="Transparent"
- text_readonly_color="EmphasisColor"
- font="SansSerifSmall"
- type="string"
- enabled="false"
- halign="left"
- layout="topleft"
- top_pad="0"
- follows="left|top|right"
- left="0"
- right="-1"
+ Description
+ </text>
+ <text_editor
+ type="string"
+ layout="topleft"
+ left="0"
+ follows="left|top|right"
+ right="-1"
+ max_length="295"
+ height="35"
+ name="role_description"
+ top_pad="0"
+ word_wrap="true">
+ </text_editor>
+ <text
+ type="string"
+ height="16"
+ layout="topleft"
+ follows="left|top"
+ left="5"
+ text_color="EmphasisColor"
+ name="static4"
+ top_pad="5"
+ width="300">
+ Assigned Members
+ </text>
+ <name_list
+ draw_stripes="true"
+ height="128"
+ layout="topleft"
+ left="0"
+ follows="left|top|right"
+ right="-1"
+ name="role_assigned_members"
+ top_pad="0" />
+ <check_box
+ height="15"
+ label="Reveal members"
+ left="5"
+ layout="topleft"
+ name="role_visible_in_list"
+ tool_tip="Sets whether members of this role are visible in the General tab to people outside of the group."
+ top_pad="4"
+ width="300" />
+ <text
+ type="string"
+ height="16"
+ layout="topleft"
+ follows="left|top"
+ left="5"
+ text_color="EmphasisColor"
+ name="static5"
+ top_pad="2"
+ width="300">
+ Allowed Abilities
+ </text>
+ <scroll_list
+ draw_stripes="true"
+ height="140"
+ layout="topleft"
+ left="0"
+ follows="left|top|right"
+ right="-1"
+ name="role_allowed_actions"
+ search_column="2"
+ tool_tip="For details of each allowed ability see the abilities tab"
+ top_pad="0">
+ <scroll_list.columns
+ label=""
+ name="icon"
+ width="2" />
+ <scroll_list.columns
+ label=""
+ name="checkbox"
+ width="20" />
+ <scroll_list.columns
+ label=""
+ name="action" />
+ </scroll_list>
+ </panel>
+ <panel
height="90"
- max_length="512"
- name="role_action_description"
- word_wrap="true">
- This Ability is &apos;Eject Members from this Group&apos;. Only an Owner can eject another Owner.
- </text_editor>
- </panel>
- <panel
- height="424"
- background_visible="false"
- bg_alpha_color="FloaterUnfocusBorderColor"
- layout="topleft"
- follows="top|left|right"
- left="0"
- right="-1"
- width="313"
- mouse_opaque="false"
- name="actions_footer"
- top_delta="0"
- top="255"
- visible="false">
- <text_editor
- bg_readonly_color="Transparent"
- text_readonly_color="EmphasisColor"
- font="SansSerifSmall"
- type="string"
- enabled="false"
- halign="left"
+ background_visible="false"
+ bg_alpha_color="FloaterUnfocusBorderColor"
layout="topleft"
- follows="left|top|right"
+ follows="top|left|right"
left="0"
right="-1"
- height="90"
- max_length="512"
- name="action_description"
- top="0"
- word_wrap="true">
- This Ability is &apos;Eject Members from this Group&apos;. Only an Owner can eject another Owner.
- </text_editor>
- <text
- type="string"
- height="16"
- layout="topleft"
- follows="left|top"
- left="5"
- name="static2"
- top_pad="1"
- width="300">
- Roles with this ability
- </text>
- <scroll_list
- height="172"
- layout="topleft"
- follows="left|top|right"
- left="5"
- right="-1"
- name="action_roles"
- top_pad="0"
- width="300" />
- <text
- type="string"
- height="16"
- layout="topleft"
- follows="left|top"
- left="5"
- name="static3"
- top_pad="5"
- width="300">
- Members with this ability
- </text>
- <name_list
- height="122"
- follows="left|top|right"
+ mouse_opaque="false"
+ name="roles_header"
+ top_pad="3"
+ visible="false">
+ <text_editor
+ bg_readonly_color="Transparent"
+ text_readonly_color="EmphasisColor"
+ font="SansSerifSmall"
+ type="string"
+ enabled="false"
+ halign="left"
+ layout="topleft"
+ top_pad="0"
+ follows="left|top|right"
+ left="0"
+ right="-1"
+ height="90"
+ max_length="512"
+ name="role_action_description"
+ word_wrap="true">
+ This Ability is &apos;Eject Members from this Group&apos;. Only an Owner can eject another Owner.
+ </text_editor>
+ </panel>
+ <panel
+ height="513"
+ background_visible="false"
+ bg_alpha_color="FloaterUnfocusBorderColor"
layout="topleft"
- left="5"
+ follows="top|left|right"
+ left="0"
right="-1"
- name="action_members"
- top_pad="0"
- width="300" />
- </panel>
+ mouse_opaque="false"
+ name="actions_footer"
+ top_delta="0"
+ top="255"
+ visible="false">
+ <text_editor
+ bg_readonly_color="Transparent"
+ text_readonly_color="EmphasisColor"
+ font="SansSerifSmall"
+ type="string"
+ enabled="false"
+ halign="left"
+ layout="topleft"
+ follows="left|top|right"
+ left="0"
+ right="-1"
+ height="90"
+ max_length="512"
+ name="action_description"
+ top="0"
+ word_wrap="true">
+ This Ability is &apos;Eject Members from this Group&apos;. Only an Owner can eject another Owner.
+ </text_editor>
+ <text
+ type="string"
+ height="16"
+ layout="topleft"
+ follows="left|top"
+ left="5"
+ name="static2"
+ top_pad="1"
+ width="300">
+ Roles with this ability
+ </text>
+ <scroll_list
+ height="216"
+ layout="topleft"
+ follows="left|top|right"
+ left="5"
+ right="-1"
+ name="action_roles"
+ top_pad="0" />
+ <text
+ type="string"
+ height="16"
+ layout="topleft"
+ follows="left|top"
+ left="5"
+ name="static3"
+ top_pad="5"
+ width="300">
+ Members with this ability
+ </text>
+ <name_list
+ height="167"
+ follows="left|top|right"
+ layout="topleft"
+ left="5"
+ right="-1"
+ name="action_members"
+ top_pad="0" />
+ </panel>
</panel>
diff --git a/indra/newview/skins/default/xui/en/panel_landmark_info.xml b/indra/newview/skins/default/xui/en/panel_landmark_info.xml
index af68bd7fee..19599efdc2 100644
--- a/indra/newview/skins/default/xui/en/panel_landmark_info.xml
+++ b/indra/newview/skins/default/xui/en/panel_landmark_info.xml
@@ -40,6 +40,10 @@
Information about this location is unavailable due to access restrictions. Please check your permissions with the parcel owner.
</string>
<string
+ name="acquired_date_ampm">
+ [wkday,datetime,slt] [mth,datetime,slt] [day,datetime,slt] [hour12,datetime,slt]:[min,datetime,slt]:[second,datetime,slt] [ampm,datetime,slt] [year,datetime,slt]
+ </string>
+ <string
name="acquired_date">
[wkday,datetime,slt] [mth,datetime,slt] [day,datetime,slt] [hour,datetime,slt]:[min,datetime,slt]:[second,datetime,slt] [year,datetime,slt]
</string>
diff --git a/indra/newview/skins/default/xui/en/panel_place_profile.xml b/indra/newview/skins/default/xui/en/panel_place_profile.xml
index 8f5292c531..bb877080b1 100644
--- a/indra/newview/skins/default/xui/en/panel_place_profile.xml
+++ b/indra/newview/skins/default/xui/en/panel_place_profile.xml
@@ -89,6 +89,10 @@
Information about this location is unavailable due to access restrictions. Please check your permissions with the parcel owner.
</string>
<string
+ name="acquired_date_ampm">
+ [wkday,datetime,local] [mth,datetime,local] [day,datetime,local] [hour12,datetime,local]:[min,datetime,local]:[second,datetime,local] [ampm,datetime,local] [year,datetime,local]
+ </string>
+ <string
name="acquired_date">
[wkday,datetime,local] [mth,datetime,local] [day,datetime,local] [hour,datetime,local]:[min,datetime,local]:[second,datetime,local] [year,datetime,local]
</string>
diff --git a/indra/newview/skins/default/xui/en/panel_preferences_general.xml b/indra/newview/skins/default/xui/en/panel_preferences_general.xml
index 101c506309..ddddb4855f 100644
--- a/indra/newview/skins/default/xui/en/panel_preferences_general.xml
+++ b/indra/newview/skins/default/xui/en/panel_preferences_general.xml
@@ -91,6 +91,37 @@
name="Traditional Chinese"
value="zh" />
</combo_box>
+ <text
+ type="string"
+ length="1"
+ follows="left|top"
+ height="15"
+ layout="topleft"
+ left="255"
+ name="time_format_textbox"
+ top="10"
+ width="200">
+ Time Format:
+ </text>
+ <combo_box
+ follows="left|top"
+ height="23"
+ layout="topleft"
+ left="255"
+ max_chars="135"
+ name="time_format_combobox"
+ width="70">
+ <combo_box.item
+ enabled="true"
+ label="1:00 AM"
+ name="12H"
+ value="0" />
+ <combo_box.item
+ enabled="true"
+ label="13:00"
+ name="24H"
+ value="1" />
+ </combo_box>
<text
font="SansSerifSmall"
type="string"
diff --git a/indra/newview/skins/default/xui/en/panel_profile_firstlife.xml b/indra/newview/skins/default/xui/en/panel_profile_firstlife.xml
index 6554dd0952..831b631505 100644
--- a/indra/newview/skins/default/xui/en/panel_profile_firstlife.xml
+++ b/indra/newview/skins/default/xui/en/panel_profile_firstlife.xml
@@ -22,7 +22,6 @@
<profile_image
name="real_world_pic"
image_name="Generic_Person_Large"
- show_loading="false"
follows="top|left"
layout="topleft"
top="10"
diff --git a/indra/newview/skins/default/xui/en/panel_profile_secondlife.xml b/indra/newview/skins/default/xui/en/panel_profile_secondlife.xml
index e361a0f28c..4d4c7f2252 100644
--- a/indra/newview/skins/default/xui/en/panel_profile_secondlife.xml
+++ b/indra/newview/skins/default/xui/en/panel_profile_secondlife.xml
@@ -74,7 +74,6 @@ Account: [ACCTTYPE]
<profile_image
name="2nd_life_pic"
image_name="Generic_Person_Large"
- show_loading="false"
layout="topleft"
follows="all"
interactable="true"
@@ -191,7 +190,7 @@ Account: [ACCTTYPE]
visible="false">
<icon
name="badge_icon"
- image_name="Beta_Tester"
+ image_name="Profile_Badge_Beta"
layout="topleft"
follows="left|top"
top="10"
diff --git a/indra/newview/skins/default/xui/en/panel_sound_devices.xml b/indra/newview/skins/default/xui/en/panel_sound_devices.xml
index 7598f7d7e5..b858d0ed02 100644
--- a/indra/newview/skins/default/xui/en/panel_sound_devices.xml
+++ b/indra/newview/skins/default/xui/en/panel_sound_devices.xml
@@ -20,6 +20,10 @@
name="name_default_system_device">
Default System Device
</string>
+ <string
+ name="device_not_loaded">
+ Device not loaded
+ </string>
<icon
follows="left|top"
height="18"
diff --git a/indra/newview/skins/default/xui/en/sidepanel_item_info.xml b/indra/newview/skins/default/xui/en/sidepanel_item_info.xml
index 1cb3eca2eb..40a88d4121 100644
--- a/indra/newview/skins/default/xui/en/sidepanel_item_info.xml
+++ b/indra/newview/skins/default/xui/en/sidepanel_item_info.xml
@@ -32,6 +32,10 @@
Owner can:
</panel.string>
<panel.string
+ name="acquiredDateAMPM">
+ [wkday,datetime,slt] [mth,datetime,slt] [day,datetime,slt] [hour12,datetime,slt]:[min,datetime,slt]:[second,datetime,slt] [ampm,datetime,slt] [year,datetime,slt]
+ </panel.string>
+ <panel.string
name="acquiredDate">
[wkday,datetime,slt] [mth,datetime,slt] [day,datetime,slt] [hour,datetime,slt]:[min,datetime,slt]:[second,datetime,slt] [year,datetime,slt]
</panel.string>
diff --git a/indra/newview/tests/llworldmap_test.cpp b/indra/newview/tests/llworldmap_test.cpp
index d5bf189d82..60172b3960 100644
--- a/indra/newview/tests/llworldmap_test.cpp
+++ b/indra/newview/tests/llworldmap_test.cpp
@@ -32,6 +32,7 @@
#include "lltrans.h"
#include "lluistring.h"
#include "../llviewertexture.h"
+#include "../llviewercontrol.h"
#include "../llworldmapmessage.h"
// Class to test
#include "../llworldmap.h"
@@ -71,6 +72,11 @@ void LLUIString::updateResult() const { }
void LLUIString::setArg(const std::string& , const std::string& ) { }
void LLUIString::assign(const std::string& ) { }
+LLControlGroup::LLControlGroup(const std::string& name) : LLInstanceTracker<LLControlGroup, std::string>(name) {}
+LLControlGroup::~LLControlGroup() {}
+bool LLControlGroup::getBOOL(std::string_view) { return true; }
+LLControlGroup gSavedSettings("test_settings");
+
// End Stubbing
// -------------------------------------------------------------------------------------------