diff --git a/autobuild.xml b/autobuild.xml
index 9284d39d7b2..9e8a386463d 100644
--- a/autobuild.xml
+++ b/autobuild.xml
@@ -1333,11 +1333,11 @@
archive
name
common
@@ -1348,9 +1348,9 @@
license_file
LICENSES/lsl_definitions.txt
copyright
- Copyright (c) 2026, Linden Research, Inc.
+ Copyright (c) 2026, Linden Lab
version
- 0.6.12
+ 0.6.13
use_scm_version
true
name
@@ -2248,11 +2248,11 @@ Copyright (c) 2012, 2014, 2015, 2016 nghttp2 contributors
archive
name
common
@@ -2263,9 +2263,9 @@ Copyright (c) 2012, 2014, 2015, 2016 nghttp2 contributors
license_file
LICENSES/fonts.txt
copyright
- Copyright 2016-2022 Brad Erickson CC-BY-4.0/MIT, Copyright 2016-2022 Twitter, Inc. CC-BY-4.0, Copyright 2013 Joe Loughry and Terence Eden MIT, Copyright (c) 2003 by Bitstream, Inc., Copyright (c) 2006 by Tavmjong Bah.
+ Copyright 2016-2022 Brad Erickson CC-BY-4.0/MIT, Copyright 2016-2022 Twitter, Inc. CC-BY-4.0, Copyright 2013 Joe Loughry and Terence Eden MIT, Copyright (c) 2003 by Bitstream, Inc., Copyright (c) 2006 by Tavmjong Bah, Copyright Yukari Hafner (Shinmera) SIL-OFL-1.1.
version
- 1.0.0.25128287087
+ 1.0.0.32996553300
name
viewer-fonts
description
diff --git a/indra/cmake/Copy3rdPartyLibs.cmake b/indra/cmake/Copy3rdPartyLibs.cmake
index def3feaa712..ec4d4141cb3 100644
--- a/indra/cmake/Copy3rdPartyLibs.cmake
+++ b/indra/cmake/Copy3rdPartyLibs.cmake
@@ -34,6 +34,7 @@ if(WINDOWS)
set(release_src_dir "${ARCH_PREBUILT_DIRS_RELEASE}")
set(release_files
openjp2.dll
+ SDL3.dll
)
# Filenames are different for 32/64 bit BugSplat file and we don't
@@ -146,6 +147,7 @@ elseif(DARWIN)
set(release_src_dir "${ARCH_PREBUILT_DIRS_RELEASE}")
set(release_files
libndofdev.dylib
+ libSDL3.dylib
)
if (TARGET ll::discord_sdk)
diff --git a/indra/cmake/SDL3.cmake b/indra/cmake/SDL3.cmake
index fe9012de171..0a2b54e1ad9 100644
--- a/indra/cmake/SDL3.cmake
+++ b/indra/cmake/SDL3.cmake
@@ -11,16 +11,11 @@ else()
set(USE_SDL_WINDOW OFF CACHE BOOL "Build with SDL window backend")
endif (LINUX)
-if(USE_SDL_WINDOW)
- use_system_binary(SDL3)
- use_prebuilt_binary(SDL3)
+use_prebuilt_binary(SDL3)
- find_library( SDL3_LIBRARY
- NAMES SDL3 SDL3.lib libSDL3.so libSDL3.dylib
- PATHS "${LIBS_PREBUILT_DIR}/lib/release" REQUIRED)
-
- target_link_libraries(ll::SDL3 INTERFACE ${SDL3_LIBRARY})
- target_include_directories(ll::SDL3 SYSTEM INTERFACE "${LIBS_PREBUILT_DIR}/include/")
-
-endif(USE_SDL_WINDOW)
+find_library( SDL3_LIBRARY
+ NAMES SDL3 SDL3.lib libSDL3.so libSDL3.dylib
+ PATHS "${LIBS_PREBUILT_DIR}/lib/release" REQUIRED)
+target_link_libraries(ll::SDL3 INTERFACE ${SDL3_LIBRARY})
+target_include_directories(ll::SDL3 SYSTEM INTERFACE "${LIBS_PREBUILT_DIR}/include/")
diff --git a/indra/llcommon/llinitparam.h b/indra/llcommon/llinitparam.h
index b220afadfca..43d027a0c5d 100644
--- a/indra/llcommon/llinitparam.h
+++ b/indra/llcommon/llinitparam.h
@@ -1767,8 +1767,13 @@ namespace LLInitParam
// implicit conversion
operator const container_t&() const { return mValues; }
+ operator container_t&() { return mValues; }
// explicit conversion
const container_t& operator()() const { return mValues; }
+ container_t& operator()() { return mValues; }
+ // direct Nth item access
+ const typename NAME_VALUE_LOOKUP::type_value_t& operator()(size_t index) const { return mValues[index]; }
+ typename NAME_VALUE_LOOKUP::type_value_t& operator()(size_t index) { return mValues[index]; }
iterator begin() { return mValues.begin(); }
iterator end() { return mValues.end(); }
@@ -2098,13 +2103,13 @@ namespace LLInitParam
class Multiple : public TypedParam
{
typedef TypedParam super_t;
- typedef Multiple self_t;
- typedef typename super_t::container_t container_t;
+ typedef Multiple self_t;
+ typedef typename super_t::container_t container_t;
typedef typename super_t::value_t value_t;
public:
- typedef typename super_t::iterator iterator;
- typedef typename super_t::const_iterator const_iterator;
+ typedef typename super_t::iterator iterator;
+ typedef typename super_t::const_iterator const_iterator;
using super_t::operator();
using super_t::operator const container_t&;
@@ -2832,3 +2837,4 @@ namespace LLInitParam
#endif // LL_LLPARAM_H
+
diff --git a/indra/llcommon/llkeybind.cpp b/indra/llcommon/llkeybind.cpp
index abb0df0b87b..8b633ad1975 100644
--- a/indra/llcommon/llkeybind.cpp
+++ b/indra/llcommon/llkeybind.cpp
@@ -37,7 +37,9 @@ LLKeyData::LLKeyData()
mMouse(CLICK_NONE),
mKey(KEY_NONE),
mMask(MASK_NONE),
- mIgnoreMasks(false)
+ mIgnoreMasks(false),
+ mControllerActionType(255),
+ mControllerAction(0)
{
}
@@ -46,7 +48,9 @@ LLKeyData::LLKeyData(EMouseClickType mouse, KEY key, MASK mask)
mMouse(mouse),
mKey(key),
mMask(mask),
- mIgnoreMasks(false)
+ mIgnoreMasks(false),
+ mControllerActionType(255),
+ mControllerAction(0)
{
}
@@ -55,7 +59,9 @@ LLKeyData::LLKeyData(EMouseClickType mouse, KEY key, bool ignore_mask)
mMouse(mouse),
mKey(key),
mMask(MASK_NONE),
- mIgnoreMasks(ignore_mask)
+ mIgnoreMasks(ignore_mask),
+ mControllerActionType(255),
+ mControllerAction(0)
{
}
@@ -64,10 +70,233 @@ LLKeyData::LLKeyData(EMouseClickType mouse, KEY key, MASK mask, bool ignore_mask
mMouse(mouse),
mKey(key),
mMask(mask),
- mIgnoreMasks(ignore_mask)
+ mIgnoreMasks(ignore_mask),
+ mControllerActionType(255),
+ mControllerAction(0)
{
}
+LLKeyData::LLKeyData(U8 actionType, U8 action)
+ :
+ mMouse(CLICK_NONE),
+ mKey(KEY_NONE),
+ mMask(MASK_NONE),
+ mIgnoreMasks(true),
+ mControllerActionType(actionType),
+ mControllerAction(action)
+{
+
+}
+
+static std::string controllerStringFromKeyData(const U8 actionType, const U8 action)
+{
+ switch (actionType)
+ {
+ case 0:
+ {
+ switch(action)
+ {
+ case 0: return "axis_left";
+ case 1: return "axis_right";
+ case 2: return "axis_forward";
+ case 3: return "axis_backward";
+ case 4: return "axis_turn_left";
+ case 5: return "axis_turn_right";
+ case 6: return "axis_look_up";
+ case 7: return "axis_look_down";
+ case 8: return "axis_up";
+ case 9: return "axis_down";
+ case 10: return "axis_roll_left";
+ case 11: return "axis_roll_right";
+ }
+ }
+ case 1:
+ {
+ switch(action)
+ {
+ case 0: return "button_a";
+ case 1: return "button_b";
+ case 2: return "button_x";
+ case 3: return "button_y";
+ case 4: return "button_back";
+ case 5: return "button_start";
+ case 6: return "button_guide";
+ case 7: return "button_leftstick";
+ case 8: return "button_rightstick";
+ case 9: return "button_leftshoulder";
+ case 10: return "button_rightshoulder";
+ case 11: return "button_dpad_up";
+ case 12: return "button_dpad_down";
+ case 13: return "button_dpad_left";
+ case 14: return "button_dpad_right";
+ case 15: return "button_misc1";
+ case 16: return "button_paddle1";
+ case 17: return "button_paddle2";
+ case 18: return "button_paddle3";
+ case 19: return "button_paddle4";
+ case 20: return "button_touchpad";
+ }
+ }
+ }
+ return "NONE";
+}
+
+static bool controllerActionFromString(const std::string& string, U8& actionType, U8& action)
+{
+ actionType = 255;
+ if(LLStringUtil::startsWith(string, "axis_")) {
+ actionType = 0;
+ if(string == "axis_left")
+ {
+ action = 0;
+ }
+ else if(string == "axis_right")
+ {
+ action = 1;
+ }
+ else if(string == "axis_forward")
+ {
+ action = 2;
+ }
+ else if(string == "axis_backward")
+ {
+ action = 3;
+ }
+ else if(string == "axis_turn_left")
+ {
+ action = 4;
+ }
+ else if(string == "axis_turn_right")
+ {
+ action = 5;
+ }
+ else if(string == "axis_look_up")
+ {
+ action = 6;
+ }
+ else if(string == "axis_look_down")
+ {
+ action = 7;
+ }
+ else if(string == "axis_up")
+ {
+ action = 8;
+ }
+ else if(string == "axis_down")
+ {
+ action = 9;
+ }
+ else if(string == "axis_roll_left")
+ {
+ action = 10;
+ }
+ else if(string == "axis_roll_right")
+ {
+ action = 11;
+ }
+ else
+ {
+ actionType = 255;
+ return false;
+ }
+ return true;
+ }
+ else if(LLStringUtil::startsWith(string, "button_")) {
+ actionType = 1;
+ if(string == "button_a")
+ {
+ action = 9;
+ }
+ else if(string == "button_b")
+ {
+ action = 1;
+ }
+ else if(string == "button_x")
+ {
+ action = 2;
+ }
+ else if(string == "button_y")
+ {
+ action = 3;
+ }
+ else if(string == "button_back")
+ {
+ action = 4;
+ }
+ else if(string == "button_start")
+ {
+ action = 5;
+ }
+ else if(string == "button_guide")
+ {
+ action = 6;
+ }
+ else if(string == "button_leftstick")
+ {
+ action = 7;
+ }
+ else if(string == "button_rightstick")
+ {
+ action = 8;
+ }
+ else if(string == "button_leftshoulder")
+ {
+ action = 9;
+ }
+ else if(string == "button_rightshoulder")
+ {
+ action = 10;
+ }
+ else if(string == "button_dpad_up")
+ {
+ action = 11;
+ }
+ else if(string == "button_dpad_down")
+ {
+ action = 12;
+ }
+ else if(string == "button_dpad_left")
+ {
+ action = 13;
+ }
+ else if(string == "button_dpad_right")
+ {
+ action = 14;
+ }
+ else if(string == "button_misc1")
+ {
+ action = 15;
+ }
+ else if(string == "button_paddle1")
+ {
+ action = 16;
+ }
+ else if(string == "button_paddle2")
+ {
+ action = 17;
+ }
+ else if(string == "button_paddle3")
+ {
+ action = 18;
+ }
+ else if(string == "button_paddle4")
+ {
+ action = 19;
+ }
+ else if(string == "button_touchpad")
+ {
+ action = 20;
+ }
+ else
+ {
+ actionType = 255;
+ return false;
+ }
+ return true;
+ }
+ return false;
+}
+
LLKeyData::LLKeyData(const LLSD &key_data)
{
if (key_data.has("mouse"))
@@ -86,6 +315,14 @@ LLKeyData::LLKeyData(const LLSD &key_data)
{
mMask = key_data["mask"].asInteger();
}
+ if (key_data.has("controller"))
+ {
+ U8 actionType = 255;
+ U8 action = 0;
+ controllerActionFromString(key_data["controller"],actionType, action);
+ mControllerActionType = actionType;
+ mControllerAction = action;
+ }
}
LLSD LLKeyData::asLLSD() const
@@ -98,6 +335,7 @@ LLSD LLKeyData::asLLSD() const
{
data["ignore_accelerators"] = (LLSD::Boolean)mIgnoreMasks;
}
+ data["controller"] = (LLSD::String)controllerStringFromKeyData(mControllerActionType, mControllerAction);
return data;
}
@@ -112,23 +350,29 @@ void LLKeyData::reset()
mKey = KEY_NONE;
mMask = MASK_NONE;
mIgnoreMasks = false;
+ mControllerActionType = 255;
+ mControllerAction = 0;
}
-bool LLKeyData::operator==(const LLKeyData& rhs)
+bool LLKeyData::operator==(const LLKeyData& rhs) const
{
if (mMouse != rhs.mMouse) return false;
if (mKey != rhs.mKey) return false;
if (mMask != rhs.mMask) return false;
if (mIgnoreMasks != rhs.mIgnoreMasks) return false;
+ if (mControllerActionType != rhs.mControllerActionType) return false;
+ if (mControllerAction != rhs.mControllerAction) return false;
return true;
}
-bool LLKeyData::operator!=(const LLKeyData& rhs)
+bool LLKeyData::operator!=(const LLKeyData& rhs) const
{
if (mMouse != rhs.mMouse) return true;
if (mKey != rhs.mKey) return true;
if (mMask != rhs.mMask) return true;
if (mIgnoreMasks != rhs.mIgnoreMasks) return true;
+ if (mControllerActionType != rhs.mControllerActionType) return true;
+ if (mControllerAction != rhs.mControllerAction) return true;
return false;
}
@@ -136,7 +380,9 @@ bool LLKeyData::canHandle(const LLKeyData& data) const
{
if (data.mKey == mKey
&& data.mMouse == mMouse
- && ((mIgnoreMasks && (data.mMask & mMask) == mMask) || data.mMask == mMask))
+ && ((mIgnoreMasks && (data.mMask & mMask) == mMask) || data.mMask == mMask)
+ && (mControllerActionType == data.mControllerActionType && mControllerAction == data.mControllerAction)
+ )
{
return true;
}
@@ -170,7 +416,7 @@ LLKeyBind::LLKeyBind(const LLSD &key_bind)
}
}
-bool LLKeyBind::operator==(const LLKeyBind& rhs)
+bool LLKeyBind::operator==(const LLKeyBind& rhs) const
{
auto size = mData.size();
if (size != rhs.mData.size()) return false;
@@ -183,7 +429,7 @@ bool LLKeyBind::operator==(const LLKeyBind& rhs)
return true;
}
-bool LLKeyBind::operator!=(const LLKeyBind& rhs)
+bool LLKeyBind::operator!=(const LLKeyBind& rhs) const
{
auto size = mData.size();
if (size != rhs.mData.size()) return true;
diff --git a/indra/llcommon/llkeybind.h b/indra/llcommon/llkeybind.h
index 0a9d4600cca..2e623c4dd78 100644
--- a/indra/llcommon/llkeybind.h
+++ b/indra/llcommon/llkeybind.h
@@ -37,14 +37,15 @@ class LL_COMMON_API LLKeyData
LLKeyData(EMouseClickType mouse, KEY key, MASK mask);
LLKeyData(EMouseClickType mouse, KEY key, bool ignore_mask);
LLKeyData(EMouseClickType mouse, KEY key, MASK mask, bool ignore_mask);
+ LLKeyData(U8 actionType, U8 action);
LLKeyData(const LLSD &key_data);
LLSD asLLSD() const;
bool isEmpty() const;
bool empty() const { return isEmpty(); };
void reset();
- bool operator==(const LLKeyData& rhs);
- bool operator!=(const LLKeyData& rhs);
+ bool operator==(const LLKeyData& rhs) const;
+ bool operator!=(const LLKeyData& rhs) const;
bool canHandle(const LLKeyData& data) const;
bool canHandle(EMouseClickType mouse, KEY key, MASK mask) const;
@@ -52,6 +53,8 @@ class LL_COMMON_API LLKeyData
EMouseClickType mMouse;
KEY mKey;
MASK mMask;
+ U8 mControllerActionType;
+ U8 mControllerAction;
// Either to expect exact match or ignore not expected masks as long as expected mask-bit is present
bool mIgnoreMasks;
};
@@ -63,8 +66,8 @@ class LLKeyBind
LLKeyBind() {}
LLKeyBind(const LLSD &key_bind);
- bool operator==(const LLKeyBind& rhs);
- bool operator!=(const LLKeyBind& rhs);
+ bool operator==(const LLKeyBind& rhs) const;
+ bool operator!=(const LLKeyBind& rhs) const;
bool isEmpty() const;
bool empty() const { return isEmpty(); };
diff --git a/indra/llcommon/llprocess.cpp b/indra/llcommon/llprocess.cpp
index 86e1f48377e..a25baac6849 100644
--- a/indra/llcommon/llprocess.cpp
+++ b/indra/llcommon/llprocess.cpp
@@ -186,7 +186,9 @@ class WritePipeImpl : public LLProcess::WritePipe
std::shared_ptr pipe) :
mDesc(desc),
mPipe(pipe),
- mStream(&mStreambuf),
+ mFill(&mBufA),
+ mFlush(&mBufB),
+ mStream(mFill),
mWritePending(false)
{}
@@ -196,7 +198,9 @@ class WritePipeImpl : public LLProcess::WritePipe
virtual size_type size() const override
{
- return mStreambuf.size();
+ // Bytes not yet handed to the OS: whatever is queued in the fill
+ // buffer plus whatever is still draining from the flush buffer.
+ return mFill->size() + mFlush->size();
}
// Called from LLProcess::tick() to initiate writing buffered data.
@@ -210,18 +214,24 @@ class WritePipeImpl : public LLProcess::WritePipe
private:
void startAsyncWrite()
{
- if (mWritePending || !mPipe || !mPipe->is_open() || mStreambuf.size() == 0)
+ if (mWritePending || !mPipe || !mPipe->is_open() || mFill->size() == 0)
return;
mWritePending = true;
- // Snapshot the number of bytes currently buffered so the async_write
- // operates on a fixed-size, stable view. Any data written to
- // get_ostream() while this write is in flight lands in the streambuf's
- // put area and is excluded from the current operation; it will be sent
- // on the next tick(). Without this snapshot, a concurrent write to
- // get_ostream() could reallocate/invalidate the buffer sequence.
- std::size_t writeSize = mStreambuf.size();
+ // Zero-copy double buffering. async_write must reference storage that
+ // stays valid AND immovable for the entire operation, but get_ostream()
+ // writes arriving while a write is in flight can grow the streambuf's
+ // put area and REALLOCATE its storage -- which would invalidate the
+ // buffer sequence mid-write and corrupt the data sent to the child.
+ //
+ // So we keep two buffers: get_ostream() always fills mFill; async_write
+ // always drains mFlush. Swapping them here, and rebinding mStream to the
+ // new (empty) mFill, means the in-flight write owns mFlush exclusively --
+ // no subsequent get_ostream() write can touch it until this write
+ // completes and mWritePending goes false again. No copy, no aliasing.
+ std::swap(mFill, mFlush);
+ mStream.rdbuf(mFill);
// Write all buffered data asynchronously. Do NOT self-chain in the
// completion handler: sending the next buffer immediately can cause
@@ -230,14 +240,16 @@ class WritePipeImpl : public LLProcess::WritePipe
// was the root cause of test 18 "more than 3 events" and test 9
// "many small messages" failures. tick() calls mWritePipe->tick() on
// every mainloop frame, so any newly-queued data will be sent then.
- asio::async_write(*mPipe, asio::buffer(mStreambuf.data(), writeSize),
+ asio::async_write(*mPipe, mFlush->data(),
[this](const boost::system::error_code& ec, std::size_t bytes_transferred)
{
mWritePending = false;
if (!ec)
{
- mStreambuf.consume(bytes_transferred);
+ // Drain the bytes we just sent, leaving mFlush empty and ready
+ // to serve as the next fill buffer after a future swap.
+ mFlush->consume(bytes_transferred);
LL_DEBUGS("LLProcess") << "Wrote " << bytes_transferred
<< " bytes to " << mDesc << LL_ENDL;
}
@@ -251,7 +263,13 @@ class WritePipeImpl : public LLProcess::WritePipe
std::string mDesc;
std::shared_ptr mPipe;
- asio::streambuf mStreambuf;
+ // Double buffering: get_ostream() fills one buffer while async_write drains
+ // the other. mFill/mFlush point at mBufA/mBufB and swap on each write; see
+ // startAsyncWrite(). mStream is always bound to *mFill.
+ asio::streambuf mBufA;
+ asio::streambuf mBufB;
+ asio::streambuf* mFill;
+ asio::streambuf* mFlush;
std::ostream mStream;
bool mWritePending;
};
diff --git a/indra/llcommon/llsd.cpp b/indra/llcommon/llsd.cpp
index ad2ce231fae..19ed6d9bd88 100644
--- a/indra/llcommon/llsd.cpp
+++ b/indra/llcommon/llsd.cpp
@@ -1043,6 +1043,31 @@ void LLSD::clear() { Impl::assignUndefined(impl); }
LLSD::Type LLSD::type() const { return safe(impl).type(); }
+bool LLSD::isEmpty() const
+{
+ switch (type())
+ {
+ case TypeUndefined:
+ return true; // Always empty
+ case TypeBinary:
+ return !asBoolean(); // Empty when default
+ case TypeInteger:
+ return !asInteger(); // Empty when default
+ case TypeReal:
+ return !asReal(); // Empty when default
+ case TypeString:
+ case TypeURI:
+ return asString().empty(); // Empty natively
+ case TypeArray:
+ case TypeMap:
+ return !size(); // Empty natively
+ default:;
+ }
+ // All other value types (TypeDate) don't have default values so can't be empty
+ return false;
+}
+
+
// Scalar Constructors
LLSD::LLSD(Boolean v) : impl(0) { ALLOC_LLSD_OBJECT; assign(v); }
LLSD::LLSD(Integer v) : impl(0) { ALLOC_LLSD_OBJECT; assign(v); }
diff --git a/indra/llcommon/llsd.h b/indra/llcommon/llsd.h
index afe35a65ba0..0b2768dc8ac 100644
--- a/indra/llcommon/llsd.h
+++ b/indra/llcommon/llsd.h
@@ -453,6 +453,7 @@ class LL_COMMON_API LLSD
bool isBinary() const { return type() == TypeBinary; }
bool isMap() const { return type() == TypeMap; }
bool isArray() const { return type() == TypeArray; }
+ bool isEmpty() const;
//@}
/** @name Automatic Cast Protection
diff --git a/indra/llcommon/llstring.cpp b/indra/llcommon/llstring.cpp
index 07adf71d18f..892671099e7 100644
--- a/indra/llcommon/llstring.cpp
+++ b/indra/llcommon/llstring.cpp
@@ -1119,18 +1119,27 @@ void LLStringOps::setupDayFormat(const std::string& data)
}
-std::string LLStringOps::getDatetimeCode (std::string key)
+std::string LLStringOps::getDatetimeCode(const std::string& key)
{
- std::map::iterator iter;
+ std::map::iterator iter = datetimeToCodes.find(key);
+ return iter == datetimeToCodes.end() ? LLStringUtil::null : iter->second;
+}
- iter = datetimeToCodes.find (key);
- if (iter != datetimeToCodes.end())
+void LLStringOps::splitString(const std::string& text, char delimiter,
+ std::function handler)
+{
+ std::size_t from = 0;
+ for (std::size_t i = 0; i < text.size(); ++i)
{
- return iter->second;
+ if (text[i] == delimiter)
+ {
+ handler(text.substr(from, i - from));
+ from = i + 1;
+ }
}
- else
+ if (from <= text.size())
{
- return std::string("");
+ handler(text.substr(from));
}
}
diff --git a/indra/llcommon/llstring.h b/indra/llcommon/llstring.h
index 7dd8256e72c..fdbaaaac539 100644
--- a/indra/llcommon/llstring.h
+++ b/indra/llcommon/llstring.h
@@ -36,6 +36,7 @@
//#include
#include
#include
+#include
#include
#include