From df41a3a7dfe7f3bc69eabef3a4afe0e9f2a8d8e1 Mon Sep 17 00:00:00 2001 From: Nat Goodspeed Date: Thu, 17 Aug 2023 09:14:54 -0400 Subject: DRTVWR-588: Finally ditch LL_USE_SYSTEM_RAND code in llrand.cpp. This conditional code hasn't been used since June 2008, possibly even earlier. --- indra/llcommon/llrand.cpp | 104 +++++++++++++++++----------------------------- 1 file changed, 39 insertions(+), 65 deletions(-) diff --git a/indra/llcommon/llrand.cpp b/indra/llcommon/llrand.cpp index cb28a8f5c3..33afc50cf7 100644 --- a/indra/llcommon/llrand.cpp +++ b/indra/llcommon/llrand.cpp @@ -58,46 +58,14 @@ * to restore uniform distribution. */ -// *NOTE: The system rand implementation is probably not correct. -#define LL_USE_SYSTEM_RAND 0 +static LLRandLagFib2281 gRandomGenerator(LLUUID::getRandomSeed()); -#if LL_USE_SYSTEM_RAND -#include -#endif +// no default implementation, only specific F64 and F32 specializations +template +inline REAL ll_internal_random(); -#if LL_USE_SYSTEM_RAND -class LLSeedRand -{ -public: - LLSeedRand() - { -#if LL_WINDOWS - srand(LLUUID::getRandomSeed()); -#else - srand48(LLUUID::getRandomSeed()); -#endif - } -}; -static LLSeedRand sRandomSeeder; -inline F64 ll_internal_random_double() -{ -#if LL_WINDOWS - return (F64)rand() / (F64)RAND_MAX; -#else - return drand48(); -#endif -} -inline F32 ll_internal_random_float() -{ -#if LL_WINDOWS - return (F32)rand() / (F32)RAND_MAX; -#else - return (F32)drand48(); -#endif -} -#else -static LLRandLagFib2281 gRandomGenerator(LLUUID::getRandomSeed()); -inline F64 ll_internal_random_double() +template <> +inline F64 ll_internal_random() { // *HACK: Through experimentation, we have found that dual core // CPUs (or at least multi-threaded processes) seem to @@ -108,15 +76,35 @@ inline F64 ll_internal_random_double() return rv; } +template <> +inline F32 ll_internal_random() +{ + return F32(ll_internal_random()); +} + +/*------------------------------ F64 aliases -------------------------------*/ +inline F64 ll_internal_random_double() +{ + return ll_internal_random(); +} + +F64 ll_drand() +{ + return ll_internal_random_double(); +} + +/*------------------------------ F32 aliases -------------------------------*/ inline F32 ll_internal_random_float() { - // The clamping rules are described above. - F32 rv = (F32)gRandomGenerator(); - if(!((rv >= 0.0f) && (rv < 1.0f))) return fmod(rv, 1.f); - return rv; + return ll_internal_random(); +} + +F32 ll_frand() +{ + return ll_internal_random_float(); } -#endif +/*-------------------------- clamped random range --------------------------*/ S32 ll_rand() { return ll_rand(RAND_MAX); @@ -130,42 +118,28 @@ S32 ll_rand(S32 val) return rv; } -F32 ll_frand() -{ - return ll_internal_random_float(); -} - -F32 ll_frand(F32 val) +template +REAL ll_grand(REAL val) { // The clamping rules are described above. - F32 rv = ll_internal_random_float() * val; + REAL rv = ll_internal_random() * val; if(val > 0) { - if(rv >= val) return 0.0f; + if(rv >= val) return REAL(); } else { - if(rv <= val) return 0.0f; + if(rv <= val) return REAL(); } return rv; } -F64 ll_drand() +F32 ll_frand(F32 val) { - return ll_internal_random_double(); + return ll_grand(val); } F64 ll_drand(F64 val) { - // The clamping rules are described above. - F64 rv = ll_internal_random_double() * val; - if(val > 0) - { - if(rv >= val) return 0.0; - } - else - { - if(rv <= val) return 0.0; - } - return rv; + return ll_grand(val); } -- cgit v1.2.3 From 0f8b8fd7a338272e3464e809b94eb0443d99e275 Mon Sep 17 00:00:00 2001 From: Nat Goodspeed Date: Thu, 17 Aug 2023 09:28:53 -0400 Subject: DRTVWR-588: Eliminate APR and Boost.Phoenix from NamedTempFile. NamedTempFile used to use APR calls to discover a suitable temp directory, synthesize a temp filename template, generate the unique file, write its content and ultimately delete it. This required a reference to gAPRPoolp as the default value of an optional constructor argument in case some usage demanded an alternative APR memory pool. It also used Boost.Phoenix placeholders to magically synthesize a callable. Replace APR calls with Boost.Filesystem; replace Boost.Phoenix with lambdas. Break out unique path generation logic as static NamedTempFile::temp_path(). In a nod to GitHub Actions builds, honor RUNNER_TEMP environment variable if set. test.cpp's RecordToTempFile need no longer pass an apr_pool_t* to NamedTempFile. NamedTempFile's constructor now accepts an optional suffix, making subclass NamedExtTempFile nearly trivial. It no longer needs to create or remove a symlink, for which it used to use APR calls. llprocess_test.cpp's NamedTempDir used to use Python's tempfile.mkdtemp() to create a temp directory, and apr_dir_remove() to destroy it. Replace both with NamedTempFile::temp_path() and Boost.Filesystem. Also add diagnostic output for LLProcess test failure. If llprocess_test cannot launch a child process, notice the APR_LOG environment variable recognized by our patched apr_suite to engage logging, and report the contents of that file. --- indra/llcommon/tests/llprocess_test.cpp | 70 ++++++++++---- indra/test/namedtempfile.h | 158 ++++++++++++-------------------- indra/test/test.cpp | 2 +- 3 files changed, 111 insertions(+), 119 deletions(-) diff --git a/indra/llcommon/tests/llprocess_test.cpp b/indra/llcommon/tests/llprocess_test.cpp index 81449b4a42..c48d913069 100644 --- a/indra/llcommon/tests/llprocess_test.cpp +++ b/indra/llcommon/tests/llprocess_test.cpp @@ -151,8 +151,38 @@ struct PythonProcessLauncher /// Launch Python script; verify that it launched void launch() { - mPy = LLProcess::create(mParams); - tut::ensure(STRINGIZE("Couldn't launch " << mDesc << " script"), bool(mPy)); + try + { + mPy = LLProcess::create(mParams); + tut::ensure(STRINGIZE("Couldn't launch " << mDesc << " script"), bool(mPy)); + } + catch (const tut::failure&) + { + // On Windows, if APR_LOG is set, our version of APR's + // apr_create_proc() logs to the specified file. If this test + // failed, try to report that log. + const char* APR_LOG = getenv("APR_LOG"); + if (APR_LOG && *APR_LOG) + { + std::ifstream inf(APR_LOG); + if (! inf.is_open()) + { + LL_WARNS() << "Couldn't open '" << APR_LOG << "'" << LL_ENDL; + } + else + { + LL_WARNS() << "==============================" << LL_ENDL; + LL_WARNS() << "From '" << APR_LOG << "':" << LL_ENDL; + std::string line; + while (std::getline(inf, line)) + { + LL_WARNS() << line << LL_ENDL; + } + LL_WARNS() << "==============================" << LL_ENDL; + } + } + throw; + } } /// Run Python script and wait for it to complete. @@ -214,30 +244,26 @@ static std::string python_out(const std::string& desc, const CONTENT& script) class NamedTempDir: public boost::noncopyable { public: - // Use python() function to create a temp directory: I've found - // nothing in either Boost.Filesystem or APR quite like Python's - // tempfile.mkdtemp(). - // Special extra bonus: on Mac, mkdtemp() reports a pathname - // starting with /var/folders/something, whereas that's really a - // symlink to /private/var/folders/something. Have to use - // realpath() to compare properly. NamedTempDir(): - mPath(python_out("mkdtemp()", - "from __future__ import with_statement\n" - "import os.path, sys, tempfile\n" - "with open(sys.argv[1], 'w') as f:\n" - " f.write(os.path.normcase(os.path.normpath(os.path.realpath(tempfile.mkdtemp()))))\n")) - {} + mPath(NamedTempFile::temp_path()), + mCreated(boost::filesystem::create_directories(mPath)) + { + mPath = boost::filesystem::canonical(mPath); + } ~NamedTempDir() { - aprchk(apr_dir_remove(mPath.c_str(), gAPRPoolp)); + if (mCreated) + { + boost::filesystem::remove_all(mPath); + } } - std::string getName() const { return mPath; } + std::string getName() const { return mPath.string(); } private: - std::string mPath; + boost::filesystem::path mPath; + bool mCreated; }; /***************************************************************************** @@ -565,7 +591,13 @@ namespace tut " f.write(os.path.normcase(os.path.normpath(os.getcwd())))\n"); // Before running, call setWorkingDirectory() py.mParams.cwd = tempdir.getName(); - ensure_equals("os.getcwd()", py.run_read(), tempdir.getName()); + std::string expected{ tempdir.getName() }; +#if LL_WINDOWS + // SIGH, don't get tripped up by "C:" != "c:" -- + // but on the Mac, using tolower() fails because "/users" != "/Users"! + expected = utf8str_tolower(expected); +#endif + ensure_equals("os.getcwd()", py.run_read(), expected); } template<> template<> diff --git a/indra/test/namedtempfile.h b/indra/test/namedtempfile.h index 7d59cad32c..525a35000d 100644 --- a/indra/test/namedtempfile.h +++ b/indra/test/namedtempfile.h @@ -13,15 +13,16 @@ #define LL_NAMEDTEMPFILE_H #include "llerror.h" -#include "llapr.h" -#include "apr_file_io.h" +#include "llstring.h" +#include "stringize.h" #include -#include -#include -#include +#include +#include #include +#include #include #include +#include /** * Create a text file with specified content "somewhere in the @@ -31,134 +32,106 @@ class NamedTempFile: public boost::noncopyable { LOG_CLASS(NamedTempFile); public: - NamedTempFile(const std::string& pfx, const std::string& content, apr_pool_t* pool=gAPRPoolp): - mPool(pool) + NamedTempFile(const std::string_view& pfx, + const std::string_view& content, + const std::string_view& sfx=std::string_view("")) { - createFile(pfx, boost::phoenix::placeholders::arg1 << content); + createFile(pfx, [&content](std::ostream& out){ out << content; }, sfx); } - // Disambiguate when passing string literal - NamedTempFile(const std::string& pfx, const char* content, apr_pool_t* pool=gAPRPoolp): - mPool(pool) + // Disambiguate when passing string literal -- unclear why a string + // literal should be ambiguous wrt std::string_view and Streamer + NamedTempFile(const std::string_view& pfx, + const char* content, + const std::string_view& sfx=std::string_view("")) { - createFile(pfx, boost::phoenix::placeholders::arg1 << content); + createFile(pfx, [&content](std::ostream& out){ out << content; }, sfx); } // Function that accepts an ostream ref and (presumably) writes stuff to // it, e.g.: // (boost::phoenix::placeholders::arg1 << "the value is " << 17 << '\n') - typedef boost::function Streamer; + typedef std::function Streamer; - NamedTempFile(const std::string& pfx, const Streamer& func, apr_pool_t* pool=gAPRPoolp): - mPool(pool) + NamedTempFile(const std::string_view& pfx, + const Streamer& func, + const std::string_view& sfx=std::string_view("")) { - createFile(pfx, func); + createFile(pfx, func, sfx); } virtual ~NamedTempFile() { - ll_apr_assert_status(apr_file_remove(mPath.c_str(), mPool)); + boost::filesystem::remove(mPath); } - virtual std::string getName() const { return mPath; } + std::string getName() const { return mPath.string(); } void peep() { std::cout << "File '" << mPath << "' contains:\n"; - std::ifstream reader(mPath.c_str()); + boost::filesystem::ifstream reader(mPath); std::string line; while (std::getline(reader, line)) std::cout << line << '\n'; std::cout << "---\n"; } + static boost::filesystem::path temp_path(const std::string_view& pfx="", + const std::string_view& sfx="") + { + // This variable is set by GitHub actions and is the recommended place + // to put temp files belonging to an actions job. + const char* RUNNER_TEMP = getenv("RUNNER_TEMP"); + boost::filesystem::path tempdir{ + // if RUNNER_TEMP is set and not empty + (RUNNER_TEMP && *RUNNER_TEMP)? + boost::filesystem::path(RUNNER_TEMP) : // use RUNNER_TEMP if available + boost::filesystem::temp_directory_path()}; // else canonical temp dir + boost::filesystem::path tempname{ + // use filename template recommended by unique_path() doc, but + // with underscores instead of hyphens: some use cases involve + // temporary Python scripts + tempdir / stringize(pfx, "%%%%_%%%%_%%%%_%%%%", sfx) }; + return boost::filesystem::unique_path(tempname); + } + protected: - void createFile(const std::string& pfx, const Streamer& func) + void createFile(const std::string_view& pfx, + const Streamer& func, + const std::string_view& sfx) { // Create file in a temporary place. - const char* tempdir = NULL; - ll_apr_assert_status(apr_temp_dir_get(&tempdir, mPool)); - - // Construct a temp filename template in that directory. - char *tempname = NULL; - ll_apr_assert_status(apr_filepath_merge(&tempname, - tempdir, - (pfx + "XXXXXX").c_str(), - 0, - mPool)); - - // Create a temp file from that template. - apr_file_t* fp = NULL; - ll_apr_assert_status(apr_file_mktemp(&fp, - tempname, - APR_CREATE | APR_WRITE | APR_EXCL, - mPool)); - // apr_file_mktemp() alters tempname with the actual name. Not until - // now is it valid to capture as our mPath. - mPath = tempname; - + mPath = temp_path(pfx, sfx); + boost::filesystem::ofstream out{ mPath }; // Write desired content. - std::ostringstream out; - // Stream stuff to it. func(out); - - std::string data(out.str()); - apr_size_t writelen(data.length()); - ll_apr_assert_status(apr_file_write(fp, data.c_str(), &writelen)); - ll_apr_assert_status(apr_file_close(fp)); - llassert_always(writelen == data.length()); } - std::string mPath; - apr_pool_t* mPool; + boost::filesystem::path mPath; }; /** * Create a NamedTempFile with a specified filename extension. This is useful * when, for instance, you must be able to use the file in a Python import * statement. - * - * A NamedExtTempFile actually has two different names. We retain the original - * no-extension name as a placeholder in the temp directory to ensure - * uniqueness; to that we link the name plus the desired extension. Naturally, - * both must be removed on destruction. */ class NamedExtTempFile: public NamedTempFile { LOG_CLASS(NamedExtTempFile); public: - NamedExtTempFile(const std::string& ext, const std::string& content, apr_pool_t* pool=gAPRPoolp): - NamedTempFile(remove_dot(ext), content, pool), - mLink(mPath + ensure_dot(ext)) - { - linkto(mLink); - } + NamedExtTempFile(const std::string& ext, const std::string_view& content): + NamedTempFile(remove_dot(ext), content, ensure_dot(ext)) + {} // Disambiguate when passing string literal - NamedExtTempFile(const std::string& ext, const char* content, apr_pool_t* pool=gAPRPoolp): - NamedTempFile(remove_dot(ext), content, pool), - mLink(mPath + ensure_dot(ext)) - { - linkto(mLink); - } + NamedExtTempFile(const std::string& ext, const char* content): + NamedTempFile(remove_dot(ext), content, ensure_dot(ext)) + {} - NamedExtTempFile(const std::string& ext, const Streamer& func, apr_pool_t* pool=gAPRPoolp): - NamedTempFile(remove_dot(ext), func, pool), - mLink(mPath + ensure_dot(ext)) - { - linkto(mLink); - } - - virtual ~NamedExtTempFile() - { - ll_apr_assert_status(apr_file_remove(mLink.c_str(), mPool)); - } - - // Since the caller has gone to the trouble to create the name with the - // extension, that should be the name we return. In this class, mPath is - // just a placeholder to ensure that future createFile() calls won't - // collide. - virtual std::string getName() const { return mLink; } + NamedExtTempFile(const std::string& ext, const Streamer& func): + NamedTempFile(remove_dot(ext), func, ensure_dot(ext)) + {} static std::string ensure_dot(const std::string& ext) { @@ -175,7 +148,7 @@ public: { return ext; } - return std::string(".") + ext; + return "." + ext; } static std::string remove_dot(const std::string& ext) @@ -187,19 +160,6 @@ public: } return ext.substr(found); } - -private: - void linkto(const std::string& path) - { - // This method assumes that since mPath (without extension) is - // guaranteed by apr_file_mktemp() to be unique, then (mPath + any - // extension) is also unique. This is likely, though not guaranteed: - // files could be created in the same temp directory other than by - // this class. - ll_apr_assert_status(apr_file_link(mPath.c_str(), path.c_str())); - } - - std::string mLink; }; #endif /* ! defined(LL_NAMEDTEMPFILE_H) */ diff --git a/indra/test/test.cpp b/indra/test/test.cpp index bb48216b2b..9dd33c574d 100644 --- a/indra/test/test.cpp +++ b/indra/test/test.cpp @@ -100,7 +100,7 @@ public: RecordToTempFile(apr_pool_t* pPool) : LLError::Recorder(), boost::noncopyable(), - mTempFile("log", "", pPool), + mTempFile("log", ""), mFile(mTempFile.getName().c_str()) { } -- cgit v1.2.3 From 48759438b747c24b536cd099d65ab0c9ea720a38 Mon Sep 17 00:00:00 2001 From: Nat Goodspeed Date: Thu, 17 Aug 2023 09:53:01 -0400 Subject: DRTVWR-588: Remove Boost Phoenix, Bind and Assign from some tests. llsdserialize_test used Boost.Foreach, Boost.Function and Boost.Bind. llleap_test used Boost.Assign. Both used Boost.Phoenix. Replace Boost.Foreach with range 'for'. Replace Boost.Function with std::function. Replace Boost.Assign with initializer lists. Replace Boost.Bind and Boost.Phoenix with lambdas. --- indra/llcommon/tests/llleap_test.cpp | 251 ++++++++++++++-------------- indra/llcommon/tests/llsdserialize_test.cpp | 39 ++--- 2 files changed, 134 insertions(+), 156 deletions(-) diff --git a/indra/llcommon/tests/llleap_test.cpp b/indra/llcommon/tests/llleap_test.cpp index 7ee36a9ea6..671873e982 100644 --- a/indra/llcommon/tests/llleap_test.cpp +++ b/indra/llcommon/tests/llleap_test.cpp @@ -17,8 +17,6 @@ // std headers #include // external library headers -#include -#include // other Linden headers #include "../test/lltut.h" #include "../test/namedtempfile.h" @@ -30,10 +28,6 @@ #include "stringize.h" #include "StringVec.h" -using boost::assign::list_of; - -StringVec sv(const StringVec& listof) { return listof; } - #if defined(LL_WINDOWS) #define sleep(secs) _sleep((secs) * 1000) @@ -104,7 +98,7 @@ namespace tut llleap_data(): reader(".py", // This logic is adapted from vita.viewerclient.receiveEvent() - boost::phoenix::placeholders::arg1 << + [](std::ostream& out){ out << "import re\n" "import os\n" "import sys\n" @@ -188,7 +182,7 @@ namespace tut "def request(pump, data):\n" " # we expect 'data' is a dict\n" " data['reply'] = _reply\n" - " send(pump, data)\n"), + " send(pump, data)\n";}), // Get the actual pathname of the NamedExtTempFile and trim off // the ".py" extension. (We could cache reader.getName() in a // separate member variable, but I happen to know getName() just @@ -213,14 +207,14 @@ namespace tut void object::test<1>() { set_test_name("multiple LLLeap instances"); - NamedTempFile script("py", - "import time\n" - "time.sleep(1)\n"); + NamedExtTempFile script("py", + "import time\n" + "time.sleep(1)\n"); LLLeapVector instances; instances.push_back(LLLeap::create(get_test_name(), - sv(list_of(PYTHON)(script.getName())))->getWeak()); + StringVec{PYTHON, script.getName()})->getWeak()); instances.push_back(LLLeap::create(get_test_name(), - sv(list_of(PYTHON)(script.getName())))->getWeak()); + StringVec{PYTHON, script.getName()})->getWeak()); // In this case we're simply establishing that two LLLeap instances // can coexist without throwing exceptions or bombing in any other // way. Wait for them to terminate. @@ -231,10 +225,10 @@ namespace tut void object::test<2>() { set_test_name("stderr to log"); - NamedTempFile script("py", - "import sys\n" - "sys.stderr.write('''Hello from Python!\n" - "note partial line''')\n"); + NamedExtTempFile script("py", + "import sys\n" + "sys.stderr.write('''Hello from Python!\n" + "note partial line''')\n"); StringVec vcommand{ PYTHON, script.getName() }; CaptureLog log(LLError::LEVEL_INFO); waitfor(LLLeap::create(get_test_name(), vcommand)); @@ -246,11 +240,11 @@ namespace tut void object::test<3>() { set_test_name("bad stdout protocol"); - NamedTempFile script("py", - "print('Hello from Python!')\n"); + NamedExtTempFile script("py", + "print('Hello from Python!')\n"); CaptureLog log(LLError::LEVEL_WARN); waitfor(LLLeap::create(get_test_name(), - sv(list_of(PYTHON)(script.getName())))); + StringVec{PYTHON, script.getName()})); ensure_contains("error log line", log.messageWith("invalid protocol"), "Hello from Python!"); } @@ -259,13 +253,13 @@ namespace tut void object::test<4>() { set_test_name("leftover stdout"); - NamedTempFile script("py", - "import sys\n" - // note lack of newline - "sys.stdout.write('Hello from Python!')\n"); + NamedExtTempFile script("py", + "import sys\n" + // note lack of newline + "sys.stdout.write('Hello from Python!')\n"); CaptureLog log(LLError::LEVEL_WARN); waitfor(LLLeap::create(get_test_name(), - sv(list_of(PYTHON)(script.getName())))); + StringVec{PYTHON, script.getName()})); ensure_contains("error log line", log.messageWith("Discarding"), "Hello from Python!"); } @@ -274,12 +268,12 @@ namespace tut void object::test<5>() { set_test_name("bad stdout len prefix"); - NamedTempFile script("py", - "import sys\n" - "sys.stdout.write('5a2:something')\n"); + NamedExtTempFile script("py", + "import sys\n" + "sys.stdout.write('5a2:something')\n"); CaptureLog log(LLError::LEVEL_WARN); waitfor(LLLeap::create(get_test_name(), - sv(list_of(PYTHON)(script.getName())))); + StringVec{PYTHON, script.getName()})); ensure_contains("error log line", log.messageWith("invalid protocol"), "5a2:"); } @@ -381,17 +375,17 @@ namespace tut set_test_name("round trip"); AckAPI api; Result result; - NamedTempFile script("py", - boost::phoenix::placeholders::arg1 << - "from " << reader_module << " import *\n" - // make a request on our little API - "request(pump='" << api.getName() << "', data={})\n" - // wait for its response - "resp = get()\n" - "result = '' if resp == dict(pump=replypump(), data='ack')\\\n" - " else 'bad: ' + str(resp)\n" - "send(pump='" << result.getName() << "', data=result)\n"); - waitfor(LLLeap::create(get_test_name(), sv(list_of(PYTHON)(script.getName())))); + NamedExtTempFile script("py", + [&](std::ostream& out){ out << + "from " << reader_module << " import *\n" + // make a request on our little API + "request(pump='" << api.getName() << "', data={})\n" + // wait for its response + "resp = get()\n" + "result = '' if resp == dict(pump=replypump(), data='ack')\\\n" + " else 'bad: ' + str(resp)\n" + "send(pump='" << result.getName() << "', data=result)\n";}); + waitfor(LLLeap::create(get_test_name(), StringVec{PYTHON, script.getName()})); result.ensure(); } @@ -419,38 +413,38 @@ namespace tut // iterations etc. in OS pipes and the LLLeap/LLProcess implementation. ReqIDAPI api; Result result; - NamedTempFile script("py", - boost::phoenix::placeholders::arg1 << - "import sys\n" - "from " << reader_module << " import *\n" - // Note that since reader imports llsd, this - // 'import *' gets us llsd too. - "sample = llsd.format_notation(dict(pump='" << - api.getName() << "', data=dict(reqid=999999, reply=replypump())))\n" - // The whole packet has length prefix too: "len:data" - "samplen = len(str(len(sample))) + 1 + len(sample)\n" - // guess how many messages it will take to - // accumulate BUFFERED_LENGTH - "count = int(" << BUFFERED_LENGTH << "/samplen)\n" - "print('Sending %s requests' % count, file=sys.stderr)\n" - "for i in range(count):\n" - " request('" << api.getName() << "', dict(reqid=i))\n" - // The assumption in this specific test that - // replies will arrive in the same order as - // requests is ONLY valid because the API we're - // invoking sends replies instantly. If the API - // had to wait for some external event before - // sending its reply, replies could arrive in - // arbitrary order, and we'd have to tick them - // off from a set. - "result = ''\n" - "for i in range(count):\n" - " resp = get()\n" - " if resp['data']['reqid'] != i:\n" - " result = 'expected reqid=%s in %s' % (i, resp)\n" - " break\n" - "send(pump='" << result.getName() << "', data=result)\n"); - waitfor(LLLeap::create(get_test_name(), sv(list_of(PYTHON)(script.getName()))), + NamedExtTempFile script("py", + [&](std::ostream& out){ out << + "import sys\n" + "from " << reader_module << " import *\n" + // Note that since reader imports llsd, this + // 'import *' gets us llsd too. + "sample = llsd.format_notation(dict(pump='" << + api.getName() << "', data=dict(reqid=999999, reply=replypump())))\n" + // The whole packet has length prefix too: "len:data" + "samplen = len(str(len(sample))) + 1 + len(sample)\n" + // guess how many messages it will take to + // accumulate BUFFERED_LENGTH + "count = int(" << BUFFERED_LENGTH << "/samplen)\n" + "print('Sending %s requests' % count, file=sys.stderr)\n" + "for i in range(count):\n" + " request('" << api.getName() << "', dict(reqid=i))\n" + // The assumption in this specific test that + // replies will arrive in the same order as + // requests is ONLY valid because the API we're + // invoking sends replies instantly. If the API + // had to wait for some external event before + // sending its reply, replies could arrive in + // arbitrary order, and we'd have to tick them + // off from a set. + "result = ''\n" + "for i in range(count):\n" + " resp = get()\n" + " if resp['data']['reqid'] != i:\n" + " result = 'expected reqid=%s in %s' % (i, resp)\n" + " break\n" + "send(pump='" << result.getName() << "', data=result)\n";}); + waitfor(LLLeap::create(get_test_name(), StringVec{PYTHON, script.getName()}), 300); // needs more realtime than most tests result.ensure(); } @@ -462,65 +456,62 @@ namespace tut { ReqIDAPI api; Result result; - NamedTempFile script("py", - boost::phoenix::placeholders::arg1 << - "import sys\n" - "from " << reader_module << " import *\n" - // Generate a very large string value. - "desired = int(sys.argv[1])\n" - // 7 chars per item: 6 digits, 1 comma - "count = int((desired - 50)/7)\n" - "large = ''.join('%06d,' % i for i in range(count))\n" - // Pass 'large' as reqid because we know the API - // will echo reqid, and we want to receive it back. - "request('" << api.getName() << "', dict(reqid=large))\n" - "try:\n" - " resp = get()\n" - "except ParseError as e:\n" - " # try to find where e.data diverges from expectation\n" - // Normally we'd expect a 'pump' key in there, - // too, with value replypump(). But Python - // serializes keys in a different order than C++, - // so incoming data start with 'data'. - // Truthfully, though, if we get as far as 'pump' - // before we find a difference, something's very - // strange. - " expect = llsd.format_notation(dict(data=dict(reqid=large)))\n" - " chunk = 40\n" - " for offset in range(0, max(len(e.data), len(expect)), chunk):\n" - " if e.data[offset:offset+chunk] != \\\n" - " expect[offset:offset+chunk]:\n" - " print('Offset %06d: expect %r,\\n'\\\n" - " ' get %r' %\\\n" - " (offset,\n" - " expect[offset:offset+chunk],\n" - " e.data[offset:offset+chunk]),\n" - " file=sys.stderr)\n" - " break\n" - " else:\n" - " print('incoming data matches expect?!', file=sys.stderr)\n" - " send('" << result.getName() << "', '%s: %s' % (e.__class__.__name__, e))\n" - " sys.exit(1)\n" - "\n" - "echoed = resp['data']['reqid']\n" - "if echoed == large:\n" - " send('" << result.getName() << "', '')\n" - " sys.exit(0)\n" - // Here we know echoed did NOT match; try to find where - "for i in range(count):\n" - " start = 7*i\n" - " end = 7*(i+1)\n" - " if end > len(echoed)\\\n" - " or echoed[start:end] != large[start:end]:\n" - " send('" << result.getName() << "',\n" - " 'at offset %s, expected %r but got %r' %\n" - " (start, large[start:end], echoed[start:end]))\n" - "sys.exit(1)\n"); + NamedExtTempFile script("py", + [&](std::ostream& out){ out << + "import sys\n" + "from " << reader_module << " import *\n" + // Generate a very large string value. + "desired = int(sys.argv[1])\n" + // 7 chars per item: 6 digits, 1 comma + "count = int((desired - 50)/7)\n" + "large = ''.join('%06d,' % i for i in range(count))\n" + // Pass 'large' as reqid because we know the API + // will echo reqid, and we want to receive it back. + "request('" << api.getName() << "', dict(reqid=large))\n" + "try:\n" + " resp = get()\n" + "except ParseError as e:\n" + " # try to find where e.data diverges from expectation\n" + // Normally we'd expect a 'pump' key in there, + // too, with value replypump(). But Python + // serializes keys in a different order than C++, + // so incoming data start with 'data'. + // Truthfully, though, if we get as far as 'pump' + // before we find a difference, something's very + // strange. + " expect = llsd.format_notation(dict(data=dict(reqid=large)))\n" + " chunk = 40\n" + " for offset in range(0, max(len(e.data), len(expect)), chunk):\n" + " if e.data[offset:offset+chunk] != \\\n" + " expect[offset:offset+chunk]:\n" + " print('Offset %06d: expect %r,\\n'\\\n" + " ' get %r' %\\\n" + " (offset,\n" + " expect[offset:offset+chunk],\n" + " e.data[offset:offset+chunk]),\n" + " file=sys.stderr)\n" + " break\n" + " else:\n" + " print('incoming data matches expect?!', file=sys.stderr)\n" + " send('" << result.getName() << "', '%s: %s' % (e.__class__.__name__, e))\n" + " sys.exit(1)\n" + "\n" + "echoed = resp['data']['reqid']\n" + "if echoed == large:\n" + " send('" << result.getName() << "', '')\n" + " sys.exit(0)\n" + // Here we know echoed did NOT match; try to find where + "for i in range(count):\n" + " start = 7*i\n" + " end = 7*(i+1)\n" + " if end > len(echoed)\\\n" + " or echoed[start:end] != large[start:end]:\n" + " send('" << result.getName() << "',\n" + " 'at offset %s, expected %r but got %r' %\n" + " (start, large[start:end], echoed[start:end]))\n" + "sys.exit(1)\n";}); waitfor(LLLeap::create(test_name, - sv(list_of - (PYTHON) - (script.getName()) - (stringize(size)))), + StringVec{PYTHON, script.getName(), stringize(size)}), 180); // try a longer timeout result.ensure(); } diff --git a/indra/llcommon/tests/llsdserialize_test.cpp b/indra/llcommon/tests/llsdserialize_test.cpp index 5dbcf4c9b8..f46682e2d7 100644 --- a/indra/llcommon/tests/llsdserialize_test.cpp +++ b/indra/llcommon/tests/llsdserialize_test.cpp @@ -44,18 +44,10 @@ typedef U32 uint32_t; #include "llstring.h" #endif -#include "boost/range.hpp" -#include "boost/foreach.hpp" -#include "boost/function.hpp" -#include "boost/bind.hpp" -#include "boost/phoenix/bind/bind_function.hpp" -#include "boost/phoenix/core/argument.hpp" -using namespace boost::phoenix; - -#include "../llsd.h" -#include "../llsdserialize.h" +#include "llsd.h" +#include "llsdserialize.h" #include "llsdutil.h" -#include "../llformat.h" +#include "llformat.h" #include "../test/lltut.h" #include "../test/namedtempfile.h" @@ -1697,13 +1689,6 @@ namespace tut struct TestPythonCompatible { TestPythonCompatible(): - // Note the peculiar insertion of __FILE__ into this string. Since - // this script is being written into a platform-dependent temp - // directory, we can't locate indra/lib/python relative to - // Python's __file__. Use __FILE__ instead, navigating relative - // to this C++ source file. Use Python raw-string syntax so - // Windows pathname backslashes won't mislead Python's string - // scanner. import_llsd("import os.path\n" "import sys\n" "from llbase import llsd\n") @@ -1801,7 +1786,7 @@ namespace tut // helper for test<3> static void writeLLSDArray(std::ostream& out, const LLSD& array) { - BOOST_FOREACH(LLSD item, llsd::inArray(array)) + for (LLSD item: llsd::inArray(array)) { LLSDSerialize::toNotation(item, out); // It's important to separate with newlines because Python's llsd @@ -1841,21 +1826,22 @@ namespace tut // Create an llsdXXXXXX file containing 'data' serialized to // notation. NamedTempFile file("llsd", - // NamedTempFile's boost::function constructor + // NamedTempFile's std::function constructor // takes a callable. To this callable it passes the // std::ostream with which it's writing the // NamedTempFile. - boost::bind(writeLLSDArray, _1, cdata)); + [cdata](std::ostream& out){ writeLLSDArray(out, cdata); }); python("read C++ notation", - placeholders::arg1 << + [this, pydata, &file](std::ostream& out) { + out << import_llsd << "def parse_each(iterable):\n" " for item in iterable:\n" " yield llsd.parse(item)\n" << pydata << // Don't forget raw-string syntax for Windows pathnames. - "verify(parse_each(open(r'" << file.getName() << "', 'rb')))\n"); + "verify(parse_each(open(r'" << file.getName() << "', 'rb')))\n"; }); } template<> template<> @@ -1869,7 +1855,8 @@ namespace tut NamedTempFile file("llsd", ""); python("write Python notation", - placeholders::arg1 << + [this, &file](std::ostream& out) { + out << import_llsd << "DATA = [\n" " 17,\n" @@ -1881,9 +1868,9 @@ namespace tut "]\n" // Don't forget raw-string syntax for Windows pathnames. // N.B. Using 'print' implicitly adds newlines. - "with open(r'" << file.getName() << "', 'w') as f:\n" + "with open(r'" << file.getName() << "', 'wb') as f:\n" " for item in DATA:\n" - " print(llsd.format_notation(item).decode(), file=f)\n"); + " print(llsd.format_notation(item), file=f)\n"; }); std::ifstream inf(file.getName().c_str()); LLSD item; -- cgit v1.2.3 From 3fbb1a496dd4aaadc11727832f3ab0cc8c64950f Mon Sep 17 00:00:00 2001 From: Nat Goodspeed Date: Thu, 17 Aug 2023 10:03:16 -0400 Subject: DRTVWR-588: Remove some unused redundant timer functionality. LLEventTimer supported static run_every(), run_at() and run_after() methods to schedule future work. This can still be done by deriving from LLEventTimer, but is better accomplished with a WorkSchedule instance. These convenience methods, which encourage use of LLEventTimer insted of WorkSchedule, weren't used except by LLEventTimeout. Remove them and the LLEventTimer::Generic subclass used to implement them. Similarly, LLEventTimeout supported static post_every(), post_at() and post_after() methods based on LLEventTimer::run_every(), run_at() and run_after(). These weren't used either. LLRunner is a very old mechanism to schedule future work that seems to be unused. Research suggests that it's indirectly engaged only by LLDeferredChain, which isn't used. LLIOSleeper is tested but isn't otherwise used. Add a deprecation warning to llrun.h prior to excision. Also replace Boost.Bind with lambdas. --- indra/llcommon/lleventfilter.cpp | 34 ++++---------------- indra/llcommon/lleventfilter.h | 13 -------- indra/llcommon/lleventtimer.h | 69 +--------------------------------------- indra/llcommon/llrun.h | 11 +++++++ 4 files changed, 18 insertions(+), 109 deletions(-) diff --git a/indra/llcommon/lleventfilter.cpp b/indra/llcommon/lleventfilter.cpp index 4cded7f88e..14c9c51830 100644 --- a/indra/llcommon/lleventfilter.cpp +++ b/indra/llcommon/lleventfilter.cpp @@ -33,20 +33,19 @@ // STL headers // std headers // external library headers -#include // other Linden headers +#include "lldate.h" #include "llerror.h" // LL_ERRS +#include "lleventtimer.h" #include "llsdutil.h" // llsd_matches() #include "stringize.h" -#include "lleventtimer.h" -#include "lldate.h" /***************************************************************************** * LLEventFilter *****************************************************************************/ LLEventFilter::LLEventFilter(LLEventPump& source, const std::string& name, bool tweak): LLEventStream(name, tweak), - mSource(source.listen(getName(), boost::bind(&LLEventFilter::post, this, _1))) + mSource(source.listen(getName(), [this](const LLSD& event){ return post(event); })) { } @@ -93,7 +92,7 @@ void LLEventTimeoutBase::actionAfter(F32 seconds, const Action& action) if (! mMainloop.connected()) { LLEventPump& mainloop(LLEventPumps::instance().obtain("mainloop")); - mMainloop = mainloop.listen(getName(), boost::bind(&LLEventTimeoutBase::tick, this, _1)); + mMainloop = mainloop.listen(getName(), [this](const LLSD& event){ return tick(event); }); } } @@ -185,27 +184,6 @@ bool LLEventTimeout::countdownElapsed() const return mTimer.hasExpired(); } -LLEventTimer* LLEventTimeout::post_every(F32 period, const std::string& pump, const LLSD& data) -{ - return LLEventTimer::run_every( - period, - [pump, data](){ LLEventPumps::instance().obtain(pump).post(data); }); -} - -LLEventTimer* LLEventTimeout::post_at(const LLDate& time, const std::string& pump, const LLSD& data) -{ - return LLEventTimer::run_at( - time, - [pump, data](){ LLEventPumps::instance().obtain(pump).post(data); }); -} - -LLEventTimer* LLEventTimeout::post_after(F32 interval, const std::string& pump, const LLSD& data) -{ - return LLEventTimer::run_after( - interval, - [pump, data](){ LLEventPumps::instance().obtain(pump).post(data); }); -} - /***************************************************************************** * LLEventBatch *****************************************************************************/ @@ -311,7 +289,7 @@ bool LLEventThrottleBase::post(const LLSD& event) // timeRemaining tells us how much longer it will be until // mInterval seconds since the last flush() call. At that time, // flush() deferred events. - alarmActionAfter(timeRemaining, boost::bind(&LLEventThrottleBase::flush, this)); + alarmActionAfter(timeRemaining, [this](){ flush(); }); } } return false; @@ -349,7 +327,7 @@ void LLEventThrottleBase::setInterval(F32 interval) // and if mAlarm is running, reset that too if (alarmRunning()) { - alarmActionAfter(timeRemaining, boost::bind(&LLEventThrottleBase::flush, this)); + alarmActionAfter(timeRemaining, [this](){ flush(); }); } } } diff --git a/indra/llcommon/lleventfilter.h b/indra/llcommon/lleventfilter.h index 7613850fb2..437a4826d4 100644 --- a/indra/llcommon/lleventfilter.h +++ b/indra/llcommon/lleventfilter.h @@ -214,19 +214,6 @@ public: LLEventTimeout(); LLEventTimeout(LLEventPump& source); - /// using LLEventTimeout as namespace for free functions - /// Post event to specified LLEventPump every period seconds. Delete - /// returned LLEventTimer* to cancel. - static LLEventTimer* post_every(F32 period, const std::string& pump, const LLSD& data); - /// Post event to specified LLEventPump at specified future time. Call - /// LLEventTimer::getInstance(returned pointer) to check whether it's still - /// pending; if so, delete the pointer to cancel. - static LLEventTimer* post_at(const LLDate& time, const std::string& pump, const LLSD& data); - /// Post event to specified LLEventPump after specified interval. Call - /// LLEventTimer::getInstance(returned pointer) to check whether it's still - /// pending; if so, delete the pointer to cancel. - static LLEventTimer* post_after(F32 interval, const std::string& pump, const LLSD& data); - protected: virtual void setCountdown(F32 seconds); virtual bool countdownElapsed() const; diff --git a/indra/llcommon/lleventtimer.h b/indra/llcommon/lleventtimer.h index dbbfe0c6e6..b5d40a0622 100644 --- a/indra/llcommon/lleventtimer.h +++ b/indra/llcommon/lleventtimer.h @@ -24,7 +24,7 @@ * $/LicenseInfo$ */ -#ifndef LL_EVENTTIMER_H +#ifndef LL_EVENTTIMER_H #define LL_EVENTTIMER_H #include "stdtypes.h" @@ -47,76 +47,9 @@ public: static void updateClass(); - /// Schedule recurring calls to generic callable every period seconds. - /// Returns a pointer; if you delete it, cancels the recurring calls. - template - static LLEventTimer* run_every(F32 period, const CALLABLE& callable); - - /// Schedule a future call to generic callable. Returns a pointer. - /// CAUTION: The object referenced by that pointer WILL BE DELETED once - /// the callback has been called! LLEventTimer::getInstance(pointer) (NOT - /// pointer->getInstance(pointer)!) can be used to test whether the - /// pointer is still valid. If it is, deleting it will cancel the - /// callback. - template - static LLEventTimer* run_at(const LLDate& time, const CALLABLE& callable); - - /// Like run_at(), but after a time delta rather than at a timestamp. - /// Same CAUTION. - template - static LLEventTimer* run_after(F32 interval, const CALLABLE& callable); - protected: LLTimer mEventTimer; F32 mPeriod; - -private: - template - class Generic; }; -template -class LLEventTimer::Generic: public LLEventTimer -{ -public: - // making TIME generic allows engaging either LLEventTimer constructor - template - Generic(const TIME& time, bool once, const CALLABLE& callable): - LLEventTimer(time), - mOnce(once), - mCallable(callable) - {} - BOOL tick() override - { - mCallable(); - // true tells updateClass() to delete this instance - return mOnce; - } - -private: - bool mOnce; - CALLABLE mCallable; -}; - -template -LLEventTimer* LLEventTimer::run_every(F32 period, const CALLABLE& callable) -{ - // return false to schedule recurring calls - return new Generic(period, false, callable); -} - -template -LLEventTimer* LLEventTimer::run_at(const LLDate& time, const CALLABLE& callable) -{ - // return true for one-shot callback - return new Generic(time, true, callable); -} - -template -LLEventTimer* LLEventTimer::run_after(F32 interval, const CALLABLE& callable) -{ - // one-shot callback after specified interval - return new Generic(interval, true, callable); -} - #endif //LL_EVENTTIMER_H diff --git a/indra/llcommon/llrun.h b/indra/llcommon/llrun.h index d610f86234..ebad5f3eaa 100644 --- a/indra/llcommon/llrun.h +++ b/indra/llcommon/llrun.h @@ -34,6 +34,17 @@ class LLRunnable; +////////////////////////////////////////////////////////////////////////////// +// DEPRECATION WARNING +// LLRunner is one of several mostly redundant ways to schedule future +// callbacks on the main thread. It seems to be unused in the current viewer. +// addRunner() is only called by LLPumpIO::sleepChain(). +// sleepChain() is only called by LLIOSleeper and LLIOSleep. +// LLIOSleeper is referenced only by tests. +// LLIOSleep is only called by LLDeferredChain. +// LLDeferredChain isn't referenced at all. +////////////////////////////////////////////////////////////////////////////// + /** * @class LLRunner * @brief This class manages a set of LLRunnable objects. -- cgit v1.2.3 From f24172d23d900bd6f9d10bb648107bbf4a755aaf Mon Sep 17 00:00:00 2001 From: Nat Goodspeed Date: Thu, 17 Aug 2023 10:34:40 -0400 Subject: DRTVWR-588: Correct typo in deprecation warning. --- indra/llcommon/llrun.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/indra/llcommon/llrun.h b/indra/llcommon/llrun.h index ebad5f3eaa..27e4a43a8d 100644 --- a/indra/llcommon/llrun.h +++ b/indra/llcommon/llrun.h @@ -38,7 +38,7 @@ class LLRunnable; // DEPRECATION WARNING // LLRunner is one of several mostly redundant ways to schedule future // callbacks on the main thread. It seems to be unused in the current viewer. -// addRunner() is only called by LLPumpIO::sleepChain(). +// addRunnable() is only called by LLPumpIO::sleepChain(). // sleepChain() is only called by LLIOSleeper and LLIOSleep. // LLIOSleeper is referenced only by tests. // LLIOSleep is only called by LLDeferredChain. -- cgit v1.2.3 From 931a2fd63de2de417adbe3e02d55af7a459bca36 Mon Sep 17 00:00:00 2001 From: Nat Goodspeed Date: Thu, 17 Aug 2023 11:23:02 -0400 Subject: DRTVWR-588: print(file=) to binary file still requires str argument. Use f.writelines((bytes, b'\n')) instead. --- indra/llcommon/tests/llsdserialize_test.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/indra/llcommon/tests/llsdserialize_test.cpp b/indra/llcommon/tests/llsdserialize_test.cpp index f46682e2d7..efd7dc9852 100644 --- a/indra/llcommon/tests/llsdserialize_test.cpp +++ b/indra/llcommon/tests/llsdserialize_test.cpp @@ -1867,10 +1867,9 @@ namespace tut "lines.''',\n" "]\n" // Don't forget raw-string syntax for Windows pathnames. - // N.B. Using 'print' implicitly adds newlines. "with open(r'" << file.getName() << "', 'wb') as f:\n" " for item in DATA:\n" - " print(llsd.format_notation(item), file=f)\n"; }); + " f.writelines((llsd.format_notation(item), b'\n'))\n"; }); std::ifstream inf(file.getName().c_str()); LLSD item; -- cgit v1.2.3 From 36ed0e98ea7bd591a798a4373e8aa78a8fca4d14 Mon Sep 17 00:00:00 2001 From: Nat Goodspeed Date: Thu, 17 Aug 2023 11:42:09 -0400 Subject: DRTVWR-588: Try harder to normalize Windows pathames to compare. --- indra/llcommon/tests/llprocess_test.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/indra/llcommon/tests/llprocess_test.cpp b/indra/llcommon/tests/llprocess_test.cpp index c48d913069..56dc2e4fa9 100644 --- a/indra/llcommon/tests/llprocess_test.cpp +++ b/indra/llcommon/tests/llprocess_test.cpp @@ -260,6 +260,7 @@ public: } std::string getName() const { return mPath.string(); } + std::string getNormalName() const { return mPath.lexically_normal().string(); } private: boost::filesystem::path mPath; @@ -591,7 +592,7 @@ namespace tut " f.write(os.path.normcase(os.path.normpath(os.getcwd())))\n"); // Before running, call setWorkingDirectory() py.mParams.cwd = tempdir.getName(); - std::string expected{ tempdir.getName() }; + std::string expected{ tempdir.getNormalName() }; #if LL_WINDOWS // SIGH, don't get tripped up by "C:" != "c:" -- // but on the Mac, using tolower() fails because "/users" != "/Users"! -- cgit v1.2.3 From b5fe9c476943807aa7526f67dd648b5ad250824b Mon Sep 17 00:00:00 2001 From: Nat Goodspeed Date: Thu, 17 Aug 2023 11:45:34 -0400 Subject: DRTVWR-588: To write b'\n' in Python source, use "b'\\n'" --- indra/llcommon/tests/llsdserialize_test.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/indra/llcommon/tests/llsdserialize_test.cpp b/indra/llcommon/tests/llsdserialize_test.cpp index efd7dc9852..bb469f0686 100644 --- a/indra/llcommon/tests/llsdserialize_test.cpp +++ b/indra/llcommon/tests/llsdserialize_test.cpp @@ -1869,7 +1869,7 @@ namespace tut // Don't forget raw-string syntax for Windows pathnames. "with open(r'" << file.getName() << "', 'wb') as f:\n" " for item in DATA:\n" - " f.writelines((llsd.format_notation(item), b'\n'))\n"; }); + " f.writelines((llsd.format_notation(item), b'\\n'))\n"; }); std::ifstream inf(file.getName().c_str()); LLSD item; -- cgit v1.2.3 From dc8f2ae2ba1a1348f86f412df7f769e6cc2fe541 Mon Sep 17 00:00:00 2001 From: Nat Goodspeed Date: Thu, 17 Aug 2023 17:01:56 -0400 Subject: DRTVWR-588: Try even harder to normalize Windows pathnames (SIGHH) --- indra/llcommon/tests/llprocess_test.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/indra/llcommon/tests/llprocess_test.cpp b/indra/llcommon/tests/llprocess_test.cpp index 56dc2e4fa9..ece567f40e 100644 --- a/indra/llcommon/tests/llprocess_test.cpp +++ b/indra/llcommon/tests/llprocess_test.cpp @@ -260,7 +260,7 @@ public: } std::string getName() const { return mPath.string(); } - std::string getNormalName() const { return mPath.lexically_normal().string(); } + std::string getNormalName() const { return mPath.lexically_normal().make_preferred().string(); } private: boost::filesystem::path mPath; -- cgit v1.2.3 From c8711f4ea5d941d12ae7e6cf99302bfc3211dd3c Mon Sep 17 00:00:00 2001 From: Mnikolenko Productengine Date: Tue, 5 Sep 2023 22:22:56 +0300 Subject: Initial prototype of embedded LUA --- autobuild.xml | 42 ++++++ indra/cmake/CMakeLists.txt | 1 + indra/cmake/Copy3rdPartyLibs.cmake | 1 + indra/cmake/Lualibs.cmake | 15 ++ indra/llcommon/CMakeLists.txt | 1 + indra/newview/CMakeLists.txt | 6 + indra/newview/llfilepicker.cpp | 5 + indra/newview/llfilepicker.h | 3 +- indra/newview/llfloaterluadebug.cpp | 92 +++++++++++++ indra/newview/llfloaterluadebug.h | 62 +++++++++ indra/newview/llluamanager.cpp | 152 +++++++++++++++++++++ indra/newview/llluamanager.h | 38 ++++++ indra/newview/llviewerfloaterreg.cpp | 3 + .../skins/default/xui/en/floater_lua_debug.xml | 98 +++++++++++++ indra/newview/skins/default/xui/en/menu_viewer.xml | 11 ++ indra/newview/viewer_manifest.py | 3 + 16 files changed, 532 insertions(+), 1 deletion(-) create mode 100644 indra/cmake/Lualibs.cmake create mode 100644 indra/newview/llfloaterluadebug.cpp create mode 100644 indra/newview/llfloaterluadebug.h create mode 100644 indra/newview/llluamanager.cpp create mode 100644 indra/newview/llluamanager.h create mode 100644 indra/newview/skins/default/xui/en/floater_lua_debug.xml diff --git a/autobuild.xml b/autobuild.xml index 9785884a40..e7c02e930f 100644 --- a/autobuild.xml +++ b/autobuild.xml @@ -1810,6 +1810,48 @@ version 1.0.561752 + lualibs + + copyright + Lua by Lua.org, PUC-Rio. + description + LUA libs API + license + lua + license_file + LICENSES/lua.txt + name + lualibs + platforms + + windows + + archive + + hash + 55bd833166d03f1467e2c7f24fa9143e + url + https://automated-builds-secondlife-com.s3.amazonaws.com/ct2/87775/805841/openssl-1.1.1l.563846-windows-563846.tar.bz2 + + name + windows + + windows64 + + archive + + hash + 6dc51cd8cad422ab1dcd67cc59af119d + url + https://s3-proxy.lindenlab.com/private-builds-secondlife-com/ct2/118146/1013526/lualibs-5.4.581683-windows64-581683.tar.bz2 + + name + windows64 + + + version + 5.4.581683 + mesa license diff --git a/indra/cmake/CMakeLists.txt b/indra/cmake/CMakeLists.txt index f0b35c08f3..af01666875 100644 --- a/indra/cmake/CMakeLists.txt +++ b/indra/cmake/CMakeLists.txt @@ -43,6 +43,7 @@ set(cmake_SOURCE_FILES LLTestCommand.cmake LLWindow.cmake Linking.cmake + Lualibs.cmake Meshoptimizer.cmake NDOF.cmake OPENAL.cmake diff --git a/indra/cmake/Copy3rdPartyLibs.cmake b/indra/cmake/Copy3rdPartyLibs.cmake index d43cc30706..8ae1b5dde5 100644 --- a/indra/cmake/Copy3rdPartyLibs.cmake +++ b/indra/cmake/Copy3rdPartyLibs.cmake @@ -61,6 +61,7 @@ if(WINDOWS) nghttp2.dll libhunspell.dll uriparser.dll + lua54.dll ) # OpenSSL diff --git a/indra/cmake/Lualibs.cmake b/indra/cmake/Lualibs.cmake new file mode 100644 index 0000000000..ec40d0f41c --- /dev/null +++ b/indra/cmake/Lualibs.cmake @@ -0,0 +1,15 @@ +# -*- cmake -*- + +include_guard() + +include(Prebuilt) + +add_library( ll::lualibs INTERFACE IMPORTED ) + +use_system_binary( lualibs ) + +use_prebuilt_binary(lualibs) + +target_link_libraries(ll::lualibs INTERFACE ${lualibs}) + +target_include_directories( ll::lualibs SYSTEM INTERFACE ${LIBS_PREBUILT_DIR}/include/lualibs) diff --git a/indra/llcommon/CMakeLists.txt b/indra/llcommon/CMakeLists.txt index ef4899978e..5dd4321330 100644 --- a/indra/llcommon/CMakeLists.txt +++ b/indra/llcommon/CMakeLists.txt @@ -13,6 +13,7 @@ include(Copy3rdPartyLibs) include(ZLIBNG) include(URIPARSER) include(Tracy) +include(lualibs) set(llcommon_SOURCE_FILES diff --git a/indra/newview/CMakeLists.txt b/indra/newview/CMakeLists.txt index dbd1f1b4ac..ca0b7692c4 100644 --- a/indra/newview/CMakeLists.txt +++ b/indra/newview/CMakeLists.txt @@ -46,6 +46,7 @@ include(VisualLeakDetector) include(ZLIBNG) include(URIPARSER) include(LLPrimitive) +include(lualibs) if (NOT HAVOK_TPV) # When using HAVOK_TPV, the library is precompiled, so no need for this @@ -235,6 +236,7 @@ set(viewer_SOURCE_FILES llfloaterlandholdings.cpp llfloaterlinkreplace.cpp llfloaterloadprefpreset.cpp + llfloaterluadebug.cpp llfloatermarketplacelistings.cpp llfloatermap.cpp llfloatermediasettings.cpp @@ -361,6 +363,7 @@ set(viewer_SOURCE_FILES lllogchat.cpp llloginhandler.cpp lllogininstance.cpp + llluamanager.cpp llmachineid.cpp llmanip.cpp llmaniprotate.cpp @@ -879,6 +882,7 @@ set(viewer_HEADER_FILES llfloaterlandholdings.h llfloaterlinkreplace.h llfloaterloadprefpreset.h + llfloaterluadebug.h llfloatermap.h llfloatermarketplacelistings.h llfloatermediasettings.h @@ -1003,6 +1007,7 @@ set(viewer_HEADER_FILES lllogchat.h llloginhandler.h lllogininstance.h + llluamanager.h llmachineid.h llmanip.h llmaniprotate.h @@ -1698,6 +1703,7 @@ if (WINDOWS) ${SHARED_LIB_STAGING_DIR}/openjp2.dll ${SHARED_LIB_STAGING_DIR}/libhunspell.dll ${SHARED_LIB_STAGING_DIR}/uriparser.dll + ${SHARED_LIB_STAGING_DIR}/lua54.dll #${SHARED_LIB_STAGING_DIR}/${LL_INTDIR}/SLVoice.exe #${SHARED_LIB_STAGING_DIR}/${LL_INTDIR}/libsndfile-1.dll #${SHARED_LIB_STAGING_DIR}/${LL_INTDIR}/vivoxoal.dll diff --git a/indra/newview/llfilepicker.cpp b/indra/newview/llfilepicker.cpp index f1f156c2e0..b9435dc9df 100644 --- a/indra/newview/llfilepicker.cpp +++ b/indra/newview/llfilepicker.cpp @@ -61,6 +61,7 @@ LLFilePicker LLFilePicker::sInstance; #define MODEL_FILTER L"Model files (*.dae)\0*.dae\0" #define SCRIPT_FILTER L"Script files (*.lsl)\0*.lsl\0" #define DICTIONARY_FILTER L"Dictionary files (*.dic; *.xcu)\0*.dic;*.xcu\0" +#define LUA_FILTER L"Script files (*.lua)\0*.lua\0" #endif #ifdef LL_DARWIN @@ -218,6 +219,10 @@ BOOL LLFilePicker::setupFilter(ELoadFilter filter) mOFN.lpstrFilter = DICTIONARY_FILTER \ L"\0"; break; + case FFLOAD_LUA: + mOFN.lpstrFilter = LUA_FILTER \ + L"\0"; + break; default: res = FALSE; break; diff --git a/indra/newview/llfilepicker.h b/indra/newview/llfilepicker.h index 692b908fff..cb453ea87e 100644 --- a/indra/newview/llfilepicker.h +++ b/indra/newview/llfilepicker.h @@ -85,7 +85,8 @@ public: FFLOAD_SCRIPT = 11, FFLOAD_DICTIONARY = 12, FFLOAD_DIRECTORY = 13, // To call from lldirpicker. - FFLOAD_EXE = 14 // Note: EXE will be treated as ALL on Windows and Linux but not on Darwin + FFLOAD_EXE = 14, // Note: EXE will be treated as ALL on Windows and Linux but not on Darwin + FFLOAD_LUA = 15 }; enum ESaveFilter diff --git a/indra/newview/llfloaterluadebug.cpp b/indra/newview/llfloaterluadebug.cpp new file mode 100644 index 0000000000..c8b330f732 --- /dev/null +++ b/indra/newview/llfloaterluadebug.cpp @@ -0,0 +1,92 @@ +/** + * @file llfloaterluadebug.cpp + * + * $LicenseInfo:firstyear=2023&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2023, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#include "llviewerprecompiledheaders.h" + +#include "llfloaterluadebug.h" + +#include "lllineeditor.h" +#include "lltexteditor.h" +#include "llviewermenufile.h" // LLFilePickerReplyThread + +#include "llagent.h" +#include "llappearancemgr.h" +#include "llfloaterreg.h" +#include "llfloaterimnearbychat.h" + +#include "llluamanager.h" + +#if LL_WINDOWS +#pragma comment(lib, "liblua54.a") +#endif + + +LLFloaterLUADebug::LLFloaterLUADebug(const LLSD &key) + : LLFloater(key) +{ +} + + +BOOL LLFloaterLUADebug::postBuild() +{ + mResultOutput = getChild("result_text"); + mLineInput = getChild("lua_cmd"); + mScriptPath = getChild("script_path"); + + getChild("execute_btn")->setClickedCallback(boost::bind(&LLFloaterLUADebug::onExecuteClicked, this)); + getChild("browse_btn")->setClickedCallback(boost::bind(&LLFloaterLUADebug::onBtnBrowse, this)); + +#if !LL_WINDOWS + getChild("execute_btn")->setEnabled(false); + getChild("browse_btn")->setEnabled(false); +#endif + + return TRUE; +} + +LLFloaterLUADebug::~LLFloaterLUADebug() +{} + +void LLFloaterLUADebug::onExecuteClicked() +{ + std::string cmd = mLineInput->getText(); + LLLUAmanager::runScriptLine(cmd); +} + +void LLFloaterLUADebug::onBtnBrowse() +{ + (new LLFilePickerReplyThread(boost::bind(&LLFloaterLUADebug::runSelectedScript, this, _1), LLFilePicker::FFLOAD_LUA, false))->getFile(); +} + +void LLFloaterLUADebug::runSelectedScript(const std::vector &filenames) +{ + std::string filepath = filenames[0]; + if (!filepath.empty()) + { + mScriptPath->setText(filepath); + LLLUAmanager::runScriptFile(filepath); + } +} + diff --git a/indra/newview/llfloaterluadebug.h b/indra/newview/llfloaterluadebug.h new file mode 100644 index 0000000000..a7a3b695d4 --- /dev/null +++ b/indra/newview/llfloaterluadebug.h @@ -0,0 +1,62 @@ +/** + * @file llfloaterluadebug.h + * + * $LicenseInfo:firstyear=2023&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2023, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#ifndef LL_LLFLOATERLUADEBUG_H +#define LL_LLFLOATERLUADEBUG_H + +#include "llfloater.h" + +extern "C" +{ +#include "lua/lua.h" +#include "lua/lauxlib.h" +#include "lua/lualib.h" +} + +class LLLineEditor; +class LLTextEditor; + +class LLFloaterLUADebug : + public LLFloater +{ + public: + LLFloaterLUADebug(const LLSD& key); + virtual ~LLFloaterLUADebug(); + + /*virtual*/ BOOL postBuild(); + + void onExecuteClicked(); + void onBtnBrowse(); + + void runSelectedScript(const std::vector &filenames); + +private: + LLTextEditor* mResultOutput; + LLLineEditor* mLineInput; + LLLineEditor* mScriptPath; +}; + +#endif // LL_LLFLOATERLUADEBUG_H + diff --git a/indra/newview/llluamanager.cpp b/indra/newview/llluamanager.cpp new file mode 100644 index 0000000000..516993c967 --- /dev/null +++ b/indra/newview/llluamanager.cpp @@ -0,0 +1,152 @@ +/** + * @file llluamanager.cpp + * @brief classes and functions for interfacing with LUA. + * + * $LicenseInfo:firstyear=2023&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2023, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + * + */ + +#include "llviewerprecompiledheaders.h" +#include "llluamanager.h" + +#include "llagent.h" +#include "llappearancemgr.h" +#include "llfloaterreg.h" +#include "llfloaterimnearbychat.h" + +extern "C" +{ +#include "lua/lua.h" +#include "lua/lauxlib.h" +#include "lua/lualib.h" +} + +#if LL_WINDOWS +#pragma comment(lib, "liblua54.a") +#endif + +int lua_printWarning(lua_State *L) +{ + std::string msg(lua_tostring(L, 1)); + + LL_WARNS() << msg << LL_ENDL; + return 1; +} + +int lua_avatar_sit(lua_State *L) +{ + gAgent.sitDown(); + return 1; +} + +int lua_avatar_stand(lua_State *L) +{ + gAgent.standUp(); + return 1; +} + +int lua_nearby_chat_send(lua_State *L) +{ + std::string msg(lua_tostring(L, 1)); + LLFloaterIMNearbyChat *nearby_chat = LLFloaterReg::findTypedInstance("nearby_chat"); + nearby_chat->sendChatFromViewer(msg, CHAT_TYPE_NORMAL, gSavedSettings.getBOOL("PlayChatAnim")); + + return 1; +} + +int lua_wear_by_name(lua_State *L) +{ + std::string folder_name(lua_tostring(L, 1)); + LLAppearanceMgr::instance().wearOutfitByName(folder_name); + + return 1; +} + +bool checkLua(lua_State *L, int r) +{ + if (r != LUA_OK) + { + std::string lua_error = lua_tostring(L, -1); + + LL_WARNS() << lua_error << LL_ENDL; + return false; + } + return true; +} + +void initLUA(lua_State *L) +{ + lua_register(L, "lua_printWarning", lua_printWarning); + + lua_register(L, "lua_avatar_sit", lua_avatar_sit); + lua_register(L, "lua_avatar_stand", lua_avatar_stand); + + lua_register(L, "lua_nearby_chat_send", lua_nearby_chat_send); + lua_register(L, "lua_wear_by_name", lua_wear_by_name); +} + +void LLLUAmanager::runScriptFile(const std::string &filename) +{ + LLCoros::instance().launch("LUAScriptFileCoro", [filename]() + { + lua_State *L = luaL_newstate(); + luaL_openlibs(L); + initLUA(L); + + auto LUA_sleep_func = [](lua_State *L) + { + F32 seconds = lua_tonumber(L, -1); + llcoro::suspendUntilTimeout(seconds); + return 0; + }; + + lua_register(L, "sleep", LUA_sleep_func); + + if (checkLua(L, luaL_dofile(L, filename.c_str()))) + { + lua_getglobal(L, "call_once_func"); + if (lua_isfunction(L, -1)) + { + if (checkLua(L, lua_pcall(L, 0, 0, 0))) {} + } + } + + lua_close(L); + }); +} + +void LLLUAmanager::runScriptLine(const std::string &cmd) +{ + LLCoros::instance().launch("LUAScriptFileCoro", [cmd]() + { + lua_State *L = luaL_newstate(); + luaL_openlibs(L); + initLUA(L); + int r = luaL_dostring(L, cmd.c_str()); + if (r != LUA_OK) + { + std::string lua_error = lua_tostring(L, -1); + LL_WARNS() << lua_error << LL_ENDL; + } + lua_close(L); + }); +} diff --git a/indra/newview/llluamanager.h b/indra/newview/llluamanager.h new file mode 100644 index 0000000000..d31f159480 --- /dev/null +++ b/indra/newview/llluamanager.h @@ -0,0 +1,38 @@ +/** + * @file llluamanager.h + * @brief classes and functions for interfacing with LUA. + * + * $LicenseInfo:firstyear=2023&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2023, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#ifndef LL_LLLUAMANAGER_H +#define LL_LLLUAMANAGER_H + +class LLLUAmanager +{ +public: + static void runScriptFile(const std::string &filename); + static void runScriptLine(const std::string &cmd); +}; + + +#endif diff --git a/indra/newview/llviewerfloaterreg.cpp b/indra/newview/llviewerfloaterreg.cpp index 0f2fe1e1cd..d00ccd05a0 100644 --- a/indra/newview/llviewerfloaterreg.cpp +++ b/indra/newview/llviewerfloaterreg.cpp @@ -90,6 +90,7 @@ #include "llfloaterlandholdings.h" #include "llfloaterlinkreplace.h" #include "llfloaterloadprefpreset.h" +#include "llfloaterluadebug.h" #include "llfloatermap.h" #include "llfloatermarketplacelistings.h" #include "llfloatermediasettings.h" @@ -385,6 +386,8 @@ void LLViewerFloaterReg::registerFloaters() LLFloaterReg::add("land_holdings", "floater_land_holdings.xml", (LLFloaterBuildFunc)&LLFloaterReg::build); LLFloaterReg::add("linkreplace", "floater_linkreplace.xml", (LLFloaterBuildFunc)&LLFloaterReg::build); LLFloaterReg::add("load_pref_preset", "floater_load_pref_preset.xml", (LLFloaterBuildFunc)&LLFloaterReg::build); + + LLFloaterReg::add("lua_debug", "floater_lua_debug.xml", (LLFloaterBuildFunc) &LLFloaterReg::build); LLFloaterReg::add("mem_leaking", "floater_mem_leaking.xml", (LLFloaterBuildFunc)&LLFloaterReg::build); diff --git a/indra/newview/skins/default/xui/en/floater_lua_debug.xml b/indra/newview/skins/default/xui/en/floater_lua_debug.xml new file mode 100644 index 0000000000..7ed6c35151 --- /dev/null +++ b/indra/newview/skins/default/xui/en/floater_lua_debug.xml @@ -0,0 +1,98 @@ + + + + LUA string: + + + + + Select title + + + + + + + + Double click me + + + diff --git a/indra/newview/scripts/lua/luafloater_gesture_list.xml b/indra/newview/scripts/lua/luafloater_gesture_list.xml new file mode 100644 index 0000000000..a38a04eed0 --- /dev/null +++ b/indra/newview/scripts/lua/luafloater_gesture_list.xml @@ -0,0 +1,21 @@ + + + + + + diff --git a/indra/newview/scripts/lua/test_luafloater_demo.lua b/indra/newview/scripts/lua/test_luafloater_demo.lua new file mode 100644 index 0000000000..a7bbdccb30 --- /dev/null +++ b/indra/newview/scripts/lua/test_luafloater_demo.lua @@ -0,0 +1,69 @@ +XML_FILE_PATH = "luafloater_demo.xml" + +leap = require 'leap' +coro = require 'coro' + +--event pump for sending actions to the floater +COMMAND_PUMP_NAME = "" +--table of floater UI events +e={} +coro.launch(function () + e = leap.request("LLFloaterReg", {op="getFloaterEvents"})["events"] + leap.done() +end) +leap.process() + +function post(action) + leap.send(COMMAND_PUMP_NAME, action) +end + +function getCurrentTime() + local currentTime = os.date("*t") + return string.format("%02d:%02d:%02d", currentTime.hour, currentTime.min, currentTime.sec) +end + +function handleEvents(event_data) + if event_data.event == e.COMMIT_EVENT then + if event_data.ctrl_name == "disable_ctrl" then + post({action="set_enabled", ctrl_name="open_btn", value = (1 - event_data.value)}) + elseif event_data.ctrl_name == "title_cmb" then + post({action="set_title", value= event_data.value}) + elseif event_data.ctrl_name == "open_btn" then + floater_name = leap.request(COMMAND_PUMP_NAME, {action="get_value", ctrl_name='openfloater_cmd'})['value'] + leap.send("LLFloaterReg", {name = floater_name, op = "showInstance"}) + end + elseif event_data.event == e.DOUBLE_CLICK_EVENT then + if event_data.ctrl_name == "show_time_lbl" then + post({action="set_value", ctrl_name="time_lbl", value= getCurrentTime()}) + end + elseif event_data.event == e.CLOSE_EVENT then + print_warning("Floater was closed") + leap.done() + --script received event pump name, after floater was built + elseif event_data.event == e.POST_BUILD_EVENT then + COMMAND_PUMP_NAME = event_data.command_name + end +end + +catch_events = leap.WaitFor:new(-1, "all_events") +function catch_events:filter(pump, data) + return data +end + +function process_events(waitfor) + event_data = waitfor:wait() + while event_data do + handleEvents(event_data) + event_data = waitfor:wait() + end +end + +local key = {xml_path = XML_FILE_PATH, op = "showLuaFloater"} + +--sign for additional events for defined control {= {action1, action2, ...}} +key.extra_events={show_time_lbl = {e.RIGHT_MOUSE_DOWN_EVENT, e.DOUBLE_CLICK_EVENT}} +leap.send("LLFloaterReg", key) + +coro.launch(process_events, catch_events) +leap.process() +print_warning("End of the script") diff --git a/indra/newview/scripts/lua/test_luafloater_gesture_list.lua b/indra/newview/scripts/lua/test_luafloater_gesture_list.lua new file mode 100644 index 0000000000..070ff8415a --- /dev/null +++ b/indra/newview/scripts/lua/test_luafloater_gesture_list.lua @@ -0,0 +1,62 @@ +XML_FILE_PATH = "luafloater_gesture_list.xml" + +leap = require 'leap' +coro = require 'coro' +LLGesture = require 'LLGesture' + +--event pump for sending actions to the floater +COMMAND_PUMP_NAME = "" +--table of floater UI events +e={} +coro.launch(function () + e = leap.request("LLFloaterReg", {op="getFloaterEvents"})["events"] + leap.done() +end) +leap.process() + +function post(action) + leap.send(COMMAND_PUMP_NAME, action) +end + +function handleEvents(event_data) + if event_data.event == e.CLOSE_EVENT then + leap.done() + elseif event_data.event == e.POST_BUILD_EVENT then + COMMAND_PUMP_NAME = event_data.command_name + gestures_uuid = LLGesture.getActiveGestures() + local action_data = {} + action_data.action = "add_list_element" + action_data.ctrl_name = "gesture_list" + gestures = {} + for uuid, info in pairs(gestures_uuid) do + element={value = uuid, columns ={column = "gesture_name", value = info.name}} + action_data.value = element + post(action_data) + end + elseif event_data.event == e.DOUBLE_CLICK_EVENT then + if event_data.ctrl_name == "gesture_list" then + LLGesture.startGesture(event_data.value) + end + end +end + +catch_events = leap.WaitFor:new(-1, "all_events") +function catch_events:filter(pump, data) + return data +end + +function process_events(waitfor) + event_data = waitfor:wait() + while event_data do + handleEvents(event_data) + event_data = waitfor:wait() + end +end + +local key = {xml_path = XML_FILE_PATH, op = "showLuaFloater"} +--receive additional events for defined control {= {action1, action2, ...}} +key.extra_events={gesture_list = {e.DOUBLE_CLICK_EVENT}} +leap.send("LLFloaterReg", key) + +coro.launch(process_events, catch_events) +leap.process() -- cgit v1.2.3 From ba6784647b53919c09ef339fd99af152aa0f8458 Mon Sep 17 00:00:00 2001 From: Mnikolenko Productengine Date: Wed, 20 Mar 2024 23:21:48 +0200 Subject: LLLuaFloater code clean up --- indra/llui/llfloater.cpp | 4 --- indra/llui/llfloater.h | 3 -- indra/llui/llfloaterreglistener.cpp | 20 ++--------- indra/llui/llfloaterreglistener.h | 1 - indra/llui/llluafloater.cpp | 39 +++++++++------------- indra/llui/llluafloater.h | 3 +- indra/newview/scripts/lua/test_luafloater_demo.lua | 2 +- .../scripts/lua/test_luafloater_gesture_list.lua | 2 +- 8 files changed, 23 insertions(+), 51 deletions(-) diff --git a/indra/llui/llfloater.cpp b/indra/llui/llfloater.cpp index b9b69b5a33..de3de53569 100644 --- a/indra/llui/llfloater.cpp +++ b/indra/llui/llfloater.cpp @@ -3623,10 +3623,6 @@ void LLFloater::applyRelativePosition() translate(new_center.mX - cur_center.mX, new_center.mY - cur_center.mY); } -bool LLFloater::isDefaultBtnName(const std::string& name) -{ - return (std::find(std::begin(sButtonNames), std::end(sButtonNames), name) != std::end(sButtonNames)); -} LLCoordFloater::LLCoordFloater(F32 x, F32 y, LLFloater& floater) : coord_t((S32)x, (S32)y) diff --git a/indra/llui/llfloater.h b/indra/llui/llfloater.h index 9d49c4538e..88f9e77777 100644 --- a/indra/llui/llfloater.h +++ b/indra/llui/llfloater.h @@ -372,9 +372,6 @@ public: void enableResizeCtrls(bool enable, bool width = true, bool height = true); bool isPositioning(LLFloaterEnums::EOpenPositioning p) const { return (p == mPositioning); } - - static bool isDefaultBtnName(const std::string& name); - protected: void applyControlsAndPosition(LLFloater* other); diff --git a/indra/llui/llfloaterreglistener.cpp b/indra/llui/llfloaterreglistener.cpp index bdf26c1db8..8316101264 100644 --- a/indra/llui/llfloaterreglistener.cpp +++ b/indra/llui/llfloaterreglistener.cpp @@ -74,11 +74,9 @@ LLFloaterRegListener::LLFloaterRegListener(): &LLFloaterRegListener::clickButton, requiredNameButton); - LLSD requiredParams; - requiredParams["xml_path"] = LLSD(); add("showLuaFloater", - "Open the new floater using XML file specified in [\"xml_path\"]", - &LLFloaterRegListener::showLuaFloater, requiredParams); + "Open the new floater using XML file specified in [\"xml_path\"] with ID in [\"reqid\"]", + &LLLuaFloater::showLuaFloater, {llsd::map("xml_path", LLSD(), "reqid", LLSD())}); add("getFloaterEvents", "Return the table of Lua Floater events which are send to the script", &LLFloaterRegListener::getLuaFloaterEvents); @@ -165,20 +163,8 @@ void LLFloaterRegListener::clickButton(const LLSD& event) const } } -void LLFloaterRegListener::showLuaFloater(const LLSD &event) const -{ - LLLuaFloater::showLuaFloater(event); -} - void LLFloaterRegListener::getLuaFloaterEvents(const LLSD &event) const { - if (event.has("reply")) - { - LLSD events_data = LLLuaFloater::getEventsData(); - - LLReqID reqID(event); - LLSD reply(reqID.makeResponse()); - LLEventPumps::instance().obtain(event["reply"]).post(reply.with("events", events_data)); - } + Response response(llsd::map("events", LLLuaFloater::getEventsData()), event); } diff --git a/indra/llui/llfloaterreglistener.h b/indra/llui/llfloaterreglistener.h index cbfb7855b9..9cb0af2de5 100644 --- a/indra/llui/llfloaterreglistener.h +++ b/indra/llui/llfloaterreglistener.h @@ -50,7 +50,6 @@ private: void instanceVisible(const LLSD& event) const; void clickButton(const LLSD& event) const; - void showLuaFloater(const LLSD &event) const; void getLuaFloaterEvents(const LLSD &event) const; }; diff --git a/indra/llui/llluafloater.cpp b/indra/llui/llluafloater.cpp index 27ae85c844..668c0edc53 100644 --- a/indra/llui/llluafloater.cpp +++ b/indra/llui/llluafloater.cpp @@ -51,8 +51,9 @@ std::map EVENT_LIST = LLLuaFloater::LLLuaFloater(const LLSD &key) : LLFloater(key), - mCommandPumpName(key["reply"].asString()), - mDispatcher("LLLuaFloater", "action") + mReplyPumpName(key["reply"].asString()), + mDispatcher("LLLuaFloater", "action"), + mReqID(key) { mListenerPumpName = LLUUID::generateNewID().asString(); @@ -69,7 +70,7 @@ LLLuaFloater::LLLuaFloater(const LLSD &key) : return false; }); - LLSD requiredParams = LLSD().with("ctrl_name", LLSD()).with("value", LLSD()); + LLSD requiredParams = llsd::map("ctrl_name", LLSD(), "value", LLSD()); mDispatcher.add("set_enabled", "", [this](const LLSD &event) { LLUICtrl *ctrl = getChild(event["ctrl_name"].asString()); @@ -103,13 +104,11 @@ LLLuaFloater::LLLuaFloater(const LLSD &key) : } }, requiredParams); - requiredParams = LLSD().with("value", LLSD()); mDispatcher.add("set_title", "", [this](const LLSD &event) { setTitle(event["value"].asString()); - }, requiredParams); + }, llsd::map("value", LLSD())); - requiredParams = LLSD().with("ctrl_name", LLSD()).with("reqid", LLSD()); mDispatcher.add("get_value", "", [this](const LLSD &event) { LLUICtrl *ctrl = getChild(event["ctrl_name"].asString()); @@ -120,7 +119,7 @@ LLLuaFloater::LLLuaFloater(const LLSD &key) : response["reqid"] = event["reqid"]; post(response); } - }, requiredParams); + }, llsd::map("ctrl_name", LLSD(), "reqid", LLSD())); } LLLuaFloater::~LLLuaFloater() @@ -131,13 +130,8 @@ LLLuaFloater::~LLLuaFloater() BOOL LLLuaFloater::postBuild() { - child_list_t::const_iterator iter = getChildList()->begin(); - child_list_t::const_iterator end = getChildList()->end(); - for (; iter != end; ++iter) + for (LLView *view : *getChildList()) { - LLView *view = *iter; - if (!view) continue; - LLUICtrl *ctrl = dynamic_cast(view); if (ctrl) { @@ -158,27 +152,24 @@ BOOL LLLuaFloater::postBuild() if (mKey.has("extra_events")) { //the first value is ctrl name, the second contains array of events to send - const LLSD &events_map = mKey["extra_events"]; - for (LLSD::map_const_iterator it = events_map.beginMap(); it != events_map.endMap(); ++it) + for (const auto &[name, data] : llsd::inMap(mKey["extra_events"])) { - std::string name = (*it).first; - LLSD data = (*it).second; - for (LLSD::array_const_iterator events_it = data.beginArray(); events_it != data.endArray(); ++events_it) + for (const auto &event : llsd::inArray(data)) { - registerCallback(name, events_it->asString()); + registerCallback(name, event); } } } //send pump name to the script after the floater is built - post(LLSD().with("command_name", mListenerPumpName).with("event", EVENT_LIST["POST_BUILD_EVENT"])); + post(llsd::map("command_name", mListenerPumpName, "event", EVENT_LIST["POST_BUILD_EVENT"])); return true; } void LLLuaFloater::onClose(bool app_quitting) { - post(LLSD().with("event", EVENT_LIST["CLOSE_EVENT"])); + post(llsd::map("event", EVENT_LIST["CLOSE_EVENT"], "app_quitting", app_quitting)); } void LLLuaFloater::registerCallback(const std::string &ctrl_name, const std::string &event) @@ -247,8 +238,10 @@ void LLLuaFloater::registerCallback(const std::string &ctrl_name, const std::str void LLLuaFloater::post(const LLSD &data) { - //send event data to the script - LLEventPumps::instance().obtain(mCommandPumpName).post(data); + // send event data to the script signed with ["reqid"] key + LLSD stamped_data(data); + mReqID.stamp(stamped_data); + LLEventPumps::instance().obtain(mReplyPumpName).post(stamped_data); } void LLLuaFloater::showLuaFloater(const LLSD &data) diff --git a/indra/llui/llluafloater.h b/indra/llui/llluafloater.h index 0dd39e7d1e..b9f96f0ad3 100644 --- a/indra/llui/llluafloater.h +++ b/indra/llui/llluafloater.h @@ -45,10 +45,11 @@ public: static LLSD getEventsData(); private: + LLReqID mReqID; LLEventDispatcher mDispatcher; LLTempBoundListener mBoundListener; std::string mListenerPumpName; - std::string mCommandPumpName; + std::string mReplyPumpName; }; #endif diff --git a/indra/newview/scripts/lua/test_luafloater_demo.lua b/indra/newview/scripts/lua/test_luafloater_demo.lua index a7bbdccb30..2cbafcec14 100644 --- a/indra/newview/scripts/lua/test_luafloater_demo.lua +++ b/indra/newview/scripts/lua/test_luafloater_demo.lua @@ -62,7 +62,7 @@ local key = {xml_path = XML_FILE_PATH, op = "showLuaFloater"} --sign for additional events for defined control {= {action1, action2, ...}} key.extra_events={show_time_lbl = {e.RIGHT_MOUSE_DOWN_EVENT, e.DOUBLE_CLICK_EVENT}} -leap.send("LLFloaterReg", key) +leap.send("LLFloaterReg", key, "floater1") coro.launch(process_events, catch_events) leap.process() diff --git a/indra/newview/scripts/lua/test_luafloater_gesture_list.lua b/indra/newview/scripts/lua/test_luafloater_gesture_list.lua index 070ff8415a..5ea2b1e30d 100644 --- a/indra/newview/scripts/lua/test_luafloater_gesture_list.lua +++ b/indra/newview/scripts/lua/test_luafloater_gesture_list.lua @@ -56,7 +56,7 @@ end local key = {xml_path = XML_FILE_PATH, op = "showLuaFloater"} --receive additional events for defined control {= {action1, action2, ...}} key.extra_events={gesture_list = {e.DOUBLE_CLICK_EVENT}} -leap.send("LLFloaterReg", key) +leap.send("LLFloaterReg", key, "floater1") coro.launch(process_events, catch_events) leap.process() -- cgit v1.2.3 From 0566af988790e95414ed18cd82206710094d8fae Mon Sep 17 00:00:00 2001 From: Nat Goodspeed Date: Thu, 21 Mar 2024 23:56:46 +0900 Subject: WIP: Add fiber.lua module and use in leap.lua and WaitQueue.lua. fiber.lua goes beyond coro.lua in that it distinguishes ready suspended coroutines from waiting suspended coroutines, and presents a rudimentary scheduler in fiber.yield(). yield() can determine that when all coroutines are waiting, it's time to retrieve the next incoming event from the viewer. Moreover, it can detect when all coroutines have completed and exit without being explicitly told. fiber.launch() associates a name with each fiber for debugging purposes. fiber.get_name() retrieves the name of the specified fiber, or the running fiber. fiber.status() is like coroutine.status(), but can return 'ready' or 'waiting' instead of 'suspended'. fiber.yield() leaves the calling fiber ready, but lets other ready fibers run. fiber.wait() suspends the calling fiber and lets other ready fibers run. fiber.wake(), called from some other coroutine, returns the passed fiber to ready status for a future call to fiber.yield(). fiber.run() drives the scheduler to run all fibers to completion. If, on completion of the subject Lua script, LuaState::expr() detects that the script loaded fiber.lua, it calls fiber.run() to finish running any dangling fibers. This lets a script make calls to fiber.launch() and then just fall off the end, leaving the implicit fiber.run() call to run them all. fiber.lua is designed to allow the main thread, as well as explicitly launched coroutines, to make leap.request() calls. This part still needs debugging. The leap.lua module now configures a fiber.set_idle() function that honors leap.done(), but calls get_event_next() and dispatches the next incoming event. leap.request() and generate() now leave the reqid stamp in the response. This lets a caller handle subsequent events with the same reqid, e.g. for LLLuaFloater. Remove leap.process(): it has been superseded by fiber.run(). Remove leap.WaitFor:iterate(): unfortunately that would run afoul of the Luau bug that prevents suspending the calling coroutine within a generic 'for' iterator function. Make leap.lua use weak tables to track WaitFor objects. Make WaitQueue:Dequeue() call fiber.wait() to suspend its caller when the queue is empty, and Enqueue() call fiber.wake() to set it ready again when a new item is pushed. Make llluamanager_test.cpp's leap test script use the fiber module to launch coroutines, instead of the coro module. Fix a bug in which its drain() function was inadvertently setting and testing the global 'item' variable instead of one local to the function. Since some other modules had the same bug, it was getting confused. Also add printf.lua, providing a printf() function. printf() is short for print(string.format()), but it can also print tables: anything not a number or string is formatted using the inspect() function. Clean up some LL_DEBUGS() output left over from debugging lua_tollsd(). --- indra/llcommon/lua_function.cpp | 128 ++++++++----- indra/newview/scripts/lua/WaitQueue.lua | 29 ++- indra/newview/scripts/lua/fiber.lua | 301 ++++++++++++++++++++++++++++++ indra/newview/scripts/lua/leap.lua | 195 ++++++++++--------- indra/newview/scripts/lua/printf.lua | 19 ++ indra/newview/tests/llluamanager_test.cpp | 34 ++-- 6 files changed, 531 insertions(+), 175 deletions(-) create mode 100644 indra/newview/scripts/lua/fiber.lua create mode 100644 indra/newview/scripts/lua/printf.lua diff --git a/indra/llcommon/lua_function.cpp b/indra/llcommon/lua_function.cpp index b5de5099ba..441e17dafd 100644 --- a/indra/llcommon/lua_function.cpp +++ b/indra/llcommon/lua_function.cpp @@ -17,6 +17,7 @@ // std headers #include #include +#include #include // std::quoted #include #include // std::unique_ptr @@ -97,8 +98,6 @@ void lua_pushstdstring(lua_State* L, const std::string& str) // reached by that block raises a Lua error. LLSD lua_tollsd(lua_State* L, int index) { - LL_DEBUGS("Lua") << "lua_tollsd(" << index << ") of " << lua_gettop(L) << " stack entries: " - << lua_what(L, index) << LL_ENDL; switch (lua_type(L, index)) { case LUA_TNONE: @@ -200,15 +199,12 @@ LLSD lua_tollsd(lua_State* L, int index) // we do, below: lua_tollsd(L, -1). If 'index' is -1, then when we // push nil, what we find at index -1 is nil, not the table! index = lua_absindex(L, index); - LL_DEBUGS("Lua") << "checking for empty table" << LL_ENDL; lua_pushnil(L); // first key - LL_DEBUGS("Lua") << lua_stack(L) << LL_ENDL; if (! lua_next(L, index)) { // it's a table, but the table is empty -- no idea if it should be // modeled as empty array or empty map -- return isUndefined(), // which can be consumed as either - LL_DEBUGS("Lua") << "empty table" << LL_ENDL; return {}; } // key is at stack index -2, value at index -1 @@ -217,8 +213,6 @@ LLSD lua_tollsd(lua_State* L, int index) LuaPopper popper(L, 2); // Remember the type of the first key auto firstkeytype{ lua_type(L, -2) }; - LL_DEBUGS("Lua") << "table not empty, first key type " << lua_typename(L, firstkeytype) - << LL_ENDL; switch (firstkeytype) { case LUA_TNUMBER: @@ -296,7 +290,6 @@ LLSD lua_tollsd(lua_State* L, int index) // crazy key territory. return lluau::error(L, "Gaps in Lua table too large for conversion to LLSD array"); } - LL_DEBUGS("Lua") << "collected " << keys.size() << " keys, max " << highkey << LL_ENDL; // right away expand the result array to the size we'll need LLSD result{ LLSD::emptyArray() }; result[highkey - 1] = LLSD(); @@ -307,7 +300,6 @@ LLSD lua_tollsd(lua_State* L, int index) // key at stack index -2, value at index -1 // We've already validated lua_tointegerx() for each key. auto key{ lua_tointeger(L, -2) }; - LL_DEBUGS("Lua") << "key " << key << ':' << LL_ENDL; // Don't forget to subtract 1 from Lua key for LLSD subscript! result[LLSD::Integer(key) - 1] = lua_tollsd(L, -1); // remove value, keep key for next iteration @@ -330,7 +322,6 @@ LLSD lua_tollsd(lua_State* L, int index) } auto key{ lua_tostdstring(L, -2) }; - LL_DEBUGS("Lua") << "map key " << std::quoted(key) << ':' << LL_ENDL; result[key] = lua_tollsd(L, -1); // remove value, keep key for next iteration lua_pop(L, 1); @@ -493,53 +484,100 @@ std::pair LuaState::expr(const std::string& desc, const std::string& // here we believe there was no error -- did the Lua fragment leave // anything on the stack? std::pair result{ lua_gettop(mState), {} }; - if (! result.first) - return result; - - // aha, at least one entry on the stack! - if (result.first == 1) + if (result.first) { - // Don't forget that lua_tollsd() can throw Lua errors. - try + // aha, at least one entry on the stack! + if (result.first == 1) { - result.second = lua_tollsd(mState, 1); + // Don't forget that lua_tollsd() can throw Lua errors. + try + { + result.second = lua_tollsd(mState, 1); + } + catch (const std::exception& error) + { + // lua_tollsd() is designed to be called from a lua_function(), + // that is, from a C++ function called by Lua. In case of error, + // it throws a Lua error to be caught by the Lua runtime. expr() + // is a peculiar use case in which our C++ code is calling + // lua_tollsd() after return from the Lua runtime. We must catch + // the exception thrown for a Lua error, else it will propagate + // out to the main coroutine and terminate the viewer -- but since + // we instead of the Lua runtime catch it, our lua_State retains + // its internal error status. Any subsequent lua_pcall() calls + // with this lua_State will report error regardless of whether the + // chunk runs successfully. Get a new lua_State(). + initLuaState(); + return { -1, stringize(LLError::Log::classname(error), ": ", error.what()) }; + } } - catch (const std::exception& error) + else { - // lua_tollsd() is designed to be called from a lua_function(), - // that is, from a C++ function called by Lua. In case of error, - // it throws a Lua error to be caught by the Lua runtime. expr() - // is a peculiar use case in which our C++ code is calling - // lua_tollsd() after return from the Lua runtime. We must catch - // the exception thrown for a Lua error, else it will propagate - // out to the main coroutine and terminate the viewer -- but since - // we instead of the Lua runtime catch it, our lua_State retains - // its internal error status. Any subsequent lua_pcall() calls - // with this lua_State will report error regardless of whether the - // chunk runs successfully. Get a new lua_State(). - initLuaState(); - return { -1, stringize(LLError::Log::classname(error), ": ", error.what()) }; + // multiple entries on the stack + try + { + for (int index = 1; index <= result.first; ++index) + { + result.second.append(lua_tollsd(mState, index)); + } + } + catch (const std::exception& error) + { + // see above comments regarding lua_State's error status + initLuaState(); + return { -1, stringize(LLError::Log::classname(error), ": ", error.what()) }; + } } - // pop the result we claimed - lua_settop(mState, 0); - return result; } + // pop everything + lua_settop(mState, 0); - // multiple entries on the stack - try - { - for (int index = 1; index <= result.first; ++index) + // If we ran a script that loaded the fiber module, finish up with a call + // to fiber.run(). That allows a script to kick off some number of fibers, + // do some work on the main thread and then fall off the end of the script + // without explicitly appending a call to fiber.run(). run() ensures the + // rest of the fibers run to completion (or error). + luaL_checkstack(mState, 4, nullptr); + // Push _MODULES table on stack + luaL_findtable(mState, LUA_REGISTRYINDEX, "_MODULES", 1); + int index = lua_gettop(mState); + bool found = false; + // Did this chunk already require('fiber')? To find out, we must search + // the _MODULES table, because our require() implementation uses the + // pathname of the module file as the key. Push nil key to start. + lua_pushnil(mState); + while (lua_next(mState, index) != 0) + { + // key is at index -2, value at index -1 + // "While traversing a table, do not call lua_tolstring directly on a + // key, unless you know that the key is actually a string. Recall that + // lua_tolstring changes the value at the given index; this confuses + // the next call to lua_next." + // https://www.lua.org/manual/5.1/manual.html#lua_next + if (lua_type(mState, -2) == LUA_TSTRING && + std::filesystem::path(lua_tostdstring(mState, -2)).stem() == "fiber") { - result.second.append(lua_tollsd(mState, index)); + found = true; + break; } + // pop value so key is at top for lua_next() + lua_pop(mState, 1); } - catch (const std::exception& error) + if (found) { - // see above comments regarding lua_State's error status - initLuaState(); - return { -1, stringize(LLError::Log::classname(error), ": ", error.what()) }; + // okay, index -1 is a table loaded from a file 'fiber.xxx' -- + // does it have a function named 'run'? + auto run_type{ lua_getfield(mState, -1, "run") }; + if (run_type == LUA_TFUNCTION) + { + // there's a fiber.run() function sitting on the top of the stack + // -- call it with no arguments, discarding anything it returns + LL_DEBUGS("Lua") << "Calling fiber.run()" << LL_ENDL; + if (! checkLua(desc, lua_pcall(mState, 0, 0, 0))) + return { -1, mError }; + } } - // pop everything + // pop everything again lua_settop(mState, 0); return result; } diff --git a/indra/newview/scripts/lua/WaitQueue.lua b/indra/newview/scripts/lua/WaitQueue.lua index 00766ccae7..b15e9c443b 100644 --- a/indra/newview/scripts/lua/WaitQueue.lua +++ b/indra/newview/scripts/lua/WaitQueue.lua @@ -2,8 +2,12 @@ -- the Dequeue() operation blocks the calling coroutine until some other -- coroutine Enqueue()s a new value. +local fiber = require('fiber') local Queue = require('Queue') +-- local debug = print_debug +local function debug(...) end + local WaitQueue = Queue:new() function WaitQueue:new() @@ -32,11 +36,9 @@ function WaitQueue:_wake_waiters() -- cases. With multiple consumers, if more than one is trying to -- Dequeue() from an empty WaitQueue, we'll have multiple waiters. -- Unlike OS threads, with cooperative concurrency it doesn't make sense - -- to "notify all": we need resume only one of the waiting Dequeue() - -- callers. But since resuming that caller might entail either Enqueue() - -- or Dequeue() calls, recheck every time around to see if we must resume - -- another waiting coroutine. - while not self:IsEmpty() and #self._waiters > 0 do + -- to "notify all": we need wake only one of the waiting Dequeue() + -- callers. + if not self:IsEmpty() and next(self._waiters) then -- Pop the oldest waiting coroutine instead of the most recent, for -- more-or-less round robin fairness. But skip any coroutines that -- have gone dead in the meantime. @@ -47,11 +49,7 @@ function WaitQueue:_wake_waiters() -- do we still have at least one waiting coroutine? if waiter then -- don't pass the head item: let the resumed coroutine retrieve it - local ok, message = coroutine.resume(waiter) - -- if resuming that waiter encountered an error, don't swallow it - if not ok then - error(message) - end + fiber.wake(waiter) end end end @@ -62,18 +60,17 @@ function WaitQueue:Dequeue() -- the queue while there are still items left, and we want the -- consumer(s) to retrieve those last few items. if self._closed then + debug('WaitQueue:Dequeue(): closed') return nil end - local coro = coroutine.running() - if coro == nil then - error("WaitQueue:Dequeue() trying to suspend main coroutine") - end + debug('WaitQueue:Dequeue(): waiting') -- add the running coroutine to the list of waiters - table.insert(self._waiters, coro) + table.insert(self._waiters, fiber.running()) -- then let somebody else run - coroutine.yield() + fiber.wait() end -- here we're sure this queue isn't empty + debug('WaitQueue:Dequeue() calling Queue.Dequeue()') return Queue.Dequeue(self) end diff --git a/indra/newview/scripts/lua/fiber.lua b/indra/newview/scripts/lua/fiber.lua new file mode 100644 index 0000000000..f18d133cc8 --- /dev/null +++ b/indra/newview/scripts/lua/fiber.lua @@ -0,0 +1,301 @@ +-- Organize Lua coroutines into fibers. + +-- In this usage, the difference between coroutines and fibers is that fibers +-- have a scheduler. Yielding a fiber means allowing other fibers, plural, to +-- run: it's more than just returning control to the specific Lua thread that +-- resumed the running coroutine. + +-- fiber.launch() creates a new fiber ready to run. +-- fiber.status() reports (augmented) status of the passed fiber: instead of +-- 'suspended', it returns either 'ready' or 'waiting' +-- fiber.yield() allows other fibers to run, but leaves the calling fiber +-- ready to run. +-- fiber.wait() marks the running fiber not ready, and resumes other fibers. +-- fiber.wake() marks the designated suspended fiber ready to run, but does +-- not yet resume it. +-- fiber.run() runs all current fibers until all have terminated (successfully +-- or with an error). + +local printf = require 'printf' +-- local debug = printf +local function debug(...) end +local coro = require 'coro' + +local fiber = {} + +-- The tables in which we track fibers must have weak keys so dead fibers +-- can be garbage-collected. +local weak_values = {__mode='v'} +local weak_keys = {__mode='k'} + +-- Track each current fiber as being either ready to run or not ready +-- (waiting). wait() moves the running fiber from ready to waiting; wake() +-- moves the designated fiber from waiting back to ready. +-- The ready table is used as a list so yield() can go round robin. +local ready = setmetatable({'main'}, weak_keys) +-- The waiting table is used as a set because order doesn't matter. +local waiting = setmetatable({}, weak_keys) + +-- Every fiber has a name, for diagnostic purposes. Names must be unique. +-- A colliding name will be suffixed with an integer. +-- Predefine 'main' with our marker so nobody else claims that name. +local names = setmetatable({main='main'}, weak_keys) +local byname = setmetatable({main='main'}, weak_values) +-- each colliding name has its own distinct suffix counter +local suffix = {} + +-- Specify a nullary idle() callback to be called whenever there are no ready +-- fibers but there are waiting fibers. The idle() callback is responsible for +-- changing zero or more waiting fibers to ready fibers by calling +-- fiber.wake(), although a given call may leave them all still waiting. +-- When there are no ready fibers, it's a good idea for the idle() function to +-- return control to a higher-level execution agent. Simply returning without +-- changing any fiber's status will spin the CPU. +-- The idle() callback can return non-nil to exit fiber.run() with that value. +function fiber._idle() + error('fiber.yield(): you must first call set_idle(nullary idle() function)') +end + +function fiber.set_idle(func) + fiber._idle = func +end + +-- Launch a new Lua fiber, ready to run. +function fiber.launch(name, func, ...) + local args = table.pack(...) + local co = coroutine.create(function() func(table.unpack(args)) end) + -- a new fiber is ready to run + table.insert(ready, co) + local namekey = name + while byname[namekey] do + if not suffix[name] then + suffix[name] = 1 + end + suffix[name] += 1 + namekey = name .. tostring(suffix[name]) + end + -- found a namekey not yet in byname: set it + byname[namekey] = co + -- and remember it as this fiber's name + names[co] = namekey +-- debug('launch(%s)', namekey) +-- debug('byname[%s] = %s', namekey, tostring(byname[namekey])) +-- debug('names[%s] = %s', tostring(co), names[co]) +-- debug('ready[-1] = %s', tostring(ready[#ready])) +end + +-- for debugging +function fiber.print_all() + print('Ready fibers:' .. if next(ready) then '' else ' none') + for _, co in pairs(ready) do + printf(' %s: %s', fiber.get_name(co), fiber.status(co)) + end + print('Waiting fibers:' .. if next(waiting) then '' else ' none') + for co in pairs(waiting) do + printf(' %s: %s', fiber.get_name(co), fiber.status(co)) + end +end + +-- return either the running coroutine or, if called from the main thread, +-- 'main' +function fiber.running() + return coroutine.running() or 'main' +end + +-- Query a fiber's name (nil for the running fiber) +function fiber.get_name(co) + if not co then + co = fiber.running() + end + if not names[co] then + return 'unknown' + end + return names[co] +end + +-- Query status of the passed fiber +function fiber.status(co) + local running = coroutine.running() + if (not co) or co == running then + -- silly to ask the status of the running fiber: it's 'running' + return 'running' + end + if co ~= 'main' then + -- for any coroutine but main, consult coroutine.status() + local status = coroutine.status(co) + if status ~= 'suspended' then + return status + end + -- here co is suspended, answer needs further refinement + else + -- co == 'main' + if not running then + -- asking about 'main' from the main fiber + return 'running' + end + -- asking about 'main' from some other fiber, so presumably main is suspended + end + -- here we know co is suspended -- but is it ready to run? + if waiting[co] then + return 'waiting' + end + -- not waiting should imply ready: sanity check + for _, maybe in pairs(ready) do + if maybe == co then + return 'ready' + end + end + -- Calls within yield() between popping the next ready fiber and + -- re-appending it to the list are in this state. Once we're done + -- debugging yield(), we could reinstate either of the below. +-- error(string.format('fiber.status(%s) is stumped', fiber.get_name(co))) +-- print(string.format('*** fiber.status(%s) is stumped', fiber.get_name(co))) + return '(unknown)' +end + +-- change the running fiber's status to waiting +local function set_waiting() + -- if called from the main fiber, inject a 'main' marker into the list + co = fiber.running() + -- delete from ready list + for i, maybe in pairs(ready) do + if maybe == co then + table.remove(ready, i) + break + end + end + -- add to waiting list + waiting[co] = true +end + +-- Suspend the current fiber until some other fiber calls fiber.wake() on it +function fiber.wait() + set_waiting() + -- now yield to other fibers + fiber.yield() +end + +-- Mark a suspended fiber as being ready to run +function fiber.wake(co) + if not waiting[co] then + error(string.format('fiber.wake(%s) but status=%s, ready=%s, waiting=%s', + names[co], fiber.status(co), ready[co], waiting[co])) + end + -- delete from waiting list + waiting[co] = nil + -- add to end of ready list + table.insert(ready, co) + -- but don't yet resume it: that happens next time we reach yield() +end + +-- Run fibers until all but main have terminated: return nil. +-- Or until configured idle() callback returns x ~= nil: return x. +function fiber.run() + -- A fiber calling run() is not also doing other useful work. Tell yield() + -- that we're waiting. Otherwise it would keep seeing that our caller is + -- ready and return to us, instead of realizing that all coroutines are + -- waiting and call idle(). + set_waiting() + local others, idle_done + repeat + debug('%s calling fiber.run() calling yield()', fiber.get_name()) + others, idle_done = fiber.yield() + debug("%s fiber.run()'s yield() returned %s, %s", fiber.get_name(), + tostring(others), tostring(idle_done)) + until (not others) + debug('%s fiber.run() done', fiber.get_name()) + fiber.wake(fiber.running()) + -- Once there are no more waiting fibers, and the only ready fiber is + -- main, return to main. All previously-launched fibers are done. Possibly + -- the chunk is done, or the chunk may decide to launch a new batch of + -- fibers. + return idle_done +end + +-- pop and return the next not-dead fiber in the ready list, or nil if none remain +local function live_ready_iter() + -- don't write + -- for co in table.remove, ready, 1 + -- because it would keep passing a new second parameter! + for co in function() return table.remove(ready, 1) end do + debug('%s live_ready_iter() sees %s, status %s', + fiber.get_name(), fiber.get_name(co), fiber.status(co)) + -- keep removing the head entry until we find one that's not dead, + -- discarding any dead coroutines along the way + if co == 'main' or coroutine.status(co) ~= 'dead' then + debug('%s live_ready_iter() returning %s', + fiber.get_name(), fiber.get_name(co)) + return co + end + end + debug('%s live_ready_iter() returning nil', fiber.get_name()) + return nil +end + +-- prune the set of waiting fibers +local function prune_waiting() + for waiter in pairs(waiting) do + if waiter ~= 'main' and coroutine.status(waiter) == 'dead' then + waiting[waiter] = nil + end + end +end + +-- Give other ready fibers a chance to run, leaving this one ready, returning +-- after a cycle. Returns: +-- * true, nil if there remain other live fibers, whether ready or waiting +-- * false, nil if this is the only remaining fiber +-- * nil, x if configured idle() callback returned non-nil x +function fiber.yield() + if coroutine.running() then + -- seize the opportunity to make sure the viewer isn't shutting down +-- check_stop() + -- this is a real coroutine, yield normally to main or whoever + coroutine.yield() + -- main certainly still exists + return true + end + + -- This is the main fiber: coroutine.yield() doesn't work. + -- Instead, resume each of the ready fibers. + -- Prune the set of waiting fibers after every time fiber business logic + -- runs (i.e. other fibers might have terminated or hit error), such as + -- here on entry. + prune_waiting() + local others, idle_stop + repeat + for co in live_ready_iter do + -- seize the opportunity to make sure the viewer isn't shutting down +-- check_stop() + -- before we re-append co, is it the only remaining entry? + others = next(ready) + -- co is live, re-append it to the ready list + table.insert(ready, co) + if co == 'main' then + -- Since we know the caller is the main fiber, it's our turn. + -- Tell caller if there are other ready or waiting fibers. + return others or next(waiting) + end + -- not main, but some other ready coroutine: + -- use coro.resume() so we'll propagate any error encountered + coro.resume(co) + prune_waiting() + end + -- Here there are no ready fibers. Are there any waiting fibers? + if not next(waiting) then + return false + end + -- there are waiting fibers: call consumer's configured idle() function + idle_stop = fiber._idle() + if idle_stop ~= nil then + return nil, idle_stop + end + prune_waiting() + -- loop "forever", that is, until: + -- * main is ready, or + -- * there are neither ready fibers nor waiting fibers, or + -- * fiber._idle() returned non-nil + until false +end + +return fiber diff --git a/indra/newview/scripts/lua/leap.lua b/indra/newview/scripts/lua/leap.lua index 81728e7230..60e8266a76 100644 --- a/indra/newview/scripts/lua/leap.lua +++ b/indra/newview/scripts/lua/leap.lua @@ -38,7 +38,10 @@ -- leap.process(). process() won't notice until the next event from the -- viewer, though. +local fiber = require('fiber') local ErrorQueue = require('ErrorQueue') +-- local debug = require('printf') +local function debug(...) end local leap = {} @@ -68,11 +71,13 @@ leap._reply, leap._command = get_event_pumps() -- later one. That means that no incoming event will ever be given to -- the old WaitForReqid object. Any coroutine waiting on the discarded -- WaitForReqid object would therefore wait forever. -leap._pending = {} +-- these are weak values tables +local weak_values = {__mode='v'} +leap._pending = setmetatable({}, weak_values) -- Our consumer will instantiate some number of WaitFor subclass objects. -- As these are traversed in descending priority order, we must keep -- them in a list. -leap._waitfors = {} +leap._waitfors = setmetatable({}, weak_values) -- It has been suggested that we should use UUIDs as ["reqid"] values, -- since UUIDs are guaranteed unique. However, as the "namespace" for -- ["reqid"] values is our very own _reply pump, we can get away with @@ -91,15 +96,13 @@ function leap.cmdpump() return leap._command end --- local inspect = require('inspect') - -- Fire and forget. Send the specified request LLSD, expecting no reply. -- In fact, should the request produce an eventual reply, it will be -- treated as an unsolicited event. -- -- See also request(), generate(). function leap.send(pump, data, reqid) --- print_debug('leap.send('..pump..', '..inspect(data)..', '..reqid..') entry') + debug('leap.send(%s, %s, %s) entry', pump, data, reqid) local data = data if type(data) == 'table' then data = table.clone(data) @@ -108,10 +111,26 @@ function leap.send(pump, data, reqid) data['reqid'] = reqid end end --- print_debug('leap.send('..pump..', '..inspect(data)..') calling post_on()') + debug('leap.send(%s, %s) calling post_on()', pump, data) post_on(pump, data) end +-- common setup code shared by request() and generate() +local function requestSetup(pump, data) + -- invent a new, unique reqid + leap._reqid += 1 + local reqid = leap._reqid + -- Instantiate a new WaitForReqid object. The priority is irrelevant + -- because, unlike the WaitFor base class, WaitForReqid does not + -- self-register on our leap._waitfors list. Instead, capture the new + -- WaitForReqid object in leap._pending so dispatch() can find it. + leap._pending[reqid] = leap.WaitForReqid:new(reqid) + -- Pass reqid to send() to stamp it into (a copy of) the request data. + debug('requestSetup(%s, %s)', pump, data) + leap.send(pump, data, reqid) + return reqid +end + -- Send the specified request LLSD, expecting exactly one reply. Block -- the calling coroutine until we receive that reply. -- @@ -131,39 +150,20 @@ end -- -- See also send(), generate(). function leap.request(pump, data) - local reqid = leap._requestSetup(pump, data) + local reqid = requestSetup(pump, data) local waitfor = leap._pending[reqid] --- print_debug('leap.request('..tostring(pump)..', '..inspect(data)..') about to wait on '.. --- tostring(waitfor)) + debug('leap.request(%s, %s) about to wait on %s', pump, data, tostring(waitfor)) local ok, response = pcall(waitfor.wait, waitfor) --- print_debug('leap.request('..tostring(pump)..', '..inspect(data)..') got '.. --- tostring(ok)..': '..inspect(response)) + debug('leap.request(%s, %s) got %s: %s', pump, data, ok, response) -- kill off temporary WaitForReqid object, even if error leap._pending[reqid] = nil if ok then - response.reqid = nil return response else error(response) end end --- common setup code shared by request() and generate() -function leap._requestSetup(pump, data) - -- invent a new, unique reqid - leap._reqid += 1 - local reqid = leap._reqid - -- Instantiate a new WaitForReqid object. The priority is irrelevant - -- because, unlike the WaitFor base class, WaitForReqid does not - -- self-register on our leap._waitfors list. Instead, capture the new - -- WaitForReqid object in leap._pending so _dispatch() can find it. - leap._pending[reqid] = leap.WaitForReqid:new(reqid) - -- Pass reqid to send() to stamp it into (a copy of) the request data. --- print_debug('leap._requestSetup('..tostring(pump)..', '..inspect(data)..')') - leap.send(pump, data, reqid) - return reqid -end - -- Send the specified request LLSD, expecting an arbitrary number of replies. -- Each one is yielded on receipt. If you omit checklast, this is an infinite -- generator; it's up to the caller to recognize when the last reply has been @@ -178,7 +178,7 @@ function leap.generate(pump, data, checklast) -- Invent a new, unique reqid. Arrange to handle incoming events -- bearing that reqid. Stamp the outbound request with that reqid, and -- send it. - local reqid = leap._requestSetup(pump, data) + local reqid = requestSetup(pump, data) local waitfor = leap._pending[reqid] local ok, response repeat @@ -186,7 +186,6 @@ function leap.generate(pump, data, checklast) if not ok then break end - response.reqid = nil coroutine.yield(response) until checklast and checklast(response) -- If we break the above loop, whether or not due to error, clean up. @@ -196,78 +195,79 @@ function leap.generate(pump, data, checklast) end end --- Kick off response processing. The calling script must create and resume one --- or more coroutines to perform viewer requests using send(), request() or --- generate() before calling this function to handle responses. --- --- While waiting for responses from the viewer, the C++ coroutine running the --- calling Lua script is blocked: no other Lua coroutine is running. -function leap.process() - leap._done = false - local ok, pump, data - while not leap._done do --- print_debug('leap.process() calling get_event_next()') - ok, pump, data = pcall(get_event_next) --- print_debug('leap.process() got '..tostring(ok)..': '..pump..', '..inspect(data)) - -- ok false means get_event_next() raised a Lua error - -- data nil means get_event_next() returned (pump, LLSD()) to indicate done - if not (ok and data) then - break - end - leap._dispatch(pump, data) - end --- print_debug('leap.process() done') +local function cleanup(message) -- we're done: clean up all pending coroutines - -- if ok, then we're just done. - -- if not ok, then 'pump' is actually the error message. - message = if ok then 'done' else pump for i, waitfor in pairs(leap._pending) do - waitfor:_exception(message) + waitfor:exception(message) end for i, waitfor in pairs(leap._waitfors) do - waitfor:_exception(message) - end - -- now that we're done with cleanup, propagate the error we caught above - if not ok then - error(pump) + waitfor:exception(message) end end -function leap.done() - leap._done = true +-- Handle an incoming (pump, data) event with no recognizable ['reqid'] +local function unsolicited(pump, data) + -- we maintain waitfors in descending priority order, so the first waitfor + -- to claim this event is the one with the highest priority + for i, waitfor in pairs(leap._waitfors) do + debug('unsolicited() checking %s', waitfor.name) + if waitfor:handle(pump, data) then + return + end + end + print_debug(string.format('unsolicited(%s, %s) discarding unclaimed event', pump, data)) end -- Route incoming (pump, data) event to the appropriate waiting coroutine. -function leap._dispatch(pump, data) +local function dispatch(pump, data) local reqid = data['reqid'] -- if the response has no 'reqid', it's not from request() or generate() if reqid == nil then - return leap._unsolicited(pump, data) + return unsolicited(pump, data) end -- have reqid; do we have a WaitForReqid? local waitfor = leap._pending[reqid] if waitfor == nil then - return leap._unsolicited(pump, data) + return unsolicited(pump, data) end -- found the right WaitForReqid object, let it handle the event - data['reqid'] = nil - waitfor:_handle(pump, data) + waitfor:handle(pump, data) end --- Handle an incoming (pump, data) event with no recognizable ['reqid'] -function leap._unsolicited(pump, data) - -- we maintain waitfors in descending priority order, so the first waitfor - -- to claim this event is the one with the highest priority - for i, waitfor in pairs(leap._waitfors) do - if waitfor:_handle(pump, data) then - return - end +-- We configure fiber.set_idle() function. fiber.yield() calls the configured +-- idle callback whenever there are waiting fibers but no ready fibers. In +-- our case, that means it's time to fetch another incoming viewer event. +fiber.set_idle(function () + -- If someone has called leap.done(), then tell fiber.yield() to break loop. + if leap._done then + cleanup('done') + return 'done' + end + debug('leap.idle() calling get_event_next()') + local ok, pump, data = pcall(get_event_next) + debug('leap.idle() got %s: %s, %s', ok, pump, data) + -- ok false means get_event_next() raised a Lua error, pump is message + if not ok then + cleanup(pump) + error(pump) + end + -- data nil means get_event_next() returned (pump, LLSD()) to indicate done + if not data then + cleanup('end') + return 'end' end --- print_debug('_unsolicited(', pump, ', ', data, ') discarding unclaimed event') + -- got a real pump, data pair + dispatch(pump, data) + -- return to fiber.yield(): any incoming message might result in one or + -- more fibers becoming ready +end) + +function leap.done() + leap._done = true end -- called by WaitFor.enable() -function leap._registerWaitFor(waitfor) +local function registerWaitFor(waitfor) table.insert(leap._waitfors, waitfor) -- keep waitfors sorted in descending order of specified priority table.sort(leap._waitfors, @@ -275,7 +275,7 @@ function leap._registerWaitFor(waitfor) end -- called by WaitFor.disable() -function leap._unregisterWaitFor(waitfor) +local function unregisterWaitFor(waitfor) for i, w in pairs(leap._waitfors) do if w == waitfor then leap._waitfors[i] = nil @@ -322,8 +322,13 @@ end -- --------------------------------- WaitFor --------------------------------- leap.WaitFor = { _id=0 } +function leap.WaitFor.tostring(self) + -- Lua (sub)classes have no name; can't prefix with that + return self.name +end + function leap.WaitFor:new(priority, name) - local obj = setmetatable({}, self) + local obj = setmetatable({__tostring=leap.WaitFor.tostring}, self) self.__index = self obj.priority = priority @@ -343,16 +348,11 @@ function leap.WaitFor:new(priority, name) return obj end -function leap.WaitFor.tostring(self) - -- Lua (sub)classes have no name; can't prefix with that - return self.name -end - -- Re-enable a disable()d WaitFor object. New WaitFor objects are -- enable()d by default. function leap.WaitFor:enable() if not self._registered then - leap._registerWaitFor(self) + registerWaitFor(self) self._registered = true end end @@ -360,7 +360,7 @@ end -- Disable an enable()d WaitFor object. function leap.WaitFor:disable() if self._registered then - leap._unregisterWaitFor(self) + unregisterWaitFor(self) self._registered = false end end @@ -368,18 +368,12 @@ end -- Block the calling coroutine until a suitable unsolicited event (one -- for which filter() returns the event) arrives. function leap.WaitFor:wait() --- print_debug(self.name .. ' about to wait') - item = self._queue:Dequeue() --- print_debug(self.name .. ' got ', item) + debug('%s about to wait', self.name) + local item = self._queue:Dequeue() + debug('%s got %s', self.name, item) return item end --- Loop over wait() calls. -function leap.WaitFor:iterate() - -- on each iteration, call self.wait(self) - return self.wait, self, nil -end - -- Override filter() to examine the incoming event in whatever way -- makes sense. -- @@ -395,9 +389,10 @@ function leap.WaitFor:filter(pump, data) error('You must override the WaitFor.filter() method') end --- called by leap._unsolicited() for each WaitFor in leap._waitfors -function leap.WaitFor:_handle(pump, data) - item = self:filter(pump, data) +-- called by unsolicited() for each WaitFor in leap._waitfors +function leap.WaitFor:handle(pump, data) + local item = self:filter(pump, data) + debug('%s.filter() returned %s', self.name, item) -- if this item doesn't pass the filter, we're not interested if not item then return false @@ -407,13 +402,13 @@ function leap.WaitFor:_handle(pump, data) return true end --- called by WaitFor:_handle() for an accepted event +-- called by WaitFor:handle() for an accepted event function leap.WaitFor:process(item) self._queue:Enqueue(item) end -- called by leap.process() when get_event_next() raises an error -function leap.WaitFor:_exception(message) +function leap.WaitFor:exception(message) print_warning(self.name .. ' error: ' .. message) self._queue:Error(message) end diff --git a/indra/newview/scripts/lua/printf.lua b/indra/newview/scripts/lua/printf.lua new file mode 100644 index 0000000000..584cd4f391 --- /dev/null +++ b/indra/newview/scripts/lua/printf.lua @@ -0,0 +1,19 @@ +-- printf(...) is short for print(string.format(...)) + +local inspect = require 'inspect' + +local function printf(...) + -- string.format() only handles numbers and strings. + -- Convert anything else to string using the inspect module. + local args = {} + for _, arg in pairs(table.pack(...)) do + if type(arg) == 'number' or type(arg) == 'string' then + table.insert(args, arg) + else + table.insert(args, inspect(arg)) + end + end + print(string.format(table.unpack(args))) +end + +return printf diff --git a/indra/newview/tests/llluamanager_test.cpp b/indra/newview/tests/llluamanager_test.cpp index 069e10e9cf..1dd081fb98 100644 --- a/indra/newview/tests/llluamanager_test.cpp +++ b/indra/newview/tests/llluamanager_test.cpp @@ -311,23 +311,27 @@ namespace tut const std::string lua( "-- test leap.lua\n" "\n" + "fiber = require('fiber')\n" "leap = require('leap')\n" - "coro = require('coro')\n" + "-- debug = require('printf')\n" + "local function debug(...) end\n" "\n" "-- negative priority ensures catchall is always last\n" "catchall = leap.WaitFor:new(-1, 'catchall')\n" "function catchall:filter(pump, data)\n" + " debug('catchall:filter(%s, %s)', pump, data)\n" " return data\n" "end\n" "\n" "-- but first, catch events with 'special' key\n" "catch_special = leap.WaitFor:new(2, 'catch_special')\n" "function catch_special:filter(pump, data)\n" + " debug('catch_special:filter(%s, %s)', pump, data)\n" " return if data['special'] ~= nil then data else nil\n" "end\n" "\n" "function drain(waitfor)\n" - " print(waitfor.name .. ' start')\n" + " debug('%s start', waitfor.name)\n" " -- It seems as though we ought to be able to code this loop\n" " -- over waitfor:wait() as:\n" " -- for item in waitfor.wait, waitfor do\n" @@ -335,28 +339,30 @@ namespace tut " -- the coroutine call stack, which prohibits coroutine.yield():\n" " -- 'attempt to yield across metamethod/C-call boundary'\n" " -- So we resort to two different calls to waitfor:wait().\n" - " item = waitfor:wait()\n" + " local item = waitfor:wait()\n" " while item do\n" - " print(waitfor.name .. ' caught', item)\n" + " debug('%s caught %s', waitfor.name, item)\n" " item = waitfor:wait()\n" " end\n" - " print(waitfor.name .. ' done')\n" + " debug('%s done', waitfor.name)\n" "end\n" "\n" "function requester(name)\n" - " print('requester('..name..') start')\n" - " response = leap.request('testpump', {name=name})\n" - " print('requester('..name..') got '..tostring(response))\n" + " debug('requester(%s) start', name)\n" + " local response = leap.request('testpump', {name=name})\n" + " debug('requester(%s) got %s', name, response)\n" " -- verify that the correct response was dispatched to this coroutine\n" " assert(response.name == name)\n" "end\n" "\n" - "coro.launch(drain, catchall)\n" - "coro.launch(drain, catch_special)\n" - "coro.launch(requester, 'a')\n" - "coro.launch(requester, 'b')\n" - "\n" - "leap.process()\n" + "-- fiber.print_all()\n" + "fiber.launch('catchall', drain, catchall)\n" + "fiber.launch('catch_special', drain, catch_special)\n" + "fiber.launch('requester(a)', requester, 'a')\n" + "-- requester(a)\n" + "fiber.launch('requester(b)', requester, 'b')\n" + "-- fiber.print_all()\n" + "-- fiber.run()\n" ); LLSD requests; -- cgit v1.2.3 From 76752d6fc00a2789d96480da2a1e862ffecc812a Mon Sep 17 00:00:00 2001 From: Mnikolenko Productengine Date: Thu, 21 Mar 2024 18:26:48 +0200 Subject: Switch to LLDispatchListener --- indra/llui/llluafloater.cpp | 68 ++++++++-------------- indra/llui/llluafloater.h | 4 +- indra/newview/scripts/lua/test_luafloater_demo.lua | 19 +++--- .../scripts/lua/test_luafloater_gesture_list.lua | 14 +++-- 4 files changed, 44 insertions(+), 61 deletions(-) diff --git a/indra/llui/llluafloater.cpp b/indra/llui/llluafloater.cpp index 668c0edc53..4ae0e28963 100644 --- a/indra/llui/llluafloater.cpp +++ b/indra/llui/llluafloater.cpp @@ -49,53 +49,40 @@ std::map EVENT_LIST = {"CLOSE_EVENT", "floater_close"} }; + + LLLuaFloater::LLLuaFloater(const LLSD &key) : - LLFloater(key), + LLFloater(key), + mDispatchListener(LLUUID::generateNewID().asString(), "action"), mReplyPumpName(key["reply"].asString()), - mDispatcher("LLLuaFloater", "action"), mReqID(key) { - mListenerPumpName = LLUUID::generateNewID().asString(); - - mBoundListener = LLEventPumps::instance().obtain(mListenerPumpName).listen(LISTENER_NAME, [this](const LLSD &event) - { - if (event.has("action")) - { - mDispatcher.try_call(event); - } - else + auto ctrl_lookup = [this](const LLSD &event, std::function cb) + { + LLUICtrl *ctrl = getChild(event["ctrl_name"].asString()); + if (!ctrl) { - LL_WARNS("LuaFloater") << "Unknown message: " << event << LL_ENDL; + LL_WARNS("LuaFloater") << "Control not found: " << event["ctrl_name"] << LL_ENDL; + return LLSD(); } - return false; - }); + return cb(ctrl, event); + }; LLSD requiredParams = llsd::map("ctrl_name", LLSD(), "value", LLSD()); - mDispatcher.add("set_enabled", "", [this](const LLSD &event) + mDispatchListener.add("set_enabled", "", [this, ctrl_lookup](const LLSD &event) { - LLUICtrl *ctrl = getChild(event["ctrl_name"].asString()); - if(ctrl) - { - ctrl->setEnabled(event["value"].asBoolean()); - } + return ctrl_lookup(event, [](LLUICtrl *ctrl, const LLSD &event) { ctrl->setEnabled(event["value"].asBoolean()); return LLSD(); }); }, requiredParams); - mDispatcher.add("set_visible", "", [this](const LLSD &event) + mDispatchListener.add("set_visible", "", [this, ctrl_lookup](const LLSD &event) { - LLUICtrl *ctrl = getChild(event["ctrl_name"].asString()); - if(ctrl) - { - ctrl->setVisible(event["value"].asBoolean()); - } + return ctrl_lookup(event, [](LLUICtrl *ctrl, const LLSD &event) { ctrl->setVisible(event["value"].asBoolean()); return LLSD(); }); }, requiredParams); - mDispatcher.add("set_value", "", [this](const LLSD &event) + mDispatchListener.add("set_value", "", [this, ctrl_lookup](const LLSD &event) { - LLUICtrl *ctrl = getChild(event["ctrl_name"].asString()); - if(ctrl) - { - ctrl->setValue(event["value"]); - } + return ctrl_lookup(event, [](LLUICtrl *ctrl, const LLSD &event) { ctrl->setValue(event["value"]); return LLSD(); }); }, requiredParams); - mDispatcher.add("add_list_element", "", [this](const LLSD &event) + + mDispatchListener.add("add_list_element", "", [this](const LLSD &event) { LLScrollListCtrl *ctrl = getChild(event["ctrl_name"].asString()); if(ctrl) @@ -104,21 +91,14 @@ LLLuaFloater::LLLuaFloater(const LLSD &key) : } }, requiredParams); - mDispatcher.add("set_title", "", [this](const LLSD &event) + mDispatchListener.add("set_title", "", [this](const LLSD &event) { setTitle(event["value"].asString()); }, llsd::map("value", LLSD())); - mDispatcher.add("get_value", "", [this](const LLSD &event) + mDispatchListener.add("get_value", "", [this, ctrl_lookup](const LLSD &event) { - LLUICtrl *ctrl = getChild(event["ctrl_name"].asString()); - if(ctrl) - { - LLSD response; - response["value"] = ctrl->getValue(); - response["reqid"] = event["reqid"]; - post(response); - } + return ctrl_lookup(event, [](LLUICtrl *ctrl, const LLSD &event) { return llsd::map("value", ctrl->getValue()); }); }, llsd::map("ctrl_name", LLSD(), "reqid", LLSD())); } @@ -162,7 +142,7 @@ BOOL LLLuaFloater::postBuild() } //send pump name to the script after the floater is built - post(llsd::map("command_name", mListenerPumpName, "event", EVENT_LIST["POST_BUILD_EVENT"])); + post(llsd::map("command_name", mDispatchListener.getPumpName(), "event", EVENT_LIST["POST_BUILD_EVENT"])); return true; } diff --git a/indra/llui/llluafloater.h b/indra/llui/llluafloater.h index b9f96f0ad3..d4c16745ee 100644 --- a/indra/llui/llluafloater.h +++ b/indra/llui/llluafloater.h @@ -46,10 +46,8 @@ public: private: LLReqID mReqID; - LLEventDispatcher mDispatcher; - LLTempBoundListener mBoundListener; + LLDispatchListener mDispatchListener; - std::string mListenerPumpName; std::string mReplyPumpName; }; #endif diff --git a/indra/newview/scripts/lua/test_luafloater_demo.lua b/indra/newview/scripts/lua/test_luafloater_demo.lua index 2cbafcec14..308cebcb88 100644 --- a/indra/newview/scripts/lua/test_luafloater_demo.lua +++ b/indra/newview/scripts/lua/test_luafloater_demo.lua @@ -39,12 +39,19 @@ function handleEvents(event_data) elseif event_data.event == e.CLOSE_EVENT then print_warning("Floater was closed") leap.done() - --script received event pump name, after floater was built - elseif event_data.event == e.POST_BUILD_EVENT then - COMMAND_PUMP_NAME = event_data.command_name end end +local key = {xml_path = XML_FILE_PATH, op = "showLuaFloater"} +--sign for additional events for defined control {= {action1, action2, ...}} +key.extra_events={show_time_lbl = {e.RIGHT_MOUSE_DOWN_EVENT, e.DOUBLE_CLICK_EVENT}} +coro.launch(function () + --script received event pump name, after floater was built + COMMAND_PUMP_NAME = leap.request("LLFloaterReg", key)["command_name"] + leap.done() +end) +leap.process() + catch_events = leap.WaitFor:new(-1, "all_events") function catch_events:filter(pump, data) return data @@ -58,12 +65,6 @@ function process_events(waitfor) end end -local key = {xml_path = XML_FILE_PATH, op = "showLuaFloater"} - ---sign for additional events for defined control {= {action1, action2, ...}} -key.extra_events={show_time_lbl = {e.RIGHT_MOUSE_DOWN_EVENT, e.DOUBLE_CLICK_EVENT}} -leap.send("LLFloaterReg", key, "floater1") - coro.launch(process_events, catch_events) leap.process() print_warning("End of the script") diff --git a/indra/newview/scripts/lua/test_luafloater_gesture_list.lua b/indra/newview/scripts/lua/test_luafloater_gesture_list.lua index 5ea2b1e30d..57f737ce9b 100644 --- a/indra/newview/scripts/lua/test_luafloater_gesture_list.lua +++ b/indra/newview/scripts/lua/test_luafloater_gesture_list.lua @@ -40,6 +40,15 @@ function handleEvents(event_data) end end +local key = {xml_path = XML_FILE_PATH, op = "showLuaFloater"} +--receive additional events for defined control {= {action1, action2, ...}} +key.extra_events={gesture_list = {e.DOUBLE_CLICK_EVENT}} +coro.launch(function () + handleEvents(leap.request("LLFloaterReg", key)) + leap.done() +end) +leap.process() + catch_events = leap.WaitFor:new(-1, "all_events") function catch_events:filter(pump, data) return data @@ -53,10 +62,5 @@ function process_events(waitfor) end end -local key = {xml_path = XML_FILE_PATH, op = "showLuaFloater"} ---receive additional events for defined control {= {action1, action2, ...}} -key.extra_events={gesture_list = {e.DOUBLE_CLICK_EVENT}} -leap.send("LLFloaterReg", key, "floater1") - coro.launch(process_events, catch_events) leap.process() -- cgit v1.2.3 From 4ffdae72392ba2f081edf8d740b688b95ac4fc65 Mon Sep 17 00:00:00 2001 From: Mnikolenko Productengine Date: Thu, 21 Mar 2024 20:24:24 +0200 Subject: Accept an array for "add_list_item" and change EVENT_LIST type --- indra/llui/llluafloater.cpp | 73 ++++++++++++++-------- indra/llui/llluafloater.h | 1 + indra/newview/scripts/lua/test_luafloater_demo.lua | 20 ++++-- .../scripts/lua/test_luafloater_gesture_list.lua | 26 +++++--- indra/newview/scripts/lua/util.lua | 10 +++ 5 files changed, 88 insertions(+), 42 deletions(-) diff --git a/indra/llui/llluafloater.cpp b/indra/llui/llluafloater.cpp index 4ae0e28963..23a336a1e8 100644 --- a/indra/llui/llluafloater.cpp +++ b/indra/llui/llluafloater.cpp @@ -35,22 +35,19 @@ const std::string LISTENER_NAME("LLLuaFloater"); -std::map EVENT_LIST = -{ - {"COMMIT_EVENT", "commit"}, - {"DOUBLE_CLICK_EVENT", "double_click"}, - {"MOUSE_ENTER_EVENT", "mouse_enter"}, - {"MOUSE_LEAVE_EVENT", "mouse_leave"}, - {"MOUSE_DOWN_EVENT", "mouse_down"}, - {"MOUSE_UP_EVENT", "mouse_up"}, - {"RIGHT_MOUSE_DOWN_EVENT", "right_mouse_down"}, - {"RIGHT_MOUSE_UP_EVENT", "right_mouse_up"}, - {"POST_BUILD_EVENT", "post_build"}, - {"CLOSE_EVENT", "floater_close"} +std::set EVENT_LIST = { + "commit", + "double_click", + "mouse_enter", + "mouse_leave", + "mouse_down", + "mouse_up", + "right_mouse_down", + "right_mouse_up", + "post_build", + "floater_close" }; - - LLLuaFloater::LLLuaFloater(const LLSD &key) : LLFloater(key), mDispatchListener(LLUUID::generateNewID().asString(), "action"), @@ -87,7 +84,18 @@ LLLuaFloater::LLLuaFloater(const LLSD &key) : LLScrollListCtrl *ctrl = getChild(event["ctrl_name"].asString()); if(ctrl) { - ctrl->addElement(event["value"]); + LLSD element_data = event["value"]; + if (element_data.isArray()) + { + for (const auto &row : llsd::inArray(element_data)) + { + ctrl->addElement(row); + } + } + else + { + ctrl->addElement(element_data); + } } }, requiredParams); @@ -117,13 +125,12 @@ BOOL LLLuaFloater::postBuild() { LLSD data; data["ctrl_name"] = view->getName(); - data["event"] = EVENT_LIST["COMMIT_EVENT"]; ctrl->setCommitCallback([this, data](LLUICtrl *ctrl, const LLSD ¶m) { LLSD event(data); event["value"] = ctrl->getValue(); - post(event); + postEvent(event, "commit"); }); } } @@ -142,14 +149,20 @@ BOOL LLLuaFloater::postBuild() } //send pump name to the script after the floater is built - post(llsd::map("command_name", mDispatchListener.getPumpName(), "event", EVENT_LIST["POST_BUILD_EVENT"])); + postEvent(llsd::map("command_name", mDispatchListener.getPumpName()), "post_build"); return true; } void LLLuaFloater::onClose(bool app_quitting) { - post(llsd::map("event", EVENT_LIST["CLOSE_EVENT"], "app_quitting", app_quitting)); + postEvent(llsd::map("app_quitting", app_quitting), "floater_close"); +} + +bool event_is(const std::string &event_name, const std::string &list_event) +{ + llassert(EVENT_LIST.find(list_event) != EVENT_LIST.end()); + return (event_name == list_event); } void LLLuaFloater::registerCallback(const std::string &ctrl_name, const std::string &event) @@ -169,31 +182,31 @@ void LLLuaFloater::registerCallback(const std::string &ctrl_name, const std::str post(event.with("x", x).with("y", y)); }; - if (event == EVENT_LIST["MOUSE_ENTER_EVENT"]) + if (event_is(event, "mouse_enter")) { ctrl->setMouseEnterCallback(mouse_event_cb); } - else if (event == EVENT_LIST["MOUSE_LEAVE_EVENT"]) + else if (event_is(event, "mouse_leave")) { ctrl->setMouseLeaveCallback(mouse_event_cb); } - else if (event == EVENT_LIST["MOUSE_DOWN_EVENT"]) + else if (event_is(event, "mouse_down")) { ctrl->setMouseDownCallback(mouse_event_coords_cb); } - else if (event == EVENT_LIST["MOUSE_UP_EVENT"]) + else if (event_is(event, "mouse_up")) { ctrl->setMouseUpCallback(mouse_event_coords_cb); } - else if (event == EVENT_LIST["RIGHT_MOUSE_DOWN_EVENT"]) + else if (event_is(event, "right_mouse_down")) { ctrl->setRightMouseDownCallback(mouse_event_coords_cb); } - else if (event == EVENT_LIST["RIGHT_MOUSE_UP_EVENT"]) + else if (event_is(event, "right_mouse_up")) { ctrl->setRightMouseUpCallback(mouse_event_coords_cb); } - else if (event == EVENT_LIST["DOUBLE_CLICK_EVENT"]) + else if (event_is(event, "double_click")) { LLScrollListCtrl *list = dynamic_cast(ctrl); if (list) @@ -224,6 +237,12 @@ void LLLuaFloater::post(const LLSD &data) LLEventPumps::instance().obtain(mReplyPumpName).post(stamped_data); } +void LLLuaFloater::postEvent(LLSD data, const std::string &event_name) +{ + llassert(EVENT_LIST.find(event_name) != EVENT_LIST.end()); + post(data.with("event", event_name)); +} + void LLLuaFloater::showLuaFloater(const LLSD &data) { std::filesystem::path fs_path(data["xml_path"].asString()); @@ -244,7 +263,7 @@ LLSD LLLuaFloater::getEventsData() LLSD event_data; for (auto &it : EVENT_LIST) { - event_data[it.first] = it.second; + event_data.append(it); } return event_data; } diff --git a/indra/llui/llluafloater.h b/indra/llui/llluafloater.h index d4c16745ee..ccc3ccb39b 100644 --- a/indra/llui/llluafloater.h +++ b/indra/llui/llluafloater.h @@ -41,6 +41,7 @@ public: void onClose(bool app_quitting); void post(const LLSD &data); + void postEvent(LLSD data, const std::string &event); static void showLuaFloater(const LLSD &data); static LLSD getEventsData(); diff --git a/indra/newview/scripts/lua/test_luafloater_demo.lua b/indra/newview/scripts/lua/test_luafloater_demo.lua index 308cebcb88..b81259c060 100644 --- a/indra/newview/scripts/lua/test_luafloater_demo.lua +++ b/indra/newview/scripts/lua/test_luafloater_demo.lua @@ -2,17 +2,25 @@ XML_FILE_PATH = "luafloater_demo.xml" leap = require 'leap' coro = require 'coro' +util = require 'util' --event pump for sending actions to the floater COMMAND_PUMP_NAME = "" --table of floater UI events -e={} +event_list={} coro.launch(function () - e = leap.request("LLFloaterReg", {op="getFloaterEvents"})["events"] + event_list = leap.request("LLFloaterReg", {op="getFloaterEvents"})["events"] leap.done() end) leap.process() +local function _event(event_name) + if not util.contains(event_list, event_name) then + print_warning("Incorrect event name: " .. event_name) + end + return event_name +end + function post(action) leap.send(COMMAND_PUMP_NAME, action) end @@ -23,7 +31,7 @@ function getCurrentTime() end function handleEvents(event_data) - if event_data.event == e.COMMIT_EVENT then + if event_data.event == _event("commit") then if event_data.ctrl_name == "disable_ctrl" then post({action="set_enabled", ctrl_name="open_btn", value = (1 - event_data.value)}) elseif event_data.ctrl_name == "title_cmb" then @@ -32,11 +40,11 @@ function handleEvents(event_data) floater_name = leap.request(COMMAND_PUMP_NAME, {action="get_value", ctrl_name='openfloater_cmd'})['value'] leap.send("LLFloaterReg", {name = floater_name, op = "showInstance"}) end - elseif event_data.event == e.DOUBLE_CLICK_EVENT then + elseif event_data.event == _event("double_click") then if event_data.ctrl_name == "show_time_lbl" then post({action="set_value", ctrl_name="time_lbl", value= getCurrentTime()}) end - elseif event_data.event == e.CLOSE_EVENT then + elseif event_data.event == _event("floater_close") then print_warning("Floater was closed") leap.done() end @@ -44,7 +52,7 @@ end local key = {xml_path = XML_FILE_PATH, op = "showLuaFloater"} --sign for additional events for defined control {= {action1, action2, ...}} -key.extra_events={show_time_lbl = {e.RIGHT_MOUSE_DOWN_EVENT, e.DOUBLE_CLICK_EVENT}} +key.extra_events={show_time_lbl = {_event("right_mouse_down"), _event("double_click")}} coro.launch(function () --script received event pump name, after floater was built COMMAND_PUMP_NAME = leap.request("LLFloaterReg", key)["command_name"] diff --git a/indra/newview/scripts/lua/test_luafloater_gesture_list.lua b/indra/newview/scripts/lua/test_luafloater_gesture_list.lua index 57f737ce9b..b46e36b4d9 100644 --- a/indra/newview/scripts/lua/test_luafloater_gesture_list.lua +++ b/indra/newview/scripts/lua/test_luafloater_gesture_list.lua @@ -2,26 +2,34 @@ XML_FILE_PATH = "luafloater_gesture_list.xml" leap = require 'leap' coro = require 'coro' +util = require 'util' LLGesture = require 'LLGesture' --event pump for sending actions to the floater COMMAND_PUMP_NAME = "" --table of floater UI events -e={} +event_list={} coro.launch(function () - e = leap.request("LLFloaterReg", {op="getFloaterEvents"})["events"] + event_list = leap.request("LLFloaterReg", {op="getFloaterEvents"})["events"] leap.done() end) leap.process() +local function _event(event_name) + if not util.contains(event_list, event_name) then + print_warning("Incorrect event name: " .. event_name) + end + return event_name +end + function post(action) leap.send(COMMAND_PUMP_NAME, action) end function handleEvents(event_data) - if event_data.event == e.CLOSE_EVENT then + if event_data.event == _event("floater_close") then leap.done() - elseif event_data.event == e.POST_BUILD_EVENT then + elseif event_data.event == _event("post_build") then COMMAND_PUMP_NAME = event_data.command_name gestures_uuid = LLGesture.getActiveGestures() local action_data = {} @@ -29,11 +37,11 @@ function handleEvents(event_data) action_data.ctrl_name = "gesture_list" gestures = {} for uuid, info in pairs(gestures_uuid) do - element={value = uuid, columns ={column = "gesture_name", value = info.name}} - action_data.value = element - post(action_data) + table.insert(gestures, {value = uuid, columns ={column = "gesture_name", value = info.name}}) end - elseif event_data.event == e.DOUBLE_CLICK_EVENT then + action_data.value = gestures + post(action_data) + elseif event_data.event == _event("double_click") then if event_data.ctrl_name == "gesture_list" then LLGesture.startGesture(event_data.value) end @@ -42,7 +50,7 @@ end local key = {xml_path = XML_FILE_PATH, op = "showLuaFloater"} --receive additional events for defined control {= {action1, action2, ...}} -key.extra_events={gesture_list = {e.DOUBLE_CLICK_EVENT}} +key.extra_events={gesture_list = {_event("double_click")}} coro.launch(function () handleEvents(leap.request("LLFloaterReg", key)) leap.done() diff --git a/indra/newview/scripts/lua/util.lua b/indra/newview/scripts/lua/util.lua index e3af633ea7..5d6042dfe5 100644 --- a/indra/newview/scripts/lua/util.lua +++ b/indra/newview/scripts/lua/util.lua @@ -36,4 +36,14 @@ function util.equal(t1, t2) return util.empty(temp) end +-- check if array-like table contains certain value +function util.contains(t, v) + for _, value in ipairs(t) do + if value == v then + return true + end + end + return false +end + return util -- cgit v1.2.3 From 7b149485e091880269dd9d7941cca47da49e652f Mon Sep 17 00:00:00 2001 From: Mnikolenko Productengine Date: Thu, 21 Mar 2024 22:05:37 +0200 Subject: mac build fix --- indra/llui/llluafloater.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/indra/llui/llluafloater.cpp b/indra/llui/llluafloater.cpp index 23a336a1e8..afc287a864 100644 --- a/indra/llui/llluafloater.cpp +++ b/indra/llui/llluafloater.cpp @@ -66,15 +66,15 @@ LLLuaFloater::LLLuaFloater(const LLSD &key) : }; LLSD requiredParams = llsd::map("ctrl_name", LLSD(), "value", LLSD()); - mDispatchListener.add("set_enabled", "", [this, ctrl_lookup](const LLSD &event) + mDispatchListener.add("set_enabled", "", [ctrl_lookup](const LLSD &event) { return ctrl_lookup(event, [](LLUICtrl *ctrl, const LLSD &event) { ctrl->setEnabled(event["value"].asBoolean()); return LLSD(); }); }, requiredParams); - mDispatchListener.add("set_visible", "", [this, ctrl_lookup](const LLSD &event) + mDispatchListener.add("set_visible", "", [ctrl_lookup](const LLSD &event) { return ctrl_lookup(event, [](LLUICtrl *ctrl, const LLSD &event) { ctrl->setVisible(event["value"].asBoolean()); return LLSD(); }); }, requiredParams); - mDispatchListener.add("set_value", "", [this, ctrl_lookup](const LLSD &event) + mDispatchListener.add("set_value", "", [ctrl_lookup](const LLSD &event) { return ctrl_lookup(event, [](LLUICtrl *ctrl, const LLSD &event) { ctrl->setValue(event["value"]); return LLSD(); }); }, requiredParams); @@ -104,7 +104,7 @@ LLLuaFloater::LLLuaFloater(const LLSD &key) : setTitle(event["value"].asString()); }, llsd::map("value", LLSD())); - mDispatchListener.add("get_value", "", [this, ctrl_lookup](const LLSD &event) + mDispatchListener.add("get_value", "", [ctrl_lookup](const LLSD &event) { return ctrl_lookup(event, [](LLUICtrl *ctrl, const LLSD &event) { return llsd::map("value", ctrl->getValue()); }); }, llsd::map("ctrl_name", LLSD(), "reqid", LLSD())); -- cgit v1.2.3 From bb39a8b223f78205a10ffcb61e3b3bfe05b3fd1a Mon Sep 17 00:00:00 2001 From: Nat Goodspeed Date: Fri, 22 Mar 2024 21:04:48 +0900 Subject: Fix a couple bugs in fiber.lua machinery. This fixes a hang if the Lua script explicitly calls fiber.run() before LuaState::expr()'s implicit fiber.run() call. Make fiber.run() remove the calling fiber from the ready list to avoid an infinite loop when all other fibers have terminated: "You're ready!" "Okay, yield()." "You're ready again!" ... But don't claim it's waiting, either, because then when all other fibers have terminated, we'd call idle() in the vain hope that something would make that one last fiber ready. WaitQueue:_wake_waiters() needs to wake waiting fibers if the queue's not empty OR it's been closed. Introduce leap.WaitFor:close() to close the queue gracefully so that a looping waiter can terminate, instead of using WaitFor:exception(), which stops the whole script once it propagates. Make leap's cleanup() function call close(). Streamline fiber.get_name() by using 'or' instead of if ... then. Streamline fiber.status() and fiber.set_waiting() by using table.find() instead of a loop. --- indra/newview/scripts/lua/ErrorQueue.lua | 4 +++ indra/newview/scripts/lua/WaitQueue.lua | 2 +- indra/newview/scripts/lua/fiber.lua | 42 ++++++++++++++----------------- indra/newview/scripts/lua/leap.lua | 9 +++++-- indra/newview/tests/llluamanager_test.cpp | 2 +- 5 files changed, 32 insertions(+), 27 deletions(-) diff --git a/indra/newview/scripts/lua/ErrorQueue.lua b/indra/newview/scripts/lua/ErrorQueue.lua index a6d4470044..076742815a 100644 --- a/indra/newview/scripts/lua/ErrorQueue.lua +++ b/indra/newview/scripts/lua/ErrorQueue.lua @@ -3,18 +3,22 @@ -- raise that error. local WaitQueue = require('WaitQueue') +-- local debug = require('printf') +local function debug(...) end local ErrorQueue = WaitQueue:new() function ErrorQueue:Error(message) -- Setting Error() is a marker, like closing the queue. Once we reach the -- error, every subsequent Dequeue() call will raise the same error. + debug('Setting self._closed to %q', message) self._closed = message self:_wake_waiters() end function ErrorQueue:Dequeue() local value = WaitQueue.Dequeue(self) + debug('ErrorQueue:Dequeue: base Dequeue() got %s', value) if value ~= nil then -- queue not yet closed, show caller return value diff --git a/indra/newview/scripts/lua/WaitQueue.lua b/indra/newview/scripts/lua/WaitQueue.lua index b15e9c443b..f69baff09b 100644 --- a/indra/newview/scripts/lua/WaitQueue.lua +++ b/indra/newview/scripts/lua/WaitQueue.lua @@ -38,7 +38,7 @@ function WaitQueue:_wake_waiters() -- Unlike OS threads, with cooperative concurrency it doesn't make sense -- to "notify all": we need wake only one of the waiting Dequeue() -- callers. - if not self:IsEmpty() and next(self._waiters) then + if ((not self:IsEmpty()) or self._closed) and next(self._waiters) then -- Pop the oldest waiting coroutine instead of the most recent, for -- more-or-less round robin fairness. But skip any coroutines that -- have gone dead in the meantime. diff --git a/indra/newview/scripts/lua/fiber.lua b/indra/newview/scripts/lua/fiber.lua index f18d133cc8..8ed99f12b7 100644 --- a/indra/newview/scripts/lua/fiber.lua +++ b/indra/newview/scripts/lua/fiber.lua @@ -104,13 +104,7 @@ end -- Query a fiber's name (nil for the running fiber) function fiber.get_name(co) - if not co then - co = fiber.running() - end - if not names[co] then - return 'unknown' - end - return names[co] + return names[co or fiber.running()] or 'unknown' end -- Query status of the passed fiber @@ -140,10 +134,8 @@ function fiber.status(co) return 'waiting' end -- not waiting should imply ready: sanity check - for _, maybe in pairs(ready) do - if maybe == co then - return 'ready' - end + if table.find(ready, co) then + return 'ready' end -- Calls within yield() between popping the next ready fiber and -- re-appending it to the list are in this state. Once we're done @@ -158,11 +150,9 @@ local function set_waiting() -- if called from the main fiber, inject a 'main' marker into the list co = fiber.running() -- delete from ready list - for i, maybe in pairs(ready) do - if maybe == co then - table.remove(ready, i) - break - end + local i = table.find(ready, co) + if i then + table.remove(ready, i) end -- add to waiting list waiting[co] = true @@ -191,11 +181,16 @@ end -- Run fibers until all but main have terminated: return nil. -- Or until configured idle() callback returns x ~= nil: return x. function fiber.run() - -- A fiber calling run() is not also doing other useful work. Tell yield() - -- that we're waiting. Otherwise it would keep seeing that our caller is - -- ready and return to us, instead of realizing that all coroutines are - -- waiting and call idle(). - set_waiting() + -- A fiber calling run() is not also doing other useful work. Remove the + -- calling fiber from the ready list. Otherwise yield() would keep seeing + -- that our caller is ready and return to us, instead of realizing that + -- all coroutines are waiting and call idle(). But don't say we're + -- waiting, either, because then when all other fibers have terminated + -- we'd call idle() forever waiting for something to make us ready again. + local i = table.find(ready, fiber.running()) + if i then + table.remove(ready, i) + end local others, idle_done repeat debug('%s calling fiber.run() calling yield()', fiber.get_name()) @@ -204,9 +199,10 @@ function fiber.run() tostring(others), tostring(idle_done)) until (not others) debug('%s fiber.run() done', fiber.get_name()) - fiber.wake(fiber.running()) + -- For whatever it's worth, put our own fiber back in the ready list. + table.insert(ready, fiber.running()) -- Once there are no more waiting fibers, and the only ready fiber is - -- main, return to main. All previously-launched fibers are done. Possibly + -- us, return to caller. All previously-launched fibers are done. Possibly -- the chunk is done, or the chunk may decide to launch a new batch of -- fibers. return idle_done diff --git a/indra/newview/scripts/lua/leap.lua b/indra/newview/scripts/lua/leap.lua index 60e8266a76..77f3a3e116 100644 --- a/indra/newview/scripts/lua/leap.lua +++ b/indra/newview/scripts/lua/leap.lua @@ -198,10 +198,10 @@ end local function cleanup(message) -- we're done: clean up all pending coroutines for i, waitfor in pairs(leap._pending) do - waitfor:exception(message) + waitfor:close() end for i, waitfor in pairs(leap._waitfors) do - waitfor:exception(message) + waitfor:close() end end @@ -407,6 +407,11 @@ function leap.WaitFor:process(item) self._queue:Enqueue(item) end +-- called by cleanup() at end +function leap.WaitFor:close() + self._queue:close() +end + -- called by leap.process() when get_event_next() raises an error function leap.WaitFor:exception(message) print_warning(self.name .. ' error: ' .. message) diff --git a/indra/newview/tests/llluamanager_test.cpp b/indra/newview/tests/llluamanager_test.cpp index 1dd081fb98..d1beed84ef 100644 --- a/indra/newview/tests/llluamanager_test.cpp +++ b/indra/newview/tests/llluamanager_test.cpp @@ -361,8 +361,8 @@ namespace tut "fiber.launch('requester(a)', requester, 'a')\n" "-- requester(a)\n" "fiber.launch('requester(b)', requester, 'b')\n" + "fiber.run()\n" "-- fiber.print_all()\n" - "-- fiber.run()\n" ); LLSD requests; -- cgit v1.2.3 From de1fc577666686fb0c3f8b38d8c6c90eb6dff414 Mon Sep 17 00:00:00 2001 From: Nat Goodspeed Date: Sat, 23 Mar 2024 17:14:33 +0900 Subject: Update sendReply(): accepting LLSD by value already copies it. --- indra/llcommon/llevents.cpp | 13 +++++-------- indra/llcommon/llevents.h | 2 +- 2 files changed, 6 insertions(+), 9 deletions(-) diff --git a/indra/llcommon/llevents.cpp b/indra/llcommon/llevents.cpp index 01bba7a620..667e047fd3 100644 --- a/indra/llcommon/llevents.cpp +++ b/indra/llcommon/llevents.cpp @@ -726,7 +726,7 @@ void LLReqID::stamp(LLSD& response) const response["reqid"] = mReqid; } -bool sendReply(const LLSD& reply, const LLSD& request, const std::string& replyKey) +bool sendReply(LLSD reply, const LLSD& request, const std::string& replyKey) { // If the original request has no value for replyKey, it's pointless to // construct or send a reply event: on which LLEventPump should we send @@ -739,13 +739,10 @@ bool sendReply(const LLSD& reply, const LLSD& request, const std::string& replyK // Here the request definitely contains replyKey; reasonable to proceed. - // Copy 'reply' to modify it. - LLSD newreply(reply); // Get the ["reqid"] element from request LLReqID reqID(request); - // and copy it to 'newreply'. - reqID.stamp(newreply); - // Send reply on LLEventPump named in request[replyKey]. Don't forget to - // send the modified 'newreply' instead of the original 'reply'. - return LLEventPumps::instance().obtain(request[replyKey]).post(newreply); + // and copy it to 'reply'. + reqID.stamp(reply); + // Send reply on LLEventPump named in request[replyKey]. + return LLEventPumps::instance().obtain(request[replyKey]).post(reply); } diff --git a/indra/llcommon/llevents.h b/indra/llcommon/llevents.h index c1dbf4392f..1f35a6de18 100644 --- a/indra/llcommon/llevents.h +++ b/indra/llcommon/llevents.h @@ -779,7 +779,7 @@ private: * Before sending the reply event, sendReply() copies the ["reqid"] item from * the request to the reply. */ -LL_COMMON_API bool sendReply(const LLSD& reply, const LLSD& request, +LL_COMMON_API bool sendReply(LLSD reply, const LLSD& request, const std::string& replyKey="reply"); #endif /* ! defined(LL_LLEVENTS_H) */ -- cgit v1.2.3 From 2dc003779443db99f46b3db6d17a1954f7b141dd Mon Sep 17 00:00:00 2001 From: Nat Goodspeed Date: Sat, 23 Mar 2024 17:43:07 +0900 Subject: Make leap.request() work even from Lua's main thread. Recast fiber.yield() as internal function scheduler(). Move fiber.run() after it so it can call scheduler() as a local function. Add new fiber.yield() that also calls scheduler(); the added value of this new fiber.yield() over plain scheduler() is that if scheduler() returns before the caller is ready (because the configured set_idle() function returned non-nil), it produces an explicit error rather than returning to its caller. So the caller can assume that when fiber.yield() returns normally, the calling fiber is ready. This allows any fiber, including the main thread, to call fiber.yield() or fiber.wait(). This supports using leap.request(), which posts a request and then waits on a WaitForReqid, which calls ErrorQueue:Dequeue(), which calls fiber.wait(). WaitQueue:_wake_waiters() must call fiber.status() instead of coroutine.status() so it understands the special token 'main'. Add a new llluamanager_test.cpp test to exercise calling leap.request() from Lua's main thread. --- indra/newview/scripts/lua/WaitQueue.lua | 2 +- indra/newview/scripts/lua/fiber.lua | 106 ++++++++++++++++++++---------- indra/newview/tests/llluamanager_test.cpp | 43 ++++++++++-- 3 files changed, 108 insertions(+), 43 deletions(-) diff --git a/indra/newview/scripts/lua/WaitQueue.lua b/indra/newview/scripts/lua/WaitQueue.lua index f69baff09b..a34dbef4d7 100644 --- a/indra/newview/scripts/lua/WaitQueue.lua +++ b/indra/newview/scripts/lua/WaitQueue.lua @@ -43,7 +43,7 @@ function WaitQueue:_wake_waiters() -- more-or-less round robin fairness. But skip any coroutines that -- have gone dead in the meantime. local waiter = table.remove(self._waiters, 1) - while waiter and coroutine.status(waiter) ~= "suspended" do + while waiter and fiber.status(waiter) == "dead" do waiter = table.remove(self._waiters, 1) end -- do we still have at least one waiting coroutine? diff --git a/indra/newview/scripts/lua/fiber.lua b/indra/newview/scripts/lua/fiber.lua index 8ed99f12b7..7dc67f510c 100644 --- a/indra/newview/scripts/lua/fiber.lua +++ b/indra/newview/scripts/lua/fiber.lua @@ -178,36 +178,6 @@ function fiber.wake(co) -- but don't yet resume it: that happens next time we reach yield() end --- Run fibers until all but main have terminated: return nil. --- Or until configured idle() callback returns x ~= nil: return x. -function fiber.run() - -- A fiber calling run() is not also doing other useful work. Remove the - -- calling fiber from the ready list. Otherwise yield() would keep seeing - -- that our caller is ready and return to us, instead of realizing that - -- all coroutines are waiting and call idle(). But don't say we're - -- waiting, either, because then when all other fibers have terminated - -- we'd call idle() forever waiting for something to make us ready again. - local i = table.find(ready, fiber.running()) - if i then - table.remove(ready, i) - end - local others, idle_done - repeat - debug('%s calling fiber.run() calling yield()', fiber.get_name()) - others, idle_done = fiber.yield() - debug("%s fiber.run()'s yield() returned %s, %s", fiber.get_name(), - tostring(others), tostring(idle_done)) - until (not others) - debug('%s fiber.run() done', fiber.get_name()) - -- For whatever it's worth, put our own fiber back in the ready list. - table.insert(ready, fiber.running()) - -- Once there are no more waiting fibers, and the only ready fiber is - -- us, return to caller. All previously-launched fibers are done. Possibly - -- the chunk is done, or the chunk may decide to launch a new batch of - -- fibers. - return idle_done -end - -- pop and return the next not-dead fiber in the ready list, or nil if none remain local function live_ready_iter() -- don't write @@ -237,16 +207,24 @@ local function prune_waiting() end end --- Give other ready fibers a chance to run, leaving this one ready, returning --- after a cycle. Returns: --- * true, nil if there remain other live fibers, whether ready or waiting +-- Run other ready fibers, leaving this one ready, returning after a cycle. +-- Returns: +-- * true, nil if there remain other live fibers, whether ready or waiting, +-- but it's our turn to run -- * false, nil if this is the only remaining fiber --- * nil, x if configured idle() callback returned non-nil x -function fiber.yield() +-- * nil, x if configured idle() callback returns non-nil x +local function scheduler() + -- scheduler() is asymmetric because Lua distinguishes the main thread + -- from other coroutines. The main thread can't yield; it can only resume + -- other coroutines. So although an arbitrary coroutine could resume still + -- other arbitrary coroutines, it could NOT resume the main thread because + -- the main thread can't yield. Therefore, scheduler() delegates its real + -- processing to the main thread. If called from a coroutine, pass control + -- back to the main thread. if coroutine.running() then -- seize the opportunity to make sure the viewer isn't shutting down -- check_stop() - -- this is a real coroutine, yield normally to main or whoever + -- this is a real coroutine, yield normally to main thread coroutine.yield() -- main certainly still exists return true @@ -294,4 +272,60 @@ function fiber.yield() until false end +-- Let other fibers run. This is useful in either of two cases: +-- * fiber.wait() calls this to run other fibers while this one is waiting. +-- fiber.yield() (and therefore fiber.wait()) works from the main thread as +-- well as from explicitly-launched fibers, without the caller having to +-- care. +-- * A long-running fiber that doesn't often call fiber.wait() should sprinkle +-- in fiber.yield() calls to interleave processing on other fibers. +function fiber.yield() + -- The difference between this and fiber.run() is that fiber.yield() + -- assumes its caller has work to do. yield() returns to its caller as + -- soon as scheduler() pops this fiber from the ready list. fiber.run() + -- continues looping until all other fibers have terminated, or the + -- set_idle() callback tells it to stop. + local others, idle_done = scheduler() + -- scheduler() returns either if we're ready, or if idle_done ~= nil. + if idle_done ~= nil then + -- Returning normally from yield() means the caller can carry on with + -- its pending work. But in this case scheduler() returned because the + -- configured set_idle() function interrupted it -- not because we're + -- actually ready. Don't return normally. + error('fiber.set_idle() interrupted yield() with: ' .. tostring(idle_done)) + end + -- We're ready! Just return to caller. In this situation we don't care + -- whether there are other ready fibers. +end + +-- Run fibers until all but main have terminated: return nil. +-- Or until configured idle() callback returns x ~= nil: return x. +function fiber.run() + -- A fiber calling run() is not also doing other useful work. Remove the + -- calling fiber from the ready list. Otherwise yield() would keep seeing + -- that our caller is ready and return to us, instead of realizing that + -- all coroutines are waiting and call idle(). But don't say we're + -- waiting, either, because then when all other fibers have terminated + -- we'd call idle() forever waiting for something to make us ready again. + local i = table.find(ready, fiber.running()) + if i then + table.remove(ready, i) + end + local others, idle_done + repeat + debug('%s calling fiber.run() calling scheduler()', fiber.get_name()) + others, idle_done = scheduler() + debug("%s fiber.run()'s scheduler() returned %s, %s", fiber.get_name(), + tostring(others), tostring(idle_done)) + until (not others) + debug('%s fiber.run() done', fiber.get_name()) + -- For whatever it's worth, put our own fiber back in the ready list. + table.insert(ready, fiber.running()) + -- Once there are no more waiting fibers, and the only ready fiber is + -- us, return to caller. All previously-launched fibers are done. Possibly + -- the chunk is done, or the chunk may decide to launch a new batch of + -- fibers. + return idle_done +end + return fiber diff --git a/indra/newview/tests/llluamanager_test.cpp b/indra/newview/tests/llluamanager_test.cpp index d1beed84ef..0fd91c1354 100644 --- a/indra/newview/tests/llluamanager_test.cpp +++ b/indra/newview/tests/llluamanager_test.cpp @@ -307,9 +307,43 @@ namespace tut template<> template<> void object::test<5>() { - set_test_name("test leap.lua"); + set_test_name("leap.request() from main thread"); const std::string lua( - "-- test leap.lua\n" + "-- leap.request() from main thread\n" + "\n" + "leap = require 'leap'\n" + "\n" + "return {\n" + " a=leap.request('echo', {data='a'}).data,\n" + " b=leap.request('echo', {data='b'}).data\n" + "}\n" + ); + + LLEventStream pump("echo", false); + LLTempBoundListener conn{ + pump.listen( + "test<5>()", + listener([](const LLSD& data) + { + LL_DEBUGS("Lua") << "echo pump got: " << data << LL_ENDL; + LLEventPumps::instance().post( + data["reply"], + llsd::map("reqid", data["reqid"], "data", data["data"])); + })) + }; + + LuaState L; + auto [count, result] = LLLUAmanager::waitScriptLine(L, lua); + ensure_equals("Lua script didn't return item", count, 1); + ensure_equals("echo failed", result, llsd::map("a", "a", "b", "b")); + } + + template<> template<> + void object::test<6>() + { + set_test_name("interleave leap.request() responses"); + const std::string lua( + "-- interleave leap.request() responses\n" "\n" "fiber = require('fiber')\n" "leap = require('leap')\n" @@ -359,16 +393,13 @@ namespace tut "fiber.launch('catchall', drain, catchall)\n" "fiber.launch('catch_special', drain, catch_special)\n" "fiber.launch('requester(a)', requester, 'a')\n" - "-- requester(a)\n" "fiber.launch('requester(b)', requester, 'b')\n" - "fiber.run()\n" - "-- fiber.print_all()\n" ); LLSD requests; LLEventStream pump("testpump", false); LLTempBoundListener conn{ - pump.listen("test<5>()", + pump.listen("test<6>()", listener([&requests](const LLSD& data) { LL_DEBUGS("Lua") << "testpump got: " << data << LL_ENDL; -- cgit v1.2.3 From 93b30f960ea327fe3c36deed72a8075ce0d13f19 Mon Sep 17 00:00:00 2001 From: Nat Goodspeed Date: Sun, 24 Mar 2024 02:16:55 +0900 Subject: Introduce LLStreamListener: bundle LLEventStream+LLTempBoundListener. This is a very common pattern, especially in test code, but elsewhere in the viewer too. Use it in llluamanager_test.cpp. --- indra/llcommon/llevents.h | 43 +++++++++++++++++++++++++ indra/newview/tests/llluamanager_test.cpp | 53 ++++++++++++------------------- 2 files changed, 64 insertions(+), 32 deletions(-) diff --git a/indra/llcommon/llevents.h b/indra/llcommon/llevents.h index 1f35a6de18..ebc893d1e6 100644 --- a/indra/llcommon/llevents.h +++ b/indra/llcommon/llevents.h @@ -151,6 +151,8 @@ typedef boost::signals2::signal LL /// Methods that forward listeners (e.g. constructed with /// boost::bind()) should accept (const LLEventListener&) typedef LLStandardSignal::slot_type LLEventListener; +/// Accept a void listener too +typedef std::function LLVoidListener; /// Result of registering a listener, supports connected(), /// disconnect() and blocked() typedef boost::signals2::connection LLBoundListener; @@ -158,6 +160,23 @@ typedef boost::signals2::connection LLBoundListener; /// referenced listener when the LLTempBoundListener instance is destroyed. typedef boost::signals2::scoped_connection LLTempBoundListener; +/// Accepting (const LLListener&) allows either LLEventListener or LLVoidListener +/// TODO: but compiler considers the constructor call ambiguous?? +class LLListener +{ +public: + LLListener(const LLEventListener& listener): + mListener(listener) + {} + LLListener(const LLVoidListener& listener): + mListener([listener](const LLSD& data){ listener(data); return false; }) + {} + operator LLEventListener() const { return mListener; } + +private: + LLEventListener mListener; +}; + /** * A common idiom for event-based code is to accept either a callable -- * directly called on completion -- or the string name of an LLEventPump on @@ -687,6 +706,30 @@ private: EventList mEventHistory; }; +/***************************************************************************** +* LLNamedListener +*****************************************************************************/ +/** + * LLNamedListener bundles a concrete LLEventPump subclass with a specific + * listener function, with an LLTempBoundListener to ensure that it's + * disconnected before destruction. + */ +template +class LL_COMMON_API LLNamedListener: PUMP +{ + using pump_t = PUMP; +public: + template + LLNamedListener(const std::string& name, LISTENER&& listener): + pump_t(name, false), // don't tweak the name + mConn(pump_t::listen("func", std::forward(listener))) + {} + +private: + LLTempBoundListener mConn; +}; +using LLStreamListener = LLNamedListener<>; + /***************************************************************************** * LLReqID *****************************************************************************/ diff --git a/indra/newview/tests/llluamanager_test.cpp b/indra/newview/tests/llluamanager_test.cpp index 0fd91c1354..872d7827fe 100644 --- a/indra/newview/tests/llluamanager_test.cpp +++ b/indra/newview/tests/llluamanager_test.cpp @@ -105,10 +105,8 @@ namespace tut void from_lua(const std::string& desc, const std::string_view& construct, const LLSD& expect) { LLSD fromlua; - LLEventStream replypump("testpump"); - LLTempBoundListener conn( - replypump.listen("llluamanager_test", - listener([&fromlua](const LLSD& data){ fromlua = data; }))); + LLStreamListener pump("testpump", + listener([&fromlua](const LLSD& data){ fromlua = data; })); const std::string lua(stringize( "data = ", construct, "\n" "post_on('testpump', data)\n" @@ -137,11 +135,9 @@ namespace tut { set_test_name("test post_on(), get_event_pumps(), get_event_next()"); StringVec posts; - LLEventStream replypump("testpump"); - LLTempBoundListener conn( - replypump.listen("test<3>", - listener([&posts](const LLSD& data) - { posts.push_back(data.asString()); }))); + LLStreamListener pump("testpump", + listener([&posts](const LLSD& data) + { posts.push_back(data.asString()); })); const std::string lua( "-- test post_on,get_event_pumps,get_event_next\n" "post_on('testpump', 'entry')\n" @@ -180,7 +176,7 @@ namespace tut void round_trip(const std::string& desc, const LLSD& send, const LLSD& expect) { - LLEventMailDrop replypump("testpump"); + LLEventMailDrop testpump("testpump"); const std::string lua( "-- test LLSD round trip\n" "replypump, cmdpump = get_event_pumps()\n" @@ -194,7 +190,7 @@ namespace tut // reached the get_event_next() call, which suspends the calling C++ // coroutine (including the Lua code running on it) until we post // something to that reply pump. - auto luapump{ llcoro::suspendUntilEventOn(replypump).asString() }; + auto luapump{ llcoro::suspendUntilEventOn(testpump).asString() }; LLEventPumps::instance().post(luapump, send); // The C++ coroutine running the Lua script is now ready to run. Run // it so it will echo the LLSD back to us. @@ -319,18 +315,13 @@ namespace tut "}\n" ); - LLEventStream pump("echo", false); - LLTempBoundListener conn{ - pump.listen( - "test<5>()", - listener([](const LLSD& data) - { - LL_DEBUGS("Lua") << "echo pump got: " << data << LL_ENDL; - LLEventPumps::instance().post( - data["reply"], - llsd::map("reqid", data["reqid"], "data", data["data"])); - })) - }; + LLStreamListener pump( + "echo", + listener([](const LLSD& data) + { + LL_DEBUGS("Lua") << "echo pump got: " << data << LL_ENDL; + sendReply(data, data); + })); LuaState L; auto [count, result] = LLLUAmanager::waitScriptLine(L, lua); @@ -397,15 +388,13 @@ namespace tut ); LLSD requests; - LLEventStream pump("testpump", false); - LLTempBoundListener conn{ - pump.listen("test<6>()", - listener([&requests](const LLSD& data) - { - LL_DEBUGS("Lua") << "testpump got: " << data << LL_ENDL; - requests.append(data); - })) - }; + LLStreamListener pump( + "testpump", + listener([&requests](const LLSD& data) + { + LL_DEBUGS("Lua") << "testpump got: " << data << LL_ENDL; + requests.append(data); + })); LuaState L; auto future = LLLUAmanager::startScriptLine(L, lua); -- cgit v1.2.3 From 41e14d35ae2dfa644716cb195545d59c468538c5 Mon Sep 17 00:00:00 2001 From: Mnikolenko Productengine Date: Mon, 25 Mar 2024 15:06:11 +0200 Subject: Add keystroke event support and allow adding text lines to the line editor --- indra/llui/llluafloater.cpp | 40 ++++++++++++++++++---- indra/newview/scripts/lua/luafloater_demo.xml | 13 ++++++- indra/newview/scripts/lua/test_luafloater_demo.lua | 4 +-- .../scripts/lua/test_luafloater_gesture_list.lua | 3 +- indra/newview/scripts/lua/util.lua | 10 ------ 5 files changed, 48 insertions(+), 22 deletions(-) diff --git a/indra/llui/llluafloater.cpp b/indra/llui/llluafloater.cpp index afc287a864..8f624788c2 100644 --- a/indra/llui/llluafloater.cpp +++ b/indra/llui/llluafloater.cpp @@ -32,6 +32,7 @@ #include "llcheckboxctrl.h" #include "llcombobox.h" #include "llscrolllistctrl.h" +#include "lltexteditor.h" const std::string LISTENER_NAME("LLLuaFloater"); @@ -45,7 +46,8 @@ std::set EVENT_LIST = { "right_mouse_down", "right_mouse_up", "post_build", - "floater_close" + "floater_close", + "keystroke" }; LLLuaFloater::LLLuaFloater(const LLSD &key) : @@ -99,6 +101,16 @@ LLLuaFloater::LLLuaFloater(const LLSD &key) : } }, requiredParams); + mDispatchListener.add("add_text", "", [this](const LLSD &event) + { + LLTextEditor *editor = getChild(event["ctrl_name"].asString()); + if (editor) + { + editor->pasteTextWithLinebreaks(stringize(event["value"])); + editor->addLineBreakChar(true); + } + }, requiredParams); + mDispatchListener.add("set_title", "", [this](const LLSD &event) { setTitle(event["value"].asString()); @@ -182,6 +194,12 @@ void LLLuaFloater::registerCallback(const std::string &ctrl_name, const std::str post(event.with("x", x).with("y", y)); }; + auto post_with_value = [this, data](LLSD& value) + { + LLSD event(data); + post(event.with("value", value)); + }; + if (event_is(event, "mouse_enter")) { ctrl->setMouseEnterCallback(mouse_event_cb); @@ -211,18 +229,26 @@ void LLLuaFloater::registerCallback(const std::string &ctrl_name, const std::str LLScrollListCtrl *list = dynamic_cast(ctrl); if (list) { - list->setDoubleClickCallback( - [this, data, list]() - { - LLSD event(data); - post(event.with("value", list->getCurrentID())); - }); + list->setDoubleClickCallback( [this, post_with_value, list](){ post_with_value(LLSD(list->getCurrentID())); }); } else { ctrl->setDoubleClickCallback(mouse_event_coords_cb); } } + else if (event_is(event, "keystroke")) + { + LLTextEditor* text_editor = dynamic_cast(ctrl); + if (text_editor) + { + text_editor->setKeystrokeCallback([this, post_with_value](LLTextEditor *editor) { post_with_value(editor->getValue()); }); + } + LLLineEditor* line_editor = dynamic_cast(ctrl); + if (line_editor) + { + line_editor->setKeystrokeCallback([this, post_with_value](LLLineEditor *editor, void* userdata) { post_with_value(editor->getValue()); }, NULL); + } + } else { LL_WARNS("LuaFloater") << "Can't register callback for unknown event: " << event << " , control: " << ctrl_name << LL_ENDL; diff --git a/indra/newview/scripts/lua/luafloater_demo.xml b/indra/newview/scripts/lua/luafloater_demo.xml index 069f229128..b2273d7718 100644 --- a/indra/newview/scripts/lua/luafloater_demo.xml +++ b/indra/newview/scripts/lua/luafloater_demo.xml @@ -1,7 +1,7 @@ + diff --git a/indra/newview/scripts/lua/test_luafloater_demo.lua b/indra/newview/scripts/lua/test_luafloater_demo.lua index b81259c060..22ed7d7b3a 100644 --- a/indra/newview/scripts/lua/test_luafloater_demo.lua +++ b/indra/newview/scripts/lua/test_luafloater_demo.lua @@ -2,7 +2,6 @@ XML_FILE_PATH = "luafloater_demo.xml" leap = require 'leap' coro = require 'coro' -util = require 'util' --event pump for sending actions to the floater COMMAND_PUMP_NAME = "" @@ -15,7 +14,7 @@ end) leap.process() local function _event(event_name) - if not util.contains(event_list, event_name) then + if not table.find(event_list, event_name) then print_warning("Incorrect event name: " .. event_name) end return event_name @@ -31,6 +30,7 @@ function getCurrentTime() end function handleEvents(event_data) + post({action="add_text", ctrl_name="events_editor", value = event_data}) if event_data.event == _event("commit") then if event_data.ctrl_name == "disable_ctrl" then post({action="set_enabled", ctrl_name="open_btn", value = (1 - event_data.value)}) diff --git a/indra/newview/scripts/lua/test_luafloater_gesture_list.lua b/indra/newview/scripts/lua/test_luafloater_gesture_list.lua index b46e36b4d9..b1ff129d85 100644 --- a/indra/newview/scripts/lua/test_luafloater_gesture_list.lua +++ b/indra/newview/scripts/lua/test_luafloater_gesture_list.lua @@ -2,7 +2,6 @@ XML_FILE_PATH = "luafloater_gesture_list.xml" leap = require 'leap' coro = require 'coro' -util = require 'util' LLGesture = require 'LLGesture' --event pump for sending actions to the floater @@ -16,7 +15,7 @@ end) leap.process() local function _event(event_name) - if not util.contains(event_list, event_name) then + if not table.find(event_list, event_name) then print_warning("Incorrect event name: " .. event_name) end return event_name diff --git a/indra/newview/scripts/lua/util.lua b/indra/newview/scripts/lua/util.lua index 5d6042dfe5..e3af633ea7 100644 --- a/indra/newview/scripts/lua/util.lua +++ b/indra/newview/scripts/lua/util.lua @@ -36,14 +36,4 @@ function util.equal(t1, t2) return util.empty(temp) end --- check if array-like table contains certain value -function util.contains(t, v) - for _, value in ipairs(t) do - if value == v then - return true - end - end - return false -end - return util -- cgit v1.2.3 From fd8c5fced1ee62e08c55adf92fb9c8d0e52d313a Mon Sep 17 00:00:00 2001 From: Nat Goodspeed Date: Mon, 25 Mar 2024 10:03:17 -0400 Subject: Remove colliding LLListener. --- indra/llcommon/llevents.h | 17 ----------------- 1 file changed, 17 deletions(-) diff --git a/indra/llcommon/llevents.h b/indra/llcommon/llevents.h index ebc893d1e6..77a405871d 100644 --- a/indra/llcommon/llevents.h +++ b/indra/llcommon/llevents.h @@ -160,23 +160,6 @@ typedef boost::signals2::connection LLBoundListener; /// referenced listener when the LLTempBoundListener instance is destroyed. typedef boost::signals2::scoped_connection LLTempBoundListener; -/// Accepting (const LLListener&) allows either LLEventListener or LLVoidListener -/// TODO: but compiler considers the constructor call ambiguous?? -class LLListener -{ -public: - LLListener(const LLEventListener& listener): - mListener(listener) - {} - LLListener(const LLVoidListener& listener): - mListener([listener](const LLSD& data){ listener(data); return false; }) - {} - operator LLEventListener() const { return mListener; } - -private: - LLEventListener mListener; -}; - /** * A common idiom for event-based code is to accept either a callable -- * directly called on completion -- or the string name of an LLEventPump on -- cgit v1.2.3 From 7f39a5bb109338b201c9cd0d3a40baac2b2e4fc1 Mon Sep 17 00:00:00 2001 From: Maxim Nikolenko Date: Mon, 25 Mar 2024 17:10:46 +0200 Subject: mac build fix --- indra/llui/llluafloater.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/indra/llui/llluafloater.cpp b/indra/llui/llluafloater.cpp index 8f624788c2..268075b05d 100644 --- a/indra/llui/llluafloater.cpp +++ b/indra/llui/llluafloater.cpp @@ -194,7 +194,7 @@ void LLLuaFloater::registerCallback(const std::string &ctrl_name, const std::str post(event.with("x", x).with("y", y)); }; - auto post_with_value = [this, data](LLSD& value) + auto post_with_value = [this, data](LLSD value) { LLSD event(data); post(event.with("value", value)); @@ -229,7 +229,7 @@ void LLLuaFloater::registerCallback(const std::string &ctrl_name, const std::str LLScrollListCtrl *list = dynamic_cast(ctrl); if (list) { - list->setDoubleClickCallback( [this, post_with_value, list](){ post_with_value(LLSD(list->getCurrentID())); }); + list->setDoubleClickCallback( [post_with_value, list](){ post_with_value(LLSD(list->getCurrentID())); }); } else { @@ -241,12 +241,12 @@ void LLLuaFloater::registerCallback(const std::string &ctrl_name, const std::str LLTextEditor* text_editor = dynamic_cast(ctrl); if (text_editor) { - text_editor->setKeystrokeCallback([this, post_with_value](LLTextEditor *editor) { post_with_value(editor->getValue()); }); + text_editor->setKeystrokeCallback([post_with_value](LLTextEditor *editor) { post_with_value(editor->getValue()); }); } LLLineEditor* line_editor = dynamic_cast(ctrl); if (line_editor) { - line_editor->setKeystrokeCallback([this, post_with_value](LLLineEditor *editor, void* userdata) { post_with_value(editor->getValue()); }, NULL); + line_editor->setKeystrokeCallback([post_with_value](LLLineEditor *editor, void* userdata) { post_with_value(editor->getValue()); }, NULL); } } else -- cgit v1.2.3 From 6cedaecad9c4830aa29068b90611b8b9da301ac9 Mon Sep 17 00:00:00 2001 From: Mnikolenko Productengine Date: Mon, 25 Mar 2024 20:41:33 +0200 Subject: Update test scripts to call leap.request() from main thread --- indra/newview/scripts/lua/test_luafloater_demo.lua | 16 ++-------------- .../newview/scripts/lua/test_luafloater_gesture_list.lua | 14 ++------------ 2 files changed, 4 insertions(+), 26 deletions(-) diff --git a/indra/newview/scripts/lua/test_luafloater_demo.lua b/indra/newview/scripts/lua/test_luafloater_demo.lua index 22ed7d7b3a..f2cf34206e 100644 --- a/indra/newview/scripts/lua/test_luafloater_demo.lua +++ b/indra/newview/scripts/lua/test_luafloater_demo.lua @@ -6,12 +6,7 @@ coro = require 'coro' --event pump for sending actions to the floater COMMAND_PUMP_NAME = "" --table of floater UI events -event_list={} -coro.launch(function () - event_list = leap.request("LLFloaterReg", {op="getFloaterEvents"})["events"] - leap.done() -end) -leap.process() +event_list=leap.request("LLFloaterReg", {op="getFloaterEvents"}).events local function _event(event_name) if not table.find(event_list, event_name) then @@ -53,12 +48,7 @@ end local key = {xml_path = XML_FILE_PATH, op = "showLuaFloater"} --sign for additional events for defined control {= {action1, action2, ...}} key.extra_events={show_time_lbl = {_event("right_mouse_down"), _event("double_click")}} -coro.launch(function () - --script received event pump name, after floater was built - COMMAND_PUMP_NAME = leap.request("LLFloaterReg", key)["command_name"] - leap.done() -end) -leap.process() +COMMAND_PUMP_NAME = leap.request("LLFloaterReg", key).command_name catch_events = leap.WaitFor:new(-1, "all_events") function catch_events:filter(pump, data) @@ -74,5 +64,3 @@ function process_events(waitfor) end coro.launch(process_events, catch_events) -leap.process() -print_warning("End of the script") diff --git a/indra/newview/scripts/lua/test_luafloater_gesture_list.lua b/indra/newview/scripts/lua/test_luafloater_gesture_list.lua index b1ff129d85..049ba757d3 100644 --- a/indra/newview/scripts/lua/test_luafloater_gesture_list.lua +++ b/indra/newview/scripts/lua/test_luafloater_gesture_list.lua @@ -7,12 +7,7 @@ LLGesture = require 'LLGesture' --event pump for sending actions to the floater COMMAND_PUMP_NAME = "" --table of floater UI events -event_list={} -coro.launch(function () - event_list = leap.request("LLFloaterReg", {op="getFloaterEvents"})["events"] - leap.done() -end) -leap.process() +event_list=leap.request("LLFloaterReg", {op="getFloaterEvents"}).events local function _event(event_name) if not table.find(event_list, event_name) then @@ -50,11 +45,7 @@ end local key = {xml_path = XML_FILE_PATH, op = "showLuaFloater"} --receive additional events for defined control {= {action1, action2, ...}} key.extra_events={gesture_list = {_event("double_click")}} -coro.launch(function () - handleEvents(leap.request("LLFloaterReg", key)) - leap.done() -end) -leap.process() +handleEvents(leap.request("LLFloaterReg", key)) catch_events = leap.WaitFor:new(-1, "all_events") function catch_events:filter(pump, data) @@ -70,4 +61,3 @@ function process_events(waitfor) end coro.launch(process_events, catch_events) -leap.process() -- cgit v1.2.3 From ac4fa418e3a7402f9d9122c726d2fbfc4b8767b2 Mon Sep 17 00:00:00 2001 From: Nat Goodspeed Date: Mon, 25 Mar 2024 16:29:17 -0400 Subject: Add LL. prefix to viewer entry points, fix existing references. --- indra/llcommon/lua_function.cpp | 13 +++++++++++- indra/newview/scripts/lua/WaitQueue.lua | 2 +- indra/newview/scripts/lua/leap.lua | 10 ++++----- indra/newview/scripts/lua/test_LLGesture.lua | 2 +- indra/newview/scripts/lua/test_luafloater_demo.lua | 6 +++--- .../scripts/lua/test_luafloater_gesture_list.lua | 2 +- indra/newview/tests/llluamanager_test.cpp | 24 +++++++++++----------- 7 files changed, 35 insertions(+), 24 deletions(-) diff --git a/indra/llcommon/lua_function.cpp b/indra/llcommon/lua_function.cpp index 441e17dafd..962e9ee2fa 100644 --- a/indra/llcommon/lua_function.cpp +++ b/indra/llcommon/lua_function.cpp @@ -439,6 +439,8 @@ void LuaState::initLuaState() LuaFunction::init(mState); // Try to make print() write to our log. lua_register(mState, "print", LuaFunction::get("print_info")); + // We don't want to have to prefix require(). + lua_register(mState, "require", LuaFunction::get("require")); } LuaState::~LuaState() @@ -646,11 +648,20 @@ LuaFunction::LuaFunction(const std::string_view& name, lua_CFunction function, void LuaFunction::init(lua_State* L) { const auto& [registry, lookup] = getRState(); + luaL_checkstack(L, 2, nullptr); + // create LL table -- + // it happens that we know exactly how many non-array members we want + lua_createtable(L, 0, int(narrow(lookup.size()))); + int idx = lua_gettop(L); for (const auto& [name, pair]: registry) { const auto& [funcptr, helptext] = pair; - lua_register(L, name.c_str(), funcptr); + // store funcptr in LL table with saved name + lua_pushcfunction(L, funcptr, name.c_str()); + lua_setfield(L, idx, name.c_str()); } + // store LL in new lua_State's globals + lua_setglobal(L, "LL"); } lua_CFunction LuaFunction::get(const std::string& key) diff --git a/indra/newview/scripts/lua/WaitQueue.lua b/indra/newview/scripts/lua/WaitQueue.lua index a34dbef4d7..1fbcc50847 100644 --- a/indra/newview/scripts/lua/WaitQueue.lua +++ b/indra/newview/scripts/lua/WaitQueue.lua @@ -5,7 +5,7 @@ local fiber = require('fiber') local Queue = require('Queue') --- local debug = print_debug +-- local debug = LL.print_debug local function debug(...) end local WaitQueue = Queue:new() diff --git a/indra/newview/scripts/lua/leap.lua b/indra/newview/scripts/lua/leap.lua index 77f3a3e116..a60819d493 100644 --- a/indra/newview/scripts/lua/leap.lua +++ b/indra/newview/scripts/lua/leap.lua @@ -51,7 +51,7 @@ local leap = {} -- _command: string name of command LLEventPump. post_to(_command, ...) -- engages LLLeapListener operations such as listening on a specified other -- LLEventPump, etc. -leap._reply, leap._command = get_event_pumps() +leap._reply, leap._command = LL.get_event_pumps() -- Dict of features added to the LEAP protocol since baseline implementation. -- Before engaging a new feature that might break an older viewer, we can -- check for the presence of that feature key. This table is solely about the @@ -112,7 +112,7 @@ function leap.send(pump, data, reqid) end end debug('leap.send(%s, %s) calling post_on()', pump, data) - post_on(pump, data) + LL.post_on(pump, data) end -- common setup code shared by request() and generate() @@ -215,7 +215,7 @@ local function unsolicited(pump, data) return end end - print_debug(string.format('unsolicited(%s, %s) discarding unclaimed event', pump, data)) + LL.print_debug(string.format('unsolicited(%s, %s) discarding unclaimed event', pump, data)) end -- Route incoming (pump, data) event to the appropriate waiting coroutine. @@ -244,7 +244,7 @@ fiber.set_idle(function () return 'done' end debug('leap.idle() calling get_event_next()') - local ok, pump, data = pcall(get_event_next) + local ok, pump, data = pcall(LL.get_event_next) debug('leap.idle() got %s: %s, %s', ok, pump, data) -- ok false means get_event_next() raised a Lua error, pump is message if not ok then @@ -414,7 +414,7 @@ end -- called by leap.process() when get_event_next() raises an error function leap.WaitFor:exception(message) - print_warning(self.name .. ' error: ' .. message) + LL.print_warning(self.name .. ' error: ' .. message) self._queue:Error(message) end diff --git a/indra/newview/scripts/lua/test_LLGesture.lua b/indra/newview/scripts/lua/test_LLGesture.lua index 5c0db6c063..5897a0e3cb 100644 --- a/indra/newview/scripts/lua/test_LLGesture.lua +++ b/indra/newview/scripts/lua/test_LLGesture.lua @@ -22,7 +22,7 @@ coro.launch(function() print(name) LLGesture.startGesture(uuid) repeat - sleep(1) + LL.sleep(1) until not LLGesture.isGesturePlaying(uuid) end print('Done.') diff --git a/indra/newview/scripts/lua/test_luafloater_demo.lua b/indra/newview/scripts/lua/test_luafloater_demo.lua index b81259c060..4e071e4abe 100644 --- a/indra/newview/scripts/lua/test_luafloater_demo.lua +++ b/indra/newview/scripts/lua/test_luafloater_demo.lua @@ -16,7 +16,7 @@ leap.process() local function _event(event_name) if not util.contains(event_list, event_name) then - print_warning("Incorrect event name: " .. event_name) + LL.print_warning("Incorrect event name: " .. event_name) end return event_name end @@ -45,7 +45,7 @@ function handleEvents(event_data) post({action="set_value", ctrl_name="time_lbl", value= getCurrentTime()}) end elseif event_data.event == _event("floater_close") then - print_warning("Floater was closed") + LL.print_warning("Floater was closed") leap.done() end end @@ -75,4 +75,4 @@ end coro.launch(process_events, catch_events) leap.process() -print_warning("End of the script") +LL.print_warning("End of the script") diff --git a/indra/newview/scripts/lua/test_luafloater_gesture_list.lua b/indra/newview/scripts/lua/test_luafloater_gesture_list.lua index b46e36b4d9..9c718c353b 100644 --- a/indra/newview/scripts/lua/test_luafloater_gesture_list.lua +++ b/indra/newview/scripts/lua/test_luafloater_gesture_list.lua @@ -17,7 +17,7 @@ leap.process() local function _event(event_name) if not util.contains(event_list, event_name) then - print_warning("Incorrect event name: " .. event_name) + LL.print_warning("Incorrect event name: " .. event_name) end return event_name end diff --git a/indra/newview/tests/llluamanager_test.cpp b/indra/newview/tests/llluamanager_test.cpp index 872d7827fe..e10dedcf53 100644 --- a/indra/newview/tests/llluamanager_test.cpp +++ b/indra/newview/tests/llluamanager_test.cpp @@ -109,7 +109,7 @@ namespace tut listener([&fromlua](const LLSD& data){ fromlua = data; })); const std::string lua(stringize( "data = ", construct, "\n" - "post_on('testpump', data)\n" + "LL.post_on('testpump', data)\n" )); LuaState L; auto [count, result] = LLLUAmanager::waitScriptLine(L, lua); @@ -140,14 +140,14 @@ namespace tut { posts.push_back(data.asString()); })); const std::string lua( "-- test post_on,get_event_pumps,get_event_next\n" - "post_on('testpump', 'entry')\n" - "post_on('testpump', 'get_event_pumps()')\n" - "replypump, cmdpump = get_event_pumps()\n" - "post_on('testpump', replypump)\n" - "post_on('testpump', 'get_event_next()')\n" - "pump, data = get_event_next()\n" - "post_on('testpump', data)\n" - "post_on('testpump', 'exit')\n" + "LL.post_on('testpump', 'entry')\n" + "LL.post_on('testpump', 'get_event_pumps()')\n" + "replypump, cmdpump = LL.get_event_pumps()\n" + "LL.post_on('testpump', replypump)\n" + "LL.post_on('testpump', 'get_event_next()')\n" + "pump, data = LL.get_event_next()\n" + "LL.post_on('testpump', data)\n" + "LL.post_on('testpump', 'exit')\n" ); LuaState L; // It's important to let the startScriptLine() coroutine run @@ -179,9 +179,9 @@ namespace tut LLEventMailDrop testpump("testpump"); const std::string lua( "-- test LLSD round trip\n" - "replypump, cmdpump = get_event_pumps()\n" - "post_on('testpump', replypump)\n" - "pump, data = get_event_next()\n" + "replypump, cmdpump = LL.get_event_pumps()\n" + "LL.post_on('testpump', replypump)\n" + "pump, data = LL.get_event_next()\n" "return data\n" ); LuaState L; -- cgit v1.2.3 From 98e6356aed0c757f16267cc2ae921f9c90a249fe Mon Sep 17 00:00:00 2001 From: Nat Goodspeed Date: Mon, 25 Mar 2024 16:57:28 -0400 Subject: Add LL.check_stop() entry point and call it in fiber scheduler(). fiber.lua's scheduler() is greedy, in the sense that it wants to run every ready Lua fiber before retrieving the next incoming event from the viewer (and possibly blocking for some real time before it becomes available). But check for viewer shutdown before resuming any suspended-but-ready Lua fiber. --- indra/llcommon/lua_function.cpp | 9 +++++++++ indra/newview/scripts/lua/fiber.lua | 4 +--- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/indra/llcommon/lua_function.cpp b/indra/llcommon/lua_function.cpp index 962e9ee2fa..9f0abd5674 100644 --- a/indra/llcommon/lua_function.cpp +++ b/indra/llcommon/lua_function.cpp @@ -681,6 +681,15 @@ std::pair LuaFunction::getState() return { registry, lookup }; } +/***************************************************************************** +* check_stop() +*****************************************************************************/ +lua_function(check_stop, "ensure that a Lua script responds to viewer shutdown") +{ + LLCoros::checkStop(); + return 0; +} + /***************************************************************************** * help() *****************************************************************************/ diff --git a/indra/newview/scripts/lua/fiber.lua b/indra/newview/scripts/lua/fiber.lua index 7dc67f510c..aebf27357f 100644 --- a/indra/newview/scripts/lua/fiber.lua +++ b/indra/newview/scripts/lua/fiber.lua @@ -222,8 +222,6 @@ local function scheduler() -- processing to the main thread. If called from a coroutine, pass control -- back to the main thread. if coroutine.running() then - -- seize the opportunity to make sure the viewer isn't shutting down --- check_stop() -- this is a real coroutine, yield normally to main thread coroutine.yield() -- main certainly still exists @@ -240,7 +238,7 @@ local function scheduler() repeat for co in live_ready_iter do -- seize the opportunity to make sure the viewer isn't shutting down --- check_stop() + LL.check_stop() -- before we re-append co, is it the only remaining entry? others = next(ready) -- co is live, re-append it to the ready list -- cgit v1.2.3 From 5b04ae7812e6cb0d0c9895aec6db6a206d4e09e8 Mon Sep 17 00:00:00 2001 From: Nat Goodspeed Date: Mon, 25 Mar 2024 17:35:28 -0400 Subject: util.lua claims functions are in alpha order - make it so. Also streamline util.contains(), given table.find(). --- indra/newview/scripts/lua/util.lua | 21 ++++++++------------- 1 file changed, 8 insertions(+), 13 deletions(-) diff --git a/indra/newview/scripts/lua/util.lua b/indra/newview/scripts/lua/util.lua index 5d6042dfe5..a2191288f6 100644 --- a/indra/newview/scripts/lua/util.lua +++ b/indra/newview/scripts/lua/util.lua @@ -2,9 +2,9 @@ local util = {} --- cheap test whether table t is empty -function util.empty(t) - return not next(t) +-- check if array-like table contains certain value +function util.contains(t, v) + return table.find(t, v) ~= nil end -- reliable count of the number of entries in table t @@ -17,6 +17,11 @@ function util.count(t) return count end +-- cheap test whether table t is empty +function util.empty(t) + return not next(t) +end + -- recursive table equality function util.equal(t1, t2) if not (type(t1) == 'table' and type(t2) == 'table') then @@ -36,14 +41,4 @@ function util.equal(t1, t2) return util.empty(temp) end --- check if array-like table contains certain value -function util.contains(t, v) - for _, value in ipairs(t) do - if value == v then - return true - end - end - return false -end - return util -- cgit v1.2.3 From 24a7842b9b08dc5cfb89fe36e5ebcd9c2598fa44 Mon Sep 17 00:00:00 2001 From: Mnikolenko Productengine Date: Tue, 26 Mar 2024 19:12:13 +0200 Subject: update scripts to use fiber.launch() --- indra/newview/scripts/lua/test_luafloater_demo.lua | 3 ++- indra/newview/scripts/lua/test_luafloater_gesture_list.lua | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/indra/newview/scripts/lua/test_luafloater_demo.lua b/indra/newview/scripts/lua/test_luafloater_demo.lua index f2cf34206e..be189bc146 100644 --- a/indra/newview/scripts/lua/test_luafloater_demo.lua +++ b/indra/newview/scripts/lua/test_luafloater_demo.lua @@ -2,6 +2,7 @@ XML_FILE_PATH = "luafloater_demo.xml" leap = require 'leap' coro = require 'coro' +fiber = require 'fiber' --event pump for sending actions to the floater COMMAND_PUMP_NAME = "" @@ -63,4 +64,4 @@ function process_events(waitfor) end end -coro.launch(process_events, catch_events) +fiber.launch("catch_events", process_events, catch_events) diff --git a/indra/newview/scripts/lua/test_luafloater_gesture_list.lua b/indra/newview/scripts/lua/test_luafloater_gesture_list.lua index 049ba757d3..7e2139468f 100644 --- a/indra/newview/scripts/lua/test_luafloater_gesture_list.lua +++ b/indra/newview/scripts/lua/test_luafloater_gesture_list.lua @@ -2,6 +2,7 @@ XML_FILE_PATH = "luafloater_gesture_list.xml" leap = require 'leap' coro = require 'coro' +fiber = require 'fiber' LLGesture = require 'LLGesture' --event pump for sending actions to the floater @@ -60,4 +61,4 @@ function process_events(waitfor) end end -coro.launch(process_events, catch_events) +fiber.launch("catch_events", process_events, catch_events) -- cgit v1.2.3 From c618758c7c91917905b1075e29944ef70e7e9b33 Mon Sep 17 00:00:00 2001 From: Nat Goodspeed Date: Wed, 27 Mar 2024 15:53:48 -0400 Subject: Run loaded `require()` module on Lua's main thread. The problem with running a `require()` module on a Lua coroutine is that it prohibits calling `leap.request()` at module load time. When a coroutine calls `leap.request()`, it must yield back to Lua's main thread -- but a `require()` module is forbidden from yielding. Running on Lua's main thread means that (after potentially giving time slices to other ready coroutines) `fiber.lua` will request the response event from the viewer, and continue processing the loaded module without having to yield. --- indra/newview/llluamanager.cpp | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/indra/newview/llluamanager.cpp b/indra/newview/llluamanager.cpp index c65f062fd7..aed6aee239 100644 --- a/indra/newview/llluamanager.cpp +++ b/indra/newview/llluamanager.cpp @@ -508,12 +508,14 @@ void LLRequireResolver::runModule(const std::string& desc, const std::string& co // Module needs to run in a new thread, isolated from the rest. // Note: we create ML on main thread so that it doesn't inherit environment of L. lua_State *GL = lua_mainthread(L); - lua_State *ML = lua_newthread(GL); +// lua_State *ML = lua_newthread(GL); + // Try loading modules on Lua's main thread instead. + lua_State *ML = GL; // lua_newthread() pushed the new thread object on GL's stack. Move to L's. - lua_xmove(GL, L, 1); +// lua_xmove(GL, L, 1); // new thread needs to have the globals sandboxed - luaL_sandboxthread(ML); +// luaL_sandboxthread(ML); { // If loadstring() returns (! LUA_OK) then there's an error message on @@ -523,7 +525,9 @@ void LLRequireResolver::runModule(const std::string& desc, const std::string& co { // luau uses Lua 5.3's version of lua_resume(): // run the coroutine on ML, "from" L, passing no arguments. - int status = lua_resume(ML, L, 0); +// int status = lua_resume(ML, L, 0); + // we expect one return value + int status = lua_pcall(ML, 0, 1, 0); if (status == LUA_OK) { @@ -545,9 +549,12 @@ void LLRequireResolver::runModule(const std::string& desc, const std::string& co } // There's now a return value (string error message or module) on top of ML. // Move return value to L's stack. - lua_xmove(ML, L, 1); + if (ML != L) + { + lua_xmove(ML, L, 1); + } // remove ML from L's stack - lua_remove(L, -2); +// lua_remove(L, -2); // // DON'T call lua_close(ML)! Since ML is only a thread of L, corrupts L too! // lua_close(ML); } -- cgit v1.2.3 From 58d5e288e0bfaa9819b68b376767a8a39a97fef8 Mon Sep 17 00:00:00 2001 From: Nat Goodspeed Date: Wed, 27 Mar 2024 16:20:36 -0400 Subject: poetry --- indra/newview/scripts/lua/Queue.lua | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/indra/newview/scripts/lua/Queue.lua b/indra/newview/scripts/lua/Queue.lua index b0a5a87f87..5ab2a8a72c 100644 --- a/indra/newview/scripts/lua/Queue.lua +++ b/indra/newview/scripts/lua/Queue.lua @@ -1,6 +1,12 @@ -- from https://create.roblox.com/docs/luau/queues#implementing-queues, -- amended per https://www.lua.org/pil/16.1.html +-- While coding some scripting in Lua +-- I found that I needed a queua +-- I thought of linked list +-- But had to resist +-- For fear it might be too obscua. + local Queue = {} function Queue:new() -- cgit v1.2.3 From af38e606865c2ed49a13bd6bd91d9604997798c8 Mon Sep 17 00:00:00 2001 From: Nat Goodspeed Date: Wed, 27 Mar 2024 16:38:45 -0400 Subject: Enhance Lua debugging output. Don't use "debug" as the name of a function to conditionally write debug messages: "debug" is a Luau built-in library, and assigning that name locally would shadow the builtin. Use "dbg" instead. Recast fiber.print_all() as fiber.format_all() that returns a string; then print_all() is simply print(format_all()). This refactoring allows us to use dbg(format_all()) as well. Add a couple new dbg() messages at fiber state changes. --- indra/newview/scripts/lua/ErrorQueue.lua | 8 +++--- indra/newview/scripts/lua/WaitQueue.lua | 11 ++++---- indra/newview/scripts/lua/fiber.lua | 45 +++++++++++++++++++------------- indra/newview/scripts/lua/leap.lua | 26 +++++++++--------- 4 files changed, 50 insertions(+), 40 deletions(-) diff --git a/indra/newview/scripts/lua/ErrorQueue.lua b/indra/newview/scripts/lua/ErrorQueue.lua index 076742815a..6ed1c10d5c 100644 --- a/indra/newview/scripts/lua/ErrorQueue.lua +++ b/indra/newview/scripts/lua/ErrorQueue.lua @@ -3,22 +3,22 @@ -- raise that error. local WaitQueue = require('WaitQueue') --- local debug = require('printf') -local function debug(...) end +-- local dbg = require('printf') +local function dbg(...) end local ErrorQueue = WaitQueue:new() function ErrorQueue:Error(message) -- Setting Error() is a marker, like closing the queue. Once we reach the -- error, every subsequent Dequeue() call will raise the same error. - debug('Setting self._closed to %q', message) + dbg('Setting self._closed to %q', message) self._closed = message self:_wake_waiters() end function ErrorQueue:Dequeue() local value = WaitQueue.Dequeue(self) - debug('ErrorQueue:Dequeue: base Dequeue() got %s', value) + dbg('ErrorQueue:Dequeue: base Dequeue() got %s', value) if value ~= nil then -- queue not yet closed, show caller return value diff --git a/indra/newview/scripts/lua/WaitQueue.lua b/indra/newview/scripts/lua/WaitQueue.lua index 1fbcc50847..ad4fdecf43 100644 --- a/indra/newview/scripts/lua/WaitQueue.lua +++ b/indra/newview/scripts/lua/WaitQueue.lua @@ -5,8 +5,8 @@ local fiber = require('fiber') local Queue = require('Queue') --- local debug = LL.print_debug -local function debug(...) end +-- local dbg = require('printf') +local function dbg(...) end local WaitQueue = Queue:new() @@ -60,17 +60,18 @@ function WaitQueue:Dequeue() -- the queue while there are still items left, and we want the -- consumer(s) to retrieve those last few items. if self._closed then - debug('WaitQueue:Dequeue(): closed') + dbg('WaitQueue:Dequeue(): closed') return nil end - debug('WaitQueue:Dequeue(): waiting') + dbg('WaitQueue:Dequeue(): waiting') -- add the running coroutine to the list of waiters + dbg('WaitQueue:Dequeue() running %s', tostring(coroutine.running() or 'main')) table.insert(self._waiters, fiber.running()) -- then let somebody else run fiber.wait() end -- here we're sure this queue isn't empty - debug('WaitQueue:Dequeue() calling Queue.Dequeue()') + dbg('WaitQueue:Dequeue() calling Queue.Dequeue()') return Queue.Dequeue(self) end diff --git a/indra/newview/scripts/lua/fiber.lua b/indra/newview/scripts/lua/fiber.lua index aebf27357f..9057e6c890 100644 --- a/indra/newview/scripts/lua/fiber.lua +++ b/indra/newview/scripts/lua/fiber.lua @@ -17,8 +17,8 @@ -- or with an error). local printf = require 'printf' --- local debug = printf -local function debug(...) end +-- local dbg = printf +local function dbg(...) end local coro = require 'coro' local fiber = {} @@ -78,22 +78,28 @@ function fiber.launch(name, func, ...) byname[namekey] = co -- and remember it as this fiber's name names[co] = namekey --- debug('launch(%s)', namekey) --- debug('byname[%s] = %s', namekey, tostring(byname[namekey])) --- debug('names[%s] = %s', tostring(co), names[co]) --- debug('ready[-1] = %s', tostring(ready[#ready])) +-- dbg('launch(%s)', namekey) +-- dbg('byname[%s] = %s', namekey, tostring(byname[namekey])) +-- dbg('names[%s] = %s', tostring(co), names[co]) +-- dbg('ready[-1] = %s', tostring(ready[#ready])) end -- for debugging -function fiber.print_all() - print('Ready fibers:' .. if next(ready) then '' else ' none') +function format_all() + output = {} + table.insert(output, 'Ready fibers:' .. if next(ready) then '' else ' none') for _, co in pairs(ready) do - printf(' %s: %s', fiber.get_name(co), fiber.status(co)) + table.insert(output, string.format(' %s: %s', fiber.get_name(co), fiber.status(co))) end - print('Waiting fibers:' .. if next(waiting) then '' else ' none') + table.insert(output, 'Waiting fibers:' .. if next(waiting) then '' else ' none') for co in pairs(waiting) do - printf(' %s: %s', fiber.get_name(co), fiber.status(co)) + table.insert(output, string.format(' %s: %s', fiber.get_name(co), fiber.status(co))) end + return table.concat(output, '\n') +end + +function fiber.print_all() + print(format_all()) end -- return either the running coroutine or, if called from the main thread, @@ -160,6 +166,7 @@ end -- Suspend the current fiber until some other fiber calls fiber.wake() on it function fiber.wait() + dbg('Fiber %q waiting', fiber.get_name()) set_waiting() -- now yield to other fibers fiber.yield() @@ -175,26 +182,27 @@ function fiber.wake(co) waiting[co] = nil -- add to end of ready list table.insert(ready, co) + dbg('Fiber %q ready', fiber.get_name(co)) -- but don't yet resume it: that happens next time we reach yield() end -- pop and return the next not-dead fiber in the ready list, or nil if none remain local function live_ready_iter() - -- don't write + -- don't write: -- for co in table.remove, ready, 1 -- because it would keep passing a new second parameter! for co in function() return table.remove(ready, 1) end do - debug('%s live_ready_iter() sees %s, status %s', + dbg('%s live_ready_iter() sees %s, status %s', fiber.get_name(), fiber.get_name(co), fiber.status(co)) -- keep removing the head entry until we find one that's not dead, -- discarding any dead coroutines along the way if co == 'main' or coroutine.status(co) ~= 'dead' then - debug('%s live_ready_iter() returning %s', + dbg('%s live_ready_iter() returning %s', fiber.get_name(), fiber.get_name(co)) return co end end - debug('%s live_ready_iter() returning nil', fiber.get_name()) + dbg('%s live_ready_iter() returning nil', fiber.get_name()) return nil end @@ -214,6 +222,7 @@ end -- * false, nil if this is the only remaining fiber -- * nil, x if configured idle() callback returns non-nil x local function scheduler() + dbg('scheduler():\n%s', format_all()) -- scheduler() is asymmetric because Lua distinguishes the main thread -- from other coroutines. The main thread can't yield; it can only resume -- other coroutines. So although an arbitrary coroutine could resume still @@ -311,12 +320,12 @@ function fiber.run() end local others, idle_done repeat - debug('%s calling fiber.run() calling scheduler()', fiber.get_name()) + dbg('%s calling fiber.run() calling scheduler()', fiber.get_name()) others, idle_done = scheduler() - debug("%s fiber.run()'s scheduler() returned %s, %s", fiber.get_name(), + dbg("%s fiber.run()'s scheduler() returned %s, %s", fiber.get_name(), tostring(others), tostring(idle_done)) until (not others) - debug('%s fiber.run() done', fiber.get_name()) + dbg('%s fiber.run() done', fiber.get_name()) -- For whatever it's worth, put our own fiber back in the ready list. table.insert(ready, fiber.running()) -- Once there are no more waiting fibers, and the only ready fiber is diff --git a/indra/newview/scripts/lua/leap.lua b/indra/newview/scripts/lua/leap.lua index a60819d493..9da1839c68 100644 --- a/indra/newview/scripts/lua/leap.lua +++ b/indra/newview/scripts/lua/leap.lua @@ -40,8 +40,8 @@ local fiber = require('fiber') local ErrorQueue = require('ErrorQueue') --- local debug = require('printf') -local function debug(...) end +-- local dbg = require('printf') +local function dbg(...) end local leap = {} @@ -102,7 +102,7 @@ end -- -- See also request(), generate(). function leap.send(pump, data, reqid) - debug('leap.send(%s, %s, %s) entry', pump, data, reqid) + dbg('leap.send(%s, %s, %s) entry', pump, data, reqid) local data = data if type(data) == 'table' then data = table.clone(data) @@ -111,7 +111,7 @@ function leap.send(pump, data, reqid) data['reqid'] = reqid end end - debug('leap.send(%s, %s) calling post_on()', pump, data) + dbg('leap.send(%s, %s) calling post_on()', pump, data) LL.post_on(pump, data) end @@ -126,7 +126,7 @@ local function requestSetup(pump, data) -- WaitForReqid object in leap._pending so dispatch() can find it. leap._pending[reqid] = leap.WaitForReqid:new(reqid) -- Pass reqid to send() to stamp it into (a copy of) the request data. - debug('requestSetup(%s, %s)', pump, data) + dbg('requestSetup(%s, %s)', pump, data) leap.send(pump, data, reqid) return reqid end @@ -152,9 +152,9 @@ end function leap.request(pump, data) local reqid = requestSetup(pump, data) local waitfor = leap._pending[reqid] - debug('leap.request(%s, %s) about to wait on %s', pump, data, tostring(waitfor)) + dbg('leap.request(%s, %s) about to wait on %s', pump, data, tostring(waitfor)) local ok, response = pcall(waitfor.wait, waitfor) - debug('leap.request(%s, %s) got %s: %s', pump, data, ok, response) + dbg('leap.request(%s, %s) got %s: %s', pump, data, ok, response) -- kill off temporary WaitForReqid object, even if error leap._pending[reqid] = nil if ok then @@ -210,7 +210,7 @@ local function unsolicited(pump, data) -- we maintain waitfors in descending priority order, so the first waitfor -- to claim this event is the one with the highest priority for i, waitfor in pairs(leap._waitfors) do - debug('unsolicited() checking %s', waitfor.name) + dbg('unsolicited() checking %s', waitfor.name) if waitfor:handle(pump, data) then return end @@ -243,9 +243,9 @@ fiber.set_idle(function () cleanup('done') return 'done' end - debug('leap.idle() calling get_event_next()') + dbg('leap.idle() calling get_event_next()') local ok, pump, data = pcall(LL.get_event_next) - debug('leap.idle() got %s: %s, %s', ok, pump, data) + dbg('leap.idle() got %s: %s, %s', ok, pump, data) -- ok false means get_event_next() raised a Lua error, pump is message if not ok then cleanup(pump) @@ -368,9 +368,9 @@ end -- Block the calling coroutine until a suitable unsolicited event (one -- for which filter() returns the event) arrives. function leap.WaitFor:wait() - debug('%s about to wait', self.name) + dbg('%s about to wait', self.name) local item = self._queue:Dequeue() - debug('%s got %s', self.name, item) + dbg('%s got %s', self.name, item) return item end @@ -392,7 +392,7 @@ end -- called by unsolicited() for each WaitFor in leap._waitfors function leap.WaitFor:handle(pump, data) local item = self:filter(pump, data) - debug('%s.filter() returned %s', self.name, item) + dbg('%s.filter() returned %s', self.name, item) -- if this item doesn't pass the filter, we're not interested if not item then return false -- cgit v1.2.3 From bfeedacf5a32fb77bd505c43126f3b5dc4394296 Mon Sep 17 00:00:00 2001 From: Mnikolenko Productengine Date: Wed, 27 Mar 2024 23:11:24 +0200 Subject: Run each script file with new LuaState --- indra/newview/llfloaterluadebug.cpp | 3 +- indra/newview/llluamanager.cpp | 39 +++++++------------ indra/newview/llluamanager.h | 8 ++-- indra/newview/scripts/lua/test_LLFloaterAbout.lua | 10 +---- indra/newview/scripts/lua/test_LLGesture.lua | 46 ++++++++++------------- 5 files changed, 39 insertions(+), 67 deletions(-) diff --git a/indra/newview/llfloaterluadebug.cpp b/indra/newview/llfloaterluadebug.cpp index b63600e0e6..f247528231 100644 --- a/indra/newview/llfloaterluadebug.cpp +++ b/indra/newview/llfloaterluadebug.cpp @@ -111,8 +111,7 @@ void LLFloaterLUADebug::runSelectedScript(const std::vector &filena if (!filepath.empty()) { mScriptPath->setText(filepath); - cleanLuaState(); - LLLUAmanager::runScriptFile(mState, filepath, [this](int count, const LLSD& result) + LLLUAmanager::runScriptFile(filepath, [this](int count, const LLSD &result) { completion(count, result); }); diff --git a/indra/newview/llluamanager.cpp b/indra/newview/llluamanager.cpp index c65f062fd7..3194182ceb 100644 --- a/indra/newview/llluamanager.cpp +++ b/indra/newview/llluamanager.cpp @@ -195,61 +195,50 @@ lua_function(get_event_next, return 2; } -void LLLUAmanager::runScriptFile(const std::string& filename, script_finished_fn cb) -{ - // A script_finished_fn is used to initialize the LuaState. - // It will be called when the LuaState is destroyed. - LuaState L(cb); - runScriptFile(L, filename); -} - -void LLLUAmanager::runScriptFile(const std::string& filename, script_result_fn cb) -{ - LuaState L; - // A script_result_fn will be called when LuaState::expr() completes. - runScriptFile(L, filename, cb); -} - LLCoros::Future> -LLLUAmanager::startScriptFile(LuaState& L, const std::string& filename) +LLLUAmanager::startScriptFile(const std::string& filename) { // Despite returning from startScriptFile(), we need this Promise to // remain alive until the callback has fired. auto promise{ std::make_shared>>() }; - runScriptFile(L, filename, + runScriptFile(filename, [promise](int count, LLSD result) { promise->set_value({ count, result }); }); return LLCoros::getFuture(*promise); } -std::pair LLLUAmanager::waitScriptFile(LuaState& L, const std::string& filename) +std::pair LLLUAmanager::waitScriptFile(const std::string& filename) { - return startScriptFile(L, filename).get(); + return startScriptFile(filename).get(); } -void LLLUAmanager::runScriptFile(LuaState& L, const std::string& filename, script_result_fn cb) +void LLLUAmanager::runScriptFile(const std::string &filename, script_result_fn result_cb, script_finished_fn finished_cb) { - LLCoros::instance().launch(filename, [&L, filename, cb]() + // A script_result_fn will be called when LuaState::expr() completes. + LLCoros::instance().launch(filename, [filename, result_cb, finished_cb]() { llifstream in_file; in_file.open(filename.c_str()); if (in_file.is_open()) { + // A script_finished_fn is used to initialize the LuaState. + // It will be called when the LuaState is destroyed. + LuaState L(finished_cb); std::string text{std::istreambuf_iterator(in_file), {}}; auto [count, result] = L.expr(filename, text); - if (cb) + if (result_cb) { - cb(count, result); + result_cb(count, result); } } else { auto msg{ stringize("unable to open script file '", filename, "'") }; LL_WARNS("Lua") << msg << LL_ENDL; - if (cb) + if (result_cb) { - cb(-1, msg); + result_cb(-1, msg); } } }); diff --git a/indra/newview/llluamanager.h b/indra/newview/llluamanager.h index fb9f1b8141..a297d14502 100644 --- a/indra/newview/llluamanager.h +++ b/indra/newview/llluamanager.h @@ -53,19 +53,17 @@ public: // results, represented as the entries of the result array. typedef std::function script_result_fn; - static void runScriptFile(const std::string &filename, script_finished_fn cb = {}); - static void runScriptFile(const std::string &filename, script_result_fn cb); - static void runScriptFile(LuaState& L, const std::string &filename, script_result_fn cb = {}); + static void runScriptFile(const std::string &filename, script_result_fn result_cb = {}, script_finished_fn finished_cb = {}); // Start running a Lua script file, returning an LLCoros::Future whose // get() method will pause the calling coroutine until it can deliver the // (count, result) pair described above. Between startScriptFile() and // Future::get(), the caller and the Lua script coroutine will run // concurrently. static LLCoros::Future> - startScriptFile(LuaState& L, const std::string& filename); + startScriptFile(const std::string& filename); // Run a Lua script file, and pause the calling coroutine until it completes. // The return value is the (count, result) pair described above. - static std::pair waitScriptFile(LuaState& L, const std::string& filename); + static std::pair waitScriptFile(const std::string& filename); static void runScriptLine(const std::string &chunk, script_finished_fn cb = {}); static void runScriptLine(const std::string &chunk, script_result_fn cb); diff --git a/indra/newview/scripts/lua/test_LLFloaterAbout.lua b/indra/newview/scripts/lua/test_LLFloaterAbout.lua index 7abc437b79..6bbf61982d 100644 --- a/indra/newview/scripts/lua/test_LLFloaterAbout.lua +++ b/indra/newview/scripts/lua/test_LLFloaterAbout.lua @@ -1,14 +1,6 @@ -- test LLFloaterAbout LLFloaterAbout = require('LLFloaterAbout') -leap = require('leap') -coro = require('coro') inspect = require('inspect') -coro.launch(function () - print(inspect(LLFloaterAbout.getInfo())) - leap.done() -end) - -leap.process() - +print(inspect(LLFloaterAbout.getInfo())) diff --git a/indra/newview/scripts/lua/test_LLGesture.lua b/indra/newview/scripts/lua/test_LLGesture.lua index 5897a0e3cb..1cce674565 100644 --- a/indra/newview/scripts/lua/test_LLGesture.lua +++ b/indra/newview/scripts/lua/test_LLGesture.lua @@ -2,31 +2,25 @@ LLGesture = require 'LLGesture' inspect = require 'inspect' -coro = require 'coro' -leap = require 'leap' -coro.launch(function() - -- getActiveGestures() returns {: {name, playing, trigger}} - gestures_uuid = LLGesture.getActiveGestures() - -- convert to {: } - gestures = {} - for uuid, info in pairs(gestures_uuid) do - gestures[info.name] = uuid - end - -- now run through the list - for name, uuid in pairs(gestures) do - if name == 'afk' then - -- afk has a long timeout, and isn't interesting to look at - continue - end - print(name) - LLGesture.startGesture(uuid) - repeat - LL.sleep(1) - until not LLGesture.isGesturePlaying(uuid) - end - print('Done.') - leap.done() -end) -leap.process() +-- getActiveGestures() returns {: {name, playing, trigger}} +gestures_uuid = LLGesture.getActiveGestures() +-- convert to {: } +gestures = {} +for uuid, info in pairs(gestures_uuid) do + gestures[info.name] = uuid +end +-- now run through the list +for name, uuid in pairs(gestures) do + if name == 'afk' then + -- afk has a long timeout, and isn't interesting to look at + continue + end + print(name) + LLGesture.startGesture(uuid) + repeat + LL.sleep(1) + until not LLGesture.isGesturePlaying(uuid) +end +print('Done.') -- cgit v1.2.3 From db6732401af13c3e283c7a0a33fc9c379a8798a6 Mon Sep 17 00:00:00 2001 From: Nat Goodspeed Date: Thu, 28 Mar 2024 07:06:54 -0400 Subject: Move our lua_register(), lua_rawlen() from lua_function.h to .cpp. --- indra/llcommon/lua_function.cpp | 3 +++ indra/llcommon/lua_function.h | 3 --- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/indra/llcommon/lua_function.cpp b/indra/llcommon/lua_function.cpp index 9f0abd5674..e731408c7d 100644 --- a/indra/llcommon/lua_function.cpp +++ b/indra/llcommon/lua_function.cpp @@ -31,6 +31,9 @@ #include "lualistener.h" #include "stringize.h" +#define lua_register(L, n, f) (lua_pushcfunction(L, (f), n), lua_setglobal(L, (n))) +#define lua_rawlen lua_objlen + /***************************************************************************** * luau namespace *****************************************************************************/ diff --git a/indra/llcommon/lua_function.h b/indra/llcommon/lua_function.h index 07848e38af..868c13c3f1 100644 --- a/indra/llcommon/lua_function.h +++ b/indra/llcommon/lua_function.h @@ -23,9 +23,6 @@ class LuaListener; -#define lua_register(L, n, f) (lua_pushcfunction(L, (f), n), lua_setglobal(L, (n))) -#define lua_rawlen lua_objlen - namespace lluau { // luau defines luaL_error() as void, but we want to use the Lua idiom of -- cgit v1.2.3 From 10af340aa35b3dcdc0c00a03743d33f33e627ad7 Mon Sep 17 00:00:00 2001 From: Nat Goodspeed Date: Thu, 28 Mar 2024 07:08:36 -0400 Subject: Clean up unused llevents.h #includes. --- indra/llcommon/llevents.h | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/indra/llcommon/llevents.h b/indra/llcommon/llevents.h index 77a405871d..c1b752a143 100644 --- a/indra/llcommon/llevents.h +++ b/indra/llcommon/llevents.h @@ -48,27 +48,13 @@ #pragma warning (pop) #endif -#include -#include // noncopyable #include -#include -#include // reference_wrapper -#include -#include #include "llsd.h" #include "llsingleton.h" #include "lldependencies.h" -#include "llstl.h" #include "llexception.h" #include "llhandle.h" -/*==========================================================================*| -// override this to allow binding free functions with more parameters -#ifndef LLEVENTS_LISTENER_ARITY -#define LLEVENTS_LISTENER_ARITY 10 -#endif -|*==========================================================================*/ - // hack for testing #ifndef testable #define testable private -- cgit v1.2.3 From 9fbfd3d0ad1ed2cb721129a59421f06fedd071bf Mon Sep 17 00:00:00 2001 From: Nat Goodspeed Date: Thu, 28 Mar 2024 07:10:10 -0400 Subject: Remove llluamanager.cpp "FIXME extremely hacky way" cruft. --- indra/newview/llluamanager.cpp | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/indra/newview/llluamanager.cpp b/indra/newview/llluamanager.cpp index c65f062fd7..d1fcf21941 100644 --- a/indra/newview/llluamanager.cpp +++ b/indra/newview/llluamanager.cpp @@ -35,16 +35,6 @@ #include "lualistener.h" #include "stringize.h" -// skip all these link dependencies for integration testing -#ifndef LL_TEST -#include "lluilistener.h" -#include "llviewercontrol.h" - -// FIXME extremely hacky way to get to the UI Listener framework. There's -// a cleaner way. -extern LLUIListener sUIListener; -#endif // ! LL_TEST - #include #include -- cgit v1.2.3 From ce73e5c5ab0c33673067d9322c98ae8800fa9224 Mon Sep 17 00:00:00 2001 From: Nat Goodspeed Date: Thu, 28 Mar 2024 07:11:31 -0400 Subject: Terminate Lua scripts hanging in LL.get_event_next(). Make LuaListener listen for "LLApp" viewer shutdown events. On receiving such, it closes its queue. Then the C++ coroutine calling getNext() wakes up with an LLThreadSafeQueue exception, and calls LLCoros::checkStop() to throw one of the exceptions recognized by LLCoros::toplevel(). Add an llluamanager_test.cpp test to verify this behavior. --- indra/llcommon/lualistener.cpp | 31 +++++++++++++++++++++++++++++-- indra/llcommon/lualistener.h | 2 ++ indra/newview/tests/llluamanager_test.cpp | 21 +++++++++++++++++++++ 3 files changed, 52 insertions(+), 2 deletions(-) diff --git a/indra/llcommon/lualistener.cpp b/indra/llcommon/lualistener.cpp index ed34133924..37ce27a2a4 100644 --- a/indra/llcommon/lualistener.cpp +++ b/indra/llcommon/lualistener.cpp @@ -36,7 +36,24 @@ LuaListener::LuaListener(lua_State* L): mListener(new LLLeapListener( "LuaListener", [this](const std::string& pump, const LLSD& data) - { return queueEvent(pump, data); })) + { return queueEvent(pump, data); })), + // Listen for shutdown events on the "LLApp" LLEventPump. + mShutdownConnection( + LLEventPumps::instance().obtain("LLApp").listen( + LLEventPump::inventName("LuaState"), + [this](const LLSD& status) + { + LL_DEBUGS("LuaListener") << "caught " << status << LL_ENDL; + const auto& statsd = status["status"]; + if (statsd.asString() != "running") + { + // If a Lua script is still blocked in getNext() during + // viewer shutdown, close the queue to wake up getNext(). + LL_DEBUGS("LuaListener") << "closing queue" << LL_ENDL; + mQueue.close(); + } + return false; + })) {} LuaListener::~LuaListener() @@ -87,5 +104,15 @@ bool LuaListener::queueEvent(const std::string& pump, const LLSD& data) LuaListener::PumpData LuaListener::getNext() { - return mQueue.pop(); + try + { + return mQueue.pop(); + } + catch (const LLThreadSafeQueueInterrupt& exc) + { + // mQueue has been closed. The only way that happens is when we detect + // viewer shutdown. Terminate the calling coroutine. + LLCoros::checkStop(); + return {}; + } } diff --git a/indra/llcommon/lualistener.h b/indra/llcommon/lualistener.h index c13b7bbd5f..40ccfba8fe 100644 --- a/indra/llcommon/lualistener.h +++ b/indra/llcommon/lualistener.h @@ -12,6 +12,7 @@ #if ! defined(LL_LUALISTENER_H) #define LL_LUALISTENER_H +#include "llevents.h" #include "llinstancetracker.h" #include "llsd.h" #include "llthreadsafequeue.h" @@ -73,6 +74,7 @@ private: LLThreadSafeQueue mQueue; std::unique_ptr mListener; + LLTempBoundListener mShutdownConnection; }; #endif /* ! defined(LL_LUALISTENER_H) */ diff --git a/indra/newview/tests/llluamanager_test.cpp b/indra/newview/tests/llluamanager_test.cpp index e10dedcf53..682289bd93 100644 --- a/indra/newview/tests/llluamanager_test.cpp +++ b/indra/newview/tests/llluamanager_test.cpp @@ -418,4 +418,25 @@ namespace tut auto [count, result] = future.get(); ensure_equals("leap.lua: " + result.asString(), count, 0); } + + template<> template<> + void object::test<7>() + { + set_test_name("stop hanging Lua script"); + const std::string lua( + "-- hanging Lua script should terminate\n" + "\n" + "LL.get_event_next()\n" + ); + LuaState L; + auto future = LLLUAmanager::startScriptLine(L, lua); + // The problem with this test is that the LuaState is destroyed + // (disconnecting the listener) before the LLTestApp instance mApp is + // destroyed (sending the shutdown event). Explicitly simulate LLApp's + // event. + LLEventPumps::instance().obtain("LLApp").post(llsd::map("status", "quitting")); + // but now we have to give the startScriptLine() coroutine a chance to run + auto [count, result] = future.get(); + ensure_equals(result.asString(), count, 0); + } } // namespace tut -- cgit v1.2.3 From fc6f8197f5e8764d0c2c0234437a7003f1ce6a0d Mon Sep 17 00:00:00 2001 From: Nat Goodspeed Date: Thu, 28 Mar 2024 11:36:46 -0400 Subject: Eliminate unreferenced exception name --- indra/llcommon/lualistener.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/indra/llcommon/lualistener.cpp b/indra/llcommon/lualistener.cpp index 37ce27a2a4..ef9782824e 100644 --- a/indra/llcommon/lualistener.cpp +++ b/indra/llcommon/lualistener.cpp @@ -108,7 +108,7 @@ LuaListener::PumpData LuaListener::getNext() { return mQueue.pop(); } - catch (const LLThreadSafeQueueInterrupt& exc) + catch (const LLThreadSafeQueueInterrupt&) { // mQueue has been closed. The only way that happens is when we detect // viewer shutdown. Terminate the calling coroutine. -- cgit v1.2.3 From 15de45261585e8b2d89c5df9091639ecca1e20c4 Mon Sep 17 00:00:00 2001 From: Nat Goodspeed Date: Thu, 28 Mar 2024 11:48:34 -0400 Subject: Ditch a couple LL_DEBUGS() messages. --- indra/llcommon/lualistener.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/indra/llcommon/lualistener.cpp b/indra/llcommon/lualistener.cpp index ef9782824e..018a31d5a8 100644 --- a/indra/llcommon/lualistener.cpp +++ b/indra/llcommon/lualistener.cpp @@ -43,13 +43,11 @@ LuaListener::LuaListener(lua_State* L): LLEventPump::inventName("LuaState"), [this](const LLSD& status) { - LL_DEBUGS("LuaListener") << "caught " << status << LL_ENDL; const auto& statsd = status["status"]; if (statsd.asString() != "running") { // If a Lua script is still blocked in getNext() during // viewer shutdown, close the queue to wake up getNext(). - LL_DEBUGS("LuaListener") << "closing queue" << LL_ENDL; mQueue.close(); } return false; -- cgit v1.2.3 From ba74839cd19c3b17757345ba81b1aa97c57f43d4 Mon Sep 17 00:00:00 2001 From: Nat Goodspeed Date: Thu, 28 Mar 2024 11:49:19 -0400 Subject: Use LLApp::setQuitting(). Expect killed-script error. --- indra/newview/tests/llluamanager_test.cpp | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/indra/newview/tests/llluamanager_test.cpp b/indra/newview/tests/llluamanager_test.cpp index 682289bd93..b907ac6619 100644 --- a/indra/newview/tests/llluamanager_test.cpp +++ b/indra/newview/tests/llluamanager_test.cpp @@ -430,13 +430,12 @@ namespace tut ); LuaState L; auto future = LLLUAmanager::startScriptLine(L, lua); - // The problem with this test is that the LuaState is destroyed - // (disconnecting the listener) before the LLTestApp instance mApp is - // destroyed (sending the shutdown event). Explicitly simulate LLApp's - // event. - LLEventPumps::instance().obtain("LLApp").post(llsd::map("status", "quitting")); + // Poke LLTestApp to send its preliminary shutdown message. + mApp.setQuitting(); // but now we have to give the startScriptLine() coroutine a chance to run auto [count, result] = future.get(); - ensure_equals(result.asString(), count, 0); + ensure_equals("killed Lua script terminated normally", count, -1); + ensure_equals("unexpected killed Lua script error", + result.asString(), "viewer is stopping"); } } // namespace tut -- cgit v1.2.3 From 53ce38b106a086a4e3bc1ed0663bb47b0f0d967c Mon Sep 17 00:00:00 2001 From: Nat Goodspeed Date: Thu, 28 Mar 2024 12:11:34 -0400 Subject: Remove rest of prototype UI access. --- indra/newview/llluamanager.cpp | 32 +------------------------------- 1 file changed, 1 insertion(+), 31 deletions(-) diff --git a/indra/newview/llluamanager.cpp b/indra/newview/llluamanager.cpp index d1fcf21941..74306a60ba 100644 --- a/indra/newview/llluamanager.cpp +++ b/indra/newview/llluamanager.cpp @@ -31,6 +31,7 @@ #include "llcoros.h" #include "llerror.h" #include "lleventcoro.h" +#include "llviewercontrol.h" #include "lua_function.h" #include "lualistener.h" #include "stringize.h" @@ -116,37 +117,6 @@ lua_function(print_warning, "print_warning(args...): WARNING level logging") return 0; } -#ifndef LL_TEST - -lua_function(run_ui_command, - "run_ui_command(name [, parameter]): " - "call specified UI command with specified parameter") -{ - int top = lua_gettop(L); - std::string func_name; - if (top >= 1) - { - func_name = lua_tostring(L,1); - } - std::string parameter; - if (top >= 2) - { - parameter = lua_tostring(L,2); - } - LL_WARNS("LUA") << "running ui func " << func_name << " parameter " << parameter << LL_ENDL; - LLSD event; - event["function"] = func_name; - if (!parameter.empty()) - { - event["parameter"] = parameter; - } - sUIListener.call(event); - - lua_settop(L, 0); - return 0; -} -#endif // ! LL_TEST - lua_function(post_on, "post_on(pumpname, data): post specified data to specified LLEventPump") { std::string pumpname{ lua_tostdstring(L, 1) }; -- cgit v1.2.3 From 836f8dc26f2041aea51c5c953b99f34859db6387 Mon Sep 17 00:00:00 2001 From: Mnikolenko Productengine Date: Mon, 1 Apr 2024 13:13:53 +0300 Subject: Add 'Lua Scripts' floater --- indra/llwindow/llwindow.h | 2 + indra/llwindow/llwindowmacosx.h | 2 + indra/llwindow/llwindowwin32.cpp | 39 ++++--- indra/llwindow/llwindowwin32.h | 2 + indra/newview/CMakeLists.txt | 2 + indra/newview/llfloaterluascripts.cpp | 123 +++++++++++++++++++++ indra/newview/llfloaterluascripts.h | 55 +++++++++ indra/newview/llluamanager.cpp | 5 + indra/newview/llluamanager.h | 5 + indra/newview/llviewerfloaterreg.cpp | 2 + .../skins/default/xui/en/floater_lua_scripts.xml | 36 ++++++ .../skins/default/xui/en/menu_lua_scripts.xml | 11 ++ indra/newview/skins/default/xui/en/menu_viewer.xml | 10 ++ 13 files changed, 279 insertions(+), 15 deletions(-) create mode 100644 indra/newview/llfloaterluascripts.cpp create mode 100644 indra/newview/llfloaterluascripts.h create mode 100644 indra/newview/skins/default/xui/en/floater_lua_scripts.xml create mode 100644 indra/newview/skins/default/xui/en/menu_lua_scripts.xml diff --git a/indra/llwindow/llwindow.h b/indra/llwindow/llwindow.h index f435d46584..9e9e424455 100644 --- a/indra/llwindow/llwindow.h +++ b/indra/llwindow/llwindow.h @@ -187,6 +187,8 @@ public: virtual void interruptLanguageTextInput() {} virtual void spawnWebBrowser(const std::string& escaped_url, bool async) {}; + virtual void openFolder(const std::string &path) {}; + static std::vector getDynamicFallbackFontList(); // Provide native key event data diff --git a/indra/llwindow/llwindowmacosx.h b/indra/llwindow/llwindowmacosx.h index 7614167213..5f728fb72e 100644 --- a/indra/llwindow/llwindowmacosx.h +++ b/indra/llwindow/llwindowmacosx.h @@ -116,6 +116,8 @@ public: void spawnWebBrowser(const std::string& escaped_url, bool async) override; F32 getSystemUISize() override; + void openFolder(const std::string &path) override; + static std::vector getDisplaysResolutionList(); static std::vector getDynamicFallbackFontList(); diff --git a/indra/llwindow/llwindowwin32.cpp b/indra/llwindow/llwindowwin32.cpp index 057d7a700e..e3ef28a27d 100644 --- a/indra/llwindow/llwindowwin32.cpp +++ b/indra/llwindow/llwindowwin32.cpp @@ -3755,6 +3755,25 @@ S32 OSMessageBoxWin32(const std::string& text, const std::string& caption, U32 t return retval; } +void shell_open(const std::string &file, bool async) +{ + // this is madness.. no, this is.. + LLWString url_wstring = utf8str_to_wstring(file); + llutf16string url_utf16 = wstring_to_utf16str(url_wstring); + + // let the OS decide what to use to open the URL + SHELLEXECUTEINFO sei = {sizeof(sei)}; + // NOTE: this assumes that SL will stick around long enough to complete the DDE message exchange + // necessary for ShellExecuteEx to complete + if (async) + { + sei.fMask = SEE_MASK_ASYNCOK; + } + sei.nShow = SW_SHOWNORMAL; + sei.lpVerb = L"open"; + sei.lpFile = url_utf16.c_str(); + ShellExecuteEx(&sei); +} void LLWindowWin32::spawnWebBrowser(const std::string& escaped_url, bool async) { @@ -3780,22 +3799,12 @@ void LLWindowWin32::spawnWebBrowser(const std::string& escaped_url, bool async) // replaced ShellExecute code with ShellExecuteEx since ShellExecute doesn't work // reliablly on Vista. - // this is madness.. no, this is.. - LLWString url_wstring = utf8str_to_wstring( escaped_url ); - llutf16string url_utf16 = wstring_to_utf16str( url_wstring ); + shell_open(escaped_url, async); +} - // let the OS decide what to use to open the URL - SHELLEXECUTEINFO sei = { sizeof( sei ) }; - // NOTE: this assumes that SL will stick around long enough to complete the DDE message exchange - // necessary for ShellExecuteEx to complete - if (async) - { - sei.fMask = SEE_MASK_ASYNCOK; - } - sei.nShow = SW_SHOWNORMAL; - sei.lpVerb = L"open"; - sei.lpFile = url_utf16.c_str(); - ShellExecuteEx( &sei ); +void LLWindowWin32::openFolder(const std::string &path) +{ + shell_open(path, false); } /* diff --git a/indra/llwindow/llwindowwin32.h b/indra/llwindow/llwindowwin32.h index ff287a140e..ed64891108 100644 --- a/indra/llwindow/llwindowwin32.h +++ b/indra/llwindow/llwindowwin32.h @@ -122,6 +122,8 @@ public: /*virtual*/ void interruptLanguageTextInput(); /*virtual*/ void spawnWebBrowser(const std::string& escaped_url, bool async); + void openFolder(const std::string &path); + /*virtual*/ F32 getSystemUISize(); LLWindowCallbacks::DragNDropResult completeDragNDropRequest( const LLCoordGL gl_coord, const MASK mask, LLWindowCallbacks::DragNDropAction action, const std::string url ); diff --git a/indra/newview/CMakeLists.txt b/indra/newview/CMakeLists.txt index b26ce7d06d..f41e481b26 100644 --- a/indra/newview/CMakeLists.txt +++ b/indra/newview/CMakeLists.txt @@ -243,6 +243,7 @@ set(viewer_SOURCE_FILES llfloaterlinkreplace.cpp llfloaterloadprefpreset.cpp llfloaterluadebug.cpp + llfloaterluascripts.cpp llfloatermarketplacelistings.cpp llfloatermap.cpp llfloatermediasettings.cpp @@ -901,6 +902,7 @@ set(viewer_HEADER_FILES llfloaterlinkreplace.h llfloaterloadprefpreset.h llfloaterluadebug.h + llfloaterluascripts.h llfloatermap.h llfloatermarketplacelistings.h llfloatermediasettings.h diff --git a/indra/newview/llfloaterluascripts.cpp b/indra/newview/llfloaterluascripts.cpp new file mode 100644 index 0000000000..87d2cf0c69 --- /dev/null +++ b/indra/newview/llfloaterluascripts.cpp @@ -0,0 +1,123 @@ +/** + * @file llfloaterluascriptsinfo.cpp + * + * $LicenseInfo:firstyear=2024&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2024, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#include "llviewerprecompiledheaders.h" + +#include "llfloaterluascripts.h" +#include "llevents.h" +#include +#include "llluamanager.h" +#include "llscrolllistctrl.h" +#include "llviewerwindow.h" +#include "llwindow.h" +#include "llviewermenu.h" + +const F32 REFRESH_INTERVAL = 1.0f; + +LLFloaterLUAScripts::LLFloaterLUAScripts(const LLSD &key) + : LLFloater(key), + mUpdateTimer(new LLTimer()), + mContextMenuHandle() +{ + mCommitCallbackRegistrar.add("Script.OpenFolder", [this](LLUICtrl*, const LLSD &userdata) + { + gViewerWindow->getWindow()->openFolder(mTargetFolderPath); + }); +} + + +BOOL LLFloaterLUAScripts::postBuild() +{ + mScriptList = getChild("scripts_list"); + mScriptList->setRightMouseDownCallback(boost::bind(&LLFloaterLUAScripts::onScrollListRightClicked, this, _1, _2, _3)); + + LLContextMenu *menu = LLUICtrlFactory::getInstance()->createFromFile( + "menu_lua_scripts.xml", gMenuHolder, LLViewerMenuHolderGL::child_registry_t::instance()); + if (menu) + { + mContextMenuHandle = menu->getHandle(); + } + + return TRUE; +} + +LLFloaterLUAScripts::~LLFloaterLUAScripts() +{ + auto menu = mContextMenuHandle.get(); + if (menu) + { + menu->die(); + mContextMenuHandle.markDead(); + } + + delete mUpdateTimer; +} + +void LLFloaterLUAScripts::draw() +{ + if (mUpdateTimer->hasExpired()) + { + populateScriptList(); + } + LLFloater::draw(); +} + +void LLFloaterLUAScripts::populateScriptList() +{ + S32 prev_pos = mScriptList->getScrollPos(); + LLSD prev_selected = mScriptList->getSelectedValue(); + mScriptList->clearRows(); + mScriptList->updateColumns(true); + std::map scripts = LLLUAmanager::getScriptNames(); + for (auto &it : scripts) + { + LLSD row; + row["value"] = it.first; + row["columns"][0]["value"] = std::filesystem::path((it.second)).stem().string(); + row["columns"][0]["column"] = "script_name"; + row["columns"][1]["value"] = it.second; + row["columns"][1]["column"] = "script_path"; + mScriptList->addElement(row); + } + mScriptList->setScrollPos(prev_pos); + mScriptList->setSelectedByValue(prev_selected, true); + mUpdateTimer->setTimerExpirySec(REFRESH_INTERVAL); +} + +void LLFloaterLUAScripts::onScrollListRightClicked(LLUICtrl *ctrl, S32 x, S32 y) +{ + LLScrollListItem *item = mScriptList->hitItem(x, y); + if (item) + { + mScriptList->selectItemAt(x, y, MASK_NONE); + auto menu = mContextMenuHandle.get(); + if (menu) + { + mTargetFolderPath = std::filesystem::path((item->getColumn(1)->getValue().asString())).parent_path().string(); + menu->show(x, y); + LLMenuGL::showPopup(this, menu, x, y); + } + } +} diff --git a/indra/newview/llfloaterluascripts.h b/indra/newview/llfloaterluascripts.h new file mode 100644 index 0000000000..c8c9e7f020 --- /dev/null +++ b/indra/newview/llfloaterluascripts.h @@ -0,0 +1,55 @@ +/** + * @file llfloaterluascriptsinfo.h + * + * $LicenseInfo:firstyear=2024&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2024, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#ifndef LL_LLFLOATERLUASCRIPTS_H +#define LL_LLFLOATERLUASCRIPTS_H + +#include "llfloater.h" + +class LLScrollListCtrl; + +class LLFloaterLUAScripts : + public LLFloater +{ + public: + LLFloaterLUAScripts(const LLSD &key); + virtual ~LLFloaterLUAScripts(); + + BOOL postBuild(); + void draw(); + +private: + void populateScriptList(); + void onScrollListRightClicked(LLUICtrl *ctrl, S32 x, S32 y); + + LLTimer* mUpdateTimer; + LLScrollListCtrl* mScriptList; + std::string mTargetFolderPath; + + LLHandle mContextMenuHandle; +}; + +#endif // LL_LLFLOATERLUASCRIPTS_H + diff --git a/indra/newview/llluamanager.cpp b/indra/newview/llluamanager.cpp index be332a7244..343c7c7459 100644 --- a/indra/newview/llluamanager.cpp +++ b/indra/newview/llluamanager.cpp @@ -48,6 +48,8 @@ #include #include +std::map LLLUAmanager::sScriptNames; + lua_function(sleep, "sleep(seconds): pause the running coroutine") { F32 seconds = lua_tonumber(L, -1); @@ -177,6 +179,8 @@ void LLLUAmanager::runScriptFile(const std::string &filename, script_result_fn r // A script_result_fn will be called when LuaState::expr() completes. LLCoros::instance().launch(filename, [filename, result_cb, finished_cb]() { + std::string coro_name = LLCoros::getName(); + sScriptNames[coro_name] = filename; llifstream in_file; in_file.open(filename.c_str()); @@ -201,6 +205,7 @@ void LLLUAmanager::runScriptFile(const std::string &filename, script_result_fn r result_cb(-1, msg); } } + sScriptNames.erase(coro_name); }); } diff --git a/indra/newview/llluamanager.h b/indra/newview/llluamanager.h index a297d14502..70728f958e 100644 --- a/indra/newview/llluamanager.h +++ b/indra/newview/llluamanager.h @@ -80,6 +80,11 @@ public: static std::pair waitScriptLine(LuaState& L, const std::string& chunk); static void runScriptOnLogin(); + + static std::map getScriptNames() { return sScriptNames; } + + private: + static std::map sScriptNames; }; class LLRequireResolver diff --git a/indra/newview/llviewerfloaterreg.cpp b/indra/newview/llviewerfloaterreg.cpp index f72ef71241..de328639f8 100644 --- a/indra/newview/llviewerfloaterreg.cpp +++ b/indra/newview/llviewerfloaterreg.cpp @@ -94,6 +94,7 @@ #include "llfloaterlinkreplace.h" #include "llfloaterloadprefpreset.h" #include "llfloaterluadebug.h" +#include "llfloaterluascripts.h" #include "llfloatermap.h" #include "llfloatermarketplacelistings.h" #include "llfloatermediasettings.h" @@ -402,6 +403,7 @@ void LLViewerFloaterReg::registerFloaters() LLFloaterReg::add("load_pref_preset", "floater_load_pref_preset.xml", (LLFloaterBuildFunc)&LLFloaterReg::build); LLFloaterReg::add("lua_debug", "floater_lua_debug.xml", (LLFloaterBuildFunc) &LLFloaterReg::build); + LLFloaterReg::add("lua_scripts", "floater_lua_scripts.xml", (LLFloaterBuildFunc) &LLFloaterReg::build); LLFloaterReg::add("mem_leaking", "floater_mem_leaking.xml", (LLFloaterBuildFunc)&LLFloaterReg::build); diff --git a/indra/newview/skins/default/xui/en/floater_lua_scripts.xml b/indra/newview/skins/default/xui/en/floater_lua_scripts.xml new file mode 100644 index 0000000000..6859201650 --- /dev/null +++ b/indra/newview/skins/default/xui/en/floater_lua_scripts.xml @@ -0,0 +1,36 @@ + + + + + + + diff --git a/indra/newview/skins/default/xui/en/menu_lua_scripts.xml b/indra/newview/skins/default/xui/en/menu_lua_scripts.xml new file mode 100644 index 0000000000..8f718abe17 --- /dev/null +++ b/indra/newview/skins/default/xui/en/menu_lua_scripts.xml @@ -0,0 +1,11 @@ + + + + + + diff --git a/indra/newview/skins/default/xui/en/menu_viewer.xml b/indra/newview/skins/default/xui/en/menu_viewer.xml index b259b101b4..40f5c40dfe 100644 --- a/indra/newview/skins/default/xui/en/menu_viewer.xml +++ b/indra/newview/skins/default/xui/en/menu_viewer.xml @@ -2539,6 +2539,16 @@ function="World.EnvPreset" function="Floater.Toggle" parameter="lua_debug" /> + + + + Date: Mon, 1 Apr 2024 15:33:15 +0300 Subject: open folder support for mac --- indra/llwindow/llwindowmacosx-objc.h | 2 ++ indra/llwindow/llwindowmacosx-objc.mm | 7 +++++++ indra/llwindow/llwindowmacosx.cpp | 5 +++++ 3 files changed, 14 insertions(+) diff --git a/indra/llwindow/llwindowmacosx-objc.h b/indra/llwindow/llwindowmacosx-objc.h index 77024d3a9c..ade303c6d4 100644 --- a/indra/llwindow/llwindowmacosx-objc.h +++ b/indra/llwindow/llwindowmacosx-objc.h @@ -177,6 +177,8 @@ void setMarkedText(unsigned short *text, unsigned int *selectedRange, unsigned i void getPreeditLocation(float *location, unsigned int length); void allowDirectMarkedTextInput(bool allow, GLViewRef glView); +void openFolderWithFinder(const char *folder_path); + NSWindowRef getMainAppWindow(); GLViewRef getGLView(); diff --git a/indra/llwindow/llwindowmacosx-objc.mm b/indra/llwindow/llwindowmacosx-objc.mm index 690fe058db..56d1798dcf 100644 --- a/indra/llwindow/llwindowmacosx-objc.mm +++ b/indra/llwindow/llwindowmacosx-objc.mm @@ -462,6 +462,13 @@ long showAlert(std::string text, std::string title, int type) return ret; } +void openFolderWithFinder(const char *folder_path) +{ + @autoreleasepool { + NSString *folderPathString = [NSString stringWithUTF8String:folder_path]; + [[NSWorkspace sharedWorkspace] openFile:folderPathString withApplication:@"Finder"]; + } +} /* GLViewRef getGLView() { diff --git a/indra/llwindow/llwindowmacosx.cpp b/indra/llwindow/llwindowmacosx.cpp index 778e5d3898..b774597eb6 100644 --- a/indra/llwindow/llwindowmacosx.cpp +++ b/indra/llwindow/llwindowmacosx.cpp @@ -2076,6 +2076,11 @@ F32 LLWindowMacOSX::getSystemUISize() return gHiDPISupport ? ::getDeviceUnitSize(mGLView) : LLWindow::getSystemUISize(); } +void LLWindowMacOSX::openFolder(const std::string &path) +{ + openFolderWithFinder(path.c_str()); +} + #if LL_OS_DRAGDROP_ENABLED /* S16 LLWindowMacOSX::dragTrackingHandler(DragTrackingMessage message, WindowRef theWindow, -- cgit v1.2.3 From f45ae0def221b4e1911e83f2da528174cf1d8a0d Mon Sep 17 00:00:00 2001 From: Nat Goodspeed Date: Tue, 2 Apr 2024 11:09:03 -0400 Subject: Defend leap.request(), generate() from garbage collection. Earlier we had blithely designated the 'pending' list (which stores WaitForReqid objects for pending request() and generate() calls) as a weak table. But the caller of request() or generate() does not hold a reference to the WaitForReqid object. Make pending hold "strong" references. Private collections (pending, waitfors) and private scalars that are never reassigned (reply, command) need not be entries in the leap table. --- indra/newview/scripts/lua/leap.lua | 77 ++++++++++++++++++++------------------ 1 file changed, 40 insertions(+), 37 deletions(-) diff --git a/indra/newview/scripts/lua/leap.lua b/indra/newview/scripts/lua/leap.lua index 9da1839c68..d19273e8bc 100644 --- a/indra/newview/scripts/lua/leap.lua +++ b/indra/newview/scripts/lua/leap.lua @@ -40,25 +40,25 @@ local fiber = require('fiber') local ErrorQueue = require('ErrorQueue') --- local dbg = require('printf') local function dbg(...) end +-- local dbg = require('printf') local leap = {} --- _reply: string name of reply LLEventPump. Any events the viewer posts to +-- reply: string name of reply LLEventPump. Any events the viewer posts to -- this pump will be queued for get_event_next(). We usually specify it as the -- reply pump for requests to internal viewer services. --- _command: string name of command LLEventPump. post_to(_command, ...) +-- command: string name of command LLEventPump. post_to(command, ...) -- engages LLLeapListener operations such as listening on a specified other -- LLEventPump, etc. -leap._reply, leap._command = LL.get_event_pumps() +local reply, command = LL.get_event_pumps() -- Dict of features added to the LEAP protocol since baseline implementation. -- Before engaging a new feature that might break an older viewer, we can -- check for the presence of that feature key. This table is solely about the -- LEAP protocol itself, the way we communicate with the viewer. To discover -- whether a given listener exists, or supports a particular operation, use --- _command's "getAPI" operation. --- For Lua, _command's "getFeatures" operation suffices? +-- command's "getAPI" operation. +-- For Lua, command's "getFeatures" operation suffices? -- leap._features = {} -- Each outstanding request() or generate() call has a corresponding @@ -67,20 +67,25 @@ leap._reply, leap._command = LL.get_event_pumps() -- we can look up the appropriate WaitForReqid object more efficiently -- in a dict than by tossing such objects into the usual waitfors list. -- Note: the ["reqid"] must be unique, otherwise we could end up --- replacing an earlier WaitForReqid object in self.pending with a +-- replacing an earlier WaitForReqid object in pending with a -- later one. That means that no incoming event will ever be given to -- the old WaitForReqid object. Any coroutine waiting on the discarded -- WaitForReqid object would therefore wait forever. --- these are weak values tables -local weak_values = {__mode='v'} -leap._pending = setmetatable({}, weak_values) +-- pending is NOT a weak table because the caller of request() or generate() +-- never sees the WaitForReqid object. pending holds the only reference, so +-- it should NOT be garbage-collected. +pending = {} -- Our consumer will instantiate some number of WaitFor subclass objects. -- As these are traversed in descending priority order, we must keep -- them in a list. -leap._waitfors = setmetatable({}, weak_values) +-- Anyone who instantiates a WaitFor subclass object should retain a reference +-- to it. Once the consuming script drops the reference, allow Lua to +-- garbage-collect the WaitFor despite its entry in waitfors. +local weak_values = {__mode='v'} +waitfors = setmetatable({}, weak_values) -- It has been suggested that we should use UUIDs as ["reqid"] values, -- since UUIDs are guaranteed unique. However, as the "namespace" for --- ["reqid"] values is our very own _reply pump, we can get away with +-- ["reqid"] values is our very own reply pump, we can get away with -- an integer. leap._reqid = 0 -- break leap.process() loop @@ -88,12 +93,12 @@ leap._done = false -- get the name of the reply pump function leap.replypump() - return leap._reply + return reply end -- get the name of the command pump function leap.cmdpump() - return leap._command + return command end -- Fire and forget. Send the specified request LLSD, expecting no reply. @@ -102,11 +107,10 @@ end -- -- See also request(), generate(). function leap.send(pump, data, reqid) - dbg('leap.send(%s, %s, %s) entry', pump, data, reqid) local data = data if type(data) == 'table' then data = table.clone(data) - data['reply'] = leap._reply + data['reply'] = reply if reqid ~= nil then data['reqid'] = reqid end @@ -122,13 +126,14 @@ local function requestSetup(pump, data) local reqid = leap._reqid -- Instantiate a new WaitForReqid object. The priority is irrelevant -- because, unlike the WaitFor base class, WaitForReqid does not - -- self-register on our leap._waitfors list. Instead, capture the new - -- WaitForReqid object in leap._pending so dispatch() can find it. - leap._pending[reqid] = leap.WaitForReqid:new(reqid) + -- self-register on our waitfors list. Instead, capture the new + -- WaitForReqid object in pending so dispatch() can find it. + local waitfor = leap.WaitForReqid:new(reqid) + pending[reqid] = waitfor -- Pass reqid to send() to stamp it into (a copy of) the request data. dbg('requestSetup(%s, %s)', pump, data) leap.send(pump, data, reqid) - return reqid + return reqid, waitfor end -- Send the specified request LLSD, expecting exactly one reply. Block @@ -150,13 +155,12 @@ end -- -- See also send(), generate(). function leap.request(pump, data) - local reqid = requestSetup(pump, data) - local waitfor = leap._pending[reqid] + local reqid, waitfor = requestSetup(pump, data) dbg('leap.request(%s, %s) about to wait on %s', pump, data, tostring(waitfor)) local ok, response = pcall(waitfor.wait, waitfor) dbg('leap.request(%s, %s) got %s: %s', pump, data, ok, response) -- kill off temporary WaitForReqid object, even if error - leap._pending[reqid] = nil + pending[reqid] = nil if ok then return response else @@ -178,8 +182,7 @@ function leap.generate(pump, data, checklast) -- Invent a new, unique reqid. Arrange to handle incoming events -- bearing that reqid. Stamp the outbound request with that reqid, and -- send it. - local reqid = requestSetup(pump, data) - local waitfor = leap._pending[reqid] + local reqid, waitfor = requestSetup(pump, data) local ok, response repeat ok, response = pcall(waitfor.wait, waitfor) @@ -189,7 +192,7 @@ function leap.generate(pump, data, checklast) coroutine.yield(response) until checklast and checklast(response) -- If we break the above loop, whether or not due to error, clean up. - leap._pending[reqid] = nil + pending[reqid] = nil if not ok then error(response) end @@ -197,10 +200,10 @@ end local function cleanup(message) -- we're done: clean up all pending coroutines - for i, waitfor in pairs(leap._pending) do + for i, waitfor in pairs(pending) do waitfor:close() end - for i, waitfor in pairs(leap._waitfors) do + for i, waitfor in pairs(waitfors) do waitfor:close() end end @@ -209,7 +212,7 @@ end local function unsolicited(pump, data) -- we maintain waitfors in descending priority order, so the first waitfor -- to claim this event is the one with the highest priority - for i, waitfor in pairs(leap._waitfors) do + for i, waitfor in pairs(waitfors) do dbg('unsolicited() checking %s', waitfor.name) if waitfor:handle(pump, data) then return @@ -226,7 +229,7 @@ local function dispatch(pump, data) return unsolicited(pump, data) end -- have reqid; do we have a WaitForReqid? - local waitfor = leap._pending[reqid] + local waitfor = pending[reqid] if waitfor == nil then return unsolicited(pump, data) end @@ -268,17 +271,17 @@ end -- called by WaitFor.enable() local function registerWaitFor(waitfor) - table.insert(leap._waitfors, waitfor) + table.insert(waitfors, waitfor) -- keep waitfors sorted in descending order of specified priority - table.sort(leap._waitfors, + table.sort(waitfors, function (lhs, rhs) return lhs.priority > rhs.priority end) end -- called by WaitFor.disable() local function unregisterWaitFor(waitfor) - for i, w in pairs(leap._waitfors) do + for i, w in pairs(waitfors) do if w == waitfor then - leap._waitfors[i] = nil + waitfors[i] = nil break end end @@ -389,7 +392,7 @@ function leap.WaitFor:filter(pump, data) error('You must override the WaitFor.filter() method') end --- called by unsolicited() for each WaitFor in leap._waitfors +-- called by unsolicited() for each WaitFor in waitfors function leap.WaitFor:handle(pump, data) local item = self:filter(pump, data) dbg('%s.filter() returned %s', self.name, item) @@ -423,8 +426,8 @@ leap.WaitForReqid = leap.WaitFor:new() function leap.WaitForReqid:new(reqid) -- priority is meaningless, since this object won't be added to the - -- priority-sorted ViewerClient.waitfors list. Use the reqid as the - -- debugging name string. + -- priority-sorted waitfors list. Use the reqid as the debugging name + -- string. local obj = leap.WaitFor:new(nil, 'WaitForReqid(' .. reqid .. ')') setmetatable(obj, self) self.__index = self -- cgit v1.2.3 From 233de4561c25df24dd787079ed7d7d98cbc40ff2 Mon Sep 17 00:00:00 2001 From: Nat Goodspeed Date: Tue, 2 Apr 2024 11:13:37 -0400 Subject: Give LLLeapListener's "listen" operation a "tweak" argument. If specified as true, "tweak" means to tweak the specified "listener" name for uniqueness. This avoids LLEventPump::listen()'s DupListenerName exception, which causes the "listen" operation to return "status" as false. --- indra/llcommon/llleaplistener.cpp | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/indra/llcommon/llleaplistener.cpp b/indra/llcommon/llleaplistener.cpp index 55e4752c5d..e8a4775b67 100644 --- a/indra/llcommon/llleaplistener.cpp +++ b/indra/llcommon/llleaplistener.cpp @@ -80,11 +80,13 @@ LLLeapListener::LLLeapListener(const std::string_view& caller, const Callback& c add("listen", "Listen to an existing LLEventPump named [\"source\"], with listener name\n" "[\"listener\"].\n" + "If [\"tweak\"] is specified as true, tweak listener name for uniqueness.\n" "By default, send events on [\"source\"] to the plugin, decorated\n" "with [\"pump\"]=[\"source\"].\n" "If [\"dest\"] specified, send undecorated events on [\"source\"] to the\n" "LLEventPump named [\"dest\"].\n" - "Returns [\"status\"] boolean indicating whether the connection was made.", + "Returns [\"status\"] boolean indicating whether the connection was made,\n" + "plus [\"listener\"] reporting (possibly tweaked) listener name.", &LLLeapListener::listen, need_source_listener); add("stoplistening", @@ -119,6 +121,7 @@ LLLeapListener::~LLLeapListener() // value_type, and Bad Things would happen if you copied an // LLTempBoundListener. (Destruction of the original would disconnect the // listener, invalidating every stored connection.) + LL_DEBUGS("LLLeapListener") << "~LLLeapListener(\"" << mCaller << "\")" << LL_ENDL; for (ListenersMap::value_type& pair : mListeners) { pair.second.disconnect(); @@ -155,6 +158,11 @@ void LLLeapListener::listen(const LLSD& request) std::string source_name = request["source"]; std::string dest_name = request["dest"]; std::string listener_name = request["listener"]; + if (request["tweak"].asBoolean()) + { + listener_name = LLEventPump::inventName(listener_name); + } + reply["listener"] = listener_name; LLEventPump & source = LLEventPumps::instance().obtain(source_name); -- cgit v1.2.3 From 1d1a278712298b91342b687d1b15b107ad51b4ba Mon Sep 17 00:00:00 2001 From: Nat Goodspeed Date: Tue, 2 Apr 2024 11:16:29 -0400 Subject: Add LL.source_path(), source_dir() Lua entry points. This helps a Lua script log its own identity, or find associated files relative to its location in the filesystem. Add more comprehensive logging around the start and end of a given Lua script, or its "p.s." fiber.run() call. --- indra/llcommon/lua_function.cpp | 45 +++++++++++++++++++++++++++++++++++++++-- indra/llcommon/lua_function.h | 3 +++ 2 files changed, 46 insertions(+), 2 deletions(-) diff --git a/indra/llcommon/lua_function.cpp b/indra/llcommon/lua_function.cpp index e731408c7d..96282df554 100644 --- a/indra/llcommon/lua_function.cpp +++ b/indra/llcommon/lua_function.cpp @@ -68,6 +68,15 @@ int lluau::loadstring(lua_State *L, const std::string &desc, const std::string & return luau_load(L, desc.data(), bytecode.get(), bytecodeSize, 0); } +std::filesystem::path lluau::source_path(lua_State* L) +{ + //Luau lua_Debug and lua_getinfo() are different compared to default Lua: + //see https://github.com/luau-lang/luau/blob/80928acb92d1e4b6db16bada6d21b1fb6fa66265/VM/include/lua.h + lua_Debug ar; + lua_getinfo(L, 1, "s", &ar); + return ar.source; +} + /***************************************************************************** * Lua <=> C++ conversions *****************************************************************************/ @@ -484,11 +493,15 @@ bool LuaState::checkLua(const std::string& desc, int r) std::pair LuaState::expr(const std::string& desc, const std::string& text) { if (! checkLua(desc, lluau::dostring(mState, desc, text))) + { + LL_WARNS("Lua") << desc << " error: " << mError << LL_ENDL; return { -1, mError }; + } // here we believe there was no error -- did the Lua fragment leave // anything on the stack? std::pair result{ lua_gettop(mState), {} }; + LL_INFOS("Lua") << desc << " done, " << result.first << " results." << LL_ENDL; if (result.first) { // aha, at least one entry on the stack! @@ -501,6 +514,7 @@ std::pair LuaState::expr(const std::string& desc, const std::string& } catch (const std::exception& error) { + LL_WARNS("Lua") << desc << " error converting result: " << error.what() << LL_ENDL; // lua_tollsd() is designed to be called from a lua_function(), // that is, from a C++ function called by Lua. In case of error, // it throws a Lua error to be caught by the Lua runtime. expr() @@ -519,15 +533,18 @@ std::pair LuaState::expr(const std::string& desc, const std::string& else { // multiple entries on the stack + int index; try { - for (int index = 1; index <= result.first; ++index) + for (index = 1; index <= result.first; ++index) { result.second.append(lua_tollsd(mState, index)); } } catch (const std::exception& error) { + LL_WARNS("Lua") << desc << " error converting result " << index << ": " + << error.what() << LL_ENDL; // see above comments regarding lua_State's error status initLuaState(); return { -1, stringize(LLError::Log::classname(error), ": ", error.what()) }; @@ -577,9 +594,13 @@ std::pair LuaState::expr(const std::string& desc, const std::string& { // there's a fiber.run() function sitting on the top of the stack // -- call it with no arguments, discarding anything it returns - LL_DEBUGS("Lua") << "Calling fiber.run()" << LL_ENDL; + LL_INFOS("Lua") << desc << " p.s. fiber.run()" << LL_ENDL; if (! checkLua(desc, lua_pcall(mState, 0, 0, 0))) + { + LL_WARNS("Lua") << desc << " p.s. fiber.run() error: " << mError << LL_ENDL; return { -1, mError }; + } + LL_INFOS("Lua") << desc << " p.s. done." << LL_ENDL; } } // pop everything again @@ -684,6 +705,26 @@ std::pair LuaFunction::getState() return { registry, lookup }; } +/***************************************************************************** +* source_path() +*****************************************************************************/ +lua_function(source_path, "return the source path of the running Lua script") +{ + luaL_checkstack(L, 1, nullptr); + lua_pushstdstring(L, lluau::source_path(L)); + return 1; +} + +/***************************************************************************** +* source_dir() +*****************************************************************************/ +lua_function(source_dir, "return the source directory of the running Lua script") +{ + luaL_checkstack(L, 1, nullptr); + lua_pushstdstring(L, lluau::source_path(L).parent_path()); + return 1; +} + /***************************************************************************** * check_stop() *****************************************************************************/ diff --git a/indra/llcommon/lua_function.h b/indra/llcommon/lua_function.h index 868c13c3f1..785aadeb0c 100644 --- a/indra/llcommon/lua_function.h +++ b/indra/llcommon/lua_function.h @@ -18,6 +18,7 @@ #include "luau/lualib.h" #include "stringize.h" #include // std::uncaught_exceptions() +#include #include // std::shared_ptr #include // std::pair @@ -49,6 +50,8 @@ namespace lluau // terminated char arrays. int dostring(lua_State* L, const std::string& desc, const std::string& text); int loadstring(lua_State* L, const std::string& desc, const std::string& text); + + std::filesystem::path source_path(lua_State* L); } // namespace lluau std::string lua_tostdstring(lua_State* L, int index); -- cgit v1.2.3 From b610b378ee3249b572d98875a0e557cbf80c2ded Mon Sep 17 00:00:00 2001 From: Nat Goodspeed Date: Tue, 2 Apr 2024 11:21:13 -0400 Subject: Use LLCoros::TempStatus when Lua is waiting on get_event_next(). When enumerating C++ coroutines, it can be useful to know that a particular Lua coroutine is simply waiting for further events. --- indra/llcommon/lualistener.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/indra/llcommon/lualistener.cpp b/indra/llcommon/lualistener.cpp index 018a31d5a8..82e32860db 100644 --- a/indra/llcommon/lualistener.cpp +++ b/indra/llcommon/lualistener.cpp @@ -104,6 +104,7 @@ LuaListener::PumpData LuaListener::getNext() { try { + LLCoros::TempStatus status("get_event_next()"); return mQueue.pop(); } catch (const LLThreadSafeQueueInterrupt&) -- cgit v1.2.3 From cc8299d153b9aa81558951cb6b1a19693fb52982 Mon Sep 17 00:00:00 2001 From: Nat Goodspeed Date: Tue, 2 Apr 2024 11:22:45 -0400 Subject: Streamline std::filesystem::path conversions in LLRequireResolver. Make LLRequireResolver capture std::filesystem::path instances, instead of std::strings, for the path to resolve and the source directory. Store the running script's containing directory instead of calling parent_path() over and over. Demote Lua LL.post_on() logging to DEBUG level instead of INFO. --- indra/newview/llluamanager.cpp | 29 +++++++++++------------------ indra/newview/llluamanager.h | 5 +++-- 2 files changed, 14 insertions(+), 20 deletions(-) diff --git a/indra/newview/llluamanager.cpp b/indra/newview/llluamanager.cpp index f3a8d2c51c..9059db9967 100644 --- a/indra/newview/llluamanager.cpp +++ b/indra/newview/llluamanager.cpp @@ -122,7 +122,7 @@ lua_function(post_on, "post_on(pumpname, data): post specified data to specified std::string pumpname{ lua_tostdstring(L, 1) }; LLSD data{ lua_tollsd(L, 2) }; lua_pop(L, 2); - LL_INFOS("Lua") << "post_on('" << pumpname << "', " << data << ")" << LL_ENDL; + LL_DEBUGS("Lua") << "post_on('" << pumpname << "', " << data << ")" << LL_ENDL; LLEventPumps::instance().obtain(pumpname).post(data); return 0; } @@ -314,19 +314,12 @@ void LLRequireResolver::resolveRequire(lua_State *L, std::string path) } LLRequireResolver::LLRequireResolver(lua_State *L, const std::string& path) : - mPathToResolve(path), + mPathToResolve(std::filesystem::path(path).lexically_normal()), L(L) { - //Luau lua_Debug and lua_getinfo() are different compared to default Lua: - //see https://github.com/luau-lang/luau/blob/80928acb92d1e4b6db16bada6d21b1fb6fa66265/VM/include/lua.h - lua_Debug ar; - lua_getinfo(L, 1, "s", &ar); - mSourceChunkname = ar.source; + mSourceDir = lluau::source_path(L).parent_path(); - std::filesystem::path fs_path(mPathToResolve); - mPathToResolve = fs_path.lexically_normal().string(); - - if (fs_path.is_absolute()) + if (mPathToResolve.is_absolute()) luaL_argerrorL(L, 1, "cannot require a full path"); } @@ -358,8 +351,8 @@ private: // push the loaded module or throw a Lua error void LLRequireResolver::findModule() { - // If mPathToResolve is absolute, this replaces mSourceChunkname.parent_path. - auto absolutePath = (std::filesystem::path((mSourceChunkname)).parent_path() / mPathToResolve).u8string(); + // If mPathToResolve is absolute, this replaces mSourceDir. + auto absolutePath = (mSourceDir / mPathToResolve).u8string(); // Push _MODULES table on stack for checking and saving to the cache luaL_findtable(L, LUA_REGISTRYINDEX, "_MODULES", 1); @@ -375,16 +368,16 @@ void LLRequireResolver::findModule() // not already cached - prep error message just in case auto fail{ - [L=L, path=mPathToResolve]() + [L=L, path=mPathToResolve.u8string()]() { luaL_error(L, "could not find require('%s')", path.data()); }}; - if (std::filesystem::path(mPathToResolve).is_absolute()) + if (mPathToResolve.is_absolute()) { // no point searching known directories for an absolute path fail(); } - std::vector lib_paths + std::vector lib_paths { gDirUtilp->getExpandedFilename(LL_PATH_SCRIPTS, "lua"), #ifdef LL_TEST @@ -395,10 +388,10 @@ void LLRequireResolver::findModule() for (const auto& path : lib_paths) { - std::string absolutePathOpt = (std::filesystem::path(path) / mPathToResolve).u8string(); + std::string absolutePathOpt = (path / mPathToResolve).u8string(); if (absolutePathOpt.empty()) - luaL_error(L, "error requiring module '%s'", mPathToResolve.data()); + luaL_error(L, "error requiring module '%s'", mPathToResolve.u8string().data()); if (findModuleImpl(absolutePathOpt)) return; diff --git a/indra/newview/llluamanager.h b/indra/newview/llluamanager.h index a297d14502..bd581376b4 100644 --- a/indra/newview/llluamanager.h +++ b/indra/newview/llluamanager.h @@ -29,6 +29,7 @@ #include "llcoros.h" #include "llsd.h" +#include #include #include #include // std::pair @@ -88,8 +89,8 @@ class LLRequireResolver static void resolveRequire(lua_State *L, std::string path); private: - std::string mPathToResolve; - std::string mSourceChunkname; + std::filesystem::path mPathToResolve; + std::filesystem::path mSourceDir; LLRequireResolver(lua_State *L, const std::string& path); -- cgit v1.2.3 From 1866d1a450b7857da82ceafbd2c6581d87b7f334 Mon Sep 17 00:00:00 2001 From: Nat Goodspeed Date: Tue, 2 Apr 2024 11:29:29 -0400 Subject: Add startup.lua module with startup.ensure(), wait() functions. This lets a calling script verify that it's running at the right point in the viewer's life cycle. A script that wants to interact with the SL agent wouldn't work if run from the viewer's command line -- unless it calls startup.wait("STATE_STARTED"), which pauses until login is complete. Modify test_luafloater_demo.lua and test_luafloater_gesture_list.lua to find their respective floater XUI files in the same directory as themselves. Make them both capture the reqid returned by the "showLuaFloater" operation, and filter for events bearing the same reqid. This paves the way for a given script to display more than one floater concurrently. Make test_luafloater_demo.lua (which does not require in-world resources) wait until 'STATE_LOGIN_WAIT', the point at which the viewer has presented the login screen. Make test_luafloater_gesture_list.lua (which interacts with the agent) wait until 'STATE_STARTED', the point at which the viewer is fully in world. Either or both can now be launched from the viewer's command line. --- indra/newview/scripts/lua/startup.lua | 101 +++++++++++++++++++++ indra/newview/scripts/lua/test_luafloater_demo.lua | 25 +++-- .../scripts/lua/test_luafloater_gesture_list.lua | 26 ++++-- 3 files changed, 138 insertions(+), 14 deletions(-) create mode 100644 indra/newview/scripts/lua/startup.lua diff --git a/indra/newview/scripts/lua/startup.lua b/indra/newview/scripts/lua/startup.lua new file mode 100644 index 0000000000..4311bb9a60 --- /dev/null +++ b/indra/newview/scripts/lua/startup.lua @@ -0,0 +1,101 @@ +-- query, wait for or mandate a particular viewer startup state + +-- During startup, the viewer steps through a sequence of numbered (and named) +-- states. This can be used to detect when, for instance, the login screen is +-- displayed, or when the viewer has finished logging in and is fully +-- in-world. + +local fiber = require 'fiber' +local leap = require 'leap' +local inspect = require 'inspect' +local function dbg(...) end +-- local dbg = require 'printf' + +-- --------------------------------------------------------------------------- +-- Get the list of startup states from the viewer. +local bynum = leap.request('LLStartUp', {op='getStateTable'})['table'] + +local byname = setmetatable( + {}, + -- set metatable to throw an error if you look up invalid state name + {__index=function(t, k) + local v = t[k] + if v then + return v + end + error(string.format('startup module passed invalid state %q', k), 2) + end}) + +-- derive byname as a lookup table to find the 0-based index for a given name +for i, name in pairs(bynum) do + -- the viewer's states are 0-based, not 1-based like Lua indexes + byname[name] = i - 1 +end +-- dbg('startup states: %s', inspect(byname)) + +-- specialize a WaitFor to track the viewer's startup state +local startup_pump = 'StartupState' +local waitfor = leap.WaitFor:new(0, startup_pump) +function waitfor:filter(pump, data) + if pump == self.name then + return data + end +end + +function waitfor:process(data) + -- keep updating startup._state for interested parties + startup._state = data.str + dbg('startup updating state to %q', data.str) + -- now pass data along to base-class method to queue + leap.WaitFor.process(self, data) +end + +-- listen for StartupState events +leap.request(leap.cmdpump(), + {op='listen', source=startup_pump, listener='startup.lua', tweak=true}) +-- poke LLStartUp to make sure we get an event +leap.send('LLStartUp', {op='postStartupState'}) + +-- --------------------------------------------------------------------------- +startup = {} + +-- wait for response from postStartupState +while not startup._state do + dbg('startup.state() waiting for first StartupState event') + waitfor:wait() +end + +-- return a list of all known startup states +function startup.list() + return bynum +end + +-- report whether state with string name 'left' is before string name 'right' +function startup.before(left, right) + return byname[left] < byname[right] +end + +-- report the viewer's current startup state +function startup.state() + return startup._state +end + +-- error if script is called before specified state string name +function startup.ensure(state) + if startup.before(startup.state(), state) then + -- tell error() to pretend this error was thrown by our caller + error('must not be called before startup state ' .. state, 2) + end +end + +-- block calling fiber until viewer has reached state with specified string name +function startup.wait(state) + dbg('startup.wait(%q)', state) + while startup.before(startup.state(), state) do + local item = waitfor:wait() + dbg('startup.wait(%q) sees %s', state, item) + end +end + +return startup + diff --git a/indra/newview/scripts/lua/test_luafloater_demo.lua b/indra/newview/scripts/lua/test_luafloater_demo.lua index c375a2abc7..c2d16d4f88 100644 --- a/indra/newview/scripts/lua/test_luafloater_demo.lua +++ b/indra/newview/scripts/lua/test_luafloater_demo.lua @@ -1,10 +1,16 @@ -XML_FILE_PATH = "luafloater_demo.xml" +XML_FILE_PATH = LL.source_dir() .. "/luafloater_demo.xml" + +scriptparts = string.split(LL.source_path(), '/') +scriptname = scriptparts[#scriptparts] +print('Running ' .. scriptname) leap = require 'leap' fiber = require 'fiber' +startup = require 'startup' --event pump for sending actions to the floater -COMMAND_PUMP_NAME = "" +local COMMAND_PUMP_NAME = "" +local reqid --table of floater UI events event_list=leap.request("LLFloaterReg", {op="getFloaterEvents"}).events @@ -41,24 +47,29 @@ function handleEvents(event_data) end elseif event_data.event == _event("floater_close") then LL.print_warning("Floater was closed") - leap.done() + return false end + return true end +startup.wait('STATE_LOGIN_WAIT') local key = {xml_path = XML_FILE_PATH, op = "showLuaFloater"} --sign for additional events for defined control {= {action1, action2, ...}} key.extra_events={show_time_lbl = {_event("right_mouse_down"), _event("double_click")}} -COMMAND_PUMP_NAME = leap.request("LLFloaterReg", key).command_name +local resp = leap.request("LLFloaterReg", key) +COMMAND_PUMP_NAME = resp.command_name +reqid = resp.reqid catch_events = leap.WaitFor:new(-1, "all_events") function catch_events:filter(pump, data) - return data + if data.reqid == reqid then + return data + end end function process_events(waitfor) event_data = waitfor:wait() - while event_data do - handleEvents(event_data) + while event_data and handleEvents(event_data) do event_data = waitfor:wait() end end diff --git a/indra/newview/scripts/lua/test_luafloater_gesture_list.lua b/indra/newview/scripts/lua/test_luafloater_gesture_list.lua index 6d4a8e0cad..a907eb2e90 100644 --- a/indra/newview/scripts/lua/test_luafloater_gesture_list.lua +++ b/indra/newview/scripts/lua/test_luafloater_gesture_list.lua @@ -1,11 +1,17 @@ -XML_FILE_PATH = "luafloater_gesture_list.xml" +XML_FILE_PATH = LL.source_dir() .. "/luafloater_gesture_list.xml" + +scriptparts = string.split(LL.source_path(), '/') +scriptname = scriptparts[#scriptparts] +print('Running ' .. scriptname) leap = require 'leap' fiber = require 'fiber' LLGesture = require 'LLGesture' +startup = require 'startup' --event pump for sending actions to the floater -COMMAND_PUMP_NAME = "" +local COMMAND_PUMP_NAME = "" +local reqid --table of floater UI events event_list=leap.request("LLFloaterReg", {op="getFloaterEvents"}).events @@ -22,9 +28,12 @@ end function handleEvents(event_data) if event_data.event == _event("floater_close") then - leap.done() - elseif event_data.event == _event("post_build") then + return false + end + + if event_data.event == _event("post_build") then COMMAND_PUMP_NAME = event_data.command_name + reqid = event_data.reqid gestures_uuid = LLGesture.getActiveGestures() local action_data = {} action_data.action = "add_list_element" @@ -40,8 +49,10 @@ function handleEvents(event_data) LLGesture.startGesture(event_data.value) end end + return true end +startup.wait('STATE_STARTED') local key = {xml_path = XML_FILE_PATH, op = "showLuaFloater"} --receive additional events for defined control {= {action1, action2, ...}} key.extra_events={gesture_list = {_event("double_click")}} @@ -49,13 +60,14 @@ handleEvents(leap.request("LLFloaterReg", key)) catch_events = leap.WaitFor:new(-1, "all_events") function catch_events:filter(pump, data) - return data + if data.reqid == reqid then + return data + end end function process_events(waitfor) event_data = waitfor:wait() - while event_data do - handleEvents(event_data) + while event_data and handleEvents(event_data) do event_data = waitfor:wait() end end -- cgit v1.2.3 From 7049485ebd0b997a097c12e094425d58db56e043 Mon Sep 17 00:00:00 2001 From: Nat Goodspeed Date: Tue, 2 Apr 2024 13:25:08 -0400 Subject: Fix std::filesystem::path - to - std::string conversions on Windows. On Windows, std::filesystem::path::value_type is wchar_t, not char -- so path::string_type is std::wstring, not std::string. So while Posix path instances implicitly convert to string, Windows path instances do not. Add explicit u8string() calls. Also add LL.abspath() Lua entry point to further facilitate finding a resource file relative to the calling Lua script. Use abspath() for both test_luafloater_demo.lua and test_luafloater_gesture_list.lua. --- indra/llcommon/lua_function.cpp | 16 ++++++++++++++-- indra/newview/scripts/lua/test_luafloater_demo.lua | 2 +- .../newview/scripts/lua/test_luafloater_gesture_list.lua | 2 +- 3 files changed, 16 insertions(+), 4 deletions(-) diff --git a/indra/llcommon/lua_function.cpp b/indra/llcommon/lua_function.cpp index 96282df554..cd83f40e85 100644 --- a/indra/llcommon/lua_function.cpp +++ b/indra/llcommon/lua_function.cpp @@ -711,7 +711,7 @@ std::pair LuaFunction::getState() lua_function(source_path, "return the source path of the running Lua script") { luaL_checkstack(L, 1, nullptr); - lua_pushstdstring(L, lluau::source_path(L)); + lua_pushstdstring(L, lluau::source_path(L).u8string()); return 1; } @@ -721,7 +721,19 @@ lua_function(source_path, "return the source path of the running Lua script") lua_function(source_dir, "return the source directory of the running Lua script") { luaL_checkstack(L, 1, nullptr); - lua_pushstdstring(L, lluau::source_path(L).parent_path()); + lua_pushstdstring(L, lluau::source_path(L).parent_path().u8string()); + return 1; +} + +/***************************************************************************** +* abspath() +*****************************************************************************/ +lua_function(abspath, + "for given filesystem path relative to running script, return absolute path") +{ + auto path{ lua_tostdstring(L, 1) }; + lua_pop(L, 1); + lua_pushstdstring(L, (lluau::source_path(L).parent_path() / path).u8string()); return 1; } diff --git a/indra/newview/scripts/lua/test_luafloater_demo.lua b/indra/newview/scripts/lua/test_luafloater_demo.lua index c2d16d4f88..ab638dcdd1 100644 --- a/indra/newview/scripts/lua/test_luafloater_demo.lua +++ b/indra/newview/scripts/lua/test_luafloater_demo.lua @@ -1,4 +1,4 @@ -XML_FILE_PATH = LL.source_dir() .. "/luafloater_demo.xml" +XML_FILE_PATH = LL.abspath("luafloater_demo.xml") scriptparts = string.split(LL.source_path(), '/') scriptname = scriptparts[#scriptparts] diff --git a/indra/newview/scripts/lua/test_luafloater_gesture_list.lua b/indra/newview/scripts/lua/test_luafloater_gesture_list.lua index a907eb2e90..3d9a9b0ad4 100644 --- a/indra/newview/scripts/lua/test_luafloater_gesture_list.lua +++ b/indra/newview/scripts/lua/test_luafloater_gesture_list.lua @@ -1,4 +1,4 @@ -XML_FILE_PATH = LL.source_dir() .. "/luafloater_gesture_list.xml" +XML_FILE_PATH = LL.abspath("luafloater_gesture_list.xml") scriptparts = string.split(LL.source_path(), '/') scriptname = scriptparts[#scriptparts] -- cgit v1.2.3 From b351888ed7d395279dfc022363e911d52ebdcc16 Mon Sep 17 00:00:00 2001 From: Mnikolenko Productengine Date: Wed, 3 Apr 2024 14:26:34 +0300 Subject: Add RAII class for adding/erasing script entries; code clean up --- indra/llwindow/llwindowwin32.cpp | 4 +--- indra/llwindow/llwindowwin32.h | 2 +- indra/newview/llfloaterluascripts.cpp | 7 ++----- indra/newview/llfloaterluascripts.h | 2 +- indra/newview/llluamanager.cpp | 4 +--- indra/newview/llluamanager.h | 18 +++++++++++++++++- 6 files changed, 23 insertions(+), 14 deletions(-) diff --git a/indra/llwindow/llwindowwin32.cpp b/indra/llwindow/llwindowwin32.cpp index e3ef28a27d..7fbc2c8ea2 100644 --- a/indra/llwindow/llwindowwin32.cpp +++ b/indra/llwindow/llwindowwin32.cpp @@ -3757,9 +3757,7 @@ S32 OSMessageBoxWin32(const std::string& text, const std::string& caption, U32 t void shell_open(const std::string &file, bool async) { - // this is madness.. no, this is.. - LLWString url_wstring = utf8str_to_wstring(file); - llutf16string url_utf16 = wstring_to_utf16str(url_wstring); + std::wstring url_utf16 = ll_convert(file); // let the OS decide what to use to open the URL SHELLEXECUTEINFO sei = {sizeof(sei)}; diff --git a/indra/llwindow/llwindowwin32.h b/indra/llwindow/llwindowwin32.h index ed64891108..320c1c8b88 100644 --- a/indra/llwindow/llwindowwin32.h +++ b/indra/llwindow/llwindowwin32.h @@ -122,7 +122,7 @@ public: /*virtual*/ void interruptLanguageTextInput(); /*virtual*/ void spawnWebBrowser(const std::string& escaped_url, bool async); - void openFolder(const std::string &path); + void openFolder(const std::string &path) override; /*virtual*/ F32 getSystemUISize(); diff --git a/indra/newview/llfloaterluascripts.cpp b/indra/newview/llfloaterluascripts.cpp index 87d2cf0c69..bd845a97d6 100644 --- a/indra/newview/llfloaterluascripts.cpp +++ b/indra/newview/llfloaterluascripts.cpp @@ -51,7 +51,7 @@ LLFloaterLUAScripts::LLFloaterLUAScripts(const LLSD &key) BOOL LLFloaterLUAScripts::postBuild() { mScriptList = getChild("scripts_list"); - mScriptList->setRightMouseDownCallback(boost::bind(&LLFloaterLUAScripts::onScrollListRightClicked, this, _1, _2, _3)); + mScriptList->setRightMouseDownCallback([this](LLUICtrl *ctrl, S32 x, S32 y, MASK mask) { onScrollListRightClicked(ctrl, x, y);}); LLContextMenu *menu = LLUICtrlFactory::getInstance()->createFromFile( "menu_lua_scripts.xml", gMenuHolder, LLViewerMenuHolderGL::child_registry_t::instance()); @@ -71,13 +71,11 @@ LLFloaterLUAScripts::~LLFloaterLUAScripts() menu->die(); mContextMenuHandle.markDead(); } - - delete mUpdateTimer; } void LLFloaterLUAScripts::draw() { - if (mUpdateTimer->hasExpired()) + if (mUpdateTimer->checkExpirationAndReset(REFRESH_INTERVAL)) { populateScriptList(); } @@ -103,7 +101,6 @@ void LLFloaterLUAScripts::populateScriptList() } mScriptList->setScrollPos(prev_pos); mScriptList->setSelectedByValue(prev_selected, true); - mUpdateTimer->setTimerExpirySec(REFRESH_INTERVAL); } void LLFloaterLUAScripts::onScrollListRightClicked(LLUICtrl *ctrl, S32 x, S32 y) diff --git a/indra/newview/llfloaterluascripts.h b/indra/newview/llfloaterluascripts.h index c8c9e7f020..548bbd10f6 100644 --- a/indra/newview/llfloaterluascripts.h +++ b/indra/newview/llfloaterluascripts.h @@ -44,7 +44,7 @@ private: void populateScriptList(); void onScrollListRightClicked(LLUICtrl *ctrl, S32 x, S32 y); - LLTimer* mUpdateTimer; + std::unique_ptr mUpdateTimer; LLScrollListCtrl* mScriptList; std::string mTargetFolderPath; diff --git a/indra/newview/llluamanager.cpp b/indra/newview/llluamanager.cpp index 343c7c7459..d2b8ca3a94 100644 --- a/indra/newview/llluamanager.cpp +++ b/indra/newview/llluamanager.cpp @@ -179,8 +179,7 @@ void LLLUAmanager::runScriptFile(const std::string &filename, script_result_fn r // A script_result_fn will be called when LuaState::expr() completes. LLCoros::instance().launch(filename, [filename, result_cb, finished_cb]() { - std::string coro_name = LLCoros::getName(); - sScriptNames[coro_name] = filename; + ScriptObserver observer(LLCoros::getName(), filename); llifstream in_file; in_file.open(filename.c_str()); @@ -205,7 +204,6 @@ void LLLUAmanager::runScriptFile(const std::string &filename, script_result_fn r result_cb(-1, msg); } } - sScriptNames.erase(coro_name); }); } diff --git a/indra/newview/llluamanager.h b/indra/newview/llluamanager.h index 70728f958e..88467cbf84 100644 --- a/indra/newview/llluamanager.h +++ b/indra/newview/llluamanager.h @@ -40,6 +40,8 @@ class LuaState; class LLLUAmanager { + friend class ScriptObserver; + public: // Pass a callback with this signature to obtain the error message, if // any, from running a script or source string. Empty msg means success. @@ -81,7 +83,7 @@ public: static void runScriptOnLogin(); - static std::map getScriptNames() { return sScriptNames; } + static const std::map getScriptNames() { return sScriptNames; } private: static std::map sScriptNames; @@ -104,4 +106,18 @@ class LLRequireResolver bool findModuleImpl(const std::string& absolutePath); void runModule(const std::string& desc, const std::string& code); }; + +// RAII class to guarantee that a script entry is erased even when coro is terminated +class ScriptObserver +{ + public: + ScriptObserver(const std::string &coro_name, const std::string &filename) : mCoroName(coro_name) + { + LLLUAmanager::sScriptNames[mCoroName] = filename; + } + ~ScriptObserver() { LLLUAmanager::sScriptNames.erase(mCoroName); } + + private: + std::string mCoroName; +}; #endif -- cgit v1.2.3 From 3b25bc10febc84f10348715dabc9590458923c0b Mon Sep 17 00:00:00 2001 From: Nat Goodspeed Date: Wed, 3 Apr 2024 11:07:36 -0400 Subject: Make ll_convert() and ll_convert_to() use std::decay_t on arg type. Among other things, this empowers ll_convert() and ll_convert_to() to accept a string literal (which might contain non-ASCII characters, e.g. __FILE__). Without this, even though we have ll_convert_impl specializations accepting const char*, passing a string literal fails because the compiler can't find a specialization specifically accepting const char[length]. --- indra/llcommon/llstring.h | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/indra/llcommon/llstring.h b/indra/llcommon/llstring.h index 0eb2770004..14aa51cb4a 100644 --- a/indra/llcommon/llstring.h +++ b/indra/llcommon/llstring.h @@ -37,6 +37,7 @@ #include #include #include +#include #include "llformat.h" #include "stdtypes.h" @@ -542,7 +543,7 @@ public: template inline operator TO() const { - return ll_convert_impl()(mRef); + return ll_convert_impl>()(mRef); } }; @@ -551,7 +552,7 @@ public: template TO ll_convert_to(const FROM& in) { - return ll_convert_impl()(in); + return ll_convert_impl>()(in); } // degenerate case -- cgit v1.2.3 From e399b02e3306a249cb161f07cac578d3f2617bab Mon Sep 17 00:00:00 2001 From: Nat Goodspeed Date: Wed, 3 Apr 2024 12:31:43 -0400 Subject: Introduce fsyspath subclass of std::filesystem::path. Our std::strings are UTF-8 encoded, so conversion from std::string to std::filesystem::path must use UTF-8 decoding. The native Windows std::filesystem::path constructor and assignment operator accepting std::string use "native narrow encoding," which mangles path strings containing UTF-8 encoded non-ASCII characters. fsyspath's std::string constructor and assignment operator explicitly engage std::filesystem::u8path() to handle encoding. u8path() is deprecated in C++20, but once we adapt fsyspath's conversion to C++20 conventions, consuming code need not be modified. --- indra/llcommon/CMakeLists.txt | 1 + indra/llcommon/fsyspath.h | 74 +++++++++++++++++++++++++++++++++++++++++ indra/llcommon/lua_function.cpp | 6 ++-- indra/llcommon/lua_function.h | 4 +-- indra/llui/llluafloater.cpp | 8 ++--- indra/newview/llluamanager.cpp | 8 ++--- indra/newview/llluamanager.h | 6 ++-- 7 files changed, 91 insertions(+), 16 deletions(-) create mode 100644 indra/llcommon/fsyspath.h diff --git a/indra/llcommon/CMakeLists.txt b/indra/llcommon/CMakeLists.txt index aed9ee080b..aa0b66f2f4 100644 --- a/indra/llcommon/CMakeLists.txt +++ b/indra/llcommon/CMakeLists.txt @@ -127,6 +127,7 @@ set(llcommon_HEADER_FILES commoncontrol.h ctype_workaround.h fix_macros.h + fsyspath.h function_types.h indra_constants.h lazyeventapi.h diff --git a/indra/llcommon/fsyspath.h b/indra/llcommon/fsyspath.h new file mode 100644 index 0000000000..aa4e0132bc --- /dev/null +++ b/indra/llcommon/fsyspath.h @@ -0,0 +1,74 @@ +/** + * @file fsyspath.h + * @author Nat Goodspeed + * @date 2024-04-03 + * @brief Adapt our UTF-8 std::strings for std::filesystem::path + * + * $LicenseInfo:firstyear=2024&license=viewerlgpl$ + * Copyright (c) 2024, Linden Research, Inc. + * $/LicenseInfo$ + */ + +#if ! defined(LL_FSYSPATH_H) +#define LL_FSYSPATH_H + +#include + +// While std::filesystem::path can be directly constructed from std::string on +// both Posix and Windows, that's not what we want on Windows. Per +// https://en.cppreference.com/w/cpp/filesystem/path/path: + +// ... the method of conversion to the native character set depends on the +// character type used by source. +// +// * If the source character type is char, the encoding of the source is +// assumed to be the native narrow encoding (so no conversion takes place on +// POSIX systems). +// * If the source character type is char8_t, conversion from UTF-8 to native +// filesystem encoding is used. (since C++20) +// * If the source character type is wchar_t, the input is assumed to be the +// native wide encoding (so no conversion takes places on Windows). + +// The trouble is that on Windows, from std::string ("source character type is +// char"), the "native narrow encoding" isn't UTF-8, so file paths containing +// non-ASCII characters get mangled. +// +// Once we're building with C++20, we could pass a UTF-8 std::string through a +// vector to engage std::filesystem::path's own UTF-8 conversion. But +// sigh, as of 2024-04-03 we're not yet there. +// +// Anyway, encapsulating the important UTF-8 conversions in our own subclass +// allows us to migrate forward to C++20 conventions without changing +// referencing code. + +class fsyspath: public std::filesystem::path +{ + using super = std::filesystem::path; + +public: + // default + fsyspath() {} + // construct from UTF-8 encoded std::string + fsyspath(const std::string& path): super(std::filesystem::u8path(path)) {} + // construct from UTF-8 encoded const char* + fsyspath(const char* path): super(std::filesystem::u8path(path)) {} + // construct from existing path + fsyspath(const super& path): super(path) {} + + fsyspath& operator=(const super& p) { super::operator=(p); return *this; } + fsyspath& operator=(const std::string& p) + { + super::operator=(std::filesystem::u8path(p)); + return *this; + } + fsyspath& operator=(const char* p) + { + super::operator=(std::filesystem::u8path(p)); + return *this; + } + + // shadow base-class string() method with UTF-8 aware method + std::string string() const { return super::u8string(); } +}; + +#endif /* ! defined(LL_FSYSPATH_H) */ diff --git a/indra/llcommon/lua_function.cpp b/indra/llcommon/lua_function.cpp index cd83f40e85..7a5668f384 100644 --- a/indra/llcommon/lua_function.cpp +++ b/indra/llcommon/lua_function.cpp @@ -17,13 +17,13 @@ // std headers #include #include -#include #include // std::quoted #include #include // std::unique_ptr #include // external library headers // other Linden headers +#include "fsyspath.h" #include "hexdump.h" #include "lleventcoro.h" #include "llsd.h" @@ -68,7 +68,7 @@ int lluau::loadstring(lua_State *L, const std::string &desc, const std::string & return luau_load(L, desc.data(), bytecode.get(), bytecodeSize, 0); } -std::filesystem::path lluau::source_path(lua_State* L) +fsyspath lluau::source_path(lua_State* L) { //Luau lua_Debug and lua_getinfo() are different compared to default Lua: //see https://github.com/luau-lang/luau/blob/80928acb92d1e4b6db16bada6d21b1fb6fa66265/VM/include/lua.h @@ -577,7 +577,7 @@ std::pair LuaState::expr(const std::string& desc, const std::string& // the next call to lua_next." // https://www.lua.org/manual/5.1/manual.html#lua_next if (lua_type(mState, -2) == LUA_TSTRING && - std::filesystem::path(lua_tostdstring(mState, -2)).stem() == "fiber") + fsyspath(lua_tostdstring(mState, -2)).stem() == "fiber") { found = true; break; diff --git a/indra/llcommon/lua_function.h b/indra/llcommon/lua_function.h index 785aadeb0c..ec1e6cdb10 100644 --- a/indra/llcommon/lua_function.h +++ b/indra/llcommon/lua_function.h @@ -16,9 +16,9 @@ #include "luau/lua.h" #include "luau/luaconf.h" #include "luau/lualib.h" +#include "fsyspath.h" #include "stringize.h" #include // std::uncaught_exceptions() -#include #include // std::shared_ptr #include // std::pair @@ -51,7 +51,7 @@ namespace lluau int dostring(lua_State* L, const std::string& desc, const std::string& text); int loadstring(lua_State* L, const std::string& desc, const std::string& text); - std::filesystem::path source_path(lua_State* L); + fsyspath source_path(lua_State* L); } // namespace lluau std::string lua_tostdstring(lua_State* L, int index); diff --git a/indra/llui/llluafloater.cpp b/indra/llui/llluafloater.cpp index 268075b05d..e584a67a00 100644 --- a/indra/llui/llluafloater.cpp +++ b/indra/llui/llluafloater.cpp @@ -26,7 +26,7 @@ #include "llluafloater.h" -#include +#include "fsyspath.h" #include "llevents.h" #include "llcheckboxctrl.h" @@ -271,12 +271,12 @@ void LLLuaFloater::postEvent(LLSD data, const std::string &event_name) void LLLuaFloater::showLuaFloater(const LLSD &data) { - std::filesystem::path fs_path(data["xml_path"].asString()); - std::string path = fs_path.lexically_normal().string(); + fsyspath fs_path(data["xml_path"].asString()); + std::string path = fs_path.lexically_normal().u8string(); if (!fs_path.is_absolute()) { std::string lib_path = gDirUtilp->getExpandedFilename(LL_PATH_SCRIPTS, "lua"); - path = (std::filesystem::path(lib_path) / path).u8string(); + path = (fsyspath(lib_path) / path).u8string(); } LLLuaFloater *floater = new LLLuaFloater(data); diff --git a/indra/newview/llluamanager.cpp b/indra/newview/llluamanager.cpp index 9059db9967..82be85a153 100644 --- a/indra/newview/llluamanager.cpp +++ b/indra/newview/llluamanager.cpp @@ -28,6 +28,7 @@ #include "llviewerprecompiledheaders.h" #include "llluamanager.h" +#include "fsyspath.h" #include "llcoros.h" #include "llerror.h" #include "lleventcoro.h" @@ -37,7 +38,6 @@ #include "stringize.h" #include -#include #include "luau/luacode.h" #include "luau/lua.h" @@ -314,7 +314,7 @@ void LLRequireResolver::resolveRequire(lua_State *L, std::string path) } LLRequireResolver::LLRequireResolver(lua_State *L, const std::string& path) : - mPathToResolve(std::filesystem::path(path).lexically_normal()), + mPathToResolve(fsyspath(path).lexically_normal()), L(L) { mSourceDir = lluau::source_path(L).parent_path(); @@ -377,12 +377,12 @@ void LLRequireResolver::findModule() fail(); } - std::vector lib_paths + std::vector lib_paths { gDirUtilp->getExpandedFilename(LL_PATH_SCRIPTS, "lua"), #ifdef LL_TEST // Build-time tests don't have the app bundle - use source tree. - std::filesystem::path(__FILE__).parent_path() / "scripts" / "lua", + fsyspath(__FILE__).parent_path() / "scripts" / "lua", #endif }; diff --git a/indra/newview/llluamanager.h b/indra/newview/llluamanager.h index bd581376b4..3c00450179 100644 --- a/indra/newview/llluamanager.h +++ b/indra/newview/llluamanager.h @@ -27,9 +27,9 @@ #ifndef LL_LLLUAMANAGER_H #define LL_LLLUAMANAGER_H +#include "fsyspath.h" #include "llcoros.h" #include "llsd.h" -#include #include #include #include // std::pair @@ -89,8 +89,8 @@ class LLRequireResolver static void resolveRequire(lua_State *L, std::string path); private: - std::filesystem::path mPathToResolve; - std::filesystem::path mSourceDir; + fsyspath mPathToResolve; + fsyspath mSourceDir; LLRequireResolver(lua_State *L, const std::string& path); -- cgit v1.2.3 From 3f876a6a1d138c31266afb6c39df7090e304efe3 Mon Sep 17 00:00:00 2001 From: Nat Goodspeed Date: Wed, 3 Apr 2024 13:04:21 -0400 Subject: Use raw string literal syntax for LLLeapListener help strings. --- indra/llcommon/llleaplistener.cpp | 42 +++++++++++++++++++-------------------- 1 file changed, 21 insertions(+), 21 deletions(-) diff --git a/indra/llcommon/llleaplistener.cpp b/indra/llcommon/llleaplistener.cpp index e8a4775b67..9b9b0f5121 100644 --- a/indra/llcommon/llleaplistener.cpp +++ b/indra/llcommon/llleaplistener.cpp @@ -70,46 +70,46 @@ LLLeapListener::LLLeapListener(const std::string_view& caller, const Callback& c { LLSD need_name(LLSDMap("name", LLSD())); add("newpump", - "Instantiate a new LLEventPump named like [\"name\"] and listen to it.\n" - "[\"type\"] == \"LLEventStream\", \"LLEventMailDrop\" et al.\n" - "Events sent through new LLEventPump will be decorated with [\"pump\"]=name.\n" - "Returns actual name in [\"name\"] (may be different if collision).", +R"-(Instantiate a new LLEventPump named like ["name"] and listen to it. +["type"] == "LLEventStream", "LLEventMailDrop" et al. +Events sent through new LLEventPump will be decorated with ["pump"]=name. +Returns actual name in ["name"] (may be different if collision).)-", &LLLeapListener::newpump, need_name); LLSD need_source_listener(LLSDMap("source", LLSD())("listener", LLSD())); add("listen", - "Listen to an existing LLEventPump named [\"source\"], with listener name\n" - "[\"listener\"].\n" - "If [\"tweak\"] is specified as true, tweak listener name for uniqueness.\n" - "By default, send events on [\"source\"] to the plugin, decorated\n" - "with [\"pump\"]=[\"source\"].\n" - "If [\"dest\"] specified, send undecorated events on [\"source\"] to the\n" - "LLEventPump named [\"dest\"].\n" - "Returns [\"status\"] boolean indicating whether the connection was made,\n" - "plus [\"listener\"] reporting (possibly tweaked) listener name.", +R"-(Listen to an existing LLEventPump named ["source"], with listener name +["listener"]. +If ["tweak"] is specified as true, tweak listener name for uniqueness. +By default, send events on ["source"] to the plugin, decorated +with ["pump"]=["source"]. +If ["dest"] specified, send undecorated events on ["source"] to the +LLEventPump named ["dest"]. +Returns ["status"] boolean indicating whether the connection was made, +plus ["listener"] reporting (possibly tweaked) listener name.)-", &LLLeapListener::listen, need_source_listener); add("stoplistening", - "Disconnect a connection previously established by \"listen\".\n" - "Pass same [\"source\"] and [\"listener\"] arguments.\n" - "Returns [\"status\"] boolean indicating whether such a listener existed.", +R"-(Disconnect a connection previously established by "listen". +Pass same ["source"] and ["listener"] arguments. +Returns ["status"] boolean indicating whether such a listener existed.)-", &LLLeapListener::stoplistening, need_source_listener); add("ping", - "No arguments, just a round-trip sanity check.", +"No arguments, just a round-trip sanity check.", &LLLeapListener::ping); add("getAPIs", - "Enumerate all LLEventAPI instances by name and description.", +"Enumerate all LLEventAPI instances by name and description.", &LLLeapListener::getAPIs); add("getAPI", - "Get name, description, dispatch key and operations for LLEventAPI [\"api\"].", +R"-(Get name, description, dispatch key and operations for LLEventAPI ["api"].)-", &LLLeapListener::getAPI, LLSD().with("api", LLSD())); add("getFeatures", - "Return an LLSD map of feature strings (deltas from baseline LEAP protocol)", +"Return an LLSD map of feature strings (deltas from baseline LEAP protocol)", static_cast(&LLLeapListener::getFeatures)); add("getFeature", - "Return the feature value with key [\"feature\"]", +R"-(Return the feature value with key ["feature"])-", &LLLeapListener::getFeature, LLSD().with("feature", LLSD())); } -- cgit v1.2.3 From 0f2d261bbfa7a827290f3acb7e6f71e29eed3a1b Mon Sep 17 00:00:00 2001 From: Nicky Date: Wed, 3 Apr 2024 21:24:05 +0200 Subject: Proper casing for Lualibs (or case sensitive filesystems do not agree with the filename) --- indra/newview/CMakeLists.txt | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/indra/newview/CMakeLists.txt b/indra/newview/CMakeLists.txt index b26ce7d06d..d98b45e471 100644 --- a/indra/newview/CMakeLists.txt +++ b/indra/newview/CMakeLists.txt @@ -48,7 +48,7 @@ include(VulkanGltf) include(ZLIBNG) include(URIPARSER) include(LLPrimitive) -include(lualibs) +include(Lualibs) if (NOT HAVOK_TPV) # When using HAVOK_TPV, the library is precompiled, so no need for this @@ -2379,4 +2379,3 @@ if (LL_TESTS) endif (LL_TESTS) check_message_template(${VIEWER_BINARY_NAME}) - -- cgit v1.2.3 From c8f7e9d0256cec90d509b0cf0109c2c7479100d0 Mon Sep 17 00:00:00 2001 From: Nicky Date: Wed, 3 Apr 2024 21:24:56 +0200 Subject: - Enable luaulib linking for Linux - Put lubLuau.Ast.a at the right most side as GCC/LD is peculiar about link order. --- indra/cmake/Lualibs.cmake | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/indra/cmake/Lualibs.cmake b/indra/cmake/Lualibs.cmake index 7edfbfdc3d..d66305a8e5 100644 --- a/indra/cmake/Lualibs.cmake +++ b/indra/cmake/Lualibs.cmake @@ -20,10 +20,10 @@ if (WINDOWS) target_link_libraries(ll::lualibs INTERFACE ${ARCH_PREBUILT_DIRS_RELEASE}/Luau.Compiler.lib) target_link_libraries(ll::lualibs INTERFACE ${ARCH_PREBUILT_DIRS_RELEASE}/Luau.Config.lib) target_link_libraries(ll::lualibs INTERFACE ${ARCH_PREBUILT_DIRS_RELEASE}/Luau.VM.lib) -elseif (DARWIN) - target_link_libraries(ll::lualibs INTERFACE ${ARCH_PREBUILT_DIRS_RELEASE}/libLuau.Ast.a) +else () target_link_libraries(ll::lualibs INTERFACE ${ARCH_PREBUILT_DIRS_RELEASE}/libLuau.CodeGen.a) target_link_libraries(ll::lualibs INTERFACE ${ARCH_PREBUILT_DIRS_RELEASE}/libLuau.Compiler.a) target_link_libraries(ll::lualibs INTERFACE ${ARCH_PREBUILT_DIRS_RELEASE}/libLuau.Config.a) target_link_libraries(ll::lualibs INTERFACE ${ARCH_PREBUILT_DIRS_RELEASE}/libLuau.VM.a) -endif (WINDOWS) + target_link_libraries(ll::lualibs INTERFACE ${ARCH_PREBUILT_DIRS_RELEASE}/libLuau.Ast.a) +endif () -- cgit v1.2.3 From 81d7fae644b759dd7b5620c7469b2941743dced8 Mon Sep 17 00:00:00 2001 From: Nat Goodspeed Date: Wed, 3 Apr 2024 15:52:46 -0400 Subject: Introduce LLInstanceTracker::destroy() methods; use in ~LuaState(). --- indra/llcommon/llinstancetracker.h | 52 ++++++++++++++++++++++++++++++++++++++ indra/llcommon/lua_function.cpp | 11 ++------ 2 files changed, 54 insertions(+), 9 deletions(-) diff --git a/indra/llcommon/llinstancetracker.h b/indra/llcommon/llinstancetracker.h index 27422e1266..22e5d9c7a7 100644 --- a/indra/llcommon/llinstancetracker.h +++ b/indra/llcommon/llinstancetracker.h @@ -275,6 +275,35 @@ protected: public: virtual const KEY& getKey() const { return mInstanceKey; } + /// for use ONLY for an object we're sure resides on the heap! + static bool destroy(const KEY& key) + { + return destroy(getInstance(key)); + } + + /// for use ONLY for an object we're sure resides on the heap! + static bool destroy(const weak_t& ptr) + { + return destroy(ptr.lock()); + } + + /// for use ONLY for an object we're sure resides on the heap! + static bool destroy(const ptr_t& ptr) + { + if (! ptr) + { + return false; + } + + // Because we store and return ptr_t instances with no-op deleters, + // merely resetting the last pointer doesn't destroy the referenced + // object. Don't even bother resetting 'ptr'. Just extract its raw + // pointer and delete that. + auto raw{ ptr.get() }; + delete raw; + return true; + } + private: LLInstanceTracker( const LLInstanceTracker& ) = delete; LLInstanceTracker& operator=( const LLInstanceTracker& ) = delete; @@ -479,6 +508,29 @@ public: template using key_snapshot_of = instance_snapshot_of; + /// for use ONLY for an object we're sure resides on the heap! + static bool destroy(const weak_t& ptr) + { + return destroy(ptr.lock()); + } + + /// for use ONLY for an object we're sure resides on the heap! + static bool destroy(const ptr_t& ptr) + { + if (! ptr) + { + return false; + } + + // Because we store and return ptr_t instances with no-op deleters, + // merely resetting the last pointer doesn't destroy the referenced + // object. Don't even bother resetting 'ptr'. Just extract its raw + // pointer and delete that. + auto raw{ ptr.get() }; + delete raw; + return true; + } + protected: LLInstanceTracker() { diff --git a/indra/llcommon/lua_function.cpp b/indra/llcommon/lua_function.cpp index 7a5668f384..c86faf1ae2 100644 --- a/indra/llcommon/lua_function.cpp +++ b/indra/llcommon/lua_function.cpp @@ -286,7 +286,7 @@ LLSD lua_tollsd(lua_State* L, int index) popper.disarm(); // Table keys are all integers: are they reasonable integers? // Arbitrary max: may bite us, but more likely to protect us - size_t array_max{ 10000 }; + const size_t array_max{ 10000 }; if (keys.size() > array_max) { return lluau::error(L, "Conversion from Lua to LLSD array limited to %d entries", @@ -459,14 +459,7 @@ LuaState::~LuaState() { // Did somebody call obtainListener() on this LuaState? // That is, is there a LuaListener key in its registry? - auto listener{ getListener() }; - if (listener) - { - // if we got a LuaListener instance, destroy it - auto lptr{ listener.get() }; - listener.reset(); - delete lptr; - } + LuaListener::destroy(getListener()); lua_close(mState); -- cgit v1.2.3 From e02ea3ddee87021a4b6fa0de874e2d6d71da65f9 Mon Sep 17 00:00:00 2001 From: Nat Goodspeed Date: Wed, 3 Apr 2024 16:32:21 -0400 Subject: LLInstanceTracker::destruct() instead of destroy(). Avoid ambiguity with LLFloater::destroy(). --- indra/llcommon/llinstancetracker.h | 16 ++++++++-------- indra/llcommon/lua_function.cpp | 2 +- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/indra/llcommon/llinstancetracker.h b/indra/llcommon/llinstancetracker.h index 22e5d9c7a7..921f743ada 100644 --- a/indra/llcommon/llinstancetracker.h +++ b/indra/llcommon/llinstancetracker.h @@ -276,19 +276,19 @@ public: virtual const KEY& getKey() const { return mInstanceKey; } /// for use ONLY for an object we're sure resides on the heap! - static bool destroy(const KEY& key) + static bool destruct(const KEY& key) { - return destroy(getInstance(key)); + return destruct(getInstance(key)); } /// for use ONLY for an object we're sure resides on the heap! - static bool destroy(const weak_t& ptr) + static bool destruct(const weak_t& ptr) { - return destroy(ptr.lock()); + return destruct(ptr.lock()); } /// for use ONLY for an object we're sure resides on the heap! - static bool destroy(const ptr_t& ptr) + static bool destruct(const ptr_t& ptr) { if (! ptr) { @@ -509,13 +509,13 @@ public: using key_snapshot_of = instance_snapshot_of; /// for use ONLY for an object we're sure resides on the heap! - static bool destroy(const weak_t& ptr) + static bool destruct(const weak_t& ptr) { - return destroy(ptr.lock()); + return destruct(ptr.lock()); } /// for use ONLY for an object we're sure resides on the heap! - static bool destroy(const ptr_t& ptr) + static bool destruct(const ptr_t& ptr) { if (! ptr) { diff --git a/indra/llcommon/lua_function.cpp b/indra/llcommon/lua_function.cpp index c86faf1ae2..332a08a444 100644 --- a/indra/llcommon/lua_function.cpp +++ b/indra/llcommon/lua_function.cpp @@ -459,7 +459,7 @@ LuaState::~LuaState() { // Did somebody call obtainListener() on this LuaState? // That is, is there a LuaListener key in its registry? - LuaListener::destroy(getListener()); + LuaListener::destruct(getListener()); lua_close(mState); -- cgit v1.2.3 From 799d1d595505acaa7f05a6d92db5f8f2d258f53e Mon Sep 17 00:00:00 2001 From: Mnikolenko Productengine Date: Mon, 8 Apr 2024 16:55:26 +0300 Subject: Add script termination option to 'Lua Scripts' floater --- indra/llcommon/llapp.cpp | 1 + indra/llcommon/lualistener.cpp | 8 +++++--- indra/llcommon/lualistener.h | 2 ++ indra/newview/llfloaterluascripts.cpp | 6 ++++++ indra/newview/llfloaterluascripts.h | 1 + indra/newview/llluamanager.cpp | 16 ++++++++++++++++ indra/newview/llluamanager.h | 5 ++++- indra/newview/skins/default/xui/en/menu_lua_scripts.xml | 8 ++++++++ 8 files changed, 43 insertions(+), 4 deletions(-) diff --git a/indra/llcommon/llapp.cpp b/indra/llcommon/llapp.cpp index b99166991f..905db9e491 100644 --- a/indra/llcommon/llapp.cpp +++ b/indra/llcommon/llapp.cpp @@ -429,6 +429,7 @@ void LLApp::setStatus(EAppStatus status) statsd = LLSD::Integer(status); } LLEventPumps::instance().obtain("LLApp").post(llsd::map("status", statsd)); + LLEventPumps::instance().obtain("LLLua").post(llsd::map("status", "close_all")); } } diff --git a/indra/llcommon/lualistener.cpp b/indra/llcommon/lualistener.cpp index 82e32860db..c11ab6b1c3 100644 --- a/indra/llcommon/lualistener.cpp +++ b/indra/llcommon/lualistener.cpp @@ -33,18 +33,20 @@ std::ostream& operator<<(std::ostream& out, const LuaListener& self) LuaListener::LuaListener(lua_State* L): super(getUniqueKey()), + mCoroName(LLCoros::getName()), mListener(new LLLeapListener( "LuaListener", [this](const std::string& pump, const LLSD& data) { return queueEvent(pump, data); })), // Listen for shutdown events on the "LLApp" LLEventPump. mShutdownConnection( - LLEventPumps::instance().obtain("LLApp").listen( + LLEventPumps::instance().obtain("LLLua").listen( LLEventPump::inventName("LuaState"), [this](const LLSD& status) { - const auto& statsd = status["status"]; - if (statsd.asString() != "running") + const auto& coro_name = status["coro"].asString(); + const auto& statsd = status["status"].asString(); + if ((statsd == "close_all") || ((statsd == "close") && (coro_name == mCoroName))) { // If a Lua script is still blocked in getNext() during // viewer shutdown, close the queue to wake up getNext(). diff --git a/indra/llcommon/lualistener.h b/indra/llcommon/lualistener.h index 40ccfba8fe..d349ee23fd 100644 --- a/indra/llcommon/lualistener.h +++ b/indra/llcommon/lualistener.h @@ -75,6 +75,8 @@ private: std::unique_ptr mListener; LLTempBoundListener mShutdownConnection; + + std::string mCoroName; }; #endif /* ! defined(LL_LUALISTENER_H) */ diff --git a/indra/newview/llfloaterluascripts.cpp b/indra/newview/llfloaterluascripts.cpp index bd845a97d6..8f6ccc50fa 100644 --- a/indra/newview/llfloaterluascripts.cpp +++ b/indra/newview/llfloaterluascripts.cpp @@ -45,6 +45,11 @@ LLFloaterLUAScripts::LLFloaterLUAScripts(const LLSD &key) { gViewerWindow->getWindow()->openFolder(mTargetFolderPath); }); + mCommitCallbackRegistrar.add("Script.Terminate", [this](LLUICtrl*, const LLSD &userdata) + { + LLEventPumps::instance().obtain("LLLua").post(llsd::map("status", "close", "coro", mCoroName)); + LLLUAmanager::terminateScript(mCoroName); + }); } @@ -113,6 +118,7 @@ void LLFloaterLUAScripts::onScrollListRightClicked(LLUICtrl *ctrl, S32 x, S32 y) if (menu) { mTargetFolderPath = std::filesystem::path((item->getColumn(1)->getValue().asString())).parent_path().string(); + mCoroName = item->getValue().asString(); menu->show(x, y); LLMenuGL::showPopup(this, menu, x, y); } diff --git a/indra/newview/llfloaterluascripts.h b/indra/newview/llfloaterluascripts.h index 548bbd10f6..c7c888b55a 100644 --- a/indra/newview/llfloaterluascripts.h +++ b/indra/newview/llfloaterluascripts.h @@ -47,6 +47,7 @@ private: std::unique_ptr mUpdateTimer; LLScrollListCtrl* mScriptList; std::string mTargetFolderPath; + std::string mCoroName; LLHandle mContextMenuHandle; }; diff --git a/indra/newview/llluamanager.cpp b/indra/newview/llluamanager.cpp index 1d55313813..35a73aaabe 100644 --- a/indra/newview/llluamanager.cpp +++ b/indra/newview/llluamanager.cpp @@ -49,6 +49,7 @@ #include std::map LLLUAmanager::sScriptNames; +std::set LLLUAmanager::sTerminationList; lua_function(sleep, "sleep(seconds): pause the running coroutine") { @@ -188,6 +189,21 @@ void LLLUAmanager::runScriptFile(const std::string &filename, script_result_fn r // A script_finished_fn is used to initialize the LuaState. // It will be called when the LuaState is destroyed. LuaState L(finished_cb); + + static int index = 0; + lua_callbacks(L)->interrupt = [](lua_State *L, int gc) + { + if (gc >= 0) + return; + + std::set scripts = LLLUAmanager::getTerminationList(); + std::string coro = LLCoros::getName(); + if (scripts.find(coro) != scripts.end()) + { + sTerminationList.erase(coro); + lluau::error(L, "Script was terminated"); + } + }; std::string text{std::istreambuf_iterator(in_file), {}}; auto [count, result] = L.expr(filename, text); if (result_cb) diff --git a/indra/newview/llluamanager.h b/indra/newview/llluamanager.h index fe4db22fca..d671719bc4 100644 --- a/indra/newview/llluamanager.h +++ b/indra/newview/llluamanager.h @@ -85,9 +85,12 @@ public: static void runScriptOnLogin(); static const std::map getScriptNames() { return sScriptNames; } + static std::set getTerminationList() { return sTerminationList; } + static void terminateScript(std::string& coro_name) { sTerminationList.insert(coro_name); } private: - static std::map sScriptNames; + static std::map sScriptNames; + static std::set sTerminationList; }; class LLRequireResolver diff --git a/indra/newview/skins/default/xui/en/menu_lua_scripts.xml b/indra/newview/skins/default/xui/en/menu_lua_scripts.xml index 8f718abe17..645fee405d 100644 --- a/indra/newview/skins/default/xui/en/menu_lua_scripts.xml +++ b/indra/newview/skins/default/xui/en/menu_lua_scripts.xml @@ -8,4 +8,12 @@ + + + + -- cgit v1.2.3 From 7947ce355964752d8385f11e2abdc4ca405c7e65 Mon Sep 17 00:00:00 2001 From: Andrey Kleshchev Date: Tue, 9 Apr 2024 00:05:05 +0300 Subject: triage#162 "Away" status is removed even by a random hover instead of having a 10 second delay to 'confirm' activity --- indra/newview/app_settings/settings.xml | 2 +- indra/newview/llappviewer.cpp | 17 +++++++++++++---- indra/newview/llviewerjoystick.cpp | 27 +++++++++++++++++++++++---- indra/newview/llviewerwindow.cpp | 20 +++++++++++++++----- 4 files changed, 52 insertions(+), 14 deletions(-) diff --git a/indra/newview/app_settings/settings.xml b/indra/newview/app_settings/settings.xml index 9b9b29cd74..84a89251a4 100644 --- a/indra/newview/app_settings/settings.xml +++ b/indra/newview/app_settings/settings.xml @@ -91,7 +91,7 @@ Type S32 Value - 300.0 + 300 AckCollectTime diff --git a/indra/newview/llappviewer.cpp b/indra/newview/llappviewer.cpp index a1fecdb981..5002846262 100644 --- a/indra/newview/llappviewer.cpp +++ b/indra/newview/llappviewer.cpp @@ -454,11 +454,20 @@ void idle_afk_check() { // check idle timers F32 current_idle = gAwayTriggerTimer.getElapsedTimeF32(); - F32 afk_timeout = gSavedSettings.getS32("AFKTimeout"); - if (afk_timeout && (current_idle > afk_timeout) && ! gAgent.getAFK()) + LLCachedControl afk_timeout(gSavedSettings, "AFKTimeout", 300); + if (afk_timeout() && (current_idle > afk_timeout())) { - LL_INFOS("IdleAway") << "Idle more than " << afk_timeout << " seconds: automatically changing to Away status" << LL_ENDL; - gAgent.setAFK(); + if (!gAgent.getAFK()) + { + LL_INFOS("IdleAway") << "Idle more than " << afk_timeout << " seconds: automatically changing to Away status" << LL_ENDL; + gAgent.setAFK(); + } + else + { + // Refresh timer so that random one click or hover won't clear the status. + // But expanding the window still should lift afk status + gAwayTimer.reset(); + } } } diff --git a/indra/newview/llviewerjoystick.cpp b/indra/newview/llviewerjoystick.cpp index e35cb26ce1..2bcec1ecee 100644 --- a/indra/newview/llviewerjoystick.cpp +++ b/indra/newview/llviewerjoystick.cpp @@ -806,6 +806,10 @@ void LLViewerJoystick::moveObjects(bool reset) { gAgent.clearAFK(); } + else + { + gAwayTriggerTimer.reset(); + } if (sDelta[0] || sDelta[1] || sDelta[2]) { @@ -980,6 +984,10 @@ void LLViewerJoystick::moveAvatar(bool reset) { gAgent.clearAFK(); } + else + { + gAwayTriggerTimer.reset(); + } setCameraNeedsUpdate(true); } @@ -1192,10 +1200,17 @@ void LLViewerJoystick::moveFlycam(bool reset) } // Clear AFK state if moved beyond the deadzone - if (!is_zero && gAwayTimer.getElapsedTimeF32() > LLAgent::MIN_AFK_TIME) - { - gAgent.clearAFK(); - } + if (!is_zero) + { + if (gAwayTimer.getElapsedTimeF32() > LLAgent::MIN_AFK_TIME) + { + gAgent.clearAFK(); + } + else + { + gAwayTriggerTimer.reset(); + } + } sFlycamPosition += LLVector3(sDelta) * sFlycamRotation; @@ -1256,6 +1271,10 @@ bool LLViewerJoystick::toggleFlycam() { gAgent.clearAFK(); } + else + { + gAwayTriggerTimer.reset(); + } mOverrideCamera = !mOverrideCamera; if (mOverrideCamera) diff --git a/indra/newview/llviewerwindow.cpp b/indra/newview/llviewerwindow.cpp index 37e64dfc17..4a8edfcf89 100644 --- a/indra/newview/llviewerwindow.cpp +++ b/indra/newview/llviewerwindow.cpp @@ -1425,11 +1425,17 @@ void LLViewerWindow::handleMouseMove(LLWindow *window, LLCoordGL pos, MASK mask mWindow->showCursorFromMouseMove(); - if (gAwayTimer.getElapsedTimeF32() > LLAgent::MIN_AFK_TIME - && !gDisconnected) - { - gAgent.clearAFK(); - } + if (!gDisconnected) + { + if (gAwayTimer.getElapsedTimeF32() > LLAgent::MIN_AFK_TIME) + { + gAgent.clearAFK(); + } + else + { + gAwayTriggerTimer.reset(); + } + } } void LLViewerWindow::handleMouseDragged(LLWindow *window, LLCoordGL pos, MASK mask) @@ -1545,6 +1551,10 @@ BOOL LLViewerWindow::handleTranslatedKeyDown(KEY key, MASK mask, BOOL repeated) { gAgent.clearAFK(); } + else + { + gAwayTriggerTimer.reset(); + } // *NOTE: We want to interpret KEY_RETURN later when it arrives as // a Unicode char, not as a keydown. Otherwise when client frame -- cgit v1.2.3 From 26ce33d8ead68c2dbcc37b2b1e040c072866fe5b Mon Sep 17 00:00:00 2001 From: Nat Goodspeed Date: Tue, 9 Apr 2024 15:21:05 -0400 Subject: Add Lua Floater class to simplify Lua script showing floaters. Add test_luafloater_demo2.lua and test_luafloater_gesture_list2.lua examples. --- indra/newview/scripts/lua/Floater.lua | 151 +++++++++++++++++++++ indra/newview/scripts/lua/leap.lua | 7 +- .../newview/scripts/lua/test_luafloater_demo2.lua | 39 ++++++ .../scripts/lua/test_luafloater_gesture_list2.lua | 27 ++++ 4 files changed, 221 insertions(+), 3 deletions(-) create mode 100644 indra/newview/scripts/lua/Floater.lua create mode 100644 indra/newview/scripts/lua/test_luafloater_demo2.lua create mode 100644 indra/newview/scripts/lua/test_luafloater_gesture_list2.lua diff --git a/indra/newview/scripts/lua/Floater.lua b/indra/newview/scripts/lua/Floater.lua new file mode 100644 index 0000000000..76efd47c43 --- /dev/null +++ b/indra/newview/scripts/lua/Floater.lua @@ -0,0 +1,151 @@ +-- Floater base class + +local leap = require 'leap' +local fiber = require 'fiber' + +-- list of all the events that a LLLuaFloater might send +local event_list = leap.request("LLFloaterReg", {op="getFloaterEvents"}).events +local event_set = {} +for _, event in pairs(event_list) do + event_set[event] = true +end + +local function _event(event_name) + if not event_set[event_name] then + error("Incorrect event name: " .. event_name, 3) + end + return event_name +end + +-- --------------------------------------------------------------------------- +local Floater = {} + +-- Pass: +-- relative file path to floater's XUI definition file +-- optional: sign up for additional events for defined control +-- {={action1, action2, ...}} +function Floater:new(path, extra) + local obj = setmetatable({}, self) + self.__index = self + + local path_parts = string.split(path, '/') + obj.name = 'Floater ' .. path_parts[#path_parts] + + obj._command = {op="showLuaFloater", xml_path=LL.abspath(path)} + if extra then + -- validate each of the actions for each specified control + for control, actions in pairs(extra) do + for _, action in pairs(actions) do + _event(action) + end + end + obj._command.extra_events = extra + end + + return obj +end + +function Floater:show() + local event = leap.request('LLFloaterReg', self._command) + self._pump = event.command_name + -- we use the returned reqid to claim subsequent unsolicited events + local reqid = event.reqid + + -- The response to 'showLuaFloater' *is* the 'post_build' event. Check if + -- subclass has a post_build() method. Honor the convention that if + -- handleEvents() returns false, we're done. + if not self:handleEvents(event) then + return + end + + local waitfor = leap.WaitFor:new(-1, self.name) + function waitfor:filter(pump, data) + if data.reqid == reqid then + return data + end + end + + fiber.launch( + self.name, + function () + event = waitfor:wait() + while event and self:handleEvents(event) do + event = waitfor:wait() + end + end) +end + +function Floater:post(action) + leap.send(self._pump, action) +end + +function Floater:request(action) + return leap.request(self._pump, action) +end + +-- local inspect = require 'inspect' + +function Floater:handleEvents(event_data) + local event = event_data.event + if event_set[event] == nil then + LL.print_warning(string.format('%s received unknown event %q', self.name, event)) + end + + -- Before checking for a general (e.g.) commit() method, first look for + -- commit_ctrl_name(): in other words, concatenate the event name with the + -- ctrl_name, with an underscore between. If there exists such a specific + -- method, call that. + local handler, ret + if event_data.ctrl_name then + local specific = event .. '_' .. event_data.ctrl_name + handler = self[specific] + if handler then + ret = handler(self, event_data) + -- Avoid 'return ret or true' because we explicitly want to allow + -- the handler to return false. + if ret ~= nil then + return ret + else + return true + end + end + end + + -- No specific "event_on_ctrl()" method found; try just "event()" + handler = self[event] + if handler then + ret = handler(self, event_data) + if ret ~= nil then + return ret + end +-- else +-- print(string.format('%s ignoring event %s', self.name, inspect(event_data))) + end + + -- We check for event() method before recognizing floater_close in case + -- the consumer needs to react specially to closing the floater. Now that + -- we've checked, recognize it ourselves. Returning false terminates the + -- anonymous fiber function launched by show(). + if event == _event('floater_close') then + LL.print_warning(self.name .. ' closed') + return false + end + return true +end + +-- onCtrl() permits a different dispatch style in which the general event() +-- method explicitly calls (e.g.) +-- self:onCtrl(event_data, { +-- ctrl_name=function() +-- self:post(...) +-- end, +-- ... +-- }) +function Floater:onCtrl(event_data, ctrl_map) + local handler = ctrl_map[event_data.ctrl_name] + if handler then + handler() + end +end + +return Floater diff --git a/indra/newview/scripts/lua/leap.lua b/indra/newview/scripts/lua/leap.lua index d19273e8bc..ade91789f0 100644 --- a/indra/newview/scripts/lua/leap.lua +++ b/indra/newview/scripts/lua/leap.lua @@ -183,14 +183,15 @@ function leap.generate(pump, data, checklast) -- bearing that reqid. Stamp the outbound request with that reqid, and -- send it. local reqid, waitfor = requestSetup(pump, data) - local ok, response + local ok, response, resumed_with repeat ok, response = pcall(waitfor.wait, waitfor) if not ok then break end - coroutine.yield(response) - until checklast and checklast(response) + -- can resume(false) to terminate generate() and clean up + resumed_with = coroutine.yield(response) + until (checklast and checklast(response)) or (resumed_with == false) -- If we break the above loop, whether or not due to error, clean up. pending[reqid] = nil if not ok then diff --git a/indra/newview/scripts/lua/test_luafloater_demo2.lua b/indra/newview/scripts/lua/test_luafloater_demo2.lua new file mode 100644 index 0000000000..9e24237d28 --- /dev/null +++ b/indra/newview/scripts/lua/test_luafloater_demo2.lua @@ -0,0 +1,39 @@ +local Floater = require 'Floater' +local leap = require 'leap' +local startup = require 'startup' + +local flt = Floater:new( + 'luafloater_demo.xml', + {show_time_lbl = {"right_mouse_down", "double_click"}}) + +-- override base-class handleEvents() to report the event data in the floater's display field +function flt:handleEvents(event_data) + self:post({action="add_text", ctrl_name="events_editor", value = event_data}) + -- forward the call to base-class handleEvents() + return Floater.handleEvents(self, event_data) +end + +function flt:commit_disable_ctrl(event_data) + self:post({action="set_enabled", ctrl_name="open_btn", value = (1 - event_data.value)}) +end + +function flt:commit_title_cmb(event_data) + self:post({action="set_title", value=event_data.value}) +end + +function flt:commit_open_btn(event_data) + floater_name = self:request({action="get_value", ctrl_name='openfloater_cmd'}).value + leap.send("LLFloaterReg", {name = floater_name, op = "showInstance"}) +end + +local function getCurrentTime() + local currentTime = os.date("*t") + return string.format("%02d:%02d:%02d", currentTime.hour, currentTime.min, currentTime.sec) +end + +function flt:double_click_show_time_lbl(event_data) + self:post({action="set_value", ctrl_name="time_lbl", value=getCurrentTime()}) +end + +startup.wait('STATE_LOGIN_WAIT') +flt:show() diff --git a/indra/newview/scripts/lua/test_luafloater_gesture_list2.lua b/indra/newview/scripts/lua/test_luafloater_gesture_list2.lua new file mode 100644 index 0000000000..d702d09c51 --- /dev/null +++ b/indra/newview/scripts/lua/test_luafloater_gesture_list2.lua @@ -0,0 +1,27 @@ +local Floater = require 'Floater' +local LLGesture = require 'LLGesture' +local startup = require 'startup' + +local flt = Floater:new( + "luafloater_gesture_list.xml", + {gesture_list = {"double_click"}}) + +function flt:post_build(event_data) + local gestures_uuid = LLGesture.getActiveGestures() + local action_data = {} + action_data.action = "add_list_element" + action_data.ctrl_name = "gesture_list" + local gestures = {} + for uuid, info in pairs(gestures_uuid) do + table.insert(gestures, {value = uuid, columns={column = "gesture_name", value = info.name}}) + end + action_data.value = gestures + self:post(action_data) +end + +function flt:double_click_gesture_list(event_data) + LLGesture.startGesture(event_data.value) +end + +startup.wait('STATE_STARTED') +flt:show() -- cgit v1.2.3 From e6aa6d22a107161b0bf08a45e9a90e749a824d9b Mon Sep 17 00:00:00 2001 From: Maxim Nikolenko Date: Tue, 9 Apr 2024 22:37:34 +0300 Subject: mac build fix - remove unused variable --- indra/newview/llluamanager.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/indra/newview/llluamanager.cpp b/indra/newview/llluamanager.cpp index 35a73aaabe..344ab1bb99 100644 --- a/indra/newview/llluamanager.cpp +++ b/indra/newview/llluamanager.cpp @@ -190,7 +190,6 @@ void LLLUAmanager::runScriptFile(const std::string &filename, script_result_fn r // It will be called when the LuaState is destroyed. LuaState L(finished_cb); - static int index = 0; lua_callbacks(L)->interrupt = [](lua_State *L, int gc) { if (gc >= 0) -- cgit v1.2.3 From a902138de15067a86a6aeb02fdabd094873da0b2 Mon Sep 17 00:00:00 2001 From: Andrey Kleshchev Date: Wed, 10 Apr 2024 00:40:01 +0300 Subject: triage#166 Select 'No Description' when clicking on a text field Default value should be selected for replacement --- indra/llui/lltexteditor.cpp | 30 +- indra/llui/lltexteditor.h | 4 + indra/newview/llsidepaneliteminfo.cpp | 29 +- indra/newview/llsidepaneliteminfo.h | 2 + .../xui/en/floater_inventory_item_properties.xml | 421 --------------------- 5 files changed, 56 insertions(+), 430 deletions(-) delete mode 100644 indra/newview/skins/default/xui/en/floater_inventory_item_properties.xml diff --git a/indra/llui/lltexteditor.cpp b/indra/llui/lltexteditor.cpp index a247e8700a..82f99fe789 100644 --- a/indra/llui/lltexteditor.cpp +++ b/indra/llui/lltexteditor.cpp @@ -263,7 +263,9 @@ LLTextEditor::LLTextEditor(const LLTextEditor::Params& p) : mShowEmojiHelper(p.show_emoji_helper), mEnableTooltipPaste(p.enable_tooltip_paste), mPassDelete(FALSE), - mKeepSelectionOnReturn(false) + mKeepSelectionOnReturn(false), + mSelectAllOnFocusReceived(false), + mSelectedOnFocusReceived(false) { mSourceID.generate(); @@ -385,6 +387,7 @@ void LLTextEditor::selectNext(const std::string& search_text_in, BOOL case_insen setCursorPos(loc); mIsSelecting = TRUE; + mSelectedOnFocusReceived = false; mSelectionEnd = mCursorPos; mSelectionStart = llmin((S32)getLength(), (S32)(mCursorPos + search_text.size())); } @@ -664,6 +667,13 @@ BOOL LLTextEditor::canSelectAll() const return TRUE; } +//virtual +void LLTextEditor::deselect() +{ + LLTextBase::deselect(); + mSelectedOnFocusReceived = false; +} + // virtual void LLTextEditor::selectAll() { @@ -681,6 +691,11 @@ void LLTextEditor::selectByCursorPosition(S32 prev_cursor_pos, S32 next_cursor_p endSelection(); } +void LLTextEditor::setSelectAllOnFocusReceived(bool b) +{ + mSelectAllOnFocusReceived = b; +} + void LLTextEditor::insertEmoji(llwchar emoji) { LL_INFOS() << "LLTextEditor::insertEmoji(" << wchar_utf8_preview(emoji) << ")" << LL_ENDL; @@ -758,8 +773,16 @@ BOOL LLTextEditor::handleMouseDown(S32 x, S32 y, MASK mask) // Delay cursor flashing resetCursorBlink(); + mSelectedOnFocusReceived = false; if (handled && !gFocusMgr.getMouseCapture()) { + if (!mask && mSelectAllOnFocusReceived) + { + mIsSelecting = false; + mSelectionStart = getLength(); + mSelectionEnd = 0; + mSelectedOnFocusReceived = true; + } gFocusMgr.setMouseCapture( this ); } return handled; @@ -2113,6 +2136,11 @@ void LLTextEditor::focusLostHelper() gEditMenuHandler = NULL; } + if (mSelectedOnFocusReceived) + { + deselect(); + } + if (mCommitOnFocusLost) { onCommit(); diff --git a/indra/llui/lltexteditor.h b/indra/llui/lltexteditor.h index 521405ec25..a4eec8874f 100644 --- a/indra/llui/lltexteditor.h +++ b/indra/llui/lltexteditor.h @@ -142,8 +142,10 @@ public: virtual BOOL canDoDelete() const; virtual void selectAll(); virtual BOOL canSelectAll() const; + virtual void deselect(); void selectByCursorPosition(S32 prev_cursor_pos, S32 next_cursor_pos); + void setSelectAllOnFocusReceived(bool b); virtual bool canLoadOrSaveToFile(); @@ -331,6 +333,8 @@ private: bool mEnableTooltipPaste; bool mPassDelete; bool mKeepSelectionOnReturn; // disabling of removing selected text after pressing of Enter + bool mSelectAllOnFocusReceived; + bool mSelectedOnFocusReceived; LLUUID mSourceID; diff --git a/indra/newview/llsidepaneliteminfo.cpp b/indra/newview/llsidepaneliteminfo.cpp index d6d5a4ef2d..2045820090 100644 --- a/indra/newview/llsidepaneliteminfo.cpp +++ b/indra/newview/llsidepaneliteminfo.cpp @@ -56,6 +56,8 @@ #include "llviewerregion.h" +const char* const DEFAULT_DESC = "(No Description)"; + class PropertiesChangedCallback : public LLInventoryCallback { public: @@ -128,6 +130,7 @@ LLSidepanelItemInfo::LLSidepanelItemInfo(const LLPanel::Params& p) , mUpdatePendingId(-1) , mIsDirty(false) /*Not ready*/ , mParentFloater(NULL) + , mLabelItemDesc(NULL) { gInventory.addObserver(this); gIdleCallbacks.addFunction(&LLSidepanelItemInfo::onIdle, (void*)this); @@ -158,10 +161,11 @@ BOOL LLSidepanelItemInfo::postBuild() mItemTypeIcon = getChild("item_type_icon"); mLabelOwnerName = getChild("LabelOwnerName"); mLabelCreatorName = getChild("LabelCreatorName"); + mLabelItemDesc = getChild("LabelItemDesc"); getChild("LabelItemName")->setPrevalidate(&LLTextValidate::validateASCIIPrintableNoPipe); getChild("LabelItemName")->setCommitCallback(boost::bind(&LLSidepanelItemInfo::onCommitName,this)); - getChild("LabelItemDesc")->setCommitCallback(boost::bind(&LLSidepanelItemInfo:: onCommitDescription, this)); + mLabelItemDesc->setCommitCallback(boost::bind(&LLSidepanelItemInfo:: onCommitDescription, this)); // Thumnail edition mChangeThumbnailBtn->setCommitCallback(boost::bind(&LLSidepanelItemInfo::onEditThumbnail, this)); // acquired date @@ -342,10 +346,14 @@ void LLSidepanelItemInfo::refreshFromItem(LLViewerInventoryItem* item) getChildView("LabelItemName")->setEnabled(is_modifiable && !is_calling_card); // for now, don't allow rename of calling cards getChild("LabelItemName")->setValue(item->getName()); getChildView("LabelItemDescTitle")->setEnabled(TRUE); - getChildView("LabelItemDesc")->setEnabled(is_modifiable); - getChild("LabelItemDesc")->setValue(item->getDescription()); getChild("item_thumbnail")->setValue(item->getThumbnailUUID()); + // Asset upload substitutes empty description with a (No Description) placeholder + std::string desc = item->getDescription(); + mLabelItemDesc->setSelectAllOnFocusReceived(desc == DEFAULT_DESC); + mLabelItemDesc->setValue(desc); + mLabelItemDesc->setEnabled(is_modifiable); + LLUIImagePtr icon_img = LLInventoryIcon::getIcon(item->getType(), item->getInventoryType(), item->getFlags(), FALSE); mItemTypeIcon->setImage(icon_img); @@ -927,17 +935,22 @@ void LLSidepanelItemInfo::onCommitDescription() LLViewerInventoryItem* item = findItem(); if(!item) return; - LLTextEditor* labelItemDesc = getChild("LabelItemDesc"); - if(!labelItemDesc) + if(!mLabelItemDesc) { return; } - if((item->getDescription() != labelItemDesc->getText()) && - (gAgent.allowOperation(PERM_MODIFY, item->getPermissions(), GP_OBJECT_MANIPULATE))) + if (!gAgent.allowOperation(PERM_MODIFY, item->getPermissions(), GP_OBJECT_MANIPULATE)) + { + return; + } + std::string old_desc = item->getDescription(); + std::string new_desc = mLabelItemDesc->getText(); + if(old_desc != new_desc) { + mLabelItemDesc->setSelectAllOnFocusReceived(false); LLPointer new_item = new LLViewerInventoryItem(item); - new_item->setDescription(labelItemDesc->getText()); + new_item->setDescription(new_desc); onCommitChanges(new_item); } } diff --git a/indra/newview/llsidepaneliteminfo.h b/indra/newview/llsidepaneliteminfo.h index b916f44520..736420bac5 100644 --- a/indra/newview/llsidepaneliteminfo.h +++ b/indra/newview/llsidepaneliteminfo.h @@ -46,6 +46,7 @@ class LLObjectInventoryObserver; class LLViewerObject; class LLPermissions; class LLTextBox; +class LLTextEditor; class LLSidepanelItemInfo : public LLPanel, public LLInventoryObserver { @@ -105,6 +106,7 @@ private: LLIconCtrl* mItemTypeIcon; LLTextBox* mLabelOwnerName; LLTextBox* mLabelCreatorName; + LLTextEditor* mLabelItemDesc; // // UI Elements diff --git a/indra/newview/skins/default/xui/en/floater_inventory_item_properties.xml b/indra/newview/skins/default/xui/en/floater_inventory_item_properties.xml deleted file mode 100644 index 850e1be372..0000000000 --- a/indra/newview/skins/default/xui/en/floater_inventory_item_properties.xml +++ /dev/null @@ -1,421 +0,0 @@ - - - - (unknown) - - - (public) - - - You can: - - - Owner can: - - - [wkday,datetime,local] [mth,datetime,local] [day,datetime,local] [hour,datetime,local]:[min,datetime,local]:[second,datetime,local] [year,datetime,local] - - - - Name: - - - - Description: - - - - Creator: - - - TestString PleaseIgnore - - + + diff --git a/indra/newview/scripts/lua/require/LLAppearance.lua b/indra/newview/scripts/lua/require/LLAppearance.lua new file mode 100644 index 0000000000..165bb6d06f --- /dev/null +++ b/indra/newview/scripts/lua/require/LLAppearance.lua @@ -0,0 +1,37 @@ +leap = require 'leap' + +local LLAppearance = {} + +function LLAppearance.addOutfit(folder) + leap.request('LLAppearance', {op='wearOutfit', append = true, folder_id=folder}) +end + +function LLAppearance.replaceOutfit(folder) + leap.request('LLAppearance', {op='wearOutfit', append = false, folder_id=folder}) +end + +function LLAppearance.addOutfitByName(folder) + leap.request('LLAppearance', {op='wearOutfitByName', append = true, folder_name=folder}) +end + +function LLAppearance.replaceOutfitByName(folder) + leap.request('LLAppearance', {op='wearOutfitByName', append = false, folder_name=folder}) +end + +function LLAppearance.wearItem(item_id, replace) + leap.send('LLAppearance', {op='wearItem', replace = replace, item_id=item_id}) +end + +function LLAppearance.detachItem(item_id) + leap.send('LLAppearance', {op='detachItem', item_id=item_id}) +end + +function LLAppearance.getOutfitsList() + return leap.request('LLAppearance', {op='getOutfitsList'})['outfits'] +end + +function LLAppearance.getOutfitItems(id) + return leap.request('LLAppearance', {op='getOutfitItems', outfit_id = id})['items'] +end + +return LLAppearance diff --git a/indra/newview/scripts/lua/test_outfits_list.lua b/indra/newview/scripts/lua/test_outfits_list.lua index 5875fd51da..dd5f914402 100644 --- a/indra/newview/scripts/lua/test_outfits_list.lua +++ b/indra/newview/scripts/lua/test_outfits_list.lua @@ -1,26 +1,107 @@ local Floater = require 'Floater' local LLAppearance = require 'LLAppearance' local startup = require 'startup' +local inspect = require 'inspect' + +local SHOW_OUTFITS = true +local SELECTED_OUTFIT_ID = {} +local DATA_MAP = {} + +local wearables_lbl = 'Show wearables' +local outfits_lbl = 'Show outfits' +local replace_cof_lbl = 'Replace COF' +local add_cof_lbl = 'Add to COF' +local outfits_title = 'Outfits' +local wear_lbl = 'Wear item' +local detach_lbl = 'Detach item' local flt = Floater:new( "luafloater_outfits_list.xml", {outfits_list = {"double_click"}}) -function flt:post_build(event_data) - local outfits_map = LLAppearance.getOutfitsList() +function get_selected_id() + return flt:request({action="get_selected_id", ctrl_name='outfits_list'}).value +end + +function populate_list() + if SHOW_OUTFITS then + DATA_MAP = LLAppearance.getOutfitsList() + else + DATA_MAP = LLAppearance.getOutfitItems(SELECTED_OUTFIT_ID) + end + local action_data = {} action_data.action = "add_list_element" action_data.ctrl_name = "outfits_list" local outfits = {} - for uuid, name in pairs(outfits_map) do + for uuid, name in pairs(DATA_MAP) do table.insert(outfits, {value = uuid, columns={column = "outfit_name", value = name}}) end action_data.value = outfits - self:post(action_data) + flt:post(action_data) +end + +function set_label(btn_name, value) + flt:post({action="set_label", ctrl_name=btn_name, value=value}) +end + +function set_enabled(btn_name, value) + flt:post({action="set_enabled", ctrl_name=btn_name, value=value}) +end + +function update_labels() + if SHOW_OUTFITS then + set_label('select_btn', wearables_lbl) + set_label('replace_btn', replace_cof_lbl) + set_label('add_btn', add_cof_lbl) + + set_enabled('select_btn', false) + flt:post({action="set_title", value=outfits_title}) + else + set_label('select_btn', outfits_lbl) + set_label('replace_btn', wear_lbl) + set_label('add_btn', detach_lbl) + + set_enabled('select_btn', true) + flt:post({action="set_title", value=DATA_MAP[SELECTED_OUTFIT_ID]}) + end + + set_enabled('replace_btn', false) + set_enabled('add_btn', false) +end + +function flt:post_build(event_data) + populate_list() +end + +function flt:commit_replace_btn(event_data) + if SHOW_OUTFITS then + LLAppearance.replaceOutfit(get_selected_id()) + else + LLAppearance.wearItem(get_selected_id(), false) + end +end + +function flt:commit_add_btn(event_data) + if SHOW_OUTFITS then + LLAppearance.addOutfit(get_selected_id()) + else + LLAppearance.detachItem(get_selected_id()) + end +end + +function flt:commit_select_btn(event_data) + SHOW_OUTFITS = not SHOW_OUTFITS + SELECTED_OUTFIT_ID = get_selected_id() + update_labels() + self:post({action="clear_list", ctrl_name='outfits_list'}) + populate_list() end -function flt:double_click_outfits_list(event_data) - LLAppearance.replaceOutfit(event_data.value) +function flt:commit_outfits_list(event_data) + set_enabled('replace_btn', true) + set_enabled('add_btn', true) + set_enabled('select_btn', true) end startup.wait('STATE_STARTED') -- cgit v1.2.3 From 6ee6d19f600bb7fee99f96e8476e2c57088dad17 Mon Sep 17 00:00:00 2001 From: Andrey Kleshchev Date: Wed, 26 Jun 2024 14:48:02 +0300 Subject: viewer#1005 Review fixes --- indra/newview/llappearancemgr.cpp | 4 ++-- indra/newview/llappearancemgr.h | 2 +- indra/newview/llfloaterinventorysettings.cpp | 9 +++++++-- indra/newview/llinventoryfunctions.cpp | 8 ++++---- indra/newview/llinventoryfunctions.h | 3 --- .../skins/default/xui/en/menu_gallery_inventory.xml | 16 ---------------- 6 files changed, 14 insertions(+), 28 deletions(-) diff --git a/indra/newview/llappearancemgr.cpp b/indra/newview/llappearancemgr.cpp index 5971799ca5..fe567f8d1a 100644 --- a/indra/newview/llappearancemgr.cpp +++ b/indra/newview/llappearancemgr.cpp @@ -2042,7 +2042,7 @@ bool LLAppearanceMgr::getCanReplaceCOF(const LLUUID& outfit_cat_id) } // Moved from LLWearableList::ContextMenu for wider utility. -bool LLAppearanceMgr::canAddWearables(const uuid_vec_t& item_ids, bool warn_on_type_mismarch) const +bool LLAppearanceMgr::canAddWearables(const uuid_vec_t& item_ids, bool warn_on_type_mismatch) const { // TODO: investigate wearables may not be loaded at this point EXT-8231 @@ -2072,7 +2072,7 @@ bool LLAppearanceMgr::canAddWearables(const uuid_vec_t& item_ids, bool warn_on_t } else { - if (warn_on_type_mismarch) + if (warn_on_type_mismatch) { LL_WARNS() << "Unexpected wearable type" << LL_ENDL; } diff --git a/indra/newview/llappearancemgr.h b/indra/newview/llappearancemgr.h index c2affc766f..51ac6522fa 100644 --- a/indra/newview/llappearancemgr.h +++ b/indra/newview/llappearancemgr.h @@ -102,7 +102,7 @@ public: bool getCanReplaceCOF(const LLUUID& outfit_cat_id); // Can we add all referenced items to the avatar? - bool canAddWearables(const uuid_vec_t& item_ids, bool warn_on_type_mismarch = true) const; + bool canAddWearables(const uuid_vec_t& item_ids, bool warn_on_type_mismatch = true) const; // Copy all items in a category. void shallowCopyCategoryContents(const LLUUID& src_id, const LLUUID& dst_id, diff --git a/indra/newview/llfloaterinventorysettings.cpp b/indra/newview/llfloaterinventorysettings.cpp index 612953b6df..62f76207a3 100644 --- a/indra/newview/llfloaterinventorysettings.cpp +++ b/indra/newview/llfloaterinventorysettings.cpp @@ -29,6 +29,7 @@ #include "llfloaterinventorysettings.h" #include "llcolorswatch.h" +#include "llviewercontrol.h" LLFloaterInventorySettings::LLFloaterInventorySettings(const LLSD& key) : LLFloater(key) @@ -43,13 +44,17 @@ LLFloaterInventorySettings::~LLFloaterInventorySettings() BOOL LLFloaterInventorySettings::postBuild() { getChild("favorites_color")->setCommitCallback(boost::bind(&LLFloaterInventorySettings::updateColorSwatch, this)); + + bool enable_color = gSavedSettings.getBOOL("InventoryFavoritesColorText"); + getChild("favorites_swatch")->setEnabled(enable_color); + return TRUE; } void LLFloaterInventorySettings::updateColorSwatch() { - bool val = getChild("favorites_color")->getEnabled(); - getChild("favorites_color")->setEnabled(val); + bool val = getChild("favorites_color")->getValue(); + getChild("favorites_swatch")->setEnabled(val); } void LLFloaterInventorySettings::applyUIColor(LLUICtrl* ctrl, const LLSD& param) diff --git a/indra/newview/llinventoryfunctions.cpp b/indra/newview/llinventoryfunctions.cpp index 4fbd8856b9..28d79b7665 100644 --- a/indra/newview/llinventoryfunctions.cpp +++ b/indra/newview/llinventoryfunctions.cpp @@ -2514,12 +2514,12 @@ void set_favorite(const LLUUID& obj_id, bool favorite) if (obj && obj->getIsLinkType()) { - obj = gInventory.getObject(obj_id); + obj = gInventory.getObject(obj->getLinkedUUID()); } if (obj && obj->getIsFavorite() != favorite) { - favorite_send(obj, obj_id, favorite); + favorite_send(obj, obj->getUUID(), favorite); } } @@ -2528,12 +2528,12 @@ void toggle_favorite(const LLUUID& obj_id) LLInventoryObject* obj = gInventory.getObject(obj_id); if (obj && obj->getIsLinkType()) { - obj = gInventory.getObject(obj_id); + obj = gInventory.getObject(obj->getLinkedUUID()); } if (obj) { - favorite_send(obj, obj_id, !obj->getIsFavorite()); + favorite_send(obj, obj->getUUID(), !obj->getIsFavorite()); } } diff --git a/indra/newview/llinventoryfunctions.h b/indra/newview/llinventoryfunctions.h index 2df9a11290..0eabf696ae 100644 --- a/indra/newview/llinventoryfunctions.h +++ b/indra/newview/llinventoryfunctions.h @@ -331,9 +331,6 @@ protected: //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // Class LLFavoritesCollector -// -// Simple class that collects calling cards that are not null, and not -// the agent. Duplicates are possible. //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ class LLFavoritesCollector : public LLInventoryCollectFunctor { diff --git a/indra/newview/skins/default/xui/en/menu_gallery_inventory.xml b/indra/newview/skins/default/xui/en/menu_gallery_inventory.xml index 11c9988dd6..929c626947 100644 --- a/indra/newview/skins/default/xui/en/menu_gallery_inventory.xml +++ b/indra/newview/skins/default/xui/en/menu_gallery_inventory.xml @@ -125,22 +125,6 @@ function="Inventory.DoToSelected" parameter="thumbnail" /> - - - - - - Date: Thu, 27 Jun 2024 18:06:13 -0400 Subject: Make test.cpp test driver recognize LOGTEST_testname. Setting LOGTEST=DEBUG, when many unit/integration tests must be rebuilt and run, can result in lots of unnecessary output. When we only want DEBUG log output from a specific test program, make test.cpp recognize an environment variable LOGTEST_testname, where 'testname' might be the full basename of the executable, or part of INTEGRATION_TEST_testname or PROJECT_foo_TEST_testname. When test.cpp notices a non-empty variable by that name, it behaves as if LOGTEST were set to that value. --- indra/test/test.cpp | 30 ++++++++++++++++++++++++++---- 1 file changed, 26 insertions(+), 4 deletions(-) diff --git a/indra/test/test.cpp b/indra/test/test.cpp index 61a4eb07c5..3cbac41f4a 100644 --- a/indra/test/test.cpp +++ b/indra/test/test.cpp @@ -35,13 +35,14 @@ */ #include "linden_common.h" -#include "llerrorcontrol.h" -#include "lltut.h" #include "chained_callback.h" -#include "stringize.h" -#include "namedtempfile.h" +#include "fsyspath.h" +#include "llerrorcontrol.h" #include "lltrace.h" #include "lltracethreadrecorder.h" +#include "lltut.h" +#include "namedtempfile.h" +#include "stringize.h" #include "apr_pools.h" #include "apr_getopt.h" @@ -545,6 +546,27 @@ int main(int argc, char **argv) // LOGTEST overrides default, but can be overridden by --debug. const char* LOGTEST = getenv("LOGTEST"); + // Sometimes we must rebuild much of the viewer before we get to the + // specific test we want to monitor, and some viewer integration tests are + // quite verbose. In addition to noticing plain LOGTEST= (for all tests), + // also notice LOGTEST_progname= (for a specific test). + std::string basename{ fsyspath(argv[0]).stem() }; + // don't make user set LOGTEST_INTEGRATION_TEST_progname or (worse) + // LOGTEST_PROJECT_foo_TEST_bar -- only LOGTEST_progname or LOGTEST_bar + auto _TEST_ = basename.find("_TEST_"); + if (_TEST_ != std::string::npos) + { + basename.erase(0, _TEST_+6); + } + std::string LOGTEST_prog_key{ "LOGTEST_" + basename }; + const char* LOGTEST_prog = getenv(LOGTEST_prog_key.c_str()); +// std::cout << LOGTEST_prog_key << "='" << (LOGTEST_prog? LOGTEST_prog : "") << "'" << std::endl; + if (LOGTEST_prog && *LOGTEST_prog) + { + LOGTEST = LOGTEST_prog; + std::cout << "LOGTEST='" << LOGTEST << "' from " << LOGTEST_prog_key << std::endl; + } + // values used for options parsing apr_status_t apr_err; const char* opt_arg = NULL; -- cgit v1.2.3 From 6ee98f4e9b5ea9ade06056a9d1f4c1e0b1c00b44 Mon Sep 17 00:00:00 2001 From: Nat Goodspeed Date: Thu, 27 Jun 2024 19:01:28 -0400 Subject: Make lua_emplace() use Luau userdata tags with destructors. It turns out that Luau does not honor PUC-Rio Lua's __gc metafunction, so despite elaborate measures, the previous lua_emplace() implementation would not have destroyed the contained C++ T object when the resulting userdata object was garbage-collected. Moreover, using LL.atexit() as the mechanism to destroy lua_emplace() userdata objects (e.g. LuaListener) would have been slightly fragile because we also want to use LL.atexit() to make the final fiber.run() call, when appropriate. Introducing an order dependency between fiber.run() and the LuaListener destructor would not be robust. Both of those problems are addressed by leveraging one of Luau's extensions over PUC-Rio Lua. A Luau userdata object can have an int tag; and a tag can have an associated C++ destructor function. When any userdata object bearing that tag is garbage-collected, Luau will call that destructor; and Luau's lua_close() function destroys all userdata objects. The resulting lua_emplace() and lua_toclass() code is far simpler. It only remains to generate a distinct int tag value for each different C++ type passed to the lua_emplace() template. unordered_map addresses that need. --- indra/llcommon/lua_function.h | 223 +++++++++--------------------------------- 1 file changed, 48 insertions(+), 175 deletions(-) diff --git a/indra/llcommon/lua_function.h b/indra/llcommon/lua_function.h index 9cdd5665dc..284f911fa8 100644 --- a/indra/llcommon/lua_function.h +++ b/indra/llcommon/lua_function.h @@ -21,8 +21,9 @@ #include "stringize.h" #include // std::uncaught_exceptions() #include // std::shared_ptr -#include +#include #include +#include #include // std::pair class LuaListener; @@ -196,28 +197,33 @@ int name##_luasub::call(lua_State* L) *****************************************************************************/ namespace { -// this closure function retrieves its bound argument to pass to -// lua_emplace_gc() -template -int lua_emplace_call_gc(lua_State* L); -// this will be the function called by the new userdata's metatable's __gc() -template -int lua_emplace_gc(lua_State* L); -// name by which we'll store the new userdata's metatable in the Registry -template -std::string lua_emplace_metaname(const std::string& Tname = LLError::Log::classname()); +// If we start engaging lua_emplace() from more than one thread, type_tags +// will need locking. +std::unordered_map type_tags; + +// find or create a new Luau userdata "tag" for type T +template +int type_tag() +{ + // The first time we encounter a given type T, assign a new distinct tag + // value based on the number of previously-created tags. But avoid tag 0, + // which is evidently the default for userdata objects created without + // explicit tags. Don't try to destroy a nonexistent T object in a random + // userdata object! + auto [entry, created] = type_tags.emplace(std::type_index(typeid(T)), int(type_tags.size()+1)); + // Luau only permits up to LUA_UTAG_LIMIT distinct userdata tags (ca. 128) + llassert(entry->second < LUA_UTAG_LIMIT); + return entry->second; +} } // anonymous namespace /** * On the stack belonging to the passed lua_State, push a Lua userdata object - * with a newly-constructed C++ object std::optional(args...). The new - * userdata has a metadata table with a __gc() function to ensure that when - * the userdata instance is garbage-collected, ~T() is called. Also call - * LL.atexit(lua_emplace_call_gc(object)) to make ~LuaState() call ~T(). - * - * We wrap the userdata object as std::optional so we can explicitly - * destroy the contained T, and detect that we've done so. + * containing a newly-constructed C++ object T(args...). The userdata has a + * Luau destructor guaranteeing that the new T instance is destroyed when the + * userdata is garbage-collected, no later than when the LuaState is + * destroyed. * * Usage: * lua_emplace(L, T constructor args...); @@ -226,178 +232,45 @@ std::string lua_emplace_metaname(const std::string& Tname = LLError::Log::classn template void lua_emplace(lua_State* L, ARGS&&... args) { - using optT = std::optional; - luaL_checkstack(L, 5, nullptr); - auto ptr = lua_newuserdata(L, sizeof(optT)); + luaL_checkstack(L, 1, nullptr); + int tag{ type_tag() }; + if (! lua_getuserdatadtor(L, tag)) + { + // We haven't yet told THIS lua_State the destructor to use for this tag. + lua_setuserdatadtor( + L, tag, + [](lua_State*, void* ptr) + { + // destroy the contained T instance + static_cast(ptr)->~T(); + }); + } + auto ptr = lua_newuserdatatagged(L, sizeof(T), tag); // stack is uninitialized userdata // For now, assume (but verify) that lua_newuserdata() returns a // conservatively-aligned ptr. If that turns out not to be the case, we // might have to discard the new userdata, overallocate its successor and // perform manual alignment -- but only if we must. - llassert((uintptr_t(ptr) % alignof(optT)) == 0); + llassert((uintptr_t(ptr) % alignof(T)) == 0); // Construct our T there using placement new - new (ptr) optT(std::in_place, std::forward(args)...); - // stack is now initialized userdata containing our T instance - - // Find or create the metatable shared by all userdata instances holding - // C++ type T. We want it to be shared across instances, but it must be - // type-specific because its __gc field is lua_emplace_gc. - auto Tname{ LLError::Log::classname() }; - auto metaname{ lua_emplace_metaname(Tname) }; - if (luaL_newmetatable(L, metaname.c_str())) - { - // just created it: populate it - auto gcname{ stringize("lua_emplace_gc<", Tname, ">") }; - lua_pushcfunction(L, lua_emplace_gc, gcname.c_str()); - // stack is userdata, metatable, lua_emplace_gc - lua_setfield(L, -2, "__gc"); - } - // stack is userdata, metatable - lua_setmetatable(L, -2); - // Stack is now userdata, initialized with T(args), - // with metatable.__gc pointing to lua_emplace_gc. - - // But wait, there's more! Use our atexit() function to ensure that this - // C++ object is eventually destroyed even if the garbage collector never - // gets around to it. - lua_getglobal(L, "LL"); - // stack contains userdata, LL - lua_getfield(L, -1, "atexit"); - // stack contains userdata, LL, LL.atexit - // ditch LL - lua_replace(L, -2); - // stack contains userdata, LL.atexit - - // We have a bit of a problem here. We want to allow the garbage collector - // to collect the userdata if it must; but we also want to register a - // cleanup function to destroy the value if (usual case) it has NOT been - // garbage-collected. The problem is that if we bind into atexit()'s queue - // a strong reference to the userdata, we ensure that the garbage - // collector cannot collect it, making our metatable with __gc function - // completely moot. And we must assume that lua_pushcclosure() binds a - // strong reference to each value passed as a closure. - - // The solution is to use one more indirection: create a weak table whose - // sole entry is the userdata. If all other references to the new userdata - // are forgotten, so the only remaining reference is the weak table, the - // userdata can be collected. Then we can bind that weak table as the - // closure value for our cleanup function. - // The new weak table will have at most 1 array value, 0 other keys. - lua_createtable(L, 1, 0); - // stack contains userdata, LL.atexit, weak_table - if (luaL_newmetatable(L, "weak_values")) - { - // stack contains userdata, LL.atexit, weak_table, weak_values - // just created "weak_values" metatable: populate it - // Registry.weak_values = {__mode="v"} - lua_pushliteral(L, "v"); - // stack contains userdata, LL.atexit, weak_table, weak_values, "v" - lua_setfield(L, -2, "__mode"); - } - // stack contains userdata, LL.atexit, weak_table, weak_values - // setmetatable(weak_table, weak_values) - lua_setmetatable(L, -2); - // stack contains userdata, LL.atexit, weak_table - lua_pushinteger(L, 1); - // stack contains userdata, LL.atexit, weak_table, 1 - // duplicate userdata - lua_pushvalue(L, -4); - // stack contains userdata, LL.atexit, weak_table, 1, userdata - // weak_table[1] = userdata - lua_settable(L, -3); - // stack contains userdata, LL.atexit, weak_table - - // push a closure binding (lua_emplace_call_gc, weak_table) - auto callgcname{ stringize("lua_emplace_call_gc<", Tname, ">") }; - lua_pushcclosure(L, lua_emplace_call_gc, callgcname.c_str(), 1); - // stack contains userdata, LL.atexit, closure - // Call LL.atexit(closure) - lua_call(L, 1, 0); - // stack contains userdata -- return that -} - -namespace { - -// passed to LL.atexit(closure(lua_emplace_call_gc, weak_table{userdata})); -// retrieves bound userdata to pass to lua_emplace_gc() -template -int lua_emplace_call_gc(lua_State* L) -{ - luaL_checkstack(L, 2, nullptr); - // retrieve the first (only) bound upvalue and push to stack top - lua_pushvalue(L, lua_upvalueindex(1)); - // This is the weak_table bound by lua_emplace(). Its one and only - // entry should be the lua_emplace() userdata -- unless userdata has - // been garbage collected. Retrieve weak_table[1]. - lua_pushinteger(L, 1); - // stack contains weak_table, 1 - lua_gettable(L, -2); - // stack contains weak_table, weak_table[1] - // If our userdata was garbage-collected, there is no weak_table[1], - // and we just retrieved nil. - if (lua_isnil(L, -1)) - { - lua_pop(L, 2); - return 0; - } - // stack contains weak_table, userdata - // ditch weak_table - lua_replace(L, -2); - // pass userdata to lua_emplace_gc() - return lua_emplace_gc(L); -} - -// set as metatable(userdata).__gc to be called by the garbage collector -template -int lua_emplace_gc(lua_State* L) -{ - using optT = std::optional; - // We're called with userdata on the stack holding an instance of type T. - auto ptr = lua_touserdata(L, -1); - llassert(ptr); - // Destroy the T object contained in optT at the void* address ptr. If - // in future lua_emplace() must manually align our optT* within the - // Lua-provided void*, derive optT* from ptr. - static_cast(ptr)->reset(); - // pop the userdata - lua_pop(L, 1); - return 0; -} - -template -std::string lua_emplace_metaname(const std::string& Tname) -{ - return stringize("lua_emplace_", Tname, "_meta"); + new (ptr) T(std::forward(args)...); + // stack is now initialized userdata containing our T instance -- return + // that } -} // anonymous namespace - /** * If the value at the passed acceptable index is a full userdata created by - * lua_emplace() -- that is, the userdata contains a non-empty - * std::optional -- return a pointer to the contained T instance. Otherwise - * (index is not a full userdata; userdata is not of type std::optional; - * std::optional is empty) return nullptr. + * lua_emplace(), return a pointer to the contained T instance. Otherwise + * (index is not a full userdata; userdata is not of type T) return nullptr. */ template T* lua_toclass(lua_State* L, int index) { - using optT = std::optional; - // recreate the name lua_emplace() uses for its metatable - auto metaname{ lua_emplace_metaname() }; // get void* pointer to userdata (if that's what it is) - void* ptr{ luaL_checkudata(L, index, metaname.c_str()) }; - if (! ptr) - return nullptr; - // Derive the optT* from ptr. If in future lua_emplace() must manually - // align our optT* within the Lua-provided void*, adjust accordingly. - optT* tptr(static_cast(ptr)); - // make sure our optT isn't empty - if (! *tptr) - return nullptr; - // looks like we still have a non-empty optT: return the *address* of the - // value() reference - return &tptr->value(); + void* ptr{ lua_touserdatatagged(L, index, type_tag()) }; + // Derive the T* from ptr. If in future lua_emplace() must manually + // align our T* within the Lua-provided void*, adjust accordingly. + return static_cast(ptr); } /***************************************************************************** -- cgit v1.2.3 From 0cc7436be1f57299384c5acad5d32e13f2f4d1cf Mon Sep 17 00:00:00 2001 From: Nat Goodspeed Date: Thu, 27 Jun 2024 19:47:00 -0400 Subject: Introduce TypeTag template whose int value differs for each T. This replaces type_tag(), which searched and possibly extended the type_tags unordered_map at runtime. If we called lua_emplace() from different threads, that would require locking type_tags. In contrast, the compiler must instantiate a distinct TypeTag for every distinct T passed to lua_emplace(), so each gets a distinct value at static initialization time. No locking is required; no lookup; no allocations. Add a test to llluamanager_test.cpp to verify that each distinct T passed to lua_emplace() gets its own TypeTag::value, and that each gets its own destructor -- but that different lua_emplace() calls with the same T share the same TypeTag::value and the same destructor. --- indra/llcommon/lua_function.cpp | 2 ++ indra/llcommon/lua_function.h | 44 ++++++++++++++++++------------- indra/newview/tests/llluamanager_test.cpp | 35 ++++++++++++++++++++++++ 3 files changed, 63 insertions(+), 18 deletions(-) diff --git a/indra/llcommon/lua_function.cpp b/indra/llcommon/lua_function.cpp index 255385b8c4..2d08de68c5 100644 --- a/indra/llcommon/lua_function.cpp +++ b/indra/llcommon/lua_function.cpp @@ -38,6 +38,8 @@ const S32 INTERRUPTS_SUSPEND_LIMIT = 100; #define lua_register(L, n, f) (lua_pushcfunction(L, (f), n), lua_setglobal(L, (n))) #define lua_rawlen lua_objlen +int DistinctInt::mValues{0}; + /***************************************************************************** * luau namespace *****************************************************************************/ diff --git a/indra/llcommon/lua_function.h b/indra/llcommon/lua_function.h index 284f911fa8..c32a586d79 100644 --- a/indra/llcommon/lua_function.h +++ b/indra/llcommon/lua_function.h @@ -195,26 +195,34 @@ int name##_luasub::call(lua_State* L) /***************************************************************************** * lua_emplace(), lua_toclass() *****************************************************************************/ -namespace { +// Every instance of DistinctInt has a different int value, barring int +// wraparound. +class DistinctInt +{ +public: + DistinctInt(): mValue(++mValues) {} + int get() const { return mValue; } + operator int() const { return mValue; } +private: + static int mValues; + int mValue; +}; -// If we start engaging lua_emplace() from more than one thread, type_tags -// will need locking. -std::unordered_map type_tags; +namespace { -// find or create a new Luau userdata "tag" for type T template -int type_tag() +struct TypeTag { - // The first time we encounter a given type T, assign a new distinct tag - // value based on the number of previously-created tags. But avoid tag 0, - // which is evidently the default for userdata objects created without - // explicit tags. Don't try to destroy a nonexistent T object in a random - // userdata object! - auto [entry, created] = type_tags.emplace(std::type_index(typeid(T)), int(type_tags.size()+1)); - // Luau only permits up to LUA_UTAG_LIMIT distinct userdata tags (ca. 128) - llassert(entry->second < LUA_UTAG_LIMIT); - return entry->second; -} + // For (std::is_same), &TypeTag::value == &TypeTag::value. + // For (! std::is_same), &TypeTag::value != &TypeTag::value. + // And every distinct instance of DistinctInt has a distinct value. + // Therefore, TypeTag::value is an int uniquely associated with each + // distinct T. + static DistinctInt value; +}; + +template +DistinctInt TypeTag::value; } // anonymous namespace @@ -233,7 +241,7 @@ template void lua_emplace(lua_State* L, ARGS&&... args) { luaL_checkstack(L, 1, nullptr); - int tag{ type_tag() }; + int tag{ TypeTag::value }; if (! lua_getuserdatadtor(L, tag)) { // We haven't yet told THIS lua_State the destructor to use for this tag. @@ -267,7 +275,7 @@ template T* lua_toclass(lua_State* L, int index) { // get void* pointer to userdata (if that's what it is) - void* ptr{ lua_touserdatatagged(L, index, type_tag()) }; + void* ptr{ lua_touserdatatagged(L, index, TypeTag::value) }; // Derive the T* from ptr. If in future lua_emplace() must manually // align our T* within the Lua-provided void*, adjust accordingly. return static_cast(ptr); diff --git a/indra/newview/tests/llluamanager_test.cpp b/indra/newview/tests/llluamanager_test.cpp index 2d525f7913..d3fc70dfd5 100644 --- a/indra/newview/tests/llluamanager_test.cpp +++ b/indra/newview/tests/llluamanager_test.cpp @@ -465,4 +465,39 @@ namespace tut ensure_equals(desc + " count: " + result.asString(), count, -1); ensure_contains(desc + " result", result.asString(), "terminated"); } + + template + struct Visible + { + Visible(T name): name(name) + { + LL_INFOS() << "Visible<" << LLError::Log::classname() << ">('" << name << "')" << LL_ENDL; + } + Visible(const Visible&) = delete; + Visible& operator=(const Visible&) = delete; + ~Visible() + { + LL_INFOS() << "~Visible<" << LLError::Log::classname() << ">('" << name << "')" << LL_ENDL; + } + T name; + }; + + template<> template<> + void object::test<9>() + { + set_test_name("track distinct lua_emplace() types"); + LuaState L; + lua_emplace>(L, "std::string 0"); + int st0tag = lua_userdatatag(L, -1); + lua_emplace>(L, "const char* 0"); + int cp0tag = lua_userdatatag(L, -1); + lua_emplace>(L, "std::string 1"); + int st1tag = lua_userdatatag(L, -1); + lua_emplace>(L, "const char* 1"); + int cp1tag = lua_userdatatag(L, -1); + lua_settop(L, 0); + ensure_equals("lua_emplace() tags diverge", st0tag, st1tag); + ensure_equals("lua_emplace() tags diverge", cp0tag, cp1tag); + ensure_not_equals("lua_emplace<>() tags collide", st0tag, cp0tag); + } } // namespace tut -- cgit v1.2.3 From 982ea7fb796924877e95bee2f9ba6b2296219139 Mon Sep 17 00:00:00 2001 From: Nat Goodspeed Date: Thu, 27 Jun 2024 23:11:18 -0400 Subject: Work around VS refusal to initialize a string --- indra/test/test.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/indra/test/test.cpp b/indra/test/test.cpp index 3cbac41f4a..22f9ccf334 100644 --- a/indra/test/test.cpp +++ b/indra/test/test.cpp @@ -550,7 +550,7 @@ int main(int argc, char **argv) // specific test we want to monitor, and some viewer integration tests are // quite verbose. In addition to noticing plain LOGTEST= (for all tests), // also notice LOGTEST_progname= (for a specific test). - std::string basename{ fsyspath(argv[0]).stem() }; + std::string basename(fsyspath(argv[0]).stem()); // don't make user set LOGTEST_INTEGRATION_TEST_progname or (worse) // LOGTEST_PROJECT_foo_TEST_bar -- only LOGTEST_progname or LOGTEST_bar auto _TEST_ = basename.find("_TEST_"); @@ -558,7 +558,7 @@ int main(int argc, char **argv) { basename.erase(0, _TEST_+6); } - std::string LOGTEST_prog_key{ "LOGTEST_" + basename }; + std::string LOGTEST_prog_key("LOGTEST_" + basename); const char* LOGTEST_prog = getenv(LOGTEST_prog_key.c_str()); // std::cout << LOGTEST_prog_key << "='" << (LOGTEST_prog? LOGTEST_prog : "") << "'" << std::endl; if (LOGTEST_prog && *LOGTEST_prog) -- cgit v1.2.3 From fbeff6d8052d4b614a0a2c8ebaf35b45379ab578 Mon Sep 17 00:00:00 2001 From: Nat Goodspeed Date: Fri, 28 Jun 2024 07:54:46 -0400 Subject: Give our fsyspath an operator std::string() conversion method. This is redundant (but harmless) on a Posix system, but it fills a missing puzzle piece on Windows. The point of fsyspath is to be able to interchange freely between fsyspath and std::string. Existing fsyspath could be constructed and assigned from std::string, and we could explicitly call its string() method to get a std::string, but an implicit fsyspath-to-string conversion that worked on Posix would trip us up on Windows. Fix that. --- indra/llcommon/fsyspath.h | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/indra/llcommon/fsyspath.h b/indra/llcommon/fsyspath.h index aa4e0132bc..3c749d84de 100644 --- a/indra/llcommon/fsyspath.h +++ b/indra/llcommon/fsyspath.h @@ -69,6 +69,11 @@ public: // shadow base-class string() method with UTF-8 aware method std::string string() const { return super::u8string(); } + // On Posix systems, where value_type is already char, this operator + // std::string() method shadows the base class operator string_type() + // method. But on Windows, where value_type is wchar_t, the base class + // doesn't have operator std::string(). Provide it. + operator std::string() const { return string(); } }; #endif /* ! defined(LL_FSYSPATH_H) */ -- cgit v1.2.3 From cfd7d8905d686411a774c47bbfc13f49882b65e6 Mon Sep 17 00:00:00 2001 From: Nat Goodspeed Date: Fri, 28 Jun 2024 08:30:41 -0400 Subject: Work around MSVC limitation: explicitly call fsyspath::string(). --- indra/test/test.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/indra/test/test.cpp b/indra/test/test.cpp index 22f9ccf334..0e863d8084 100644 --- a/indra/test/test.cpp +++ b/indra/test/test.cpp @@ -550,7 +550,9 @@ int main(int argc, char **argv) // specific test we want to monitor, and some viewer integration tests are // quite verbose. In addition to noticing plain LOGTEST= (for all tests), // also notice LOGTEST_progname= (for a specific test). - std::string basename(fsyspath(argv[0]).stem()); + // (Why doesn't MSVC notice fsyspath::operator std::string()? + // Why must we explicitly call fsyspath::string()?) + std::string basename(fsyspath(argv[0]).stem().string()); // don't make user set LOGTEST_INTEGRATION_TEST_progname or (worse) // LOGTEST_PROJECT_foo_TEST_bar -- only LOGTEST_progname or LOGTEST_bar auto _TEST_ = basename.find("_TEST_"); -- cgit v1.2.3 From ac56718929044e101dc2c7bfe3ebc5dff565b76d Mon Sep 17 00:00:00 2001 From: Nat Goodspeed Date: Fri, 28 Jun 2024 13:21:14 -0400 Subject: Add LuaAutorunPath, LuaCommandPath and LuaRequirePath settings. Remove AutorunLuaScriptFile and the LLLUAmanager::runScriptOnLogin() method that checked it. Instead, iterate over LuaAutorunPath directories at viewer startup, iterate over *.lua files in each and implicitly run those. LuaCommandPath and LuaRequirePath are not yet implemented. --- indra/newview/app_settings/settings.xml | 634 +++++++++++++++++--------------- indra/newview/llappviewer.cpp | 19 + indra/newview/llluamanager.cpp | 21 -- indra/newview/llluamanager.h | 2 - indra/newview/llstartup.cpp | 3 - 5 files changed, 350 insertions(+), 329 deletions(-) diff --git a/indra/newview/app_settings/settings.xml b/indra/newview/app_settings/settings.xml index 2463d56eae..cdaf9a47dd 100644 --- a/indra/newview/app_settings/settings.xml +++ b/indra/newview/app_settings/settings.xml @@ -346,7 +346,7 @@ Value 0.5 - AudioStreamingMedia + AudioStreamingMedia Comment Enable streaming @@ -1291,7 +1291,7 @@ Type Boolean Value - 0 + 0 CameraPositionSmoothing @@ -1907,17 +1907,17 @@ Value - DebugAvatarRezTime - - Comment - Display times for avatars to resolve. - Persist - 1 - Type - Boolean - Value - 0 - + DebugAvatarRezTime + + Comment + Display times for avatars to resolve. + Persist + 1 + Type + Boolean + Value + 0 + DebugAvatarLocalTexLoadedTime Comment @@ -2292,39 +2292,39 @@ Value 0 - DefaultFemaleAvatar - - Comment - Default Female Avatar - Persist - 1 - Type - String - Value - Female Shape & Outfit - - DefaultLoginLocation - - Comment - Startup destination default (if not specified on command line) - Persist - 0 - Type - String - Value - - - DefaultMaleAvatar - - Comment - Default Male Avatar - Persist - 1 - Type - String - Value - Male Shape & Outfit - + DefaultFemaleAvatar + + Comment + Default Female Avatar + Persist + 1 + Type + String + Value + Female Shape & Outfit + + DefaultLoginLocation + + Comment + Startup destination default (if not specified on command line) + Persist + 0 + Type + String + Value + + + DefaultMaleAvatar + + Comment + Default Male Avatar + Persist + 1 + Type + String + Value + Male Shape & Outfit + DestinationGuideURL Comment @@ -2699,7 +2699,7 @@ Value 0 - FirstSelectedEnabledPopups + FirstSelectedEnabledPopups Comment Return false if there is not enable popup selected in the list of floater preferences popups @@ -3521,39 +3521,39 @@ Value 0 - InventoryLinking - - Comment - Enable ability to create links to folders and items via "Paste as link". - Persist - 1 - Type - Boolean - Value - 1 - - InventoryOutboxLogging - - Comment - Enable debug output associated with the Merchant Outbox. - Persist - 1 - Type - Boolean - Value - 0 - - InventoryOutboxMakeVisible - - Comment - Enable making the Merchant Outbox and Inbox visible in the inventory for debug purposes. - Persist - 1 - Type - Boolean - Value - 0 - + InventoryLinking + + Comment + Enable ability to create links to folders and items via "Paste as link". + Persist + 1 + Type + Boolean + Value + 1 + + InventoryOutboxLogging + + Comment + Enable debug output associated with the Merchant Outbox. + Persist + 1 + Type + Boolean + Value + 0 + + InventoryOutboxMakeVisible + + Comment + Enable making the Merchant Outbox and Inbox visible in the inventory for debug purposes. + Persist + 1 + Type + Boolean + Value + 0 + InventoryOutboxMaxFolderCount Comment @@ -3807,8 +3807,8 @@ Value 0.25 - Jpeg2000AdvancedCompression - + Jpeg2000AdvancedCompression + Comment Use advanced Jpeg2000 compression options (precincts, blocks, ordering, markers) Persist @@ -3817,9 +3817,9 @@ Boolean Value 0 - - Jpeg2000PrecinctsSize - + + Jpeg2000PrecinctsSize + Comment Size of image precincts. Assumed square and same for all levels. Must be power of 2. Persist @@ -3828,9 +3828,9 @@ S32 Value 256 - - Jpeg2000BlocksSize - + + Jpeg2000BlocksSize + Comment Size of encoding blocks. Assumed square and same for all levels. Must be power of 2. Max 64, Min 4. Persist @@ -3839,7 +3839,7 @@ S32 Value 64 - + KeepAspectForSnapshot Comment @@ -3917,10 +3917,23 @@ Value Monospace + LuaAutorunPath + + Comment + Directories containing scripts to autorun at viewer startup + Persist + 1 + Type + LLSD + Value + + scripts/lua/auto + + LuaChunk Comment - Zero or more Lua chunks to run + Zero or more Lua chunks to run from command line Persist 0 Type @@ -3928,10 +3941,36 @@ Value + LuaCommandPath + + Comment + Directories containing scripts recognized as chat slash commands + Persist + 1 + Type + LLSD + Value + + scripts/lua + + + LuaRequirePath + + Comment + Directories containing Lua modules loadable by require() + Persist + 1 + Type + LLSD + Value + + scripts/lua/require + + LuaScript Comment - Zero or more Lua script files to run + Zero or more Lua script files to run from command line Persist 0 Type @@ -4469,17 +4508,17 @@ Value - MarketplaceListingsLogging - - Comment - Enable debug output associated with the Marketplace Listings (SLM) API. - Persist - 1 - Type - Boolean - Value - 0 - + MarketplaceListingsLogging + + Comment + Enable debug output associated with the Marketplace Listings (SLM) API. + Persist + 1 + Type + Boolean + Value + 0 + MarketplaceURL Comment @@ -5365,28 +5404,28 @@ Value 1000 - FakeInitialOutfitName - - Comment - Pretend that this is first time login and specified name was chosen - Persist - 1 - Type + FakeInitialOutfitName + + Comment + Pretend that this is first time login and specified name was chosen + Persist + 1 + Type String Value - - MyOutfitsAutofill - - Comment - Always autofill My Outfits from library when empty (else happens just once). - Persist - 1 - Type - Boolean - Value - 0 - + + MyOutfitsAutofill + + Comment + Always autofill My Outfits from library when empty (else happens just once). + Persist + 1 + Type + Boolean + Value + 0 + NearMeRange Comment @@ -5506,7 +5545,7 @@ Type Boolean Value - 0 + 0 NonvisibleObjectsInMemoryTime @@ -5517,7 +5556,7 @@ Type U32 Value - 64 + 64 NoPreload @@ -6429,7 +6468,7 @@ Value 6.0 - PreferredMaturity + PreferredMaturity Comment Setting for the user's preferred maturity level (consts in indra_constants.h) @@ -6438,7 +6477,7 @@ Type U32 Value - 13 + 13 PreviewAmbientColor @@ -6608,8 +6647,8 @@ PrimMediaMasterEnabled - - Comment + + Comment Whether or not Media on a Prim is enabled. Persist 1 @@ -6618,9 +6657,9 @@ Value 1 - PrimMediaControlsUseHoverControlSet - - Comment + PrimMediaControlsUseHoverControlSet + + Comment Whether or not hovering over prim media uses minimal "hover" controls or the authored control set. Persist 1 @@ -6629,17 +6668,17 @@ Value 0 - PrimMediaDragNDrop - - Comment - Enable drag and drop of URLs onto prim faces - Persist - 1 - Type - Boolean - Value - 1 - + PrimMediaDragNDrop + + Comment + Enable drag and drop of URLs onto prim faces + Persist + 1 + Type + Boolean + Value + 1 + PrimMediaMaxRetries Comment @@ -6673,7 +6712,7 @@ Value 5.0 - PrimMediaMaxSortedQueueSize + PrimMediaMaxSortedQueueSize Comment Maximum number of objects the viewer will load media for initially @@ -6684,7 +6723,7 @@ Value 100000 - PrimMediaMaxRoundRobinQueueSize + PrimMediaMaxRoundRobinQueueSize Comment Maximum number of objects the viewer will continuously update media for @@ -8788,17 +8827,17 @@ Value 1024 - RenderHeroProbeDistance - - Comment - Distance in meters for hero probes to render out to. - Persist - 1 - Type - F32 - Value - 8 - + RenderHeroProbeDistance + + Comment + Distance in meters for hero probes to render out to. + Persist + 1 + Type + F32 + Value + 8 + RenderHeroProbeUpdateRate Comment @@ -9363,17 +9402,17 @@ Value 1 - RenderTransparentWater - - Comment - Render water as transparent. Setting to false renders water as opaque with a simple texture applied. + RenderTransparentWater + + Comment + Render water as transparent. Setting to false renders water as opaque with a simple texture applied. Persist 1 Type Boolean Value 1 - + RenderTreeLODFactor Comment @@ -9655,18 +9694,18 @@ Value 1 - RenderPreferStreamDraw - - Comment - Use GL_STREAM_DRAW in place of GL_DYNAMIC_DRAW - Persist - 1 - Type - Boolean - Value - 0 - - RenderVolumeLODFactor + RenderPreferStreamDraw + + Comment + Use GL_STREAM_DRAW in place of GL_DYNAMIC_DRAW + Persist + 1 + Type + Boolean + Value + 0 + + RenderVolumeLODFactor Comment Controls level of detail of primitives (multiplier for current screen area when calculated level of detail) @@ -9765,18 +9804,18 @@ Value 0 - ReportBugURL - - Comment - URL used for filing bugs from viewer - Persist - 1 - Type - String - Value - https://feedback.secondlife.com/ - - RevokePermsOnStopAnimation + ReportBugURL + + Comment + URL used for filing bugs from viewer + Persist + 1 + Type + String + Value + https://feedback.secondlife.com/ + + RevokePermsOnStopAnimation Comment Clear animation permssions when choosing "Stop Animating Me" @@ -10029,39 +10068,39 @@ Value 400.0 - SceneLoadingMonitorEnabled - - Comment - Enabled scene loading monitor if set - Persist - 0 - Type - Boolean - Value - 0 - - SceneLoadingMonitorSampleTime - - Comment - Time between screen samples when monitor scene load (seconds) - Persist - 1 - Type - F32 - Value - 0.25 - - SceneLoadingMonitorPixelDiffThreshold - - Comment - Amount of pixels changed required to consider the scene as still loading (square root of fraction of pixels on screen) - Persist - 1 - Type - F32 - Value - 0.02 - + SceneLoadingMonitorEnabled + + Comment + Enabled scene loading monitor if set + Persist + 0 + Type + Boolean + Value + 0 + + SceneLoadingMonitorSampleTime + + Comment + Time between screen samples when monitor scene load (seconds) + Persist + 1 + Type + F32 + Value + 0.25 + + SceneLoadingMonitorPixelDiffThreshold + + Comment + Amount of pixels changed required to consider the scene as still loading (square root of fraction of pixels on screen) + Persist + 1 + Type + F32 + Value + 0.02 + ScriptHelpFollowsCursor Comment @@ -10238,7 +10277,7 @@ Value 0 - AvatarNameTagMode + AvatarNameTagMode Comment Select Avatar Name Tag Mode @@ -10447,7 +10486,7 @@ Value 1 - ShowScriptErrors + ShowScriptErrors Comment Show script errors @@ -10458,7 +10497,7 @@ Value 1 - ShowScriptErrorsLocation + ShowScriptErrorsLocation Comment Show script error in chat (0) or window (1). @@ -10717,8 +10756,8 @@ Display results of find events that are flagged as moderate Persist 1 - HideFromEditor - 1 + HideFromEditor + 1 Type Boolean Value @@ -10730,8 +10769,8 @@ Display results of find events that are flagged as adult Persist 1 - HideFromEditor - 1 + HideFromEditor + 1 Type Boolean Value @@ -10743,8 +10782,8 @@ Display results of find land sales that are flagged as general Persist 1 - HideFromEditor - 1 + HideFromEditor + 1 Type Boolean Value @@ -10756,8 +10795,8 @@ Display results of find land sales that are flagged as moderate Persist 1 - HideFromEditor - 1 + HideFromEditor + 1 Type Boolean Value @@ -10769,8 +10808,8 @@ Display results of find land sales that are flagged as adult Persist 1 - HideFromEditor - 1 + HideFromEditor + 1 Type Boolean Value @@ -10782,8 +10821,8 @@ Display results of find places or find popular that are in general sims Persist 1 - HideFromEditor - 1 + HideFromEditor + 1 Type Boolean Value @@ -10795,8 +10834,8 @@ Display results of find places or find popular that are in moderate sims Persist 1 - HideFromEditor - 1 + HideFromEditor + 1 Type Boolean Value @@ -10808,8 +10847,8 @@ Display results of find places or find popular that are in adult sims Persist 1 - HideFromEditor - 1 + HideFromEditor + 1 Type Boolean Value @@ -10947,17 +10986,17 @@ Value 0 - ShowTutorial - - Comment - Show tutorial window on login - Persist - 1 - Type - Boolean - Value - 0 - + ShowTutorial + + Comment + Show tutorial window on login + Persist + 1 + Type + Boolean + Value + 0 + ShowVoiceVisualizersInCalls Comment @@ -13529,17 +13568,17 @@ Value 0.40000000596 - moapbeacon - - Comment - Beacon / Highlight media on a prim sources - Persist - 1 - Type - Boolean - Value - 0 - + moapbeacon + + Comment + Beacon / Highlight media on a prim sources + Persist + 1 + Type + Boolean + Value + 0 + particlesbeacon Comment @@ -13639,17 +13678,17 @@ Value 0 - SLURLDragNDrop - - Comment - Enable drag and drop of SLURLs onto the viewer - Persist - 1 - Type - Boolean - Value - 1 - + SLURLDragNDrop + + Comment + Enable drag and drop of SLURLs onto the viewer + Persist + 1 + Type + Boolean + Value + 1 + SLURLPassToOtherInstance Comment @@ -13837,10 +13876,10 @@ LLSD Value - snapshot - postcard - mini_map - beacons + snapshot + postcard + mini_map + beacons LandmarksSortedByDate @@ -14590,7 +14629,7 @@ Value 0 - LocalTerrainAsset1 + LocalTerrainAsset1 Comment If set to a non-null UUID, overrides the terrain asset locally for all regions with material assets. Local terrain assets are not visible to others. Please keep in mind that this debug setting may be temporary. Do not rely on this setting existing in future viewer builds. @@ -14601,7 +14640,7 @@ Value 00000000-0000-0000-0000-000000000000 - LocalTerrainAsset2 + LocalTerrainAsset2 Comment If set to a non-null UUID, overrides the terrain asset locally for all regions with material assets. Local terrain assets are not visible to others. Please keep in mind that this debug setting may be temporary. Do not rely on this setting existing in future viewer builds. @@ -14612,7 +14651,7 @@ Value 00000000-0000-0000-0000-000000000000 - LocalTerrainAsset3 + LocalTerrainAsset3 Comment If set to a non-null UUID, overrides the terrain asset locally for all regions with material assets. Local terrain assets are not visible to others. Please keep in mind that this debug setting may be temporary. Do not rely on this setting existing in future viewer builds. @@ -14623,7 +14662,7 @@ Value 00000000-0000-0000-0000-000000000000 - LocalTerrainAsset4 + LocalTerrainAsset4 Comment If set to a non-null UUID, overrides the terrain asset locally for all regions with material assets. Local terrain assets are not visible to others. Please keep in mind that this debug setting may be temporary. Do not rely on this setting existing in future viewer builds. @@ -14634,7 +14673,7 @@ Value 00000000-0000-0000-0000-000000000000 - PathfindingRetrieveNeighboringRegion + PathfindingRetrieveNeighboringRegion Comment Download a neighboring region when visualizing a pathfinding navmesh (default val 99 means do not download neighbors). @@ -14643,9 +14682,9 @@ Type U32 Value - 99 + 99 - PathfindingNavMeshClear + PathfindingNavMeshClear Comment Background color when displaying pathfinding navmesh. @@ -14805,7 +14844,7 @@ 1.0 - PathfindingTestPathValidEndColor + PathfindingTestPathValidEndColor Comment Color of the pathfinding test-pathing tool end-point when the path is valid. @@ -14837,7 +14876,7 @@ 1.0 - PathfindingTestPathColor + PathfindingTestPathColor Comment Color of the pathfinding test-path when the path is valid. @@ -15397,17 +15436,6 @@ Value 3 - AutorunLuaScriptName - - Comment - Script name to autorun after login. - Persist - 1 - Type - String - Value - default.lua - ResetUIScaleOnFirstRun Comment @@ -15485,17 +15513,17 @@ Value 300 - StatsReportFileInterval - - Comment - Interval to save viewer stats file data - Persist - 1 - Type - F32 - Value - 0.2 - + StatsReportFileInterval + + Comment + Interval to save viewer stats file data + Persist + 1 + Type + F32 + Value + 0.2 + StatsReportSkipZeroDataSaves Comment diff --git a/indra/newview/llappviewer.cpp b/indra/newview/llappviewer.cpp index 65e5e4f783..ef12fe0bd3 100644 --- a/indra/newview/llappviewer.cpp +++ b/indra/newview/llappviewer.cpp @@ -117,6 +117,7 @@ #include "lldiriterator.h" #include "llexperiencecache.h" #include "llimagej2c.h" +#include "llluamanager.h" #include "llmemory.h" #include "llprimitive.h" #include "llurlaction.h" @@ -1218,6 +1219,24 @@ bool LLAppViewer::init() // no completion callback: we don't need to know LLLUAmanager::runScriptFile(script); }); + processComposeSwitch( + "LuaAutorunPath", "LuaAutorunPath", + [](const LLSD& directory) + { + // each directory can be relative to the viewer's install + // directory -- if directory is already absolute, operator/() + // preserves it + auto abspath(fsyspath(gDirUtilp->getAppRODataDir()) / directory.asString()); + std::string absdir(abspath.string()); + LL_DEBUGS("InitInfo") << "LuaAutorunPath: " << absdir << LL_ENDL; + LLDirIterator scripts(absdir, "*.lua"); + std::string script; + while (scripts.next(script)) + { + LL_DEBUGS("InitInfo") << "LuaAutorunPath: " << absdir << ": " << script << LL_ENDL; + LLLUAmanager::runScriptFile((abspath / script).string()); + } + }); if (gSavedSettings.getBOOL("QAMode") && gSavedSettings.getS32("QAModeEventHostPort") > 0) { diff --git a/indra/newview/llluamanager.cpp b/indra/newview/llluamanager.cpp index 3ed72c34f3..77f197b2d2 100644 --- a/indra/newview/llluamanager.cpp +++ b/indra/newview/llluamanager.cpp @@ -265,27 +265,6 @@ void LLLUAmanager::runScriptLine(LuaState& L, const std::string& chunk, script_r }); } -void LLLUAmanager::runScriptOnLogin() -{ -#ifndef LL_TEST - std::string filename = gSavedSettings.getString("AutorunLuaScriptName"); - if (filename.empty()) - { - LL_INFOS() << "Script name wasn't set." << LL_ENDL; - return; - } - - filename = gDirUtilp->getExpandedFilename(LL_PATH_USER_SETTINGS, filename); - if (!gDirUtilp->fileExists(filename)) - { - LL_INFOS() << filename << " was not found." << LL_ENDL; - return; - } - - runScriptFile(filename); -#endif // ! LL_TEST -} - std::string read_file(const std::string &name) { llifstream in_file; diff --git a/indra/newview/llluamanager.h b/indra/newview/llluamanager.h index af9dcf70c2..dcbb91f799 100644 --- a/indra/newview/llluamanager.h +++ b/indra/newview/llluamanager.h @@ -82,8 +82,6 @@ public: // The return value is the (count, result) pair described above. static std::pair waitScriptLine(LuaState& L, const std::string& chunk); - static void runScriptOnLogin(); - static const std::map getScriptNames() { return sScriptNames; } private: diff --git a/indra/newview/llstartup.cpp b/indra/newview/llstartup.cpp index 470e512694..3cf0def66e 100644 --- a/indra/newview/llstartup.cpp +++ b/indra/newview/llstartup.cpp @@ -208,7 +208,6 @@ #include "llstacktrace.h" #include "threadpool.h" -#include "llluamanager.h" #include "llperfstats.h" @@ -2422,8 +2421,6 @@ bool idle_startup() LLPerfStats::StatsRecorder::setAutotuneInit(); - LLLUAmanager::runScriptOnLogin(); - return TRUE; } -- cgit v1.2.3 From 07f0f12bcbe864177a145b074c2739eaf08f2c5c Mon Sep 17 00:00:00 2001 From: Mnikolenko Productengine Date: Mon, 1 Jul 2024 13:02:49 +0300 Subject: Move error strings to strings.xml; pass wearable type and is_worn flag for outfit items --- indra/newview/llappearancelistener.cpp | 20 +++++++++++++------- indra/newview/llappearancemgr.cpp | 9 +++++---- indra/newview/scripts/lua/test_outfits_list.lua | 8 +++++++- indra/newview/skins/default/xui/en/strings.xml | 4 ++++ 4 files changed, 29 insertions(+), 12 deletions(-) diff --git a/indra/newview/llappearancelistener.cpp b/indra/newview/llappearancelistener.cpp index 11037a0078..3db2c64b78 100644 --- a/indra/newview/llappearancelistener.cpp +++ b/indra/newview/llappearancelistener.cpp @@ -29,8 +29,9 @@ #include "llappearancemgr.h" #include "llinventoryfunctions.h" -#include "stringize.h" +#include "lltransutil.h" #include "llwearableitemslist.h" +#include "stringize.h" LLAppearanceListener::LLAppearanceListener() : LLEventAPI("LLAppearance", @@ -65,7 +66,7 @@ LLAppearanceListener::LLAppearanceListener() &LLAppearanceListener::getOutfitsList); add("getOutfitItems", - "Return the table of items(id and name) inside specified outfit folder", + "Return the table of items with info(id : name, wearable_type, is_worn) inside specified outfit folder", &LLAppearanceListener::getOutfitItems); } @@ -76,19 +77,19 @@ void LLAppearanceListener::wearOutfit(LLSD const &data) LLViewerInventoryCategory* cat = gInventory.getCategory(data["folder_id"].asUUID()); if (!cat) { - response.error(stringize("Couldn't find outfit ", data["folder_id"].asUUID())); + response.error(stringize(LLTrans::getString("OutfitNotFound"), data["folder_id"].asUUID())); return; } if (LLFolderType::lookupIsProtectedType(cat->getPreferredType())) { - response.error(stringize("Can't wear system folder ", data["folder_id"].asUUID())); + response.error(stringize(LLTrans::getString("SystemFolderNotWorn"), data["folder_id"].asUUID())); return; } bool append = data["append"].asBoolean(); bool can_wear = append ? LLAppearanceMgr::instance().getCanAddToCOF(cat->getUUID()) : LLAppearanceMgr::instance().getCanReplaceCOF(cat->getUUID()); if (!can_wear) { - std::string msg = append ? "Can't add to COF outfit " : "Can't replace COF with outfit "; + std::string msg = append ? LLTrans::getString("OutfitNotAdded") : LLTrans::getString("OutfitNotReplaced"); response.error(stringize(msg, std::quoted(cat->getName()), " , id: ", cat->getUUID())); return; } @@ -141,7 +142,7 @@ void LLAppearanceListener::getOutfitItems(LLSD const &data) LLViewerInventoryCategory *cat = gInventory.getCategory(outfit_id); if (!cat || cat->getPreferredType() != LLFolderType::FT_OUTFIT) { - response.error(stringize("Can't find outfit folder with id: ", outfit_id.asString())); + response.error(stringize(LLTrans::getString("OutfitNotFound"), outfit_id.asString())); } LLInventoryModel::cat_array_t cat_array; LLInventoryModel::item_array_t item_array; @@ -151,7 +152,12 @@ void LLAppearanceListener::getOutfitItems(LLSD const &data) LLSD items_data; for (const LLPointer &it : item_array) { - items_data[it->getUUID().asString()] = it->getName(); + LLSD info; + info["name"] = it->getName(); + info["wearable_type"] = LLWearableType::getInstance()->getTypeName(it->isWearableType() ? it->getWearableType() : LLWearableType::WT_NONE); + info["is_worn"] = get_is_item_worn(it); + + items_data[it->getUUID().asString()] = info; } response["items"] = items_data; diff --git a/indra/newview/llappearancemgr.cpp b/indra/newview/llappearancemgr.cpp index 19b81f5a79..3d13f8afc8 100644 --- a/indra/newview/llappearancemgr.cpp +++ b/indra/newview/llappearancemgr.cpp @@ -49,6 +49,7 @@ #include "lloutfitslist.h" #include "llselectmgr.h" #include "llsidepanelappearance.h" +#include "lltransutil.h" #include "llviewerobjectlist.h" #include "llvoavatar.h" #include "llvoavatarself.h" @@ -2941,14 +2942,14 @@ bool LLAppearanceMgr::wearOutfitByName(const std::string& name, bool append, std bool is_system_folder = LLFolderType::lookupIsProtectedType(cat->getPreferredType()); if (is_system_folder) { - error_msg = stringize("Can't wear system folder ", std::quoted(name)); + error_msg = stringize(LLTrans::getString("SystemFolderNotWorn"), std::quoted(name)); return false; } bool can_wear = append ? getCanAddToCOF(cat->getUUID()) : getCanReplaceCOF(cat->getUUID()); if (!can_wear) { - std::string msg = append ? "Can't add to COF outfit " : "Can't replace COF with outfit "; - error_msg = stringize(msg, std::quoted(name), " , id: ", cat->getUUID()); + std::string msg = append ? LLTrans::getString("OutfitNotAdded") : LLTrans::getString("OutfitNotReplaced"); + error_msg = stringize(msg, std::quoted(name), ", id: ", cat->getUUID()); LL_WARNS() << error_msg << LL_ENDL; return false; } @@ -2956,7 +2957,7 @@ bool LLAppearanceMgr::wearOutfitByName(const std::string& name, bool append, std } else { - error_msg = stringize("Couldn't find outfit ", std::quoted(name)); + error_msg = stringize(LLTrans::getString("OutfitNotFound"), std::quoted(name)); LL_WARNS() << error_msg << LL_ENDL; return false; } diff --git a/indra/newview/scripts/lua/test_outfits_list.lua b/indra/newview/scripts/lua/test_outfits_list.lua index dd5f914402..1011029f34 100644 --- a/indra/newview/scripts/lua/test_outfits_list.lua +++ b/indra/newview/scripts/lua/test_outfits_list.lua @@ -34,7 +34,13 @@ function populate_list() action_data.action = "add_list_element" action_data.ctrl_name = "outfits_list" local outfits = {} - for uuid, name in pairs(DATA_MAP) do + for uuid, info in pairs(DATA_MAP) do + name = {} + if SHOW_OUTFITS then + name = info + else + name = info.name + end table.insert(outfits, {value = uuid, columns={column = "outfit_name", value = name}}) end action_data.value = outfits diff --git a/indra/newview/skins/default/xui/en/strings.xml b/indra/newview/skins/default/xui/en/strings.xml index 76a2660dbb..492b29fbc7 100644 --- a/indra/newview/skins/default/xui/en/strings.xml +++ b/indra/newview/skins/default/xui/en/strings.xml @@ -4028,6 +4028,10 @@ Please check http://status.secondlifegrid.net to see if there is a known problem Delete selected item? There are no items in this outfit + + + + Select an editor by setting the environment variable LL_SCRIPT_EDITOR or the ExternalEditor setting. -- cgit v1.2.3 From 3961bac0ef705775883a4b37f2b6a84e41b82c05 Mon Sep 17 00:00:00 2001 From: Maxim Nikolenko Date: Mon, 1 Jul 2024 14:47:52 +0300 Subject: build fix --- indra/newview/llappearancelistener.cpp | 5 +++-- indra/newview/llappearancemgr.cpp | 13 +++++++++++-- indra/newview/llappearancemgr.h | 3 ++- 3 files changed, 16 insertions(+), 5 deletions(-) diff --git a/indra/newview/llappearancelistener.cpp b/indra/newview/llappearancelistener.cpp index 3db2c64b78..b29eb48a1b 100644 --- a/indra/newview/llappearancelistener.cpp +++ b/indra/newview/llappearancelistener.cpp @@ -100,7 +100,7 @@ void LLAppearanceListener::wearOutfitByName(LLSD const &data) { Response response(LLSD(), data); std::string error_msg; - if (!LLAppearanceMgr::instance().wearOutfitByName(data["folder_name"].asString(), data["append"].asBoolean(), error_msg)) + if (!LLAppearanceMgr::instance().wearOutfitByName(data["folder_name"].asString(), error_msg, data["append"].asBoolean())) { response.error(error_msg); } @@ -147,7 +147,8 @@ void LLAppearanceListener::getOutfitItems(LLSD const &data) LLInventoryModel::cat_array_t cat_array; LLInventoryModel::item_array_t item_array; - gInventory.collectDescendentsIf(outfit_id, cat_array, item_array, LLInventoryModel::EXCLUDE_TRASH, LLFindOutfitItems()); + LLFindOutfitItems collector = LLFindOutfitItems(); + gInventory.collectDescendentsIf(outfit_id, cat_array, item_array, LLInventoryModel::EXCLUDE_TRASH, collector); LLSD items_data; for (const LLPointer &it : item_array) diff --git a/indra/newview/llappearancemgr.cpp b/indra/newview/llappearancemgr.cpp index 3d13f8afc8..e4a545a55b 100644 --- a/indra/newview/llappearancemgr.cpp +++ b/indra/newview/llappearancemgr.cpp @@ -2904,7 +2904,7 @@ void LLAppearanceMgr::wearInventoryCategoryOnAvatar( LLInventoryCategory* catego LLAppearanceMgr::changeOutfit(TRUE, category->getUUID(), append); } -bool LLAppearanceMgr::wearOutfitByName(const std::string& name, bool append, std::string& error_msg) +bool LLAppearanceMgr::wearOutfitByName(const std::string& name, std::string& error_msg, bool append) { LL_INFOS("Avatar") << self_av_string() << "Wearing category " << name << LL_ENDL; @@ -2950,7 +2950,6 @@ bool LLAppearanceMgr::wearOutfitByName(const std::string& name, bool append, std { std::string msg = append ? LLTrans::getString("OutfitNotAdded") : LLTrans::getString("OutfitNotReplaced"); error_msg = stringize(msg, std::quoted(name), ", id: ", cat->getUUID()); - LL_WARNS() << error_msg << LL_ENDL; return false; } LLAppearanceMgr::wearInventoryCategory(cat, copy_items, append); @@ -2958,6 +2957,16 @@ bool LLAppearanceMgr::wearOutfitByName(const std::string& name, bool append, std else { error_msg = stringize(LLTrans::getString("OutfitNotFound"), std::quoted(name)); + return false; + } + return true; +} + +bool LLAppearanceMgr::wearOutfitByName(const std::string& name, bool append) +{ + std::string error_msg; + if(!wearOutfitByName(name, error_msg, append)) + { LL_WARNS() << error_msg << LL_ENDL; return false; } diff --git a/indra/newview/llappearancemgr.h b/indra/newview/llappearancemgr.h index d7b6cd5a61..adc783be5a 100644 --- a/indra/newview/llappearancemgr.h +++ b/indra/newview/llappearancemgr.h @@ -59,7 +59,8 @@ public: void wearInventoryCategory(LLInventoryCategory* category, bool copy, bool append); void wearInventoryCategoryOnAvatar(LLInventoryCategory* category, bool append); void wearCategoryFinal(const LLUUID& cat_id, bool copy_items, bool append); - bool wearOutfitByName(const std::string &name, bool append = false, std::string &error_msg = std::string()); + bool wearOutfitByName(const std::string &name, std::string &error_msg, bool append = false); + bool wearOutfitByName(const std::string &name, bool append = false); void changeOutfit(bool proceed, const LLUUID& category, bool append); void replaceCurrentOutfit(const LLUUID& new_outfit); void renameOutfit(const LLUUID& outfit_id); -- cgit v1.2.3 From 212868a8c3f803b387da602b6440f69c2c617e40 Mon Sep 17 00:00:00 2001 From: Nat Goodspeed Date: Tue, 2 Jul 2024 10:35:34 -0400 Subject: Promote LuaRemover from llluamanager.cpp to lua_function.h. --- indra/llcommon/lua_function.h | 34 ++++++++++++++++++++++++++++++++-- indra/newview/llluamanager.cpp | 25 ------------------------- 2 files changed, 32 insertions(+), 27 deletions(-) diff --git a/indra/llcommon/lua_function.h b/indra/llcommon/lua_function.h index c32a586d79..b3b1f40ae5 100644 --- a/indra/llcommon/lua_function.h +++ b/indra/llcommon/lua_function.h @@ -118,11 +118,12 @@ private: * LuaPopper *****************************************************************************/ /** - * LuaPopper is an RAII struct whose role is to pop some number of entries + * LuaPopper is an RAII class whose role is to pop some number of entries * from the Lua stack if the calling function exits early. */ -struct LuaPopper +class LuaPopper { +public: LuaPopper(lua_State* L, int count): mState(L), mCount(count) @@ -136,10 +137,39 @@ struct LuaPopper void disarm() { set(0); } void set(int count) { mCount = count; } +private: lua_State* mState; int mCount; }; +/***************************************************************************** +* LuaRemover +*****************************************************************************/ +/** + * Remove a particular stack index on exit from enclosing scope. + * If you pass a negative index (meaning relative to the current stack top), + * converts to an absolute index. The point of LuaRemover is to remove the + * entry at the specified index regardless of subsequent pushes to the stack. + */ +class LuaRemover +{ +public: + LuaRemover(lua_State* L, int index): + mState(L), + mIndex(lua_absindex(L, index)) + {} + LuaRemover(const LuaRemover&) = delete; + LuaRemover& operator=(const LuaRemover&) = delete; + ~LuaRemover() + { + lua_remove(mState, mIndex); + } + +private: + lua_State* mState; + int mIndex; +}; + /***************************************************************************** * lua_function (and helper class LuaFunction) *****************************************************************************/ diff --git a/indra/newview/llluamanager.cpp b/indra/newview/llluamanager.cpp index 77f197b2d2..c95ce9c57b 100644 --- a/indra/newview/llluamanager.cpp +++ b/indra/newview/llluamanager.cpp @@ -309,31 +309,6 @@ LLRequireResolver::LLRequireResolver(lua_State *L, const std::string& path) : luaL_argerrorL(L, 1, "cannot require a full path"); } -/** - * Remove a particular stack index on exit from enclosing scope. - * If you pass a negative index (meaning relative to the current stack top), - * converts to an absolute index. The point of LuaRemover is to remove the - * entry at the specified index regardless of subsequent pushes to the stack. - */ -class LuaRemover -{ -public: - LuaRemover(lua_State* L, int index): - mState(L), - mIndex(lua_absindex(L, index)) - {} - LuaRemover(const LuaRemover&) = delete; - LuaRemover& operator=(const LuaRemover&) = delete; - ~LuaRemover() - { - lua_remove(mState, mIndex); - } - -private: - lua_State* mState; - int mIndex; -}; - // push the loaded module or throw a Lua error void LLRequireResolver::findModule() { -- cgit v1.2.3 From f8357732a108e579c285a76a53c550b8b5c0a153 Mon Sep 17 00:00:00 2001 From: Nat Goodspeed Date: Tue, 2 Jul 2024 12:09:59 -0400 Subject: Make require() implementation honor LuaRequirePath setting. Remove LL_TEST special case from require() code (to search in the viewer's source tree). Instead, make llluamanager_test.cpp append to LuaRequirePath to get the same effect. --- indra/newview/llluamanager.cpp | 18 +++++++---------- indra/newview/tests/llluamanager_test.cpp | 33 +++++++++++++++++++++++++++++++ 2 files changed, 40 insertions(+), 11 deletions(-) diff --git a/indra/newview/llluamanager.cpp b/indra/newview/llluamanager.cpp index c95ce9c57b..ccfa08078e 100644 --- a/indra/newview/llluamanager.cpp +++ b/indra/newview/llluamanager.cpp @@ -32,6 +32,7 @@ #include "llcoros.h" #include "llerror.h" #include "lleventcoro.h" +#include "llsdutil.h" #include "llviewercontrol.h" #include "lua_function.h" #include "lualistener.h" @@ -338,18 +339,13 @@ void LLRequireResolver::findModule() fail(); } - std::vector lib_paths + LLSD lib_paths(gSavedSettings.getLLSD("LuaRequirePath")); + LL_DEBUGS("Lua") << "LuaRequirePath = " << lib_paths << LL_ENDL; + for (const auto& path : llsd::inArray(lib_paths)) { - gDirUtilp->getExpandedFilename(LL_PATH_SCRIPTS, "lua", "require"), -#ifdef LL_TEST - // Build-time tests don't have the app bundle - use source tree. - fsyspath(__FILE__).parent_path() / "scripts" / "lua" / "require", -#endif - }; - - for (const auto& path : lib_paths) - { - std::string absolutePathOpt = (path / mPathToResolve).u8string(); + // if path is already absolute, operator/() preserves it + auto abspath(fsyspath(gDirUtilp->getAppRODataDir()) / path.asString()); + std::string absolutePathOpt = (abspath / mPathToResolve).u8string(); if (absolutePathOpt.empty()) luaL_error(L, "error requiring module '%s'", mPathToResolve.u8string().data()); diff --git a/indra/newview/tests/llluamanager_test.cpp b/indra/newview/tests/llluamanager_test.cpp index d3fc70dfd5..55e87acaea 100644 --- a/indra/newview/tests/llluamanager_test.cpp +++ b/indra/newview/tests/llluamanager_test.cpp @@ -21,6 +21,7 @@ #include "../llcommon/tests/StringVec.h" #include "../test/lltut.h" #include "llapp.h" +#include "llcontrol.h" #include "lldate.h" #include "llevents.h" #include "lleventcoro.h" @@ -39,6 +40,8 @@ public: bool frame() override { return true; } }; +LLControlGroup gSavedSettings("Global"); + template auto listener(CALLABLE&& callable) { @@ -57,6 +60,36 @@ namespace tut { struct llluamanager_data { + llluamanager_data() + { + // Load gSavedSettings from source tree + // indra/newview/tests/llluamanager_test.cpp => + // indra/newview + auto newview{ fsyspath(__FILE__).parent_path().parent_path() }; + auto settings{ newview / "app_settings" / "settings.xml" }; + // true suppresses implicit declare; implicit declare requires + // that every variable in settings.xml has a Comment, which many don't. + gSavedSettings.loadFromFile(settings.u8string().c_str(), true); + // At test time, since we don't have the app bundle available, + // extend LuaRequirePath to include the require directory in the + // source tree. + auto require{ (newview / "scripts" / "lua" / "require").u8string() }; + auto paths{ gSavedSettings.getLLSD("LuaRequirePath") }; + bool found = false; + for (const auto& path : llsd::inArray(paths)) + { + if (path.asString() == require) + { + found = true; + break; + } + } + if (! found) + { + paths.append(require); + gSavedSettings.setLLSD("LuaRequirePath", paths); + } + } // We need an LLApp instance because LLLUAmanager uses coroutines, // which suspend, and when a coroutine suspends it checks LLApp state, // and if it's not APP_STATUS_RUNNING the coroutine terminates. -- cgit v1.2.3 From 9a3c770a3bf430da8878a8691cee9b726a5f026c Mon Sep 17 00:00:00 2001 From: Nat Goodspeed Date: Tue, 2 Jul 2024 13:12:40 -0400 Subject: Eliminate c_str() calls from LLControlGroup::loadFromFile() calls. Passing std::string::c_str() to a (const std::string&) function parameter is worse than clutter, it's pointless overhead: it forces the compiler to construct a new std::string instance, instead of passing a const reference to the one you already have in hand. --- indra/llxml/tests/llcontrol_test.cpp | 12 ++++++------ indra/newview/tests/llluamanager_test.cpp | 2 +- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/indra/llxml/tests/llcontrol_test.cpp b/indra/llxml/tests/llcontrol_test.cpp index f5f8b285f7..595c6a600b 100644 --- a/indra/llxml/tests/llcontrol_test.cpp +++ b/indra/llxml/tests/llcontrol_test.cpp @@ -97,7 +97,7 @@ namespace tut template<> template<> void control_group_t::test<1>() { - int results = mCG->loadFromFile(mTestConfigFile.c_str()); + int results = mCG->loadFromFile(mTestConfigFile); ensure("number of settings", (results == 1)); ensure("value of setting", (mCG->getU32("TestSetting") == 12)); } @@ -106,14 +106,14 @@ namespace tut template<> template<> void control_group_t::test<2>() { - int results = mCG->loadFromFile(mTestConfigFile.c_str()); + int results = mCG->loadFromFile(mTestConfigFile); mCG->setU32("TestSetting", 13); ensure_equals("value of changed setting", mCG->getU32("TestSetting"), 13); LLControlGroup test_cg("foo2"); std::string temp_test_file = (mTestConfigDir + "setting_llsd_temp.xml"); mCleanups.push_back(temp_test_file); mCG->saveToFile(temp_test_file.c_str(), TRUE); - results = test_cg.loadFromFile(temp_test_file.c_str()); + results = test_cg.loadFromFile(temp_test_file); ensure("number of changed settings loaded", (results == 1)); ensure("value of changed settings loaded", (test_cg.getU32("TestSetting") == 13)); } @@ -126,7 +126,7 @@ namespace tut // a default settings file that declares variables, rather than a user // settings file. When loadFromFile() encounters an unrecognized user // settings variable, it forcibly preserves it (CHOP-962). - int results = mCG->loadFromFile(mTestConfigFile.c_str(), true); + int results = mCG->loadFromFile(mTestConfigFile, true); LLControlVariable* control = mCG->getControl("TestSetting"); LLSD new_value = 13; control->setValue(new_value, FALSE); @@ -135,7 +135,7 @@ namespace tut std::string temp_test_file = (mTestConfigDir + "setting_llsd_persist_temp.xml"); mCleanups.push_back(temp_test_file); mCG->saveToFile(temp_test_file.c_str(), TRUE); - results = test_cg.loadFromFile(temp_test_file.c_str()); + results = test_cg.loadFromFile(temp_test_file); //If we haven't changed any settings, then we shouldn't have any settings to load ensure("number of non-persisted changed settings loaded", (results == 0)); } @@ -144,7 +144,7 @@ namespace tut template<> template<> void control_group_t::test<4>() { - int results = mCG->loadFromFile(mTestConfigFile.c_str()); + int results = mCG->loadFromFile(mTestConfigFile); ensure("number of settings", (results == 1)); mCG->getControl("TestSetting")->getSignal()->connect(boost::bind(&this->handleListenerTest)); mCG->setU32("TestSetting", 13); diff --git a/indra/newview/tests/llluamanager_test.cpp b/indra/newview/tests/llluamanager_test.cpp index 55e87acaea..824ddd445b 100644 --- a/indra/newview/tests/llluamanager_test.cpp +++ b/indra/newview/tests/llluamanager_test.cpp @@ -69,7 +69,7 @@ namespace tut auto settings{ newview / "app_settings" / "settings.xml" }; // true suppresses implicit declare; implicit declare requires // that every variable in settings.xml has a Comment, which many don't. - gSavedSettings.loadFromFile(settings.u8string().c_str(), true); + gSavedSettings.loadFromFile(settings.u8string(), true); // At test time, since we don't have the app bundle available, // extend LuaRequirePath to include the require directory in the // source tree. -- cgit v1.2.3 From 66fb45ddc7dd1a994f6b8312687cb73dbb1281dd Mon Sep 17 00:00:00 2001 From: Nat Goodspeed Date: Wed, 3 Jul 2024 14:03:56 -0400 Subject: Add llsd::toArray() and llsd::toMap() utility functions. These encapsulate looping over a C++ iterable (be it a sequence container or an associative container) and returning an LLSD array or map, respectively, derived from the C++ container. By default, each C++ container item is directly converted to LLSD. Also make LLSDParam slightly more efficient by using std::vector::emplace_back() instead of push_back(), which supports std::vector, so we need not use std::shared_ptr. --- indra/llcommon/llsdutil.h | 73 +++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 64 insertions(+), 9 deletions(-) diff --git a/indra/llcommon/llsdutil.h b/indra/llcommon/llsdutil.h index aa497c53c7..7c31dc8aa0 100644 --- a/indra/llcommon/llsdutil.h +++ b/indra/llcommon/llsdutil.h @@ -365,15 +365,14 @@ private: // subject function has returned, so we must ensure that any constructed // LLSDParam lives just as long as this LLSDParam does. Putting // each LLSDParam on the heap and capturing a smart pointer in a vector - // works. We would have liked to use std::unique_ptr, but vector entries - // must be copyable. + // works. // (Alternatively we could assume that every instance of LLSDParam // will be asked for at most ONE conversion. We could store a scalar // std::unique_ptr and, when constructing an new LLSDParam, assert that // the unique_ptr is empty. But some future change in usage patterns, and // consequent failure of that assertion, would be very mysterious. Instead // of explaining how to fix it, just fix it now.) - mutable std::vector> converters_; + mutable std::vector> converters_; public: LLSDParam(const LLSD& value): value_(value) {} @@ -389,9 +388,9 @@ public: { // capture 'ptr' with the specific subclass type because converters_ // only stores LLSDParamBase pointers - auto ptr{ std::make_shared>>(value_) }; + auto ptr{ new LLSDParam>(value_) }; // keep the new converter alive until we ourselves are destroyed - converters_.push_back(ptr); + converters_.emplace_back(ptr); return *ptr; } }; @@ -474,12 +473,12 @@ public: } }; -namespace llsd -{ - /***************************************************************************** * range-based for-loop helpers for LLSD *****************************************************************************/ +namespace llsd +{ + /// Usage: for (LLSD item : inArray(someLLSDarray)) { ... } class inArray { @@ -525,7 +524,9 @@ private: } // namespace llsd - +/***************************************************************************** +* deep and shallow clone +*****************************************************************************/ // Creates a deep clone of an LLSD object. Maps, Arrays and binary objects // are duplicated, atomic primitives (Boolean, Integer, Real, etc) simply // use a shared reference. @@ -553,6 +554,60 @@ LLSD shallow(LLSD value, LLSD filter=LLSD()) { return llsd_shallow(value, filter } // namespace llsd +/***************************************************************************** +* toArray(), toMap() +*****************************************************************************/ +namespace llsd +{ + +// For some T convertible to LLSD, given std::vector myVec, +// toArray(myVec) returns an LLSD array whose entries correspond to the +// items in myVec. +// For some U convertible to LLSD, given function U xform(const T&), +// toArray(myVec, xform) returns an LLSD array whose every entry is +// xform(item) of the corresponding item in myVec. +// toArray() actually works with any container usable with range +// 'for', not just std::vector. +// (Once we get C++20 we can use std::identity instead of this default lambda.) +template +LLSD toArray(const C& container, FUNC&& func=[](const auto& arg){ return arg; }) +{ + LLSD array; + for (const auto& item : container) + { + array.append(std::forward(func)(item)); + } + return array; +} + +// For some T convertible to LLSD, given std::map myMap, +// toMap(myMap) returns an LLSD map whose entries correspond to the +// (key, value) pairs in myMap. +// For some U convertible to LLSD, given function +// std::pair xform(const std::pair&), +// toMap(myMap, xform) returns an LLSD map whose every entry is +// xform(pair) of the corresponding (key, value) pair in myMap. +// toMap() actually works with any container usable with range 'for', not +// just std::map. It need not even be an associative container, as long as +// you pass an xform function that returns std::pair. +// (Once we get C++20 we can use std::identity instead of this default lambda.) +template +LLSD toMap(const C& container, FUNC&& func=[](const auto& arg){ return arg; }) +{ + LLSD map; + for (const auto& pair : container) + { + const auto& [key, value] = std::forward(func)(pair); + map[key] = value; + } + return map; +} + +} // namespace llsd + +/***************************************************************************** +* boost::hash +*****************************************************************************/ // Specialization for generating a hash value from an LLSD block. namespace boost { -- cgit v1.2.3 From a877e3a0994a19d522e77d6781844341197dd6dc Mon Sep 17 00:00:00 2001 From: Nat Goodspeed Date: Wed, 3 Jul 2024 14:05:25 -0400 Subject: Use llsd::toMap() to return LLSD maps from "LLAppearance" listener. --- indra/newview/llappearancelistener.cpp | 31 +++++++++++++------------------ 1 file changed, 13 insertions(+), 18 deletions(-) diff --git a/indra/newview/llappearancelistener.cpp b/indra/newview/llappearancelistener.cpp index b29eb48a1b..2a3133433a 100644 --- a/indra/newview/llappearancelistener.cpp +++ b/indra/newview/llappearancelistener.cpp @@ -127,12 +127,9 @@ void LLAppearanceListener::getOutfitsList(LLSD const &data) LLIsType is_category(LLAssetType::AT_CATEGORY); gInventory.collectDescendentsIf(outfits_id, cat_array, item_array, LLInventoryModel::EXCLUDE_TRASH, is_category); - LLSD outfits_data; - for (const LLPointer &cat : cat_array) - { - outfits_data[cat->getUUID().asString()] = cat->getName(); - } - response["outfits"] = outfits_data; + response["outfits"] = llsd::toMap(cat_array, + [](const LLPointer &cat) + { return std::make_pair(cat->getUUID().asString(), cat->getName()); }); } void LLAppearanceListener::getOutfitItems(LLSD const &data) @@ -150,16 +147,14 @@ void LLAppearanceListener::getOutfitItems(LLSD const &data) LLFindOutfitItems collector = LLFindOutfitItems(); gInventory.collectDescendentsIf(outfit_id, cat_array, item_array, LLInventoryModel::EXCLUDE_TRASH, collector); - LLSD items_data; - for (const LLPointer &it : item_array) - { - LLSD info; - info["name"] = it->getName(); - info["wearable_type"] = LLWearableType::getInstance()->getTypeName(it->isWearableType() ? it->getWearableType() : LLWearableType::WT_NONE); - info["is_worn"] = get_is_item_worn(it); - - items_data[it->getUUID().asString()] = info; - } - - response["items"] = items_data; + response["items"] = llsd::toMap(item_array, + [](const LLPointer &it) + { + return std::make_pair( + it->getUUID().asString(), + llsd::map( + "name", it->getName(), + "wearable_type", LLWearableType::getInstance()->getTypeName(it->isWearableType() ? it->getWearableType() : LLWearableType::WT_NONE), + "is_worn", get_is_item_worn(it))); + }); } -- cgit v1.2.3 From f010ab9d28396894f7510b090b4e366cd3a72289 Mon Sep 17 00:00:00 2001 From: Andrey Kleshchev Date: Wed, 3 Jul 2024 13:00:32 +0300 Subject: viewer#1300 Small 'favorites' improvement --- indra/newview/llinventoryfunctions.cpp | 32 ++++++++++++++++++++++++++------ indra/newview/llinventoryfunctions.h | 1 + indra/newview/llpanelwearing.cpp | 2 +- indra/newview/llwearableitemslist.cpp | 2 +- 4 files changed, 29 insertions(+), 8 deletions(-) diff --git a/indra/newview/llinventoryfunctions.cpp b/indra/newview/llinventoryfunctions.cpp index ca27ea8b3f..eac5661847 100644 --- a/indra/newview/llinventoryfunctions.cpp +++ b/indra/newview/llinventoryfunctions.cpp @@ -2458,12 +2458,6 @@ private: void favorite_send(LLInventoryObject* obj, const LLUUID& obj_id, bool favorite) { - LLSD val; - if (favorite) - { - val = true; - } // else leave undefined to remove unneeded metadata field - LLSD updates; if (favorite) { @@ -2517,6 +2511,13 @@ void set_favorite(const LLUUID& obj_id, bool favorite) if (obj && obj->getIsLinkType()) { + if (!favorite && obj->getIsFavorite()) + { + // Links currently aren't supposed to be favorites, + // instead should show state of the original + LL_INFOS("Inventory") << "Recovering proper 'favorites' state of a link " << obj_id << LL_ENDL; + favorite_send(obj, obj_id, false); + } obj = gInventory.getObject(obj->getLinkedUUID()); } @@ -2540,6 +2541,25 @@ void toggle_favorite(const LLUUID& obj_id) } } +void toggle_favorites(const uuid_vec_t& ids) +{ + if (ids.size() == 0) + { + return; + } + if (ids.size() == 1) + { + toggle_favorite(ids[0]); + return; + } + + bool new_val = !get_is_favorite(ids.front()); + for (uuid_vec_t::const_iterator it = ids.begin(); it != ids.end(); ++it) + { + set_favorite(*it, new_val); + } +} + std::string get_searchable_description(LLInventoryModel* model, const LLUUID& item_id) { if (model) diff --git a/indra/newview/llinventoryfunctions.h b/indra/newview/llinventoryfunctions.h index fdb6fc77d7..f2fa7b3fe5 100644 --- a/indra/newview/llinventoryfunctions.h +++ b/indra/newview/llinventoryfunctions.h @@ -120,6 +120,7 @@ bool get_is_favorite(const LLInventoryObject* object); bool get_is_favorite(const LLUUID& obj_id); void set_favorite(const LLUUID& obj_id, bool favorite); void toggle_favorite(const LLUUID& obj_id); +void toggle_favorites(const uuid_vec_t& ids); std::string get_searchable_description(LLInventoryModel* model, const LLUUID& item_id); std::string get_searchable_creator_name(LLInventoryModel* model, const LLUUID& item_id); std::string get_searchable_UUID(LLInventoryModel* model, const LLUUID& item_id); diff --git a/indra/newview/llpanelwearing.cpp b/indra/newview/llpanelwearing.cpp index be8321e7bc..79b40e392a 100644 --- a/indra/newview/llpanelwearing.cpp +++ b/indra/newview/llpanelwearing.cpp @@ -113,7 +113,7 @@ protected: boost::bind(&LLAppearanceMgr::removeItemsFromAvatar, LLAppearanceMgr::getInstance(), mUUIDs, no_op)); registrar.add("Wearing.Detach", boost::bind(&LLAppearanceMgr::removeItemsFromAvatar, LLAppearanceMgr::getInstance(), mUUIDs, no_op)); - registrar.add("Wearing.Favorite", boost::bind(toggle_favorite, mUUIDs.front())); + registrar.add("Wearing.Favorite", boost::bind(toggle_favorites, mUUIDs)); LLContextMenu* menu = createFromFile("menu_wearing_tab.xml"); updateMenuItemsVisibility(menu); diff --git a/indra/newview/llwearableitemslist.cpp b/indra/newview/llwearableitemslist.cpp index 3ddd0c30ea..982e397748 100644 --- a/indra/newview/llwearableitemslist.cpp +++ b/indra/newview/llwearableitemslist.cpp @@ -936,7 +936,7 @@ LLContextMenu* LLWearableItemsList::ContextMenu::createMenu() // Register handlers for attachments. registrar.add("Attachment.Detach", boost::bind(&LLAppearanceMgr::removeItemsFromAvatar, LLAppearanceMgr::getInstance(), ids, no_op)); - registrar.add("Attachment.Favorite", boost::bind(toggle_favorite, selected_id)); + registrar.add("Attachment.Favorite", boost::bind(toggle_favorites, ids)); registrar.add("Attachment.Touch", boost::bind(handle_attachment_touch, selected_id)); registrar.add("Attachment.Profile", boost::bind(show_item_profile, selected_id)); registrar.add("Object.Attach", boost::bind(LLViewerAttachMenu::attachObjects, ids, _2)); -- cgit v1.2.3 From ece0f4eb566af937d724f60f934beb6dfcb4d493 Mon Sep 17 00:00:00 2001 From: Mnikolenko Productengine Date: Fri, 5 Jul 2024 16:03:51 +0300 Subject: clean up and rename demo script --- indra/newview/llappearancelistener.cpp | 93 +++++++++-------- indra/newview/llappearancelistener.h | 5 +- indra/newview/llappearancemgr.cpp | 28 ++++- indra/newview/llappearancemgr.h | 1 + indra/newview/llwearableitemslist.h | 3 +- indra/newview/scripts/lua/require/LLAppearance.lua | 28 ++--- indra/newview/scripts/lua/test_LLAppearance.lua | 114 +++++++++++++++++++++ indra/newview/scripts/lua/test_outfits_list.lua | 114 --------------------- 8 files changed, 205 insertions(+), 181 deletions(-) create mode 100644 indra/newview/scripts/lua/test_LLAppearance.lua delete mode 100644 indra/newview/scripts/lua/test_outfits_list.lua diff --git a/indra/newview/llappearancelistener.cpp b/indra/newview/llappearancelistener.cpp index 2a3133433a..75eaf29186 100644 --- a/indra/newview/llappearancelistener.cpp +++ b/indra/newview/llappearancelistener.cpp @@ -38,28 +38,20 @@ LLAppearanceListener::LLAppearanceListener() "API to wear a specified outfit and wear/remove individual items") { add("wearOutfit", - "Wear outfit by folder id: [folder_id]" + "Wear outfit by folder id: [folder_id] OR by folder name: [folder_name]\n" "When [\"append\"] is true, outfit will be added to COF\n" "otherwise it will replace current oufit", - &LLAppearanceListener::wearOutfit, - llsd::map("folder_id", LLSD(), "append", LLSD())); + &LLAppearanceListener::wearOutfit); - add("wearOutfitByName", - "Wear outfit by folder name: [folder_name]" - "When [\"append\"] is true, outfit will be added to COF\n" - "otherwise it will replace current oufit", - &LLAppearanceListener::wearOutfitByName, - llsd::map("folder_name", LLSD(), "append", LLSD())); - - add("wearItem", - "Wear item by item id: [item_id]", - &LLAppearanceListener::wearItem, - llsd::map("item_id", LLSD(), "replace", LLSD())); + add("wearItems", + "Wear items by id: [items_id]", + &LLAppearanceListener::wearItems, + llsd::map("items_id", LLSD(), "replace", LLSD())); - add("detachItem", - "Detach item by item id: [item_id]", - &LLAppearanceListener::detachItem, - llsd::map("item_id", LLSD())); + add("detachItems", + "Detach items by id: [items_id]", + &LLAppearanceListener::detachItems, + llsd::map("items_id", LLSD())); add("getOutfitsList", "Return the table with Outfits info(id and name)", @@ -74,46 +66,61 @@ LLAppearanceListener::LLAppearanceListener() void LLAppearanceListener::wearOutfit(LLSD const &data) { Response response(LLSD(), data); - LLViewerInventoryCategory* cat = gInventory.getCategory(data["folder_id"].asUUID()); - if (!cat) + if (!data.has("folder_id") && !data.has("folder_name")) { - response.error(stringize(LLTrans::getString("OutfitNotFound"), data["folder_id"].asUUID())); - return; + return response.error("Either [folder_id] or [folder_name] is required"); } - if (LLFolderType::lookupIsProtectedType(cat->getPreferredType())) + + std::string error_msg; + bool result(false); + bool append = data.has("append") ? data["append"].asBoolean() : false; + if (data.has("folder_id")) { - response.error(stringize(LLTrans::getString("SystemFolderNotWorn"), data["folder_id"].asUUID())); - return; + result = LLAppearanceMgr::instance().wearOutfit(data["folder_id"].asUUID(), error_msg, append); } - bool append = data["append"].asBoolean(); - bool can_wear = append ? LLAppearanceMgr::instance().getCanAddToCOF(cat->getUUID()) : LLAppearanceMgr::instance().getCanReplaceCOF(cat->getUUID()); - if (!can_wear) + else { - std::string msg = append ? LLTrans::getString("OutfitNotAdded") : LLTrans::getString("OutfitNotReplaced"); - response.error(stringize(msg, std::quoted(cat->getName()), " , id: ", cat->getUUID())); - return; + result = LLAppearanceMgr::instance().wearOutfitByName(data["folder_name"].asString(), error_msg, append); } - LLAppearanceMgr::instance().wearInventoryCategory(cat, false, append); -} -void LLAppearanceListener::wearOutfitByName(LLSD const &data) -{ - Response response(LLSD(), data); - std::string error_msg; - if (!LLAppearanceMgr::instance().wearOutfitByName(data["folder_name"].asString(), error_msg, data["append"].asBoolean())) + if (!result) { response.error(error_msg); } } -void LLAppearanceListener::wearItem(LLSD const &data) +void LLAppearanceListener::wearItems(LLSD const &data) { - LLAppearanceMgr::instance().wearItemOnAvatar(data["item_id"].asUUID(), true, data["replace"].asBoolean()); + if (data["items_id"].isArray()) + { + uuid_vec_t ids; + for (const auto &id : llsd::inArray(data["items_id"])) + { + ids.push_back(id); + } + LLAppearanceMgr::instance().wearItemsOnAvatar(ids, true, data["replace"].asBoolean()); + } + else + { + LLAppearanceMgr::instance().wearItemOnAvatar(data["items_id"].asUUID(), true, data["replace"].asBoolean()); + } } -void LLAppearanceListener::detachItem(LLSD const &data) +void LLAppearanceListener::detachItems(LLSD const &data) { - LLAppearanceMgr::instance().removeItemFromAvatar(data["item_id"].asUUID()); + if (data["items_id"].isArray()) + { + uuid_vec_t ids; + for (const auto &id : llsd::inArray(data["items_id"])) + { + ids.push_back(id); + } + LLAppearanceMgr::instance().removeItemsFromAvatar(ids); + } + else + { + LLAppearanceMgr::instance().removeItemFromAvatar(data["items_id"].asUUID()); + } } void LLAppearanceListener::getOutfitsList(LLSD const &data) @@ -139,7 +146,7 @@ void LLAppearanceListener::getOutfitItems(LLSD const &data) LLViewerInventoryCategory *cat = gInventory.getCategory(outfit_id); if (!cat || cat->getPreferredType() != LLFolderType::FT_OUTFIT) { - response.error(stringize(LLTrans::getString("OutfitNotFound"), outfit_id.asString())); + return response.error(stringize(LLTrans::getString("OutfitNotFound"), outfit_id.asString())); } LLInventoryModel::cat_array_t cat_array; LLInventoryModel::item_array_t item_array; diff --git a/indra/newview/llappearancelistener.h b/indra/newview/llappearancelistener.h index 00553c072f..04c5eac2eb 100644 --- a/indra/newview/llappearancelistener.h +++ b/indra/newview/llappearancelistener.h @@ -36,9 +36,8 @@ public: private: void wearOutfit(LLSD const &data); - void wearOutfitByName(LLSD const &data); - void wearItem(LLSD const &data); - void detachItem(LLSD const &data); + void wearItems(LLSD const &data); + void detachItems(LLSD const &data); void getOutfitsList(LLSD const &data); void getOutfitItems(LLSD const &data); }; diff --git a/indra/newview/llappearancemgr.cpp b/indra/newview/llappearancemgr.cpp index e4a545a55b..7a34006323 100644 --- a/indra/newview/llappearancemgr.cpp +++ b/indra/newview/llappearancemgr.cpp @@ -2939,8 +2939,8 @@ bool LLAppearanceMgr::wearOutfitByName(const std::string& name, std::string& err if(cat) { - bool is_system_folder = LLFolderType::lookupIsProtectedType(cat->getPreferredType()); - if (is_system_folder) + // don't allow wearing a system folder + if (LLFolderType::lookupIsProtectedType(cat->getPreferredType())) { error_msg = stringize(LLTrans::getString("SystemFolderNotWorn"), std::quoted(name)); return false; @@ -2962,6 +2962,30 @@ bool LLAppearanceMgr::wearOutfitByName(const std::string& name, std::string& err return true; } +bool LLAppearanceMgr::wearOutfit(const LLUUID &cat_id, std::string &error_msg, bool append) +{ + LLViewerInventoryCategory *cat = gInventory.getCategory(cat_id); + if (!cat) + { + error_msg = stringize(LLTrans::getString("OutfitNotFound"), cat_id); + return false; + } + if (LLFolderType::lookupIsProtectedType(cat->getPreferredType())) + { + error_msg = stringize(LLTrans::getString("SystemFolderNotWorn"), cat_id); + return false; + } + bool can_wear = append ? LLAppearanceMgr::instance().getCanAddToCOF(cat_id) : LLAppearanceMgr::instance().getCanReplaceCOF(cat_id); + if (!can_wear) + { + std::string msg = append ? LLTrans::getString("OutfitNotAdded") : LLTrans::getString("OutfitNotReplaced"); + error_msg = stringize(msg, std::quoted(cat->getName()), " , id: ", cat_id); + return false; + } + LLAppearanceMgr::instance().wearInventoryCategory(cat, false, append); + return true; +} + bool LLAppearanceMgr::wearOutfitByName(const std::string& name, bool append) { std::string error_msg; diff --git a/indra/newview/llappearancemgr.h b/indra/newview/llappearancemgr.h index adc783be5a..b795494f94 100644 --- a/indra/newview/llappearancemgr.h +++ b/indra/newview/llappearancemgr.h @@ -59,6 +59,7 @@ public: void wearInventoryCategory(LLInventoryCategory* category, bool copy, bool append); void wearInventoryCategoryOnAvatar(LLInventoryCategory* category, bool append); void wearCategoryFinal(const LLUUID& cat_id, bool copy_items, bool append); + bool wearOutfit(const LLUUID &cat_id, std::string &error_msg, bool append = false); bool wearOutfitByName(const std::string &name, std::string &error_msg, bool append = false); bool wearOutfitByName(const std::string &name, bool append = false); void changeOutfit(bool proceed, const LLUUID& category, bool append); diff --git a/indra/newview/llwearableitemslist.h b/indra/newview/llwearableitemslist.h index a24679961f..15033f5e9a 100644 --- a/indra/newview/llwearableitemslist.h +++ b/indra/newview/llwearableitemslist.h @@ -506,9 +506,8 @@ protected: LLWearableType::EType mMenuWearableType; }; -class LLFindOutfitItems : public LLInventoryCollectFunctor +struct LLFindOutfitItems : public LLInventoryCollectFunctor { - public: LLFindOutfitItems() {} virtual ~LLFindOutfitItems() {} virtual bool operator()(LLInventoryCategory *cat, LLInventoryItem *item); diff --git a/indra/newview/scripts/lua/require/LLAppearance.lua b/indra/newview/scripts/lua/require/LLAppearance.lua index 165bb6d06f..f533d22daf 100644 --- a/indra/newview/scripts/lua/require/LLAppearance.lua +++ b/indra/newview/scripts/lua/require/LLAppearance.lua @@ -1,29 +1,23 @@ -leap = require 'leap' +local leap = require 'leap' local LLAppearance = {} -function LLAppearance.addOutfit(folder) - leap.request('LLAppearance', {op='wearOutfit', append = true, folder_id=folder}) +function LLAppearance.wearOutfit(folder, action) + action = action or 'add' + leap.request('LLAppearance', {op='wearOutfit', append = (action == 'add'), folder_id=folder}) end -function LLAppearance.replaceOutfit(folder) - leap.request('LLAppearance', {op='wearOutfit', append = false, folder_id=folder}) +function LLAppearance.wearOutfitByName(folder, action) + action = action or 'add' + leap.request('LLAppearance', {op='wearOutfit', append = (action == 'add'), folder_name=folder}) end -function LLAppearance.addOutfitByName(folder) - leap.request('LLAppearance', {op='wearOutfitByName', append = true, folder_name=folder}) +function LLAppearance.wearItems(items_id, replace) + leap.send('LLAppearance', {op='wearItems', replace = replace, items_id=items_id}) end -function LLAppearance.replaceOutfitByName(folder) - leap.request('LLAppearance', {op='wearOutfitByName', append = false, folder_name=folder}) -end - -function LLAppearance.wearItem(item_id, replace) - leap.send('LLAppearance', {op='wearItem', replace = replace, item_id=item_id}) -end - -function LLAppearance.detachItem(item_id) - leap.send('LLAppearance', {op='detachItem', item_id=item_id}) +function LLAppearance.detachItems(items_id) + leap.send('LLAppearance', {op='detachItems', items_id=items_id}) end function LLAppearance.getOutfitsList() diff --git a/indra/newview/scripts/lua/test_LLAppearance.lua b/indra/newview/scripts/lua/test_LLAppearance.lua new file mode 100644 index 0000000000..5ddd9f15ff --- /dev/null +++ b/indra/newview/scripts/lua/test_LLAppearance.lua @@ -0,0 +1,114 @@ +local Floater = require 'Floater' +local LLAppearance = require 'LLAppearance' +local startup = require 'startup' +local inspect = require 'inspect' + +local SHOW_OUTFITS = true +local SELECTED_OUTFIT_ID = {} +local DATA_MAP = {} + +local wearables_lbl = 'Show wearables' +local outfits_lbl = 'Show outfits' +local replace_cof_lbl = 'Replace COF' +local add_cof_lbl = 'Add to COF' +local outfits_title = 'Outfits' +local wear_lbl = 'Wear item' +local detach_lbl = 'Detach item' + +local flt = Floater:new( + "luafloater_outfits_list.xml", + {outfits_list = {"double_click"}}) + +function get_selected_id() + return flt:request({action="get_selected_id", ctrl_name='outfits_list'}).value +end + +function populate_list() + if SHOW_OUTFITS then + DATA_MAP = LLAppearance.getOutfitsList() + else + DATA_MAP = LLAppearance.getOutfitItems(SELECTED_OUTFIT_ID) + end + + local action_data = {} + action_data.action = "add_list_element" + action_data.ctrl_name = "outfits_list" + local outfits = {} + for uuid, info in pairs(DATA_MAP) do + name = {} + if SHOW_OUTFITS then + name = info + else + name = info.name + end + table.insert(outfits, {value = uuid, columns={column = "outfit_name", value = name}}) + end + action_data.value = outfits + flt:post(action_data) +end + +function set_label(btn_name, value) + flt:post({action="set_label", ctrl_name=btn_name, value=value}) +end + +function set_enabled(btn_name, value) + flt:post({action="set_enabled", ctrl_name=btn_name, value=value}) +end + +function update_labels() + if SHOW_OUTFITS then + set_label('select_btn', wearables_lbl) + set_label('replace_btn', replace_cof_lbl) + set_label('add_btn', add_cof_lbl) + + set_enabled('select_btn', false) + flt:post({action="set_title", value=outfits_title}) + else + set_label('select_btn', outfits_lbl) + set_label('replace_btn', wear_lbl) + set_label('add_btn', detach_lbl) + + set_enabled('select_btn', true) + flt:post({action="set_title", value=DATA_MAP[SELECTED_OUTFIT_ID]}) + end + + set_enabled('replace_btn', false) + set_enabled('add_btn', false) +end + +function flt:post_build(event_data) + populate_list() +end + +function flt:commit_replace_btn(event_data) + if SHOW_OUTFITS then + LLAppearance.wearOutfit(get_selected_id(), 'replace') + else + LLAppearance.wearItems(get_selected_id(), false) + end +end + +function flt:commit_add_btn(event_data) + if SHOW_OUTFITS then + LLAppearance.wearOutfit(get_selected_id(), 'add') + else + LLAppearance.detachItems(get_selected_id()) + end +end + +function flt:commit_select_btn(event_data) + SHOW_OUTFITS = not SHOW_OUTFITS + SELECTED_OUTFIT_ID = get_selected_id() + update_labels() + self:post({action="clear_list", ctrl_name='outfits_list'}) + populate_list() +end + +function flt:commit_outfits_list(event_data) + set_enabled('replace_btn', true) + set_enabled('add_btn', true) + set_enabled('select_btn', true) +end + +startup.wait('STATE_STARTED') +flt:show() diff --git a/indra/newview/scripts/lua/test_outfits_list.lua b/indra/newview/scripts/lua/test_outfits_list.lua deleted file mode 100644 index 1011029f34..0000000000 --- a/indra/newview/scripts/lua/test_outfits_list.lua +++ /dev/null @@ -1,114 +0,0 @@ -local Floater = require 'Floater' -local LLAppearance = require 'LLAppearance' -local startup = require 'startup' -local inspect = require 'inspect' - -local SHOW_OUTFITS = true -local SELECTED_OUTFIT_ID = {} -local DATA_MAP = {} - -local wearables_lbl = 'Show wearables' -local outfits_lbl = 'Show outfits' -local replace_cof_lbl = 'Replace COF' -local add_cof_lbl = 'Add to COF' -local outfits_title = 'Outfits' -local wear_lbl = 'Wear item' -local detach_lbl = 'Detach item' - -local flt = Floater:new( - "luafloater_outfits_list.xml", - {outfits_list = {"double_click"}}) - -function get_selected_id() - return flt:request({action="get_selected_id", ctrl_name='outfits_list'}).value -end - -function populate_list() - if SHOW_OUTFITS then - DATA_MAP = LLAppearance.getOutfitsList() - else - DATA_MAP = LLAppearance.getOutfitItems(SELECTED_OUTFIT_ID) - end - - local action_data = {} - action_data.action = "add_list_element" - action_data.ctrl_name = "outfits_list" - local outfits = {} - for uuid, info in pairs(DATA_MAP) do - name = {} - if SHOW_OUTFITS then - name = info - else - name = info.name - end - table.insert(outfits, {value = uuid, columns={column = "outfit_name", value = name}}) - end - action_data.value = outfits - flt:post(action_data) -end - -function set_label(btn_name, value) - flt:post({action="set_label", ctrl_name=btn_name, value=value}) -end - -function set_enabled(btn_name, value) - flt:post({action="set_enabled", ctrl_name=btn_name, value=value}) -end - -function update_labels() - if SHOW_OUTFITS then - set_label('select_btn', wearables_lbl) - set_label('replace_btn', replace_cof_lbl) - set_label('add_btn', add_cof_lbl) - - set_enabled('select_btn', false) - flt:post({action="set_title", value=outfits_title}) - else - set_label('select_btn', outfits_lbl) - set_label('replace_btn', wear_lbl) - set_label('add_btn', detach_lbl) - - set_enabled('select_btn', true) - flt:post({action="set_title", value=DATA_MAP[SELECTED_OUTFIT_ID]}) - end - - set_enabled('replace_btn', false) - set_enabled('add_btn', false) -end - -function flt:post_build(event_data) - populate_list() -end - -function flt:commit_replace_btn(event_data) - if SHOW_OUTFITS then - LLAppearance.replaceOutfit(get_selected_id()) - else - LLAppearance.wearItem(get_selected_id(), false) - end -end - -function flt:commit_add_btn(event_data) - if SHOW_OUTFITS then - LLAppearance.addOutfit(get_selected_id()) - else - LLAppearance.detachItem(get_selected_id()) - end -end - -function flt:commit_select_btn(event_data) - SHOW_OUTFITS = not SHOW_OUTFITS - SELECTED_OUTFIT_ID = get_selected_id() - update_labels() - self:post({action="clear_list", ctrl_name='outfits_list'}) - populate_list() -end - -function flt:commit_outfits_list(event_data) - set_enabled('replace_btn', true) - set_enabled('add_btn', true) - set_enabled('select_btn', true) -end - -startup.wait('STATE_STARTED') -flt:show() -- cgit v1.2.3 From 826236f1bc065fba257d7954d11ac98c59493445 Mon Sep 17 00:00:00 2001 From: Andrey Kleshchev Date: Thu, 4 Jul 2024 17:31:26 +0300 Subject: SL-18721 Move window shutdown further down --- indra/newview/llappviewer.cpp | 60 +++++++++++++++++++++---------------------- 1 file changed, 30 insertions(+), 30 deletions(-) diff --git a/indra/newview/llappviewer.cpp b/indra/newview/llappviewer.cpp index 6168f09437..f36a292e9e 100644 --- a/indra/newview/llappviewer.cpp +++ b/indra/newview/llappviewer.cpp @@ -1882,36 +1882,6 @@ bool LLAppViewer::cleanup() // Clean up before GL is shut down because we might be holding on to objects with texture references LLSelectMgr::cleanupGlobals(); - LL_INFOS() << "Shutting down OpenGL" << LL_ENDL; - - // Shut down OpenGL - if( gViewerWindow) - { - gViewerWindow->shutdownGL(); - - // Destroy window, and make sure we're not fullscreen - // This may generate window reshape and activation events. - // Therefore must do this before destroying the message system. - delete gViewerWindow; - gViewerWindow = NULL; - LL_INFOS() << "ViewerWindow deleted" << LL_ENDL; - } - - LLSplashScreen::show(); - LLSplashScreen::update(LLTrans::getString("ShuttingDown")); - - LL_INFOS() << "Cleaning up Keyboard & Joystick" << LL_ENDL; - - // viewer UI relies on keyboard so keep it aound until viewer UI isa gone - delete gKeyboard; - gKeyboard = NULL; - - if (LLViewerJoystick::instanceExists()) - { - // Turn off Space Navigator and similar devices - LLViewerJoystick::getInstance()->terminate(); - } - LL_INFOS() << "Cleaning up Objects" << LL_ENDL; LLViewerObject::cleanupVOClasses(); @@ -2071,6 +2041,36 @@ bool LLAppViewer::cleanup() sTextureFetch->shutDownTextureCacheThread() ; LLLFSThread::sLocal->shutdown(); + ms_sleep(200); + LL_INFOS() << "Shutting down OpenGL" << LL_ENDL; + + // Shut down OpenGL + if (gViewerWindow) + { + gViewerWindow->shutdownGL(); + + // Destroy window, and make sure we're not fullscreen + // This may generate window reshape and activation events. + // Therefore must do this before destroying the message system. + delete gViewerWindow; + gViewerWindow = NULL; + LL_INFOS() << "ViewerWindow deleted" << LL_ENDL; + } + + LLSplashScreen::show(); + LLSplashScreen::update(LLTrans::getString("ShuttingDown")); + + LL_INFOS() << "Cleaning up Keyboard & Joystick" << LL_ENDL; + + // viewer UI relies on keyboard so keep it aound until viewer UI isa gone + delete gKeyboard; + gKeyboard = NULL; + + if (LLViewerJoystick::instanceExists()) + { + // Turn off Space Navigator and similar devices + LLViewerJoystick::getInstance()->terminate(); + } LL_INFOS() << "Shutting down message system" << LL_ENDL; end_messaging_system(); -- cgit v1.2.3 From a620e58daccf92b5b8d61347312739720ed2b51a Mon Sep 17 00:00:00 2001 From: Andrey Kleshchev Date: Thu, 4 Jul 2024 17:31:26 +0300 Subject: SL-18721 Don't 'post' window destruction, make thread do it automatically --- indra/llwindow/llwindowwin32.cpp | 70 ++++++++++++++++++++++------------------ indra/newview/llappviewer.cpp | 2 +- 2 files changed, 40 insertions(+), 32 deletions(-) diff --git a/indra/llwindow/llwindowwin32.cpp b/indra/llwindow/llwindowwin32.cpp index aea7367337..abf86e363c 100644 --- a/indra/llwindow/llwindowwin32.cpp +++ b/indra/llwindow/llwindowwin32.cpp @@ -352,7 +352,12 @@ struct LLWindowWin32::LLWindowWin32Thread : public LL::ThreadPool void run() override; - // closes queue, wakes thread, waits until thread closes + // Detroys handles and window + // Either post to or call from window thread + void destroyWindow(); + + // Closes queue, wakes thread, waits until thread closes. + // Call from main thread bool wakeAndDestroy(); void glReady() @@ -424,7 +429,7 @@ struct LLWindowWin32::LLWindowWin32Thread : public LL::ThreadPool // *HACK: Attempt to prevent startup crashes by deferring memory accounting // until after some graphics setup. See SL-20177. -Cosmic,2023-09-18 bool mGLReady = false; - bool mDeleteOnExit = false; + LLAtomicBool mDeleteOnExit = false; // best guess at available video memory in MB std::atomic mAvailableVRAM; @@ -873,6 +878,7 @@ LLWindowWin32::LLWindowWin32(LLWindowCallbacks* callbacks, LLWindowWin32::~LLWindowWin32() { delete mDragDrop; + mDragDrop = NULL; delete [] mWindowTitle; mWindowTitle = NULL; @@ -884,6 +890,7 @@ LLWindowWin32::~LLWindowWin32() mWindowClassName = NULL; delete mWindowThread; + mWindowThread = NULL; } void LLWindowWin32::show() @@ -4931,6 +4938,7 @@ void LLWindowWin32::LLWindowWin32Thread::run() #endif } + destroyWindow(); cleanupDX(); if (mDeleteOnExit) @@ -4939,6 +4947,35 @@ void LLWindowWin32::LLWindowWin32Thread::run() } } +void LLWindowWin32::LLWindowWin32Thread::destroyWindow() +{ + if (mWindowHandleThrd != NULL && IsWindow(mWindowHandleThrd)) + { + if (mhDCThrd) + { + if (!ReleaseDC(mWindowHandleThrd, mhDCThrd)) + { + LL_WARNS("Window") << "Release of ghDC failed!" << LL_ENDL; + } + mhDCThrd = NULL; + } + + // This causes WM_DESTROY to be sent *immediately* + if (!destroy_window_handler(mWindowHandleThrd)) + { + LL_WARNS("Window") << "Failed to destroy Window! " << std::hex << GetLastError() << LL_ENDL; + } + } + else + { + // Something killed the window while we were busy destroying gl or handle somehow got broken + LL_WARNS("Window") << "Failed to destroy Window, invalid handle!" << LL_ENDL; + } + mWindowHandleThrd = NULL; + mhDCThrd = NULL; + mGLReady = false; +} + bool LLWindowWin32::LLWindowWin32Thread::wakeAndDestroy() { if (mQueue->isClosed()) @@ -4954,35 +4991,6 @@ bool LLWindowWin32::LLWindowWin32Thread::wakeAndDestroy() // Schedule destruction HWND old_handle = mWindowHandleThrd; - post([this]() - { - if (IsWindow(mWindowHandleThrd)) - { - if (mhDCThrd) - { - if (!ReleaseDC(mWindowHandleThrd, mhDCThrd)) - { - LL_WARNS("Window") << "Release of ghDC failed!" << LL_ENDL; - } - mhDCThrd = NULL; - } - - // This causes WM_DESTROY to be sent *immediately* - if (!destroy_window_handler(mWindowHandleThrd)) - { - LL_WARNS("Window") << "Failed to destroy Window! " << std::hex << GetLastError() << LL_ENDL; - } - } - else - { - // Something killed the window while we were busy destroying gl or handle somehow got broken - LL_WARNS("Window") << "Failed to destroy Window, invalid handle!" << LL_ENDL; - } - mWindowHandleThrd = NULL; - mhDCThrd = NULL; - mGLReady = false; - }); - mDeleteOnExit = true; SetWindowLongPtr(old_handle, GWLP_USERDATA, NULL); diff --git a/indra/newview/llappviewer.cpp b/indra/newview/llappviewer.cpp index f36a292e9e..ab3cdfc5a4 100644 --- a/indra/newview/llappviewer.cpp +++ b/indra/newview/llappviewer.cpp @@ -2041,7 +2041,7 @@ bool LLAppViewer::cleanup() sTextureFetch->shutDownTextureCacheThread() ; LLLFSThread::sLocal->shutdown(); - ms_sleep(200); + LL_INFOS() << "Shutting down OpenGL" << LL_ENDL; // Shut down OpenGL -- cgit v1.2.3 From b27606feca00172cdfd7467ff3e216824f1c9518 Mon Sep 17 00:00:00 2001 From: Mnikolenko Productengine Date: Mon, 8 Jul 2024 13:56:46 +0300 Subject: Lua api for Snapshot and demo script --- indra/newview/llviewerwindow.cpp | 2 +- indra/newview/llviewerwindowlistener.cpp | 52 +++++++++++++++++++---------- indra/newview/scripts/lua/require/UI.lua | 15 +++++++++ indra/newview/scripts/lua/test_snapshot.lua | 15 +++++++++ 4 files changed, 65 insertions(+), 19 deletions(-) create mode 100644 indra/newview/scripts/lua/test_snapshot.lua diff --git a/indra/newview/llviewerwindow.cpp b/indra/newview/llviewerwindow.cpp index b637bcbdac..9f60f71d28 100644 --- a/indra/newview/llviewerwindow.cpp +++ b/indra/newview/llviewerwindow.cpp @@ -4840,7 +4840,7 @@ BOOL LLViewerWindow::saveSnapshot(const std::string& filepath, S32 image_width, LL_INFOS() << "Saving snapshot to: " << filepath << LL_ENDL; LLPointer raw = new LLImageRaw; - BOOL success = rawSnapshot(raw, image_width, image_height, TRUE, FALSE, show_ui, show_hud, do_rebuild); + BOOL success = rawSnapshot(raw, image_width, image_height, TRUE, FALSE, show_ui, show_hud, do_rebuild, 0, type); if (success) { diff --git a/indra/newview/llviewerwindowlistener.cpp b/indra/newview/llviewerwindowlistener.cpp index da7e18af5c..cba4fbc391 100644 --- a/indra/newview/llviewerwindowlistener.cpp +++ b/indra/newview/llviewerwindowlistener.cpp @@ -43,22 +43,13 @@ LLViewerWindowListener::LLViewerWindowListener(LLViewerWindow* llviewerwindow): mViewerWindow(llviewerwindow) { // add() every method we want to be able to invoke via this event API. - LLSD saveSnapshotArgs; - saveSnapshotArgs["filename"] = LLSD::String(); - saveSnapshotArgs["reply"] = LLSD::String(); - // The following are optional, so don't build them into required prototype. -// saveSnapshotArgs["width"] = LLSD::Integer(); -// saveSnapshotArgs["height"] = LLSD::Integer(); -// saveSnapshotArgs["showui"] = LLSD::Boolean(); -// saveSnapshotArgs["showhud"] = LLSD::Boolean(); -// saveSnapshotArgs["rebuild"] = LLSD::Boolean(); -// saveSnapshotArgs["type"] = LLSD::String(); add("saveSnapshot", - "Save screenshot: [\"filename\"], [\"width\"], [\"height\"], [\"showui\"], [\"showhud\"], [\"rebuild\"], [\"type\"]\n" + "Save screenshot: [\"filename\"] (extension may be specified: bmp, jpeg, png)\n" + "[\"width\"], [\"height\"], [\"showui\"], [\"showhud\"], [\"rebuild\"], [\"type\"]\n" "type: \"COLOR\", \"DEPTH\"\n" "Post on [\"reply\"] an event containing [\"ok\"]", &LLViewerWindowListener::saveSnapshot, - saveSnapshotArgs); + llsd::map("filename", LLSD(), "reply", LLSD())); add("requestReshape", "Resize the window: [\"w\"], [\"h\"]", &LLViewerWindowListener::requestReshape); @@ -66,12 +57,15 @@ LLViewerWindowListener::LLViewerWindowListener(LLViewerWindow* llviewerwindow): void LLViewerWindowListener::saveSnapshot(const LLSD& event) const { + Response response(LLSD(), event); + typedef std::map TypeMap; TypeMap types; #define tp(name) types[#name] = LLSnapshotModel::SNAPSHOT_TYPE_##name tp(COLOR); tp(DEPTH); -#undef tp +#undef tp + // Our add() call should ensure that the incoming LLSD does in fact // contain our required arguments. Deal with the optional ones. S32 width (mViewerWindow->getWindowWidthRaw()); @@ -94,14 +88,36 @@ void LLViewerWindowListener::saveSnapshot(const LLSD& event) const TypeMap::const_iterator found = types.find(event["type"]); if (found == types.end()) { - LL_ERRS("LLViewerWindowListener") << "LLViewerWindowListener::saveSnapshot(): " - << "unrecognized type " << event["type"] << LL_ENDL; - return; + return response.error(stringize("Unrecognized type ", std::quoted(event["type"].asString()), " [\"COLOR\"] or [\"DEPTH\"] is expected.")); } type = found->second; } - bool ok = mViewerWindow->saveSnapshot(event["filename"], width, height, showui, showhud, rebuild, type); - sendReply(LLSDMap("ok", ok), event); + + std::string filename(event["filename"]); + if (filename.empty()) + { + return response.error(stringize("File path is empty.")); + } + + LLSnapshotModel::ESnapshotFormat format(LLSnapshotModel::SNAPSHOT_FORMAT_BMP); + std::string ext = gDirUtilp->getExtension(filename); + if (ext.empty()) + { + filename.append(".bmp"); + } + else if (ext == "png") + { + format = LLSnapshotModel::SNAPSHOT_FORMAT_PNG; + } + else if (ext == "jpeg" || ext == "jpg") + { + format = LLSnapshotModel::SNAPSHOT_FORMAT_JPEG; + } + else if (ext != "bmp") + { + return response.error(stringize("Unrecognized format. [\"png\"], [\"jpeg\"] or [\"bmp\"] is expected.")); + } + response["result"] = mViewerWindow->saveSnapshot(filename, width, height, showui, showhud, rebuild, type, format); } void LLViewerWindowListener::requestReshape(LLSD const & event_data) const diff --git a/indra/newview/scripts/lua/require/UI.lua b/indra/newview/scripts/lua/require/UI.lua index eb1a4017c7..8fcc446e09 100644 --- a/indra/newview/scripts/lua/require/UI.lua +++ b/indra/newview/scripts/lua/require/UI.lua @@ -122,4 +122,19 @@ function UI.type(...) end end +-- *************************************************************************** +-- Snapshot +-- *************************************************************************** +-- UI.snapshot{filename=filename -- extension may be specified: bmp, jpeg, png +-- [, type='COLOR' | 'DEPTH'] +-- [, width=width][, height=height] -- uses current window size if not specified +-- [, showui=true][, showhud=true] +-- [, rebuild=false]} +function UI.snapshot(...) + local args = mapargs('filename,width,height,showui,showhud,rebuild,type', ...) + leap.request('LLViewerWindow', {op='saveSnapshot', filename = args.filename, + width=args.width, height=args.height, + showui=args.showui, showhud=args.showhud, + rebuild=args.rebuild, type=args.type}) +end return UI diff --git a/indra/newview/scripts/lua/test_snapshot.lua b/indra/newview/scripts/lua/test_snapshot.lua new file mode 100644 index 0000000000..d7c878833b --- /dev/null +++ b/indra/newview/scripts/lua/test_snapshot.lua @@ -0,0 +1,15 @@ +local UI = require 'UI' + +PATH = 'E:\\' +-- 'png', 'jpeg' or 'bmp' +EXT = '.png' + +NAME_SIMPLE = 'Snapshot_simple' .. '_' .. os.date("%Y-%m-%d_%H-%M-%S") +UI.snapshot(PATH .. NAME_SIMPLE .. EXT) + +NAME_ARGS = 'Snapshot_args' .. '_' .. os.date("%Y-%m-%d_%H-%M-%S") + +-- 'COLOR' or 'DEPTH' +TYPE = 'COLOR' +UI.snapshot{PATH .. NAME_ARGS .. EXT, width = 700, height = 400, + type = TYPE, showui = false, showhud = false} -- cgit v1.2.3 From 6f62e8b59191ee622591c91a55d02bb6c808e85a Mon Sep 17 00:00:00 2001 From: Nat Goodspeed Date: Mon, 8 Jul 2024 16:21:51 -0400 Subject: Quote "LLAppearance" op="wearOutfit" folder_id and folder_name args --- indra/newview/llappearancelistener.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/indra/newview/llappearancelistener.cpp b/indra/newview/llappearancelistener.cpp index 75eaf29186..b8aca96b43 100644 --- a/indra/newview/llappearancelistener.cpp +++ b/indra/newview/llappearancelistener.cpp @@ -38,7 +38,7 @@ LLAppearanceListener::LLAppearanceListener() "API to wear a specified outfit and wear/remove individual items") { add("wearOutfit", - "Wear outfit by folder id: [folder_id] OR by folder name: [folder_name]\n" + "Wear outfit by folder id: [\"folder_id\"] OR by folder name: [\"folder_name\"]\n" "When [\"append\"] is true, outfit will be added to COF\n" "otherwise it will replace current oufit", &LLAppearanceListener::wearOutfit); -- cgit v1.2.3 From 2918ebc081f63bffd5c482375f7fcbbb2faea175 Mon Sep 17 00:00:00 2001 From: Nat Goodspeed Date: Mon, 8 Jul 2024 16:23:29 -0400 Subject: Combine LLAppearanceMgr::wearOutfit() and wearOutfitByName() into new private wearOutfit(LLInventoryCategory*) method. --- indra/newview/llappearancemgr.cpp | 63 +++++++++++++++------------------------ indra/newview/llappearancemgr.h | 4 +++ 2 files changed, 28 insertions(+), 39 deletions(-) diff --git a/indra/newview/llappearancemgr.cpp b/indra/newview/llappearancemgr.cpp index 7a34006323..a8bc9d8e52 100644 --- a/indra/newview/llappearancemgr.cpp +++ b/indra/newview/llappearancemgr.cpp @@ -2904,6 +2904,17 @@ void LLAppearanceMgr::wearInventoryCategoryOnAvatar( LLInventoryCategory* catego LLAppearanceMgr::changeOutfit(TRUE, category->getUUID(), append); } +bool LLAppearanceMgr::wearOutfitByName(const std::string& name, bool append) +{ + std::string error_msg; + if(!wearOutfitByName(name, error_msg, append)) + { + LL_WARNS() << error_msg << LL_ENDL; + return false; + } + return true; +} + bool LLAppearanceMgr::wearOutfitByName(const std::string& name, std::string& error_msg, bool append) { LL_INFOS("Avatar") << self_av_string() << "Wearing category " << name << LL_ENDL; @@ -2937,63 +2948,37 @@ bool LLAppearanceMgr::wearOutfitByName(const std::string& name, std::string& err } } - if(cat) - { - // don't allow wearing a system folder - if (LLFolderType::lookupIsProtectedType(cat->getPreferredType())) - { - error_msg = stringize(LLTrans::getString("SystemFolderNotWorn"), std::quoted(name)); - return false; - } - bool can_wear = append ? getCanAddToCOF(cat->getUUID()) : getCanReplaceCOF(cat->getUUID()); - if (!can_wear) - { - std::string msg = append ? LLTrans::getString("OutfitNotAdded") : LLTrans::getString("OutfitNotReplaced"); - error_msg = stringize(msg, std::quoted(name), ", id: ", cat->getUUID()); - return false; - } - LLAppearanceMgr::wearInventoryCategory(cat, copy_items, append); - } - else - { - error_msg = stringize(LLTrans::getString("OutfitNotFound"), std::quoted(name)); - return false; - } - return true; + return wearOutfit(std::quoted(name), cat, error_msg, copy_items, append); } bool LLAppearanceMgr::wearOutfit(const LLUUID &cat_id, std::string &error_msg, bool append) { LLViewerInventoryCategory *cat = gInventory.getCategory(cat_id); + return wearOutfit(stringize(cat_id), cat, error_msg, false, append); +} + +bool LLAppearanceMgr::wearOutfit(const std::string &desc, LLInventoryCategory* cat, + std::string &error_msg, bool copy_items, bool append) +{ if (!cat) { - error_msg = stringize(LLTrans::getString("OutfitNotFound"), cat_id); + error_msg = stringize(LLTrans::getString("OutfitNotFound"), desc); return false; } + // don't allow wearing a system folder if (LLFolderType::lookupIsProtectedType(cat->getPreferredType())) { - error_msg = stringize(LLTrans::getString("SystemFolderNotWorn"), cat_id); + error_msg = stringize(LLTrans::getString("SystemFolderNotWorn"), std::quoted(cat->getName())); return false; } - bool can_wear = append ? LLAppearanceMgr::instance().getCanAddToCOF(cat_id) : LLAppearanceMgr::instance().getCanReplaceCOF(cat_id); + bool can_wear = append ? getCanAddToCOF(cat->getUUID()) : getCanReplaceCOF(cat->getUUID()); if (!can_wear) { std::string msg = append ? LLTrans::getString("OutfitNotAdded") : LLTrans::getString("OutfitNotReplaced"); - error_msg = stringize(msg, std::quoted(cat->getName()), " , id: ", cat_id); - return false; - } - LLAppearanceMgr::instance().wearInventoryCategory(cat, false, append); - return true; -} - -bool LLAppearanceMgr::wearOutfitByName(const std::string& name, bool append) -{ - std::string error_msg; - if(!wearOutfitByName(name, error_msg, append)) - { - LL_WARNS() << error_msg << LL_ENDL; + error_msg = stringize(msg, std::quoted(cat->getName()), " , id: ", cat->getUUID()); return false; } + wearInventoryCategory(cat, copy_items, append); return true; } diff --git a/indra/newview/llappearancemgr.h b/indra/newview/llappearancemgr.h index b795494f94..11d209e6b5 100644 --- a/indra/newview/llappearancemgr.h +++ b/indra/newview/llappearancemgr.h @@ -263,6 +263,10 @@ private: static void onOutfitRename(const LLSD& notification, const LLSD& response); + // used by both wearOutfit(LLUUID) and wearOutfitByName(std::string) + bool wearOutfit(const std::string &desc, LLInventoryCategory* cat, + std::string &error_msg, bool copy_items, bool append); + bool mAttachmentInvLinkEnabled; bool mOutfitIsDirty; bool mIsInUpdateAppearanceFromCOF; // to detect recursive calls. -- cgit v1.2.3 From 0e5cee5d6ddad76ccd37b5ed6bf8d692435bb7dc Mon Sep 17 00:00:00 2001 From: Nat Goodspeed Date: Mon, 8 Jul 2024 16:40:02 -0400 Subject: Slightly simplify LLAppearanceListener::wearItems(), detachItems(). --- indra/newview/llappearancelistener.cpp | 34 ++++++++++++++++++---------------- 1 file changed, 18 insertions(+), 16 deletions(-) diff --git a/indra/newview/llappearancelistener.cpp b/indra/newview/llappearancelistener.cpp index b8aca96b43..c39799c5e9 100644 --- a/indra/newview/llappearancelistener.cpp +++ b/indra/newview/llappearancelistener.cpp @@ -91,36 +91,38 @@ void LLAppearanceListener::wearOutfit(LLSD const &data) void LLAppearanceListener::wearItems(LLSD const &data) { - if (data["items_id"].isArray()) + const LLSD& items_id{ data["items_id"] }; + uuid_vec_t ids; + if (! items_id.isArray()) { - uuid_vec_t ids; - for (const auto &id : llsd::inArray(data["items_id"])) + ids.push_back(items_id.asUUID()); + } + else // array + { + for (const auto &id : llsd::inArray(items_id)) { ids.push_back(id); } - LLAppearanceMgr::instance().wearItemsOnAvatar(ids, true, data["replace"].asBoolean()); - } - else - { - LLAppearanceMgr::instance().wearItemOnAvatar(data["items_id"].asUUID(), true, data["replace"].asBoolean()); } + LLAppearanceMgr::instance().wearItemsOnAvatar(ids, true, data["replace"].asBoolean()); } void LLAppearanceListener::detachItems(LLSD const &data) { - if (data["items_id"].isArray()) + const LLSD& items_id{ data["items_id"] }; + uuid_vec_t ids; + if (! items_id.isArray()) { - uuid_vec_t ids; - for (const auto &id : llsd::inArray(data["items_id"])) + ids.push_back(items_id.asUUID()); + } + else // array + { + for (const auto &id : llsd::inArray(items_id)) { ids.push_back(id); } - LLAppearanceMgr::instance().removeItemsFromAvatar(ids); - } - else - { - LLAppearanceMgr::instance().removeItemFromAvatar(data["items_id"].asUUID()); } + LLAppearanceMgr::instance().removeItemsFromAvatar(ids); } void LLAppearanceListener::getOutfitsList(LLSD const &data) -- cgit v1.2.3 From b8c09ed80d076f44b08d6aaa59fffd98abd55811 Mon Sep 17 00:00:00 2001 From: Nat Goodspeed Date: Mon, 8 Jul 2024 17:18:48 -0400 Subject: The I/O manipulator std::quoted() must be passed to an ostream. --- indra/newview/llappearancemgr.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/indra/newview/llappearancemgr.cpp b/indra/newview/llappearancemgr.cpp index a8bc9d8e52..26c41b19ed 100644 --- a/indra/newview/llappearancemgr.cpp +++ b/indra/newview/llappearancemgr.cpp @@ -2948,7 +2948,7 @@ bool LLAppearanceMgr::wearOutfitByName(const std::string& name, std::string& err } } - return wearOutfit(std::quoted(name), cat, error_msg, copy_items, append); + return wearOutfit(stringize(std::quoted(name)), cat, error_msg, copy_items, append); } bool LLAppearanceMgr::wearOutfit(const LLUUID &cat_id, std::string &error_msg, bool append) -- cgit v1.2.3 From f009f8da6b140f02f6463a43d336ab2644782fa1 Mon Sep 17 00:00:00 2001 From: Nat Goodspeed Date: Tue, 9 Jul 2024 10:24:32 -0400 Subject: Introduce LLSDParam> and LLSDParam>. Use LLSDParam in LLAppearanceListener::wearItems() and detachItems() to build the vector of LLUUIDs from the passed LLSD array. --- indra/llcommon/llsdutil.h | 61 ++++++++++++++++++++++++++++++++++ indra/newview/llappearancelistener.cpp | 33 +++--------------- 2 files changed, 66 insertions(+), 28 deletions(-) diff --git a/indra/llcommon/llsdutil.h b/indra/llcommon/llsdutil.h index 7c31dc8aa0..3f59222e9b 100644 --- a/indra/llcommon/llsdutil.h +++ b/indra/llcommon/llsdutil.h @@ -524,6 +524,67 @@ private: } // namespace llsd +/***************************************************************************** +* LLSDParam> +*****************************************************************************/ +// Given an LLSD array, return a const std::vector&, where T is a type +// supported by LLSDParam. Bonus: if the LLSD value is actually a scalar, +// return a single-element vector containing the converted value. +template +class LLSDParam>: public LLSDParamBase +{ +public: + LLSDParam(const LLSD& array) + { + // treat undefined "array" as empty vector + if (array.isDefined()) + { + // what if it's a scalar? + if (! array.isArray()) + { + v.push_back(LLSDParam(array)); + } + else // really is an array + { + // reserve space for the array entries + v.reserve(array.size()); + for (const auto& item : llsd::inArray(array)) + { + v.push_back(LLSDParam(item)); + } + } + } + } + + operator const std::vector&() const { return v; } + +private: + std::vector v; +}; + +/***************************************************************************** +* LLSDParam> +*****************************************************************************/ +// Given an LLSD map, return a const std::map&, where T is a +// type supported by LLSDParam. +template +class LLSDParam>: public LLSDParamBase +{ +public: + LLSDParam(const LLSD& map) + { + for (const auto& pair : llsd::inMap(map)) + { + m[pair.first] = LLSDParam(pair.second); + } + } + + operator const std::map&() const { return m; } + +private: + std::map m; +}; + /***************************************************************************** * deep and shallow clone *****************************************************************************/ diff --git a/indra/newview/llappearancelistener.cpp b/indra/newview/llappearancelistener.cpp index c39799c5e9..0dab352311 100644 --- a/indra/newview/llappearancelistener.cpp +++ b/indra/newview/llappearancelistener.cpp @@ -91,38 +91,15 @@ void LLAppearanceListener::wearOutfit(LLSD const &data) void LLAppearanceListener::wearItems(LLSD const &data) { - const LLSD& items_id{ data["items_id"] }; - uuid_vec_t ids; - if (! items_id.isArray()) - { - ids.push_back(items_id.asUUID()); - } - else // array - { - for (const auto &id : llsd::inArray(items_id)) - { - ids.push_back(id); - } - } - LLAppearanceMgr::instance().wearItemsOnAvatar(ids, true, data["replace"].asBoolean()); + LLAppearanceMgr::instance().wearItemsOnAvatar( + LLSDParam(data["items_id"]), + true, data["replace"].asBoolean()); } void LLAppearanceListener::detachItems(LLSD const &data) { - const LLSD& items_id{ data["items_id"] }; - uuid_vec_t ids; - if (! items_id.isArray()) - { - ids.push_back(items_id.asUUID()); - } - else // array - { - for (const auto &id : llsd::inArray(items_id)) - { - ids.push_back(id); - } - } - LLAppearanceMgr::instance().removeItemsFromAvatar(ids); + LLAppearanceMgr::instance().removeItemsFromAvatar( + LLSDParam(data["items_id"])); } void LLAppearanceListener::getOutfitsList(LLSD const &data) -- cgit v1.2.3 From 98761798b9a118c46e5482b1f36fef1260c9c5f9 Mon Sep 17 00:00:00 2001 From: Mnikolenko Productengine Date: Wed, 10 Jul 2024 12:03:49 +0300 Subject: Simplify passing keys to leap.request --- indra/newview/llviewerwindowlistener.cpp | 4 ++-- indra/newview/scripts/lua/require/UI.lua | 6 ++---- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/indra/newview/llviewerwindowlistener.cpp b/indra/newview/llviewerwindowlistener.cpp index cba4fbc391..52f413792a 100644 --- a/indra/newview/llviewerwindowlistener.cpp +++ b/indra/newview/llviewerwindowlistener.cpp @@ -47,9 +47,9 @@ LLViewerWindowListener::LLViewerWindowListener(LLViewerWindow* llviewerwindow): "Save screenshot: [\"filename\"] (extension may be specified: bmp, jpeg, png)\n" "[\"width\"], [\"height\"], [\"showui\"], [\"showhud\"], [\"rebuild\"], [\"type\"]\n" "type: \"COLOR\", \"DEPTH\"\n" - "Post on [\"reply\"] an event containing [\"ok\"]", + "Post on [\"reply\"] an event containing [\"result\"]", &LLViewerWindowListener::saveSnapshot, - llsd::map("filename", LLSD(), "reply", LLSD())); + llsd::map("filename", LLSD::String(), "reply", LLSD())); add("requestReshape", "Resize the window: [\"w\"], [\"h\"]", &LLViewerWindowListener::requestReshape); diff --git a/indra/newview/scripts/lua/require/UI.lua b/indra/newview/scripts/lua/require/UI.lua index 8fcc446e09..1eee4657f4 100644 --- a/indra/newview/scripts/lua/require/UI.lua +++ b/indra/newview/scripts/lua/require/UI.lua @@ -132,9 +132,7 @@ end -- [, rebuild=false]} function UI.snapshot(...) local args = mapargs('filename,width,height,showui,showhud,rebuild,type', ...) - leap.request('LLViewerWindow', {op='saveSnapshot', filename = args.filename, - width=args.width, height=args.height, - showui=args.showui, showhud=args.showhud, - rebuild=args.rebuild, type=args.type}) + args.op = 'saveSnapshot' + return leap.request('LLViewerWindow', args).result end return UI -- cgit v1.2.3 From 8c94ff566a4f9076607d1b988f3eb7ad7e200bd9 Mon Sep 17 00:00:00 2001 From: Nat Goodspeed Date: Wed, 10 Jul 2024 15:14:13 -0400 Subject: Remove ability to reuse a LuaState between LLLUAmanager functions. Remove LLLUAmanager::mumbleScriptLine() LuaState& parameters. Make startScriptLine(), waitScriptLine() and runScriptLine() exactly parallel to startScriptFile(), waitScriptFile() and runScriptFile(). That means that runScriptLine()'s C++ coroutine instantiates and destroys its own LuaState, which means that LL.atexit() functions will run on the Lua-specific C++ coroutine rather than (say) the viewer's main coroutine. Introduce LLLUAmanager::script_result typedef for std::pair and use in method returns. Remove LuaState::initLuaState(); move its logic back into the constructor. Remove initLuaState() calls in the expr() error cases: they're moot now that we won't get subsequent expr() calls on the same LuaState instance. Remove LLFloaterLUADebug "Use clean lua_State" checkbox and the cleanLuaState() method. Remove mState member. Remove explicit LuaState declarations from LLLUAmanager tests. Adapt one test for implicit LuaState: it was directly calling LuaState::obtainListener() to discover the LuaListener's reply-pump name. But since that test also captures two leap.request() calls from the Lua script, it can just look at the "reply" key in either of those requests. --- indra/llcommon/lua_function.cpp | 18 ++------ indra/llcommon/lua_function.h | 2 - indra/newview/llfloaterluadebug.cpp | 13 +----- indra/newview/llfloaterluadebug.h | 2 - indra/newview/llluamanager.cpp | 48 +++++++++------------- indra/newview/llluamanager.h | 20 ++++----- .../skins/default/xui/en/floater_lua_debug.xml | 9 ---- indra/newview/tests/llluamanager_test.cpp | 34 ++++++--------- 8 files changed, 47 insertions(+), 99 deletions(-) diff --git a/indra/llcommon/lua_function.cpp b/indra/llcommon/lua_function.cpp index 2d08de68c5..4acb09d564 100644 --- a/indra/llcommon/lua_function.cpp +++ b/indra/llcommon/lua_function.cpp @@ -478,19 +478,11 @@ void lua_pushllsd(lua_State* L, const LLSD& data) *****************************************************************************/ LuaState::LuaState(script_finished_fn cb): mCallback(cb), - mState(nullptr) + mState(luaL_newstate()) { - initLuaState(); -} - -void LuaState::initLuaState() -{ - if (mState) - { - lua_close(mState); - } - mState = luaL_newstate(); luaL_openlibs(mState); + // publish to this new lua_State all the LL entry points we defined using + // the lua_function() macro LuaFunction::init(mState); // Try to make print() write to our log. lua_register(mState, "print", LuaFunction::get("print_info")); @@ -607,8 +599,7 @@ std::pair LuaState::expr(const std::string& desc, const std::string& // we instead of the Lua runtime catch it, our lua_State retains // its internal error status. Any subsequent lua_pcall() calls // with this lua_State will report error regardless of whether the - // chunk runs successfully. Get a new lua_State(). - initLuaState(); + // chunk runs successfully. return { -1, stringize(LLError::Log::classname(error), ": ", error.what()) }; } } @@ -628,7 +619,6 @@ std::pair LuaState::expr(const std::string& desc, const std::string& LL_WARNS("Lua") << desc << " error converting result " << index << ": " << error.what() << LL_ENDL; // see above comments regarding lua_State's error status - initLuaState(); return { -1, stringize(LLError::Log::classname(error), ": ", error.what()) }; } } diff --git a/indra/llcommon/lua_function.h b/indra/llcommon/lua_function.h index b3b1f40ae5..7f7a7566f3 100644 --- a/indra/llcommon/lua_function.h +++ b/indra/llcommon/lua_function.h @@ -87,8 +87,6 @@ public: ~LuaState(); - void initLuaState(); - bool checkLua(const std::string& desc, int r); // expr() is for when we want to capture any results left on the stack diff --git a/indra/newview/llfloaterluadebug.cpp b/indra/newview/llfloaterluadebug.cpp index f715327ec8..9981126e4f 100644 --- a/indra/newview/llfloaterluadebug.cpp +++ b/indra/newview/llfloaterluadebug.cpp @@ -27,7 +27,6 @@ #include "llfloaterluadebug.h" -#include "llcheckboxctrl.h" #include "lllineeditor.h" #include "lltexteditor.h" #include "llviewermenufile.h" // LLFilePickerReplyThread @@ -92,8 +91,7 @@ void LLFloaterLUADebug::onExecuteClicked() mResultOutput->setValue(""); std::string cmd = mLineInput->getText(); - cleanLuaState(); - LLLUAmanager::runScriptLine(mState, cmd, [this](int count, const LLSD& result) + LLLUAmanager::runScriptLine(cmd, [this](int count, const LLSD& result) { completion(count, result); }); @@ -174,12 +172,3 @@ void LLFloaterLUADebug::completion(int count, const LLSD& result) sep = ", "; } } - -void LLFloaterLUADebug::cleanLuaState() -{ - if(getChild("clean_lua_state")->get()) - { - //Reinit to clean lua_State - mState.initLuaState(); - } -} diff --git a/indra/newview/llfloaterluadebug.h b/indra/newview/llfloaterluadebug.h index ae30b7cf25..e513d9a095 100644 --- a/indra/newview/llfloaterluadebug.h +++ b/indra/newview/llfloaterluadebug.h @@ -58,14 +58,12 @@ class LLFloaterLUADebug : private: void completion(int count, const LLSD& result); - void cleanLuaState(); LLTempBoundListener mOutConnection; LLTextEditor* mResultOutput; LLLineEditor* mLineInput; LLLineEditor* mScriptPath; - LuaState mState; U32 mAck{ 0 }; bool mExecuting{ false }; }; diff --git a/indra/newview/llluamanager.cpp b/indra/newview/llluamanager.cpp index ccfa08078e..7014c59e4e 100644 --- a/indra/newview/llluamanager.cpp +++ b/indra/newview/llluamanager.cpp @@ -162,24 +162,25 @@ lua_function(get_event_next, return 2; } -LLCoros::Future> +LLCoros::Future LLLUAmanager::startScriptFile(const std::string& filename) { // Despite returning from startScriptFile(), we need this Promise to // remain alive until the callback has fired. - auto promise{ std::make_shared>>() }; + auto promise{ std::make_shared>() }; runScriptFile(filename, [promise](int count, LLSD result) { promise->set_value({ count, result }); }); return LLCoros::getFuture(*promise); } -std::pair LLLUAmanager::waitScriptFile(const std::string& filename) +LLLUAmanager::script_result LLLUAmanager::waitScriptFile(const std::string& filename) { return startScriptFile(filename).get(); } -void LLLUAmanager::runScriptFile(const std::string &filename, script_result_fn result_cb, script_finished_fn finished_cb) +void LLLUAmanager::runScriptFile(const std::string &filename, script_result_fn result_cb, + script_finished_fn finished_cb) { // A script_result_fn will be called when LuaState::expr() completes. LLCoros::instance().launch(filename, [filename, result_cb, finished_cb]() @@ -212,39 +213,25 @@ void LLLUAmanager::runScriptFile(const std::string &filename, script_result_fn r }); } -void LLLUAmanager::runScriptLine(const std::string& chunk, script_finished_fn cb) -{ - // A script_finished_fn is used to initialize the LuaState. - // It will be called when the LuaState is destroyed. - LuaState L(cb); - runScriptLine(L, chunk); -} - -void LLLUAmanager::runScriptLine(const std::string& chunk, script_result_fn cb) -{ - LuaState L; - // A script_result_fn will be called when LuaState::expr() completes. - runScriptLine(L, chunk, cb); -} - -LLCoros::Future> -LLLUAmanager::startScriptLine(LuaState& L, const std::string& chunk) +LLCoros::Future +LLLUAmanager::startScriptLine(const std::string& chunk) { // Despite returning from startScriptLine(), we need this Promise to // remain alive until the callback has fired. - auto promise{ std::make_shared>>() }; - runScriptLine(L, chunk, + auto promise{ std::make_shared>() }; + runScriptLine(chunk, [promise](int count, LLSD result) { promise->set_value({ count, result }); }); return LLCoros::getFuture(*promise); } -std::pair LLLUAmanager::waitScriptLine(LuaState& L, const std::string& chunk) +LLLUAmanager::script_result LLLUAmanager::waitScriptLine(const std::string& chunk) { - return startScriptLine(L, chunk).get(); + return startScriptLine(chunk).get(); } -void LLLUAmanager::runScriptLine(LuaState& L, const std::string& chunk, script_result_fn cb) +void LLLUAmanager::runScriptLine(const std::string& chunk, script_result_fn result_cb, + script_finished_fn finished_cb) { // find a suitable abbreviation for the chunk string std::string shortchunk{ chunk }; @@ -256,12 +243,15 @@ void LLLUAmanager::runScriptLine(LuaState& L, const std::string& chunk, script_r shortchunk = stringize(shortchunk.substr(0, shortlen), "..."); std::string desc{ "lua: " + shortchunk }; - LLCoros::instance().launch(desc, [&L, desc, chunk, cb]() + LLCoros::instance().launch(desc, [desc, chunk, result_cb, finished_cb]() { + // A script_finished_fn is used to initialize the LuaState. + // It will be called when the LuaState is destroyed. + LuaState L(finished_cb); auto [count, result] = L.expr(desc, chunk); - if (cb) + if (result_cb) { - cb(count, result); + result_cb(count, result); } }); } diff --git a/indra/newview/llluamanager.h b/indra/newview/llluamanager.h index dcbb91f799..50f922a80f 100644 --- a/indra/newview/llluamanager.h +++ b/indra/newview/llluamanager.h @@ -55,32 +55,32 @@ public: // count > 1 with result.isArray() means the script returned multiple // results, represented as the entries of the result array. typedef std::function script_result_fn; + // same semantics as script_result_fn parameters + typedef std::pair script_result; - static void runScriptFile(const std::string &filename, script_result_fn result_cb = {}, script_finished_fn finished_cb = {}); + static void runScriptFile(const std::string &filename, script_result_fn result_cb = {}, + script_finished_fn finished_cb = {}); // Start running a Lua script file, returning an LLCoros::Future whose // get() method will pause the calling coroutine until it can deliver the // (count, result) pair described above. Between startScriptFile() and // Future::get(), the caller and the Lua script coroutine will run // concurrently. - static LLCoros::Future> - startScriptFile(const std::string& filename); + static LLCoros::Future startScriptFile(const std::string& filename); // Run a Lua script file, and pause the calling coroutine until it completes. // The return value is the (count, result) pair described above. - static std::pair waitScriptFile(const std::string& filename); + static script_result waitScriptFile(const std::string& filename); - static void runScriptLine(const std::string &chunk, script_finished_fn cb = {}); - static void runScriptLine(const std::string &chunk, script_result_fn cb); - static void runScriptLine(LuaState& L, const std::string &chunk, script_result_fn cb = {}); + static void runScriptLine(const std::string &chunk, script_result_fn result_cb = {}, + script_finished_fn finished_cb = {}); // Start running a Lua chunk, returning an LLCoros::Future whose // get() method will pause the calling coroutine until it can deliver the // (count, result) pair described above. Between startScriptLine() and // Future::get(), the caller and the Lua script coroutine will run // concurrently. - static LLCoros::Future> - startScriptLine(LuaState& L, const std::string& chunk); + static LLCoros::Future startScriptLine(const std::string& chunk); // Run a Lua chunk, and pause the calling coroutine until it completes. // The return value is the (count, result) pair described above. - static std::pair waitScriptLine(LuaState& L, const std::string& chunk); + static script_result waitScriptLine(const std::string& chunk); static const std::map getScriptNames() { return sScriptNames; } diff --git a/indra/newview/skins/default/xui/en/floater_lua_debug.xml b/indra/newview/skins/default/xui/en/floater_lua_debug.xml index 012ea6f254..15027f1647 100644 --- a/indra/newview/skins/default/xui/en/floater_lua_debug.xml +++ b/indra/newview/skins/default/xui/en/floater_lua_debug.xml @@ -25,15 +25,6 @@ width="100"> LUA string: - () { set_test_name("test Lua results"); - LuaState L; for (auto& luax : lua_expressions) { auto [count, result] = - LLLUAmanager::waitScriptLine(L, "return " + luax.expr); + LLLUAmanager::waitScriptLine("return " + luax.expr); auto desc{ stringize("waitScriptLine(", luax.desc, "): ") }; // if count < 0, report Lua error message ensure_equals(desc + result.asString(), count, 1); @@ -144,8 +143,7 @@ namespace tut "data = ", construct, "\n" "LL.post_on('testpump', data)\n" )); - LuaState L; - auto [count, result] = LLLUAmanager::waitScriptLine(L, lua); + auto [count, result] = LLLUAmanager::waitScriptLine(lua); // We woke up again ourselves because the coroutine running Lua has // finished. But our Lua chunk didn't actually return anything, so we // expect count to be 0 and result to be undefined. @@ -182,11 +180,10 @@ namespace tut "LL.post_on('testpump', data)\n" "LL.post_on('testpump', 'exit')\n" ); - LuaState L; // It's important to let the startScriptLine() coroutine run // concurrently with ours until we've had a chance to post() our // reply. - auto future = LLLUAmanager::startScriptLine(L, lua); + auto future = LLLUAmanager::startScriptLine(lua); StringVec expected{ "entry", "get_event_pumps()", @@ -217,8 +214,7 @@ namespace tut "pump, data = LL.get_event_next()\n" "return data\n" ); - LuaState L; - auto future = LLLUAmanager::startScriptLine(L, lua); + auto future = LLLUAmanager::startScriptLine(lua); // We woke up again ourselves because the coroutine running Lua has // reached the get_event_next() call, which suspends the calling C++ // coroutine (including the Lua code running on it) until we post @@ -356,8 +352,7 @@ namespace tut sendReply(data, data); })); - LuaState L; - auto [count, result] = LLLUAmanager::waitScriptLine(L, lua); + auto [count, result] = LLLUAmanager::waitScriptLine(lua); ensure_equals("Lua script didn't return item", count, 1); ensure_equals("echo failed", result, llsd::map("a", "a", "b", "b")); } @@ -371,18 +366,18 @@ namespace tut "\n" "fiber = require('fiber')\n" "leap = require('leap')\n" - "-- debug = require('printf')\n" "local function debug(...) end\n" + "-- debug = require('printf')\n" "\n" "-- negative priority ensures catchall is always last\n" - "catchall = leap.WaitFor:new(-1, 'catchall')\n" + "catchall = leap.WaitFor(-1, 'catchall')\n" "function catchall:filter(pump, data)\n" " debug('catchall:filter(%s, %s)', pump, data)\n" " return data\n" "end\n" "\n" "-- but first, catch events with 'special' key\n" - "catch_special = leap.WaitFor:new(2, 'catch_special')\n" + "catch_special = leap.WaitFor(2, 'catch_special')\n" "function catch_special:filter(pump, data)\n" " debug('catch_special:filter(%s, %s)', pump, data)\n" " return if data['special'] ~= nil then data else nil\n" @@ -429,10 +424,7 @@ namespace tut requests.append(data); })); - LuaState L; - auto future = LLLUAmanager::startScriptLine(L, lua); - auto replyname{ L.obtainListener().getReplyName() }; - auto& replypump{ LLEventPumps::instance().obtain(replyname) }; + auto future = LLLUAmanager::startScriptLine(lua); // LuaState::expr() periodically interrupts a running chunk to ensure // the rest of our coroutines get cycles. Nonetheless, for this test // we have to wait until both requester() coroutines have posted and @@ -444,6 +436,8 @@ namespace tut llcoro::suspend(); } ensure_equals("didn't get both requests", requests.size(), 2); + auto replyname{ requests[0]["reply"].asString() }; + auto& replypump{ LLEventPumps::instance().obtain(replyname) }; // moreover, we expect they arrived in the order they were created ensure_equals("a wasn't first", requests[0]["name"].asString(), "a"); ensure_equals("b wasn't second", requests[1]["name"].asString(), "b"); @@ -468,8 +462,7 @@ namespace tut "\n" "LL.get_event_next()\n" ); - LuaState L; - auto future = LLLUAmanager::startScriptLine(L, lua); + auto future = LLLUAmanager::startScriptLine(lua); // Poke LLTestApp to send its preliminary shutdown message. mApp.setQuitting(); // but now we have to give the startScriptLine() coroutine a chance to run @@ -491,8 +484,7 @@ namespace tut " x = 1\n" "end\n" ); - LuaState L; - auto [count, result] = LLLUAmanager::waitScriptLine(L, lua); + auto [count, result] = LLLUAmanager::waitScriptLine(lua); // We expect the above erroneous script has been forcibly terminated // because it ran too long without doing any actual work. ensure_equals(desc + " count: " + result.asString(), count, -1); -- cgit v1.2.3 From e84e65e88f9953fbc7f88408c9c2821f97f4dee6 Mon Sep 17 00:00:00 2001 From: Mnikolenko Productengine Date: Fri, 12 Jul 2024 15:23:57 +0300 Subject: Show description and actual value of LLSD type setting --- indra/newview/llfloatersettingsdebug.cpp | 14 ++++++++++++++ indra/newview/llfloatersettingsdebug.h | 1 + .../skins/default/xui/en/floater_settings_debug.xml | 13 ++++++++++++- 3 files changed, 27 insertions(+), 1 deletion(-) diff --git a/indra/newview/llfloatersettingsdebug.cpp b/indra/newview/llfloatersettingsdebug.cpp index 1c10db4e0d..a494b715a2 100644 --- a/indra/newview/llfloatersettingsdebug.cpp +++ b/indra/newview/llfloatersettingsdebug.cpp @@ -35,6 +35,7 @@ #include "llviewercontrol.h" #include "lltexteditor.h" #include "llclipboard.h" +#include "llsdutil.h" LLFloaterSettingsDebug::LLFloaterSettingsDebug(const LLSD& key) @@ -54,6 +55,7 @@ BOOL LLFloaterSettingsDebug::postBuild() mComment = getChild("comment_text"); mSettingName = getChild("setting_name_txt"); + mLLSDVal = getChild("llsd_text"); mCopyBtn = getChild("copy_btn"); getChild("filter_input")->setCommitCallback(boost::bind(&LLFloaterSettingsDebug::setSearchFilter, this, _2)); @@ -472,6 +474,17 @@ void LLFloaterSettingsDebug::updateControl(LLControlVariable* controlp) color_swatch->setValue(sd); break; } + case TYPE_LLSD: + { + mLLSDVal->setVisible(true); + std::string new_text = ll_pretty_print_sd(sd); + // Don't setText if not nessesary, it will reset scroll + if (mLLSDVal->getText() != new_text) + { + mLLSDVal->setText(new_text); + } + break; + } default: mComment->setText(std::string("unknown")); break; @@ -638,6 +651,7 @@ void LLFloaterSettingsDebug::hideUIControls() getChildView("val_text")->setVisible(false); getChildView("default_btn")->setVisible(false); getChildView("boolean_combo")->setVisible(false); + mLLSDVal->setVisible(false); mSettingName->setVisible(false); mCopyBtn->setVisible(false); mComment->setVisible(false); diff --git a/indra/newview/llfloatersettingsdebug.h b/indra/newview/llfloatersettingsdebug.h index e52d5ac863..20aa9bb159 100644 --- a/indra/newview/llfloatersettingsdebug.h +++ b/indra/newview/llfloatersettingsdebug.h @@ -69,6 +69,7 @@ private: protected: class LLTextEditor* mComment; + LLTextEditor* mLLSDVal; LLTextBox* mSettingName; LLButton* mCopyBtn; diff --git a/indra/newview/skins/default/xui/en/floater_settings_debug.xml b/indra/newview/skins/default/xui/en/floater_settings_debug.xml index 6fd8f2b255..0b8190df7e 100644 --- a/indra/newview/skins/default/xui/en/floater_settings_debug.xml +++ b/indra/newview/skins/default/xui/en/floater_settings_debug.xml @@ -69,7 +69,7 @@ visible="false" use_ellipses="true" text_color="White" - width="240"> + width="225"> Debug setting name + -- cgit v1.2.3 From c9a8de49df04c55b94eee0fe5fb1eb628701cdcb Mon Sep 17 00:00:00 2001 From: Mnikolenko Productengine Date: Fri, 12 Jul 2024 15:38:19 +0300 Subject: fix for 'Run' button --- indra/newview/llfloaterluadebug.cpp | 6 ------ 1 file changed, 6 deletions(-) diff --git a/indra/newview/llfloaterluadebug.cpp b/indra/newview/llfloaterluadebug.cpp index 9981126e4f..ef24481464 100644 --- a/indra/newview/llfloaterluadebug.cpp +++ b/indra/newview/llfloaterluadebug.cpp @@ -104,12 +104,6 @@ void LLFloaterLUADebug::onBtnBrowse() void LLFloaterLUADebug::onBtnRun() { - if (mExecuting) - { - LL_DEBUGS("Lua") << "recursive call to onBtnRun()" << LL_ENDL; - return; - } - TempSet executing(mExecuting, true); std::vector filenames; std::string filepath = mScriptPath->getText(); if (!filepath.empty()) -- cgit v1.2.3 From 50d60c2518710e92cff05b806624b11ac714369f Mon Sep 17 00:00:00 2001 From: Maxim Nikolenko Date: Wed, 17 Jul 2024 16:46:00 +0300 Subject: Lua api for adding new menu items to the Top menu --- indra/newview/lluilistener.cpp | 104 ++++++++++++++++++++++++++++ indra/newview/lluilistener.h | 9 ++- indra/newview/scripts/lua/require/UI.lua | 28 ++++++++ indra/newview/scripts/lua/test_top_menu.lua | 34 +++++++++ 4 files changed, 172 insertions(+), 3 deletions(-) create mode 100644 indra/newview/scripts/lua/test_top_menu.lua diff --git a/indra/newview/lluilistener.cpp b/indra/newview/lluilistener.cpp index 4afd7f1766..c73c93859a 100644 --- a/indra/newview/lluilistener.cpp +++ b/indra/newview/lluilistener.cpp @@ -34,10 +34,13 @@ // std headers // external library headers // other Linden headers +#include "llmenugl.h" #include "llui.h" // getRootView(), resolvePath() #include "lluictrl.h" #include "llerror.h" +extern LLMenuBarGL* gMenuBarView; + #define THROTTLE_PERIOD 1.5 // required seconds between throttled functions #define MIN_THROTTLE 0.5 @@ -57,6 +60,31 @@ LLUIListener::LLUIListener(): "current value as [\"value\"] reply.", &LLUIListener::getValue, llsd::map("path", LLSD(), "reply", LLSD())); + + LLSD required_args = llsd::map("name", LLSD(), "label", LLSD(), "reply", LLSD()); + add("addMenu", + "Add new drop-down menu [\"name\"] with displayed [\"label\"] to the Top menu.", + &LLUIListener::addMenu, + required_args); + + required_args.insert("parent_menu", LLSD()); + add("addMenuBranch", + "Add new menu branch [\"name\"] with displayed [\"label\"]\n" + "to the [\"parent_menu\"] within the Top menu.", + &LLUIListener::addMenuBranch, + required_args); + + add("addMenuItem", + "Add new menu item [\"name\"] with displayed [\"label\"]\n" + "and call-on-click UI function [\"func\"] with optional [\"param\"]\n" + "to the [\"parent_menu\"] within the Top menu.", + &LLUIListener::addMenuItem, + required_args.with("func", LLSD())); + + add("addMenuSeparator", + "Add menu separator to the [\"parent_menu\"] within the Top menu.", + &LLUIListener::addMenuSeparator, + llsd::map("parent_menu", LLSD(), "reply", LLSD())); } typedef LLUICtrl::CommitCallbackInfo cb_info; @@ -120,3 +148,79 @@ void LLUIListener::getValue(const LLSD&event) const response.error(stringize("UI control ", std::quoted(event["path"].asString()), " was not found")); } } + +LLMenuGL::Params get_params(const LLSD&event) +{ + LLMenuGL::Params item_params; + item_params.name = event["name"]; + item_params.label = event["label"]; + item_params.can_tear_off = true; + return item_params; +} + +LLMenuGL* get_parent_menu(LLEventAPI::Response& response, const LLSD&event) +{ + LLMenuGL* parent_menu = gMenuBarView->findChildMenuByName(event["parent_menu"], true); + if(!parent_menu) + { + response.error(stringize("Parent menu ", std::quoted(event["parent_menu"].asString()), " was not found")); + } + return parent_menu; +} + +void LLUIListener::addMenu(const LLSD&event) const +{ + Response response(LLSD(), event); + LLMenuGL::Params item_params = get_params(event); + if(!gMenuBarView->appendMenu(LLUICtrlFactory::create(item_params))) + { + response.error(stringize("Menu ", std::quoted(event["name"].asString()), " was not added")); + } +} + +void LLUIListener::addMenuBranch(const LLSD&event) const +{ + Response response(LLSD(), event); + if(LLMenuGL* parent_menu = get_parent_menu(response, event)) + { + LLMenuGL::Params item_params = get_params(event); + if(!parent_menu->appendMenu(LLUICtrlFactory::create(item_params))) + { + response.error(stringize("Menu branch ", std::quoted(event["name"].asString()), " was not added")); + } + } +} + +void LLUIListener::addMenuItem(const LLSD&event) const +{ + Response response(LLSD(), event); + LLMenuItemCallGL::Params item_params; + item_params.name = event["name"]; + item_params.label = event["label"]; + LLUICtrl::CommitCallbackParam item_func; + item_func.function_name = event["func"]; + if (event.has("param")) + { + item_func.parameter = event["param"]; + } + item_params.on_click = item_func; + if(LLMenuGL* parent_menu = get_parent_menu(response, event)) + { + if(!parent_menu->append(LLUICtrlFactory::create(item_params))) + { + response.error(stringize("Menu item ", std::quoted(event["name"].asString()), " was not added")); + } + } +} + +void LLUIListener::addMenuSeparator(const LLSD&event) const +{ + Response response(LLSD(), event); + if(LLMenuGL* parent_menu = get_parent_menu(response, event)) + { + if(!parent_menu->addSeparator()) + { + response.error("Separator was not added"); + } + } +} diff --git a/indra/newview/lluilistener.h b/indra/newview/lluilistener.h index 0df2afb3fe..cbb5014300 100644 --- a/indra/newview/lluilistener.h +++ b/indra/newview/lluilistener.h @@ -39,12 +39,15 @@ class LLUIListener: public LLEventAPI public: LLUIListener(); -// FIXME These fields are intended to be private, changed here to support very hacky code in llluamanager.cpp -public: +private: void call(const LLSD& event); void getValue(const LLSD&event) const; - private: + void addMenu(const LLSD&event) const; + void addMenuBranch(const LLSD&event) const; + void addMenuItem(const LLSD&event) const; + void addMenuSeparator(const LLSD&event) const; + F64 mLastUntrustedThrottle {0}; F64 mLastMinThrottle {0}; }; diff --git a/indra/newview/scripts/lua/require/UI.lua b/indra/newview/scripts/lua/require/UI.lua index 1eee4657f4..28488ff3e1 100644 --- a/indra/newview/scripts/lua/require/UI.lua +++ b/indra/newview/scripts/lua/require/UI.lua @@ -135,4 +135,32 @@ function UI.snapshot(...) args.op = 'saveSnapshot' return leap.request('LLViewerWindow', args).result end + +-- *************************************************************************** +-- Top menu +-- *************************************************************************** + +function UI.addMenu(...) + local args = mapargs('name,label', ...) + args.op = 'addMenu' + return leap.request('UI', args) +end + +function UI.addMenuBranch(...) + local args = mapargs('name,label,parent_menu', ...) + args.op = 'addMenuBranch' + return leap.request('UI', args) +end + +function UI.addMenuItem(...) + local args = mapargs('name,label,parent_menu,func,param', ...) + args.op = 'addMenuItem' + return leap.request('UI', args) +end + +function UI.addMenuSeparator(...) + local args = mapargs('parent_menu', ...) + args.op = 'addMenuSeparator' + return leap.request('UI', args) +end return UI diff --git a/indra/newview/scripts/lua/test_top_menu.lua b/indra/newview/scripts/lua/test_top_menu.lua new file mode 100644 index 0000000000..780a384c92 --- /dev/null +++ b/indra/newview/scripts/lua/test_top_menu.lua @@ -0,0 +1,34 @@ +UI = require 'UI' + +--Add new drop-down 'LUA Menu' to the Top menu. +local MENU_NAME = "lua_menu" +UI.addMenu{name=MENU_NAME,label="LUA Menu"} + +--Add two new menu items to the 'LUA Menu': 'Debug console' and 'Scripts' +UI.addMenuItem{name="lua_debug",label="Debug console", + param="lua_debug", + func="Floater.ToggleOrBringToFront", + parent_menu=MENU_NAME} + +UI.addMenuItem{name="lua_scripts",label="Scripts", + param="lua_scripts", + func="Floater.ToggleOrBringToFront", + parent_menu=MENU_NAME} + +--Add menu separator to the 'LUA Menu' under added menu items +UI.addMenuSeparator{parent_menu=MENU_NAME} + +--Add two new menu branch 'About...' to the 'LUA Menu' +local BRANCH_NAME = "about_branch" +UI.addMenuBranch{name="about_branch",label="About...",parent_menu=MENU_NAME} + +--Add two new menu items to the 'About...' branch +UI.addMenuItem{name="lua_info",label="Lua...", + param="https://www.lua.org/about.html", + func="Advanced.ShowURL", + parent_menu=BRANCH_NAME} + +UI.addMenuItem{name="lua_info",label="Luau...", + param="https://luau-lang.org/", + func="Advanced.ShowURL", + parent_menu=BRANCH_NAME} -- cgit v1.2.3 From 1745701280d8a18f51ef09aacba4f76a4c543681 Mon Sep 17 00:00:00 2001 From: Andrey Lihatskiy Date: Thu, 18 Jul 2024 08:01:24 +0300 Subject: Remove trailing whitespaces to make pre-commit happy --- indra/llwebrtc/llwebrtc.cpp | 6 +-- indra/llwebrtc/llwebrtc.h | 4 +- indra/llwebrtc/llwebrtc_impl.h | 2 +- indra/media_plugins/base/media_plugin_base.cpp | 6 +-- .../cef/linux/volume_catcher_pipewire.cpp | 10 ++-- .../cef/linux/volume_catcher_pulseaudio.cpp | 10 ++-- indra/media_plugins/cef/volume_catcher_null.cpp | 2 +- .../gstreamer10/media_plugin_gstreamer10.cpp | 60 +++++++++++----------- .../shaders/class2/deferred/reflectionProbeF.glsl | 6 +-- 9 files changed, 53 insertions(+), 53 deletions(-) diff --git a/indra/llwebrtc/llwebrtc.cpp b/indra/llwebrtc/llwebrtc.cpp index d5bd913315..6dc632aba4 100644 --- a/indra/llwebrtc/llwebrtc.cpp +++ b/indra/llwebrtc/llwebrtc.cpp @@ -458,7 +458,7 @@ void ll_set_device_module_render_device(rtc::scoped_refptrSetPlayoutDevice(webrtc::AudioDeviceModule::kDefaultDevice); } - else + else { device_module->SetPlayoutDevice(device); } @@ -656,7 +656,7 @@ void LLWebRTCImpl::freePeerConnection(LLWebRTCPeerConnectionInterface* peer_conn // Most peer connection (signaling) happens on // the signaling thread. -LLWebRTCPeerConnectionImpl::LLWebRTCPeerConnectionImpl() : +LLWebRTCPeerConnectionImpl::LLWebRTCPeerConnectionImpl() : mWebRTCImpl(nullptr), mPeerConnection(nullptr), mMute(false), @@ -1171,7 +1171,7 @@ void LLWebRTCPeerConnectionImpl::OnSuccess(webrtc::SessionDescriptionInterface * { observer->OnOfferAvailable(mangled_sdp); } - + mPeerConnection->SetLocalDescription(std::unique_ptr( webrtc::CreateSessionDescription(webrtc::SdpType::kOffer, mangled_sdp)), rtc::scoped_refptr(this)); diff --git a/indra/llwebrtc/llwebrtc.h b/indra/llwebrtc/llwebrtc.h index f447ea990a..54eefc8554 100644 --- a/indra/llwebrtc/llwebrtc.h +++ b/indra/llwebrtc/llwebrtc.h @@ -129,7 +129,7 @@ class LLWebRTCDeviceInterface }; virtual void setAudioConfig(AudioConfig config) = 0; - + // instructs webrtc to refresh the device list. virtual void refreshDevices() = 0; @@ -231,7 +231,7 @@ class LLWebRTCSignalingObserver class LLWebRTCPeerConnectionInterface { public: - + struct InitOptions { // equivalent of PeerConnectionInterface::IceServer diff --git a/indra/llwebrtc/llwebrtc_impl.h b/indra/llwebrtc/llwebrtc_impl.h index 6672f8ce90..2fb5525519 100644 --- a/indra/llwebrtc/llwebrtc_impl.h +++ b/indra/llwebrtc/llwebrtc_impl.h @@ -235,7 +235,7 @@ class LLWebRTCImpl : public LLWebRTCDeviceInterface, public webrtc::AudioDeviceS // The factory that allows creation of native webrtc PeerConnections. rtc::scoped_refptr mPeerConnectionFactory; - + rtc::scoped_refptr mAudioProcessingModule; // more native webrtc stuff diff --git a/indra/media_plugins/base/media_plugin_base.cpp b/indra/media_plugins/base/media_plugin_base.cpp index b21f29ac32..9bb3bad035 100644 --- a/indra/media_plugins/base/media_plugin_base.cpp +++ b/indra/media_plugins/base/media_plugin_base.cpp @@ -270,13 +270,13 @@ pid_t getParentPid( pid_t aPid ) { std::string line; line.resize( 1024, 0 ); - in.getline( &line[0], line.length() ); + in.getline( &line[0], line.length() ); auto i = line.find( "PPid:" ); - + if( i == std::string::npos ) continue; - + char const *pIn = line.c_str() + 5; // Skip over pid; while( *pIn != 0 && isspace( *pIn ) ) ++pIn; diff --git a/indra/media_plugins/cef/linux/volume_catcher_pipewire.cpp b/indra/media_plugins/cef/linux/volume_catcher_pipewire.cpp index 27fea547c9..a8f1366d6f 100755 --- a/indra/media_plugins/cef/linux/volume_catcher_pipewire.cpp +++ b/indra/media_plugins/cef/linux/volume_catcher_pipewire.cpp @@ -1,4 +1,4 @@ -/** +/** * @file volume_catcher_pipewire.cpp * @brief A Linux-specific, PipeWire-specific hack to detect and volume-adjust new audio sources * @@ -6,21 +6,21 @@ * $LicenseInfo:firstyear=2010&license=viewerlgpl$ * Second Life Viewer Source Code * Copyright (C) 2010, Linden Research, Inc. - * + * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; * version 2.1 of the License only. - * + * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. - * + * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * + * * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA * $/LicenseInfo$ * @endcond diff --git a/indra/media_plugins/cef/linux/volume_catcher_pulseaudio.cpp b/indra/media_plugins/cef/linux/volume_catcher_pulseaudio.cpp index 9417c49d38..f8a48a91fd 100755 --- a/indra/media_plugins/cef/linux/volume_catcher_pulseaudio.cpp +++ b/indra/media_plugins/cef/linux/volume_catcher_pulseaudio.cpp @@ -1,4 +1,4 @@ -/** +/** * @file volume_catcher_pulseaudio.cpp * @brief A Linux-specific, PulseAudio-specific hack to detect and volume-adjust new audio sources * @@ -6,21 +6,21 @@ * $LicenseInfo:firstyear=2010&license=viewerlgpl$ * Second Life Viewer Source Code * Copyright (C) 2010, Linden Research, Inc. - * + * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; * version 2.1 of the License only. - * + * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. - * + * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * + * * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA * $/LicenseInfo$ * @endcond diff --git a/indra/media_plugins/cef/volume_catcher_null.cpp b/indra/media_plugins/cef/volume_catcher_null.cpp index c6028da45b..1f0f1e8e38 100644 --- a/indra/media_plugins/cef/volume_catcher_null.cpp +++ b/indra/media_plugins/cef/volume_catcher_null.cpp @@ -1,4 +1,4 @@ -/** +/** * @file volume_catcher_null.cpp * @brief A null implementation of volume level control of all audio channels opened by a process. * We are using this for the macOS version for now until we can understand how to make the diff --git a/indra/media_plugins/gstreamer10/media_plugin_gstreamer10.cpp b/indra/media_plugins/gstreamer10/media_plugin_gstreamer10.cpp index 3f636915ea..0f45c151a2 100644 --- a/indra/media_plugins/gstreamer10/media_plugin_gstreamer10.cpp +++ b/indra/media_plugins/gstreamer10/media_plugin_gstreamer10.cpp @@ -1,4 +1,4 @@ -/** +/** * @file media_plugin_gstreamer10.cpp * @brief GStreamer-1.0 plugin for LLMedia API plugin system * @@ -6,21 +6,21 @@ * $LicenseInfo:firstyear=2016&license=viewerlgpl$ * Second Life Viewer Source Code * Copyright (C) 2016, Linden Research, Inc. / Nicky Dasmijn - * + * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; * version 2.1 of the License only. - * + * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. - * + * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * + * * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA * $/LicenseInfo$ * @endcond @@ -77,7 +77,7 @@ private: bool navigateTo( const std::string urlIn ); bool seek( double time_sec ); bool setVolume( float volume ); - + // misc bool pause(); bool stop(); @@ -86,7 +86,7 @@ private: double MIN_LOOP_SEC = 1.0F; U32 INTERNAL_TEXTURE_SIZE = 1024; - + bool mIsLooping; enum ECommand { @@ -110,9 +110,9 @@ private: void mouseMove( int x, int y ); static bool mDoneInit; - + guint mBusWatchID; - + float mVolume; int mDepth; @@ -120,10 +120,10 @@ private: // padded texture size we need to write into int mTextureWidth; int mTextureHeight; - + bool mSeekWanted; double mSeekDestination; - + // Very GStreamer-specific GMainLoop *mPump; // event pump for this media GstElement *mPlaybin; @@ -148,7 +148,7 @@ MediaPluginGStreamer10::MediaPluginGStreamer10( LLPluginInstance::sendMessageFun gboolean MediaPluginGStreamer10::processGSTEvents(GstBus *bus, GstMessage *message) { - if (!message) + if (!message) return TRUE; // shield against GStreamer bug switch (GST_MESSAGE_TYPE (message)) @@ -213,7 +213,7 @@ gboolean MediaPluginGStreamer10::processGSTEvents(GstBus *bus, GstMessage *messa { GError *err = nullptr; gchar *debug = nullptr; - + llgst_message_parse_info (message, &err, &debug); if (err) llg_error_free (err); @@ -225,7 +225,7 @@ gboolean MediaPluginGStreamer10::processGSTEvents(GstBus *bus, GstMessage *messa { GError *err = nullptr; gchar *debug = nullptr; - + llgst_message_parse_warning (message, &err, &debug); if (err) llg_error_free (err); @@ -239,7 +239,7 @@ gboolean MediaPluginGStreamer10::processGSTEvents(GstBus *bus, GstMessage *messa { double eos_pos_sec = 0.0F; bool got_eos_position = getTimePos(eos_pos_sec); - + if (got_eos_position && eos_pos_sec < MIN_LOOP_SEC) { // if we know that the movie is really short, don't @@ -326,7 +326,7 @@ bool MediaPluginGStreamer10::update(int milliseconds) return false; // error // DEBUGMSG("updating media..."); - + // sanity check if (nullptr == mPump || nullptr == mPlaybin) { @@ -353,13 +353,13 @@ bool MediaPluginGStreamer10::update(int milliseconds) } // check for availability of a new frame - + if( !mAppSink ) return true; if( GST_STATE(mPlaybin) != GST_STATE_PLAYING) // Do not try to pull a sample if not in playing state return true; - + GstSample *pSample = llgst_app_sink_pull_sample( mAppSink ); if(!pSample) return false; // Done playing @@ -379,7 +379,7 @@ bool MediaPluginGStreamer10::update(int milliseconds) if( !mPixels || width == 0 || height == 0) return true; - + GstBuffer *pBuffer = llgst_sample_get_buffer ( pSample ); GstMapInfo map; llgst_buffer_map ( pBuffer, &map, GST_MAP_READ); @@ -396,7 +396,7 @@ bool MediaPluginGStreamer10::update(int milliseconds) U8 *pTexelOut = mPixels + (row * mTextureWidth * mDepth ); #else U8 *pTexelOut = mPixels + ((mTextureHeight-row-1) * mTextureWidth * mDepth ); -#endif +#endif for( int col = 0; col < mTextureWidth; ++col ) { pTexelOut[ 0 ] = pTexelIn[0]; @@ -527,7 +527,7 @@ bool MediaPluginGStreamer10::getTimePos(double &sec_out) { got_position = false; } - + } // If all the preconditions succeeded... we can trust the result. if (got_position) @@ -592,7 +592,7 @@ bool MediaPluginGStreamer10::load() setStatus(STATUS_ERROR); return false; } - + llg_object_set(mPlaybin, "video-sink", mAppSink, nullptr); return true; @@ -677,7 +677,7 @@ bool MediaPluginGStreamer10::startup() // Protect against GStreamer resetting the locale, yuck. static std::string saved_locale; saved_locale = setlocale(LC_ALL, nullptr); - + llgst_debug_set_default_threshold( GST_LEVEL_WARNING ); llgst_debug_add_log_function( LogFunction, nullptr, nullptr ); llgst_debug_set_active( false ); @@ -699,7 +699,7 @@ bool MediaPluginGStreamer10::startup() llg_error_free(err); return false; } - + mDoneInit = true; } @@ -771,7 +771,7 @@ void MediaPluginGStreamer10::receiveMessage(const char *message_string) { // no response is necessary here. double time = message_in.getValueReal("time"); - + // Convert time to milliseconds for update() update((int)(time * 1000.0f)); } @@ -863,7 +863,7 @@ void MediaPluginGStreamer10::receiveMessage(const char *message_string) mTextureHeight = texture_height; memset( mPixels, 0, mTextureWidth*mTextureHeight*mDepth ); } - + LLPluginMessage message(LLPLUGIN_MESSAGE_CLASS_MEDIA, "size_change_request"); message.setValue("name", mTextureSegmentName); message.setValueS32("width", INTERNAL_TEXTURE_SIZE ); @@ -876,14 +876,14 @@ void MediaPluginGStreamer10::receiveMessage(const char *message_string) { std::string uri = message_in.getValue("uri"); navigateTo( uri ); - sendStatus(); + sendStatus(); } else if(message_name == "mouse_event") { std::string event = message_in.getValue("event"); S32 x = message_in.getValueS32("x"); S32 y = message_in.getValueS32("y"); - + if(event == "down") { mouseDown(x, y); @@ -948,10 +948,10 @@ int init_media_plugin(LLPluginInstance::sendMessageFunction host_send_func, void MediaPluginGStreamer10 *self = new MediaPluginGStreamer10(host_send_func, host_user_data); *plugin_send_func = MediaPluginGStreamer10::staticReceiveMessage; *plugin_user_data = (void*)self; - + return 0; // okay } - else + else { return -1; // failed to init } diff --git a/indra/newview/app_settings/shaders/class2/deferred/reflectionProbeF.glsl b/indra/newview/app_settings/shaders/class2/deferred/reflectionProbeF.glsl index 35848ff4cd..4fb24ca0b1 100644 --- a/indra/newview/app_settings/shaders/class2/deferred/reflectionProbeF.glsl +++ b/indra/newview/app_settings/shaders/class2/deferred/reflectionProbeF.glsl @@ -37,7 +37,7 @@ void sampleReflectionProbes(inout vec3 ambenv, inout vec3 glossenv, vec2 tc, vec3 pos, vec3 norm, float glossiness, bool transparent, vec3 amblit_linear) { ambenv = vec3(reflection_probe_ambiance * 0.25); - + vec3 refnormpersp = normalize(reflect(pos.xyz, norm.xyz)); vec3 env_vec = env_mat * refnormpersp; glossenv = srgb_to_linear(texture(environmentMap, env_vec).rgb); @@ -59,7 +59,7 @@ void sampleReflectionProbesLegacy(out vec3 ambenv, out vec3 glossenv, out vec3 l vec2 tc, vec3 pos, vec3 norm, float glossiness, float envIntensity, bool transparent, vec3 amblit_linear) { ambenv = vec3(reflection_probe_ambiance * 0.25); - + vec3 refnormpersp = normalize(reflect(pos.xyz, norm.xyz)); vec3 env_vec = env_mat * refnormpersp; @@ -70,7 +70,7 @@ void sampleReflectionProbesLegacy(out vec3 ambenv, out vec3 glossenv, out vec3 l void applyGlossEnv(inout vec3 color, vec3 glossenv, vec4 spec, vec3 pos, vec3 norm) { - + } void applyLegacyEnv(inout vec3 color, vec3 legacyenv, vec4 spec, vec3 pos, vec3 norm, float envIntensity) -- cgit v1.2.3 From 35ee96709ef2704a2636a11c67d61190dd6bdd50 Mon Sep 17 00:00:00 2001 From: Nat Goodspeed Date: Thu, 18 Jul 2024 12:55:38 -0400 Subject: Make `LLEventPump::listen()` also accept new `LLAwareListener`. `listen()` still takes `LLEventListener`, a `callable(const LLSD&)`, but now also accepts `LLAwareListener`, a `callable(const LLBoundListener&, const LLSD&)`. This uses `boost::signals2::signal::connect_extended()`, which, when the signal is called, passes to a connected listener the `LLBoundListener` (aka `boost::signals2::connection`) representing its own connection. This allows a listener to disconnect itself when done. Internally, `listen_impl()` now always uses `connect_extended()`. When passed a classic `LLEventListener`, `listen()` wraps it in a lambda that ignores the passed `LLBoundListener`. `listen()` also now accepts `LLVoidListener`, and internally wraps it in a lambda that returns `false` on its behalf. --- indra/llcommon/lleventfilter.cpp | 13 +++--- indra/llcommon/lleventfilter.h | 7 ++-- indra/llcommon/llevents.cpp | 11 +++-- indra/llcommon/llevents.h | 70 +++++++++++++++++++++++++++---- indra/newview/tests/llluamanager_test.cpp | 25 ++++------- 5 files changed, 86 insertions(+), 40 deletions(-) diff --git a/indra/llcommon/lleventfilter.cpp b/indra/llcommon/lleventfilter.cpp index da19946e3b..2b5401e9f7 100644 --- a/indra/llcommon/lleventfilter.cpp +++ b/indra/llcommon/lleventfilter.cpp @@ -365,21 +365,22 @@ bool LLEventLogProxy::post(const LLSD& event) /* override */ } LLBoundListener LLEventLogProxy::listen_impl(const std::string& name, - const LLEventListener& target, + const LLAwareListener& target, const NameList& after, const NameList& before) { LL_DEBUGS("LogProxy") << "LLEventLogProxy('" << getName() << "').listen('" << name << "')" << LL_ENDL; return mPump.listen(name, - [this, name, target](const LLSD& event)->bool - { return listener(name, target, event); }, + [this, name, target](const LLBoundListener& conn, const LLSD& event) + { return listener(conn, name, target, event); }, after, before); } -bool LLEventLogProxy::listener(const std::string& name, - const LLEventListener& target, +bool LLEventLogProxy::listener(const LLBoundListener& conn, + const std::string& name, + const LLAwareListener& target, const LLSD& event) const { auto eventminus = event; @@ -391,7 +392,7 @@ bool LLEventLogProxy::listener(const std::string& name, } std::string hdr{STRINGIZE(getName() << " to " << name << " " << counter)}; LL_INFOS("LogProxy") << hdr << ": " << eventminus << LL_ENDL; - bool result = target(eventminus); + bool result = target(conn, eventminus); LL_INFOS("LogProxy") << hdr << " => " << result << LL_ENDL; return result; } diff --git a/indra/llcommon/lleventfilter.h b/indra/llcommon/lleventfilter.h index 9988459aae..de706d72eb 100644 --- a/indra/llcommon/lleventfilter.h +++ b/indra/llcommon/lleventfilter.h @@ -459,7 +459,7 @@ public: LLEventLogProxy(LLEventPump& source, const std::string& name, bool tweak=false); /// register a new listener - LLBoundListener listen_impl(const std::string& name, const LLEventListener& target, + LLBoundListener listen_impl(const std::string& name, const LLAwareListener& target, const NameList& after, const NameList& before); /// Post an event to all listeners @@ -469,8 +469,9 @@ private: /// This method intercepts each call to any target listener. We pass it /// the listener name and the caller's intended target listener plus the /// posted LLSD event. - bool listener(const std::string& name, - const LLEventListener& target, + bool listener(const LLBoundListener& conn, + const std::string& name, + const LLAwareListener& target, const LLSD& event) const; LLEventPump& mPump; diff --git a/indra/llcommon/llevents.cpp b/indra/llcommon/llevents.cpp index 5a6e13cb7d..98bd990f31 100644 --- a/indra/llcommon/llevents.cpp +++ b/indra/llcommon/llevents.cpp @@ -415,7 +415,7 @@ void LLEventPump::reset() //mDeps.clear(); } -LLBoundListener LLEventPump::listen_impl(const std::string& name, const LLEventListener& listener, +LLBoundListener LLEventPump::listen_impl(const std::string& name, const LLAwareListener& listener, const NameList& after, const NameList& before) { @@ -575,7 +575,7 @@ LLBoundListener LLEventPump::listen_impl(const std::string& name, const LLEventL } // Now that newNode has a value that places it appropriately in mSignal, // connect it. - LLBoundListener bound = mSignal->connect(nodePosition, listener); + LLBoundListener bound = mSignal->connect_extended(nodePosition, listener); if (!name.empty()) { // note that we are not tracking anonymous listeners here either. @@ -659,7 +659,7 @@ bool LLEventMailDrop::post(const LLSD& event) } LLBoundListener LLEventMailDrop::listen_impl(const std::string& name, - const LLEventListener& listener, + const LLAwareListener& listener, const NameList& after, const NameList& before) { @@ -668,7 +668,10 @@ LLBoundListener LLEventMailDrop::listen_impl(const std::string& name, // Remove any that this listener consumes -- Effective STL, Item 9. for (auto hi(mEventHistory.begin()), hend(mEventHistory.end()); hi != hend; ) { - if (listener(*hi)) + // We don't actually have an LLBoundListener in hand, and we won't + // until the base-class listen_impl() call below. Pass an empty + // instance. + if (listener({}, *hi)) { // new listener consumed this event, erase it hi = mEventHistory.erase(hi); diff --git a/indra/llcommon/llevents.h b/indra/llcommon/llevents.h index d0686bd8b5..8ef3a5af95 100644 --- a/indra/llcommon/llevents.h +++ b/indra/llcommon/llevents.h @@ -32,12 +32,13 @@ #if ! defined(LL_LLEVENTS_H) #define LL_LLEVENTS_H -#include +#include +#include #include #include +#include +#include #include -#include -#include #if LL_WINDOWS #pragma warning (push) #pragma warning (disable : 4263) // boost::signals2::expired_slot::what() has const mismatch @@ -137,6 +138,10 @@ typedef boost::signals2::signal LL /// Methods that forward listeners (e.g. constructed with /// boost::bind()) should accept (const LLEventListener&) typedef LLStandardSignal::slot_type LLEventListener; +/// Support a listener accepting (const LLBoundListener&, const LLSD&). +/// Note that LLBoundListener::disconnect() is a const method: this feature is +/// specifically intended to allow a listener to disconnect itself when done. +typedef LLStandardSignal::extended_slot_type LLAwareListener; /// Accept a void listener too typedef std::function LLVoidListener; /// Result of registering a listener, supports connected(), @@ -522,18 +527,37 @@ public: * call, allows us to optimize away the second and subsequent dependency * sorts. * - * If name is set to LLEventPump::ANONYMOUS listen will bypass the entire + * If name is set to LLEventPump::ANONYMOUS, listen() will bypass the entire * dependency and ordering calculation. In this case, it is critical that * the result be assigned to a LLTempBoundListener or the listener is - * manually disconnected when no longer needed since there will be no + * manually disconnected when no longer needed, since there will be no * way to later find and disconnect this listener manually. */ + template LLBoundListener listen(const std::string& name, - const LLEventListener& listener, + LISTENER&& listener, const NameList& after=NameList(), const NameList& before=NameList()) { - return listen_impl(name, listener, after, before); + if constexpr (std::is_invocable_v) + { + // wrap classic LLEventListener in LLAwareListener lambda + return listenb( + name, + [listener=std::move(listener)] + (const LLBoundListener&, const LLSD& event) + { + return listener(event); + }, + after, + before); + } + else + { + static_assert(std::is_invocable_v, + "LLEventPump::listen() listener has bad parameter signature"); + return listenb(name, std::forward(listener), after, before); + } } /// Get the LLBoundListener associated with the passed name (dummy @@ -579,7 +603,35 @@ private: LLMutex mConnectionListMutex; protected: - virtual LLBoundListener listen_impl(const std::string& name, const LLEventListener&, + template + LLBoundListener listenb(const std::string& name, + LISTENER&& listener, + const NameList& after=NameList(), + const NameList& before=NameList()) + { + using result_t = std::decay_t; + if constexpr (std::is_same_v) + { + return listen_impl(name, std::forward(listener), after, before); + } + else + { + static_assert(std::is_same_v, + "LLEventPump::listen() listener has bad return type"); + // wrap void listener in one that returns bool + return listen_impl( + name, + [listener=std::move(listener)] + (const LLBoundListener& conn, const LLSD& event) + { + listener(conn, event); + return false; + }, + after, + before); + } + } + virtual LLBoundListener listen_impl(const std::string& name, const LLAwareListener&, const NameList& after, const NameList& before); @@ -654,7 +706,7 @@ public: void discard(); protected: - virtual LLBoundListener listen_impl(const std::string& name, const LLEventListener&, + virtual LLBoundListener listen_impl(const std::string& name, const LLAwareListener&, const NameList& after, const NameList& before) override; diff --git a/indra/newview/tests/llluamanager_test.cpp b/indra/newview/tests/llluamanager_test.cpp index 26a4ac95e3..3209d93d39 100644 --- a/indra/newview/tests/llluamanager_test.cpp +++ b/indra/newview/tests/llluamanager_test.cpp @@ -42,17 +42,6 @@ public: LLControlGroup gSavedSettings("Global"); -template -auto listener(CALLABLE&& callable) -{ - return [callable=std::forward(callable)] - (const LLSD& data) - { - callable(data); - return false; - }; -} - /***************************************************************************** * TUT *****************************************************************************/ @@ -138,7 +127,7 @@ namespace tut { LLSD fromlua; LLStreamListener pump("testpump", - listener([&fromlua](const LLSD& data){ fromlua = data; })); + [&fromlua](const LLSD& data){ fromlua = data; }); const std::string lua(stringize( "data = ", construct, "\n" "LL.post_on('testpump', data)\n" @@ -167,8 +156,8 @@ namespace tut set_test_name("test post_on(), get_event_pumps(), get_event_next()"); StringVec posts; LLStreamListener pump("testpump", - listener([&posts](const LLSD& data) - { posts.push_back(data.asString()); })); + [&posts](const LLSD& data) + { posts.push_back(data.asString()); }); const std::string lua( "-- test post_on,get_event_pumps,get_event_next\n" "LL.post_on('testpump', 'entry')\n" @@ -346,11 +335,11 @@ namespace tut LLStreamListener pump( "echo", - listener([](const LLSD& data) + [](const LLSD& data) { LL_DEBUGS("Lua") << "echo pump got: " << data << LL_ENDL; sendReply(data, data); - })); + }); auto [count, result] = LLLUAmanager::waitScriptLine(lua); ensure_equals("Lua script didn't return item", count, 1); @@ -424,11 +413,11 @@ namespace tut LLSD requests; LLStreamListener pump( "testpump", - listener([&requests](const LLSD& data) + [&requests](const LLSD& data) { LL_DEBUGS("Lua") << "testpump got: " << data << LL_ENDL; requests.append(data); - })); + }); auto future = LLLUAmanager::startScriptLine(lua); // LuaState::expr() periodically interrupts a running chunk to ensure -- cgit v1.2.3 From f2f0fa7fd0efc221f1358dd4e440b5d51a5fb8b4 Mon Sep 17 00:00:00 2001 From: Nat Goodspeed Date: Thu, 18 Jul 2024 13:29:34 -0400 Subject: Ditch `LLEventTrackable` aka `boost::signals2::trackable`. Remove documented `LLEventPump` support for `LLEventTrackable`. That claimed support was always a little bit magical/fragile. IF: * a class included `LLEventTrackable` as a base class AND * an instance of that class was managed by `boost::shared_ptr` AND * you passed one of that class's methods and the `boost::shared_ptr` specifically to `boost::bind()` AND * the resulting `boost::bind()` object was passed into `LLEventPump::listen()` THEN the promise was that on destruction of that object, that listener would automatically be disconnected -- instead of leaving a dangling pointer bound into the `LLEventPump`, causing a crash on the next `LLEventPump::post()` call. The only existing code in the viewer code base that exercised `LLEventTrackable` functionality was in test programs. When the viewer calls `LLEventPump::listen()`, it typically stores the resulting connection object in an `LLTempBoundListener` variable, which guarantees disconnection on destruction of that variable. The fact that `LLEventTrackable` support is specific to `boost::bind()`, that it silently fails to keep its promise with `std::bind()` or a lambda or any other form of C++ callable, makes it untrustworthy for new code. Note that the code base still uses `boost::signals2::trackable` for other `boost::signals2::signal` instances not associated with `LLEventPump`. We are not changing those at this time. --- indra/llcommon/llevents.h | 45 +---------------- indra/llui/llnotifications.h | 2 +- indra/llui/llnotificationslistener.cpp | 16 +++--- indra/newview/llspeakers.h | 2 +- indra/test/llevents_tut.cpp | 58 +--------------------- .../viewer_components/login/tests/lllogin_test.cpp | 4 +- 6 files changed, 15 insertions(+), 112 deletions(-) diff --git a/indra/llcommon/llevents.h b/indra/llcommon/llevents.h index 8ef3a5af95..abc25ba400 100644 --- a/indra/llcommon/llevents.h +++ b/indra/llcommon/llevents.h @@ -363,57 +363,14 @@ testable: InstanceTypes mTypes; }; -/***************************************************************************** -* LLEventTrackable -*****************************************************************************/ -/** - * LLEventTrackable wraps boost::signals2::trackable, which resembles - * boost::trackable. Derive your listener class from LLEventTrackable instead, - * and use something like - * LLEventPump::listen(boost::bind(&YourTrackableSubclass::method, - * instance, _1)). This will implicitly disconnect when the object - * referenced by @c instance is destroyed. - * - * @note - * LLEventTrackable doesn't address a couple of cases: - * * Object destroyed during call - * - You enter a slot call in thread A. - * - Thread B destroys the object, which of course disconnects it from any - * future slot calls. - * - Thread A's call uses 'this', which now refers to a defunct object. - * Undefined behavior results. - * * Call during destruction - * - @c MySubclass is derived from LLEventTrackable. - * - @c MySubclass registers one of its own methods using - * LLEventPump::listen(). - * - The @c MySubclass object begins destruction. ~MySubclass() - * runs, destroying state specific to the subclass. (For instance, a - * Foo* data member is deleted but not zeroed.) - * - The listening method will not be disconnected until - * ~LLEventTrackable() runs. - * - Before we get there, another thread posts data to the @c LLEventPump - * instance, calling the @c MySubclass method. - * - The method in question relies on valid @c MySubclass state. (For - * instance, it attempts to dereference the Foo* pointer that was - * deleted but not zeroed.) - * - Undefined behavior results. - */ -typedef boost::signals2::trackable LLEventTrackable; - /***************************************************************************** * LLEventPump *****************************************************************************/ /** * LLEventPump is the base class interface through which we access the * concrete subclasses such as LLEventStream. - * - * @NOTE - * LLEventPump derives from LLEventTrackable so that when you "chain" - * LLEventPump instances together, they will automatically disconnect on - * destruction. Please see LLEventTrackable documentation for situations in - * which this may be perilous across threads. */ -class LL_COMMON_API LLEventPump: public LLEventTrackable +class LL_COMMON_API LLEventPump { public: static const std::string ANONYMOUS; // constant for anonymous listeners. diff --git a/indra/llui/llnotifications.h b/indra/llui/llnotifications.h index ab4f009a80..206c521592 100644 --- a/indra/llui/llnotifications.h +++ b/indra/llui/llnotifications.h @@ -734,7 +734,7 @@ typedef std::multimap LLNotificationMap; // all of the built-in tests should attach to the "Visible" channel // class LLNotificationChannelBase : - public LLEventTrackable, + public boost::signals2::trackable, public LLRefCount { LOG_CLASS(LLNotificationChannelBase); diff --git a/indra/llui/llnotificationslistener.cpp b/indra/llui/llnotificationslistener.cpp index ace9e37e25..2ad1689a45 100644 --- a/indra/llui/llnotificationslistener.cpp +++ b/indra/llui/llnotificationslistener.cpp @@ -204,7 +204,7 @@ void LLNotificationsListener::ignore(const LLSD& params) const } } -class LLNotificationsListener::Forwarder: public LLEventTrackable +class LLNotificationsListener::Forwarder: public boost::signals2::trackable { LOG_CLASS(LLNotificationsListener::Forwarder); public: @@ -213,8 +213,10 @@ public: mRespond(false) { // Connect to the specified channel on construction. Because - // LLEventTrackable is a base, we should automatically disconnect when - // destroyed. + // boost::signals2::trackable is a base, because we use boost::bind() + // below, and because connectPassedFilter() directly calls + // boost::signals2::signal::connect(), we should automatically + // disconnect when destroyed. LLNotificationChannelPtr channelptr(llnotifications.getChannel(channel)); if (channelptr) { @@ -252,10 +254,10 @@ void LLNotificationsListener::forward(const LLSD& params) if (! forward) { // This is a request to stop forwarding notifications on the specified - // channel. The rest of the params don't matter. - // Because mForwarders contains scoped_ptrs, erasing the map entry - // DOES delete the heap Forwarder object. Because Forwarder derives - // from LLEventTrackable, destroying it disconnects it from the + // channel. The rest of the params don't matter. Because mForwarders + // contains scoped_ptrs, erasing the map entry DOES delete the heap + // Forwarder object. Because Forwarder derives from + // boost::signals2::trackable, destroying it disconnects it from the // channel. mForwarders.erase(channel); return; diff --git a/indra/newview/llspeakers.h b/indra/newview/llspeakers.h index 234de42953..d3304dba59 100644 --- a/indra/newview/llspeakers.h +++ b/indra/newview/llspeakers.h @@ -37,7 +37,7 @@ class LLSpeakerMgr; class LLAvatarName; // data for a given participant in a voice channel -class LLSpeaker : public LLRefCount, public LLOldEvents::LLObservable, public LLHandleProvider, public boost::signals2::trackable +class LLSpeaker : public LLRefCount, public LLOldEvents::LLObservable, public LLHandleProvider { public: typedef enum e_speaker_type diff --git a/indra/test/llevents_tut.cpp b/indra/test/llevents_tut.cpp index 875ca9ad89..f9cc99203b 100644 --- a/indra/test/llevents_tut.cpp +++ b/indra/test/llevents_tut.cpp @@ -429,7 +429,7 @@ void events_object::test<9>() { set_test_name("listen(boost::bind(...TempListener...))"); // listen() can't do anything about a plain TempListener instance: - // it's not managed with shared_ptr, nor is it an LLEventTrackable subclass + // it's not managed with shared_ptr bool live = false; LLEventPump& heaptest(pumps.obtain("heaptest")); LLBoundListener connection; @@ -453,60 +453,4 @@ void events_object::test<9>() heaptest.stopListening("temp"); } -class TempTrackableListener: public TempListener, public LLEventTrackable -{ -public: - TempTrackableListener(const std::string& name, bool& liveFlag): - TempListener(name, liveFlag) - {} -}; - -template<> template<> -void events_object::test<10>() -{ - set_test_name("listen(boost::bind(...TempTrackableListener ref...))"); - bool live = false; - LLEventPump& heaptest(pumps.obtain("heaptest")); - LLBoundListener connection; - { - TempTrackableListener tempListener("temp", live); - ensure("TempTrackableListener constructed", live); - connection = heaptest.listen(tempListener.getName(), - boost::bind(&TempTrackableListener::call, - boost::ref(tempListener), _1)); - heaptest.post(1); - check_listener("received", tempListener, 1); - } // presumably this will make tempListener go away? - // verify that - ensure("TempTrackableListener destroyed", ! live); - ensure("implicit disconnect", ! connection.connected()); - // now just make sure we don't blow up trying to access a freed object! - heaptest.post(2); -} - -template<> template<> -void events_object::test<11>() -{ - set_test_name("listen(boost::bind(...TempTrackableListener pointer...))"); - bool live = false; - LLEventPump& heaptest(pumps.obtain("heaptest")); - LLBoundListener connection; - { - TempTrackableListener* newListener(new TempTrackableListener("temp", live)); - ensure("TempTrackableListener constructed", live); - connection = heaptest.listen(newListener->getName(), - boost::bind(&TempTrackableListener::call, - newListener, _1)); - heaptest.post(1); - check_listener("received", *newListener, 1); - // explicitly destroy newListener - delete newListener; - } - // verify that - ensure("TempTrackableListener destroyed", ! live); - ensure("implicit disconnect", ! connection.connected()); - // now just make sure we don't blow up trying to access a freed object! - heaptest.post(2); -} - } // namespace tut diff --git a/indra/viewer_components/login/tests/lllogin_test.cpp b/indra/viewer_components/login/tests/lllogin_test.cpp index 8aea3b37aa..f051f8c67f 100644 --- a/indra/viewer_components/login/tests/lllogin_test.cpp +++ b/indra/viewer_components/login/tests/lllogin_test.cpp @@ -66,7 +66,7 @@ * Helper classes *****************************************************************************/ // This is a listener to receive results from lllogin. -class LoginListener: public LLEventTrackable +class LoginListener { std::string mName; LLSD mLastEvent; @@ -137,7 +137,7 @@ public: } }; -class LLXMLRPCListener: public LLEventTrackable +class LLXMLRPCListener { std::string mName; LLSD mEvent; -- cgit v1.2.3 From cd64842e338fb216f0772e371278c56b921f6f87 Mon Sep 17 00:00:00 2001 From: Nat Goodspeed Date: Thu, 18 Jul 2024 14:24:36 -0400 Subject: Improve viewer's defense against `LLEventAPI` failures. `LLEventAPI` is specifically intended to allow a LEAP plugin, or a Lua script, to access certain viewer functionality. Errors in external code like that cannot be addressed during viewer development. Any code path that allows external code in any form to crash the viewer opens up a potential abuse vector, if a trusting user runs external code from an untrustworthy source. `LLDispatchListener` reports exceptions back to its invoker, if the invoker provides a "reply" `LLEventPump` name. Absent "reply", though, `LLDispatchListener` is documented to let any such exception propagate. That behavior may be okay for internal use, but in the case of the `LLEventAPI` subclass, it veers into the abuse scenario described above. Make `LLEventAPI` ensure that any exception propagating from `LLDispatchListener` is caught and logged, but not propagated. Also enrich error reporting for the "batch" `LLDispatchListener` operations. --- indra/llcommon/lleventapi.cpp | 20 ++++++++++++++++++++ indra/llcommon/lleventapi.h | 1 + indra/llcommon/lleventdispatcher.cpp | 15 +++++++++------ indra/llcommon/lleventdispatcher.h | 4 +++- 4 files changed, 33 insertions(+), 7 deletions(-) diff --git a/indra/llcommon/lleventapi.cpp b/indra/llcommon/lleventapi.cpp index 8b724256b8..4672371b4f 100644 --- a/indra/llcommon/lleventapi.cpp +++ b/indra/llcommon/lleventapi.cpp @@ -55,6 +55,26 @@ LLEventAPI::~LLEventAPI() { } +bool LLEventAPI::process(const LLSD& event) const +{ + // LLDispatchListener is documented to let DispatchError propagate if the + // incoming request has no "reply" key. That may be fine for internal-only + // use, but LLEventAPI opens the door for external requests. It should NOT + // be possible for any external requester to crash the viewer with an + // unhandled exception, especially not by something as simple as omitting + // the "reply" key. + try + { + return LLDispatchListener::process(event); + } + catch (const std::exception& err) + { + // log the exception, but otherwise ignore it + LL_WARNS("LLEventAPI") << LLError::Log::classname(err) << ": " << err.what() << LL_ENDL; + return false; + } +} + LLEventAPI::Response::Response(const LLSD& seed, const LLSD& request, const LLSD::String& replyKey): mResp(seed), mReq(request), diff --git a/indra/llcommon/lleventapi.h b/indra/llcommon/lleventapi.h index 3ae820db51..da7c58e6f0 100644 --- a/indra/llcommon/lleventapi.h +++ b/indra/llcommon/lleventapi.h @@ -157,6 +157,7 @@ protected: LLEventAPI(const LL::LazyEventAPIParams&); private: + bool process(const LLSD& event) const override; std::string mDesc; }; diff --git a/indra/llcommon/lleventdispatcher.cpp b/indra/llcommon/lleventdispatcher.cpp index 9dd6864cff..12b966fadf 100644 --- a/indra/llcommon/lleventdispatcher.cpp +++ b/indra/llcommon/lleventdispatcher.cpp @@ -800,7 +800,7 @@ void LLDispatchListener::call_one(const LLSD& name, const LLSD& event) const { result = (*this)(event); } - catch (const DispatchError& err) + catch (const std::exception& err) { if (! event.has(mReplyKey)) { @@ -810,7 +810,10 @@ void LLDispatchListener::call_one(const LLSD& name, const LLSD& event) const // Here there was an error and the incoming event has mReplyKey. Reply // with a map containing an "error" key explaining the problem. - return reply(llsd::map("error", err.what()), event); + return reply(llsd::map("error", + stringize(LLError::Log::classname(err), + ": ", err.what())), + event); } // We seem to have gotten a valid result. But we don't know whether the @@ -846,7 +849,7 @@ void LLDispatchListener::call_map(const LLSD& reqmap, const LLSD& event) const { // in case of errors, tell user the dispatch key, the fact that // we're processing a request map and the current key in that map - SetState(this, '[', key, '[', name, "]]"); + SetState transient(this, '[', key, '[', name, "]]"); // With this form, capture return value even if undefined: // presence of the key in the response map can be used to detect // which request keys succeeded. @@ -869,7 +872,7 @@ void LLDispatchListener::call_map(const LLSD& reqmap, const LLSD& event) const if (! event.has(mReplyKey)) { // can't send reply, throw - sCallFail(error); + callFail(error); } else { @@ -923,7 +926,7 @@ void LLDispatchListener::call_array(const LLSD& reqarray, const LLSD& event) con // in case of errors, tell user the dispatch key, the fact that // we're processing a request array, the current entry in that // array and the corresponding callable name - SetState(this, '[', key, '[', i, "]=", name, ']'); + SetState transient(this, '[', key, '[', i, "]=", name, ']'); // With this form, capture return value even if undefined results.append((*this)(name, args)); } @@ -944,7 +947,7 @@ void LLDispatchListener::call_array(const LLSD& reqarray, const LLSD& event) con if (! event.has(mReplyKey)) { // can't send reply, throw - sCallFail(error); + callFail(error); } else { diff --git a/indra/llcommon/lleventdispatcher.h b/indra/llcommon/lleventdispatcher.h index 5adaa3ebae..698412fdb4 100644 --- a/indra/llcommon/lleventdispatcher.h +++ b/indra/llcommon/lleventdispatcher.h @@ -853,8 +853,10 @@ public: std::string getPumpName() const { return getName(); } +protected: + virtual bool process(const LLSD& event) const; + private: - bool process(const LLSD& event) const; void call_one(const LLSD& name, const LLSD& event) const; void call_map(const LLSD& reqmap, const LLSD& event) const; void call_array(const LLSD& reqarray, const LLSD& event) const; -- cgit v1.2.3 From b79bf898c63d06486c978cbeecbe533e5872b56c Mon Sep 17 00:00:00 2001 From: Nat Goodspeed Date: Thu, 18 Jul 2024 14:42:26 -0400 Subject: Guarantee that the "login" LLEventPump has exactly that name. We used to allow "tweaking" the name. Don't. --- indra/viewer_components/login/lllogin.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/indra/viewer_components/login/lllogin.cpp b/indra/viewer_components/login/lllogin.cpp index 2a0468f3ad..dfaaff21c5 100644 --- a/indra/viewer_components/login/lllogin.cpp +++ b/indra/viewer_components/login/lllogin.cpp @@ -53,7 +53,9 @@ class LLLogin::Impl { public: Impl(): - mPump("login", true) // Create the module's event pump with a tweaked (unique) name. + // Create the module's event pump, and do not tweak the name. Multiple + // parties depend on this LLEventPump having exactly the name "login". + mPump("login", false) { mValidAuthResponse["status"] = LLSD(); mValidAuthResponse["errorcode"] = LLSD(); -- cgit v1.2.3 From 25e9c61eef179c7ad79d19261f72c1bc85a3ff1b Mon Sep 17 00:00:00 2001 From: Andrey Lihatskiy Date: Wed, 24 Jul 2024 09:00:03 +0300 Subject: Temporarily disable linux build --- .github/workflows/build.yaml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index 186306dc86..a4c354ff77 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -45,7 +45,8 @@ jobs: needs: setvar strategy: matrix: - runner: [windows-large, macos-12-xl, linux-large] + # runner: [windows-large, macos-12-xl, linux-large] + runner: [windows-large, macos-12-xl] configuration: [Release, ReleaseOS] Linden: [true] include: -- cgit v1.2.3 From decc2d3aa5ba8dc583dae5396a5ae8ca738412dd Mon Sep 17 00:00:00 2001 From: Mnikolenko Productengine Date: Thu, 25 Jul 2024 18:15:24 +0300 Subject: Lua api for Follow Camera control --- indra/newview/llagentlistener.cpp | 74 +++++-- indra/newview/llagentlistener.h | 5 +- .../scripts/lua/luafloater_camera_control.xml | 244 +++++++++++++++++++++ indra/newview/scripts/lua/require/LLAgent.lua | 28 +++ indra/newview/scripts/lua/test_camera_control.lua | 52 +++++ 5 files changed, 379 insertions(+), 24 deletions(-) create mode 100644 indra/newview/scripts/lua/luafloater_camera_control.xml create mode 100644 indra/newview/scripts/lua/require/LLAgent.lua create mode 100644 indra/newview/scripts/lua/test_camera_control.lua diff --git a/indra/newview/llagentlistener.cpp b/indra/newview/llagentlistener.cpp index 54998f3945..38a9d58c1f 100644 --- a/indra/newview/llagentlistener.cpp +++ b/indra/newview/llagentlistener.cpp @@ -31,6 +31,7 @@ #include "llagentlistener.h" #include "llagent.h" +#include "llagentcamera.h" #include "llvoavatar.h" #include "llcommandhandler.h" #include "llslurl.h" @@ -69,13 +70,6 @@ LLAgentListener::LLAgentListener(LLAgent &agent) add("resetAxes", "Set the agent to a fixed orientation (optionally specify [\"lookat\"] = array of [x, y, z])", &LLAgentListener::resetAxes); - add("getAxes", - "Obsolete - use getPosition instead\n" - "Send information about the agent's orientation on [\"reply\"]:\n" - "[\"euler\"]: map of {roll, pitch, yaw}\n" - "[\"quat\"]: array of [x, y, z, w] quaternion values", - &LLAgentListener::getAxes, - LLSDMap("reply", LLSD())); add("getPosition", "Send information about the agent's position and orientation on [\"reply\"]:\n" "[\"region\"]: array of region {x, y, z} position\n" @@ -138,6 +132,21 @@ LLAgentListener::LLAgentListener(LLAgent &agent) "[\"contrib\"]: user's land contribution to this group\n", &LLAgentListener::getGroups, LLSDMap("reply", LLSD())); + add("setCameraParams", + "Set Follow camera params, and then activate it:\n" + "[\"camera_pos\"]: vector3\n" + "[\"focus_pos\"]: vector3\n" + "[\"focus_offset\"]: vector3\n" + "[\"camera_locked\"]: boolean\n" + "[\"focus_locked\"]: boolean", + &LLAgentListener::setFollowCamParams); + add("setFollowCamActive", + "Set Follow camera active or deactivate it using boolean [\"active\"]", + &LLAgentListener::setFollowCamActive, + llsd::map("active", LLSD())); + add("removeCameraParams", + "Reset Follow camera params", + &LLAgentListener::removeFollowCamParams); } void LLAgentListener::requestTeleport(LLSD const & event_data) const @@ -296,22 +305,6 @@ void LLAgentListener::resetAxes(const LLSD& event_data) const } } -void LLAgentListener::getAxes(const LLSD& event_data) const -{ - LLQuaternion quat(mAgent.getQuat()); - F32 roll, pitch, yaw; - quat.getEulerAngles(&roll, &pitch, &yaw); - // The official query API for LLQuaternion's [x, y, z, w] values is its - // public member mQ... - LLSD reply = LLSD::emptyMap(); - reply["quat"] = llsd_copy_array(boost::begin(quat.mQ), boost::end(quat.mQ)); - reply["euler"] = LLSD::emptyMap(); - reply["euler"]["roll"] = roll; - reply["euler"]["pitch"] = pitch; - reply["euler"]["yaw"] = yaw; - sendReply(reply, event_data); -} - void LLAgentListener::getPosition(const LLSD& event_data) const { F32 roll, pitch, yaw; @@ -519,3 +512,38 @@ void LLAgentListener::getGroups(const LLSD& event) const } sendReply(LLSDMap("groups", reply), event); } + +void LLAgentListener::setFollowCamParams(const LLSD& event) const +{ + if (event.has("camera_pos")) + { + LLFollowCamMgr::getInstance()->setPosition(gAgentID, LLVector3(event["camera_pos"])); + } + if (event.has("focus_pos")) + { + LLFollowCamMgr::getInstance()->setFocus(gAgentID, LLVector3(event["focus_pos"])); + } + if (event.has("focus_offset")) + { + LLFollowCamMgr::getInstance()->setFocusOffset(gAgentID, LLVector3(event["focus_offset"])); + } + if (event.has("camera_locked")) + { + LLFollowCamMgr::getInstance()->setPositionLocked(gAgentID, event["camera_locked"]); + } + if (event.has("focus_locked")) + { + LLFollowCamMgr::getInstance()->setFocusLocked(gAgentID, event["focus_locked"]); + } + LLFollowCamMgr::getInstance()->setCameraActive(gAgentID, true); +} + +void LLAgentListener::setFollowCamActive(LLSD const & event) const +{ + LLFollowCamMgr::getInstance()->setCameraActive(gAgentID, event["active"]); +} + +void LLAgentListener::removeFollowCamParams(LLSD const & event) const +{ + LLFollowCamMgr::getInstance()->removeFollowCamParams(gAgentID); +} diff --git a/indra/newview/llagentlistener.h b/indra/newview/llagentlistener.h index c544d089ce..2a24de3f52 100644 --- a/indra/newview/llagentlistener.h +++ b/indra/newview/llagentlistener.h @@ -48,7 +48,6 @@ private: void requestStand(LLSD const & event_data) const; void requestTouch(LLSD const & event_data) const; void resetAxes(const LLSD& event_data) const; - void getAxes(const LLSD& event_data) const; void getGroups(const LLSD& event) const; void getPosition(const LLSD& event_data) const; void startAutoPilot(const LLSD& event_data); @@ -58,6 +57,10 @@ private: void stopAutoPilot(const LLSD& event_data) const; void lookAt(LLSD const & event_data) const; + void setFollowCamParams(LLSD const & event_data) const; + void setFollowCamActive(LLSD const & event_data) const; + void removeFollowCamParams(LLSD const & event_data) const; + LLViewerObject * findObjectClosestTo( const LLVector3 & position ) const; private: diff --git a/indra/newview/scripts/lua/luafloater_camera_control.xml b/indra/newview/scripts/lua/luafloater_camera_control.xml new file mode 100644 index 0000000000..0601a363e5 --- /dev/null +++ b/indra/newview/scripts/lua/luafloater_camera_control.xml @@ -0,0 +1,244 @@ + + + + Camera position: + + + + + + + + + + Focus position: + + + + + + + + + + Lock: + + + + + + + diff --git a/indra/newview/scripts/lua/require/LLAgent.lua b/indra/newview/scripts/lua/require/LLAgent.lua new file mode 100644 index 0000000000..7c6a842555 --- /dev/null +++ b/indra/newview/scripts/lua/require/LLAgent.lua @@ -0,0 +1,28 @@ +local leap = require 'leap' +local mapargs = require 'mapargs' + +local LLAgent = {} + +function LLAgent.getRegionPosition() + return leap.request('LLAgent', {op = 'getPosition'}).region +end + +function LLAgent.getGlobalPosition() + return leap.request('LLAgent', {op = 'getPosition'}).global +end + +function LLAgent.setCamera(...) + local args = mapargs('camera_pos,focus_pos,focus_offset,camera_locked,focus_locked', ...) + args.op = 'setCameraParams' + leap.send('LLAgent', args) +end + +function LLAgent.setFollowCamActive(active) + leap.send('LLAgent', {op = 'setFollowCamActive', active = active}) +end + +function LLAgent.removeCamParams() + leap.send('LLAgent', {op = 'removeCameraParams'}) +end + +return LLAgent diff --git a/indra/newview/scripts/lua/test_camera_control.lua b/indra/newview/scripts/lua/test_camera_control.lua new file mode 100644 index 0000000000..db76201932 --- /dev/null +++ b/indra/newview/scripts/lua/test_camera_control.lua @@ -0,0 +1,52 @@ +local Floater = require 'Floater' +local LLAgent = require 'LLAgent' +local leap = require 'leap' +local startup = require 'startup' +local inspect = require 'inspect' + +local flt = Floater('luafloater_camera_control.xml') + +function getValue(ctrl_name) + return flt:request({action="get_value", ctrl_name=ctrl_name}).value +end + +function setValue(ctrl_name, value) + flt:post({action="set_value", ctrl_name=ctrl_name, value=value}) +end + +function flt:commit_update_btn(event_data) + lock_focus = getValue('lock_focus_ctrl') + lock_camera = self:request({action="get_value", ctrl_name='lock_camera_ctrl'}).value + + local camera_pos = {getValue('cam_x'),getValue('cam_y'),getValue('cam_z')} + local focus_pos = {getValue('focus_x'),getValue('focus_y'),getValue('focus_z')} + + + LLAgent.setCamera{camera_pos=camera_pos,focus_pos=focus_pos, + focus_locked = lock_focus,camera_locked = lock_camera} + + self:post({action="add_text", ctrl_name="events_editor", + value = {'Updating FollowCam params', 'camera_pos:', camera_pos, 'focus_pos:', focus_pos, 'lock_focus:', lock_focus, 'lock_camera:', lock_camera}}) +end + +function flt:commit_agent_cam_btn(event_data) + agent_pos = LLAgent.getRegionPosition() + setValue('cam_x', math.floor(agent_pos[1])) + setValue('cam_y', math.floor(agent_pos[2])) + setValue('cam_z', math.floor(agent_pos[3])) +end + +function flt:commit_agent_focus_btn(event_data) + agent_pos = LLAgent.getRegionPosition() + setValue('focus_x', math.floor(agent_pos[1])) + setValue('focus_y', math.floor(agent_pos[2])) + setValue('focus_z', math.floor(agent_pos[3])) +end + +function flt:commit_reset_btn(event_data) + LLAgent.removeCamParams() + LLAgent.setFollowCamActive(false) +end + +startup.wait('STATE_LOGIN_WAIT') +flt:show() -- cgit v1.2.3 From 4edcebdb31b7d49faf94b60a66c9921e90e23899 Mon Sep 17 00:00:00 2001 From: Mnikolenko Productengine Date: Thu, 25 Jul 2024 18:29:06 +0300 Subject: Script clean up --- indra/newview/scripts/lua/test_camera_control.lua | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/indra/newview/scripts/lua/test_camera_control.lua b/indra/newview/scripts/lua/test_camera_control.lua index db76201932..e7ad69b473 100644 --- a/indra/newview/scripts/lua/test_camera_control.lua +++ b/indra/newview/scripts/lua/test_camera_control.lua @@ -1,8 +1,6 @@ local Floater = require 'Floater' local LLAgent = require 'LLAgent' -local leap = require 'leap' local startup = require 'startup' -local inspect = require 'inspect' local flt = Floater('luafloater_camera_control.xml') @@ -16,17 +14,16 @@ end function flt:commit_update_btn(event_data) lock_focus = getValue('lock_focus_ctrl') - lock_camera = self:request({action="get_value", ctrl_name='lock_camera_ctrl'}).value + lock_camera = getValue('lock_camera_ctrl') + local camera_pos = {getValue('cam_x'), getValue('cam_y'), getValue('cam_z')} + local focus_pos = {getValue('focus_x'), getValue('focus_y'), getValue('focus_z')} - local camera_pos = {getValue('cam_x'),getValue('cam_y'),getValue('cam_z')} - local focus_pos = {getValue('focus_x'),getValue('focus_y'),getValue('focus_z')} - - - LLAgent.setCamera{camera_pos=camera_pos,focus_pos=focus_pos, - focus_locked = lock_focus,camera_locked = lock_camera} + LLAgent.setCamera{camera_pos=camera_pos, focus_pos=focus_pos, + focus_locked=lock_focus, camera_locked=lock_camera} self:post({action="add_text", ctrl_name="events_editor", - value = {'Updating FollowCam params', 'camera_pos:', camera_pos, 'focus_pos:', focus_pos, 'lock_focus:', lock_focus, 'lock_camera:', lock_camera}}) + value = {'Updating FollowCam params', 'camera_pos:', camera_pos, 'focus_pos:', focus_pos, + 'lock_focus:', lock_focus, 'lock_camera:', lock_camera}}) end function flt:commit_agent_cam_btn(event_data) -- cgit v1.2.3 From 8ae0e6c07a8272c04d8a0f122e6204cf24f547af Mon Sep 17 00:00:00 2001 From: Andrey Kleshchev Date: Thu, 25 Jul 2024 16:15:18 +0300 Subject: viewer#2102 Update feature notification to notify about Favorites and remove old notifications --- indra/newview/llagent.cpp | 15 +++++---------- indra/newview/llpanelface.cpp | 4 ---- .../default/xui/en/floater_new_feature_notification.xml | 12 +----------- 3 files changed, 6 insertions(+), 25 deletions(-) diff --git a/indra/newview/llagent.cpp b/indra/newview/llagent.cpp index 8e15a08373..95ba14dda7 100644 --- a/indra/newview/llagent.cpp +++ b/indra/newview/llagent.cpp @@ -120,8 +120,8 @@ const F32 MIN_FIDGET_TIME = 8.f; // seconds const F32 MAX_FIDGET_TIME = 20.f; // seconds const S32 UI_FEATURE_VERSION = 1; -// For version 1: 1 - inventory, 2 - gltf -const S32 UI_FEATURE_FLAGS = 3; +// For version 1, flag holds: 1 - inventory thumbnails, 2 - gltf, 4 - inventory favorites +const S32 UI_FEATURE_FLAGS = 7; // The agent instance. LLAgent gAgent; @@ -604,7 +604,7 @@ void LLAgent::getFeatureVersionAndFlags(S32& version, S32& flags) if (feature_version.isInteger()) { version = feature_version.asInteger(); - flags = 1; // inventory flag + flags = 3; // show 'favorites' notification } else if (feature_version.isMap()) { @@ -630,13 +630,8 @@ void LLAgent::showLatestFeatureNotification(const std::string key) if (key == "inventory") { - // Notify user about new thumbnail support - flag = 1; - } - - if (key == "gltf") - { - flag = 2; + // Notify user about new favorites support + flag = 4; } if ((flags & flag) == 0) diff --git a/indra/newview/llpanelface.cpp b/indra/newview/llpanelface.cpp index 38af68bfff..c8e876ee7a 100644 --- a/indra/newview/llpanelface.cpp +++ b/indra/newview/llpanelface.cpp @@ -505,10 +505,6 @@ LLPanelFace::~LLPanelFace() void LLPanelFace::onVisibilityChange(BOOL new_visibility) { - if (new_visibility) - { - gAgent.showLatestFeatureNotification("gltf"); - } LLPanel::onVisibilityChange(new_visibility); } diff --git a/indra/newview/skins/default/xui/en/floater_new_feature_notification.xml b/indra/newview/skins/default/xui/en/floater_new_feature_notification.xml index 57b74b360a..9981e5d893 100644 --- a/indra/newview/skins/default/xui/en/floater_new_feature_notification.xml +++ b/indra/newview/skins/default/xui/en/floater_new_feature_notification.xml @@ -17,17 +17,7 @@ New inventory features -You can now add preview images to inventory items and view a folder in its own window. -Learn more in this [https://community.secondlife.com/blogs/entry/13637-new-features-inventory-item-preview-and-single-folder-view/ blogpost] - - -New GLTF PBR materials support - - -You can now use expanded material support with the ability to import and edit GLTF Physically Based Rendering (PBR) Materials. -In order to support the addition of the GLTF format, some areas in the viewer may appear darker than usual. - -Learn more about [https://wiki.secondlife.com/wiki/PBR_Materials Physically Based Rendering (PBR)] +You can now mark items and folders as favorites. Favorited items will appear in the Favorites tab of inventory and by default will be highlighted with a star in the main inventory view. Date: Mon, 22 Jul 2024 18:47:41 +0300 Subject: Update expired cert in integration test see fe8c976 for more info --- indra/newview/tests/llsechandler_basic_test.cpp | 670 ++++++++++++------------ 1 file changed, 345 insertions(+), 325 deletions(-) diff --git a/indra/newview/tests/llsechandler_basic_test.cpp b/indra/newview/tests/llsechandler_basic_test.cpp index bfe32406cb..cf37a4713c 100644 --- a/indra/newview/tests/llsechandler_basic_test.cpp +++ b/indra/newview/tests/llsechandler_basic_test.cpp @@ -232,381 +232,402 @@ namespace tut "Certificate:\n" " Data:\n" " Version: 3 (0x2)\n" - " Serial Number:\n" - " 82:2f:8f:eb:8d:06:24:b0\n" + " Serial Number: ef:54:d8:f7:da:18:e8:19\n" " Signature Algorithm: sha256WithRSAEncryption\n" " Issuer: C=US, ST=California, L=San Francisco, O=Linden Lab, OU=Second Life Engineering, CN=Integration Test Root CA/emailAddress=noreply@lindenlab.com\n" " Validity\n" - " Not Before: May 22 22:19:45 2018 GMT\n" - " Not After : May 17 22:19:45 2038 GMT\n" + " Not Before: Jul 23 11:46:26 2024 GMT\n" + " Not After : Jul 21 11:46:26 2034 GMT\n" " Subject: C=US, ST=California, L=San Francisco, O=Linden Lab, OU=Second Life Engineering, CN=Integration Test Root CA/emailAddress=noreply@lindenlab.com\n" " Subject Public Key Info:\n" " Public Key Algorithm: rsaEncryption\n" " Public-Key: (4096 bit)\n" " Modulus:\n" - " 00:bd:e0:79:dd:3b:a6:ac:87:d0:39:f0:58:c7:a4:\n" - " 42:42:f6:5f:93:b0:36:04:b5:e2:d5:f7:2a:c0:6c:\n" - " a0:13:d2:1e:02:81:57:02:50:4c:57:b7:ef:27:9e:\n" - " f6:f1:f1:30:30:72:1e:57:34:e5:3f:82:3c:21:c4:\n" - " 66:d2:73:63:6c:91:e6:dd:49:9e:9c:b1:34:6a:81:\n" - " 45:a1:6e:c4:50:28:f2:d8:e3:fe:80:2f:83:aa:28:\n" - " 91:b4:8c:57:c9:f1:16:d9:0c:87:3c:25:80:a0:81:\n" - " 8d:71:f2:96:e2:16:f1:97:c4:b0:d8:53:bb:13:6c:\n" - " 73:54:2f:29:94:85:cf:86:6e:75:71:ad:39:e3:fc:\n" - " 39:12:53:93:1c:ce:39:e0:33:da:49:b7:3d:af:b0:\n" - " 37:ce:77:09:03:27:32:70:c0:9c:7f:9c:89:ce:90:\n" - " 45:b0:7d:94:8b:ff:13:27:ba:88:7f:ae:c4:aa:73:\n" - " d5:47:b8:87:69:89:80:0c:c1:22:18:78:c2:0d:47:\n" - " d9:10:ff:80:79:0d:46:71:ec:d9:ba:c9:f3:77:fd:\n" - " 92:6d:1f:0f:d9:54:18:6d:f6:72:24:5c:5c:3d:43:\n" - " 49:35:3e:1c:28:de:7e:44:dc:29:c3:9f:62:04:46:\n" - " aa:c4:e6:69:6a:15:f8:e3:74:1c:14:e9:f4:97:7c:\n" - " 30:6c:d4:28:fc:2a:0e:1d:6d:39:2e:1d:f9:17:43:\n" - " 35:5d:23:e7:ba:e3:a8:e9:97:6b:3c:3e:23:ef:d8:\n" - " bc:fb:7a:57:37:39:93:59:03:fc:78:ca:b1:31:ef:\n" - " 26:19:ed:56:e1:63:c3:ad:99:80:5b:47:b5:03:35:\n" - " 5f:fe:6a:a6:21:63:ec:50:fb:4e:c9:f9:ae:a5:66:\n" - " d0:55:33:8d:e6:c5:50:5a:c6:8f:5c:34:45:a7:72:\n" - " da:50:f6:66:4c:19:f5:d1:e4:fb:11:8b:a1:b5:4e:\n" - " 09:43:81:3d:39:28:86:3b:fe:07:28:97:02:b5:3a:\n" - " 07:5f:4a:20:80:1a:7d:a4:8c:f7:6c:f6:c5:9b:f6:\n" - " 61:e5:c7:b0:c3:d5:58:38:7b:bb:47:1e:34:d6:16:\n" - " 55:c5:d2:6c:b0:93:77:b1:90:69:06:b1:53:cb:1b:\n" - " 84:71:cf:b8:87:1b:1e:44:35:b4:2b:bb:04:59:58:\n" - " 0b:e8:93:d8:ae:21:9b:b1:1c:89:30:ae:11:80:77:\n" - " cc:16:f3:d6:35:ed:a1:b3:70:b3:4f:cd:a1:56:99:\n" - " ee:0e:c0:00:a4:09:70:c3:5b:0b:be:a1:07:18:dd:\n" - " c6:f4:6d:8b:58:bc:f9:bb:4b:01:2c:f6:cc:2c:9b:\n" - " 87:0e:b1:4f:9c:10:be:fc:45:e2:a4:ec:7e:fc:ff:\n" - " 45:b8:53\n" + " 00:c6:cc:07:f4:0b:17:06:4d:a6:30:b4:c7:02:6b:\n" + " 9d:a4:47:a6:09:0e:60:1a:32:d4:6b:42:88:ee:c5:\n" + " b9:e9:fb:b5:0b:60:dc:a2:45:92:a5:bb:88:12:fc:\n" + " 42:1a:80:32:79:16:62:7a:97:af:84:28:53:3c:c1:\n" + " f2:68:c0:4e:45:e4:0a:63:f9:34:1d:a2:8b:cc:70:\n" + " df:c6:65:c0:ba:31:32:d2:9d:0c:c8:ce:dc:11:12:\n" + " a4:11:fa:d3:c8:56:e2:31:8a:e3:fb:91:40:da:25:\n" + " 55:d1:f2:75:9b:4d:fa:b8:1f:b5:6d:9b:e1:fe:5d:\n" + " e8:c4:02:79:14:ef:7d:5a:b3:3a:1e:b6:d0:60:2c:\n" + " 90:dc:22:e2:c5:ae:85:1f:b4:9d:7a:20:f8:af:63:\n" + " 56:25:1a:64:f3:9c:3f:9a:cf:68:08:0a:37:db:d0:\n" + " a3:65:26:db:80:82:ff:e0:1b:51:c8:ee:f6:ad:c2:\n" + " b4:f2:ab:d2:e8:85:86:77:28:d0:63:4a:71:78:41:\n" + " e3:8c:7f:71:51:31:af:24:3f:fa:8d:d0:d8:0b:e2:\n" + " 7e:79:33:8a:bb:d2:00:9e:2e:c8:cd:d5:50:92:b8:\n" + " 5c:5a:0b:99:ef:05:39:67:da:be:70:36:51:37:37:\n" + " 20:6f:84:ab:29:11:00:7b:38:32:ba:0b:bc:34:a6:\n" + " b5:c6:a7:f0:c0:25:2d:38:0b:72:40:ab:cf:e6:ff:\n" + " 97:75:ff:e2:a9:3c:2a:57:ce:e4:52:20:8c:de:fe:\n" + " 68:ce:54:85:37:ba:b3:7f:2e:53:58:ea:9b:ac:79:\n" + " 6b:16:65:b8:11:88:5a:46:eb:9e:9e:80:3c:89:91:\n" + " 35:e0:c5:33:45:c8:86:4d:25:51:39:b1:72:97:2b:\n" + " b3:c8:c9:e8:11:cd:32:41:c8:c1:56:22:7e:33:81:\n" + " 85:61:ab:da:9e:6e:5f:24:1c:0f:9b:fa:da:9d:86:\n" + " 1a:66:f6:32:2a:10:80:ea:72:7a:4a:ef:c0:f2:7c:\n" + " 43:02:e6:70:19:6a:e1:02:0a:00:80:51:1c:a3:03:\n" + " 8b:6d:89:9f:91:37:90:d6:d8:9c:73:77:06:9e:bc:\n" + " 95:89:66:ee:43:40:a3:ee:43:a3:f6:2d:43:dd:7b:\n" + " f0:2f:0b:12:37:49:b7:81:5a:e2:54:6d:71:88:ff:\n" + " fe:7e:41:25:35:4c:b4:b9:62:65:dd:9f:1f:7a:06:\n" + " 6e:2b:20:58:78:da:08:66:a8:f1:89:de:8f:7f:5c:\n" + " 5e:c2:72:33:7f:b6:8e:41:4c:26:f6:4c:d4:0e:11:\n" + " 44:da:c7:14:f7:8b:79:4e:53:29:87:15:b1:12:e9:\n" + " 19:2b:54:33:d6:2e:7f:bd:42:20:be:fc:d7:9c:b4:\n" + " 7a:0a:db\n" " Exponent: 65537 (0x10001)\n" " X509v3 extensions:\n" - " X509v3 Subject Key Identifier: \n" - " 8A:22:C6:9C:2E:11:F3:40:0C:CE:82:0C:22:59:FF:F8:7F:D0:B9:13\n" - " X509v3 Authority Key Identifier: \n" - " keyid:8A:22:C6:9C:2E:11:F3:40:0C:CE:82:0C:22:59:FF:F8:7F:D0:B9:13\n" + " X509v3 Subject Key Identifier:\n" + " 4D:7D:AE:0D:A5:5E:22:5A:6A:8F:19:61:54:B3:58:CB:7B:C0:BD:DA\n" + " X509v3 Authority Key Identifier:\n" + " keyid:4D:7D:AE:0D:A5:5E:22:5A:6A:8F:19:61:54:B3:58:CB:7B:C0:BD:DA\n" "\n" - " X509v3 Basic Constraints: critical\n" + " X509v3 Basic Constraints:\n" " CA:TRUE\n" - " X509v3 Key Usage: critical\n" - " Digital Signature, Certificate Sign, CRL Sign\n" " Signature Algorithm: sha256WithRSAEncryption\n" - " b3:cb:33:eb:0e:02:64:f4:55:9a:3d:03:9a:cf:6a:4c:18:43:\n" - " f7:42:cb:65:dc:61:52:e5:9f:2f:42:97:3c:93:16:22:d4:af:\n" - " ae:b2:0f:c3:9b:ef:e0:cc:ee:b6:b1:69:a3:d8:da:26:c3:ad:\n" - " 3b:c5:64:dc:9f:d4:c2:53:4b:91:6d:c4:92:09:0b:ac:f0:99:\n" - " be:6f:b9:3c:03:4a:6d:9f:01:5d:ec:5a:9a:f3:a7:e5:3b:2c:\n" - " 99:57:7d:7e:25:15:68:20:12:30:96:16:86:f5:db:74:90:60:\n" - " fe:8b:df:99:f6:f7:62:49:9f:bc:8d:45:23:0a:c8:73:b8:79:\n" - " 80:3c:b9:e5:72:85:4b:b3:81:66:74:a2:72:92:4c:44:fd:7b:\n" - " 46:2e:21:a2:a9:81:a2:f3:26:4d:e3:89:7d:78:b0:c6:6f:b5:\n" - " 87:cb:ee:25:ed:27:1f:75:13:fa:6d:e9:37:73:ad:07:bb:af:\n" - " d3:6c:87:ea:02:01:70:bd:53:aa:ce:39:2c:d4:66:39:33:aa:\n" - " d1:9c:ee:67:e3:a9:45:d2:7b:2e:54:09:af:70:5f:3f:5a:67:\n" - " 2e:6c:72:ef:e0:9d:92:28:4a:df:ba:0b:b7:23:ca:5b:04:11:\n" - " 45:d1:51:e9:ea:c9:ec:54:fa:34:46:ae:fc:dc:6c:f8:1e:2c:\n" - " 9e:f4:71:51:8d:b5:a1:26:9a:13:30:be:1e:41:25:59:58:05:\n" - " 2c:64:c8:f9:5e:38:ae:dc:93:b0:8a:d6:38:74:02:cb:ce:ce:\n" - " 95:31:76:f6:7c:bf:a4:a1:8e:27:fd:ca:74:82:d1:e1:4d:b6:\n" - " 48:51:fa:c5:17:59:22:a3:84:be:82:c8:83:ec:61:a0:f4:ee:\n" - " 2c:e3:a3:ea:e5:51:c9:d3:4f:db:85:bd:ba:7a:52:14:b6:03:\n" - " ed:43:17:d8:d7:1c:22:5e:c9:56:d9:d6:81:96:11:e3:5e:01:\n" - " 40:91:30:09:da:a3:5f:d3:27:60:e5:9d:6c:da:d0:f0:39:01:\n" - " 23:4a:a6:15:7a:4a:82:eb:ec:72:4a:1d:36:dc:6f:83:c4:85:\n" - " 84:b5:8d:cd:09:e5:12:63:f3:21:56:c8:64:6b:db:b8:cf:d4:\n" - " df:ca:a8:24:8e:df:8d:63:a5:96:84:bf:ff:8b:7e:46:7a:f0:\n" - " c7:73:7c:70:8a:f5:17:d0:ac:c8:89:1e:d7:89:42:0f:4d:66:\n" - " c4:d8:bb:36:a8:ae:ca:e1:cf:e2:88:f6:cf:b0:44:4a:5f:81:\n" - " 50:4b:d6:28:81:cd:6c:f0:ec:e6:09:08:f2:59:91:a2:69:ac:\n" - " c7:81:fa:ab:61:3e:db:6f:f6:7f:db:1a:9e:b9:5d:cc:cc:33:\n" - " fa:95:c6:f7:8d:4b:30:f3\n" + " 5b:40:71:96:c8:d1:57:3f:fc:f2:3c:75:fb:c9:a6:a7:63:8a:\n" + " 22:23:96:0f:40:77:77:e2:7f:76:fc:5f:7b:1c:bd:ea:ca:f0:\n" + " be:1a:fd:59:e6:0e:00:d1:78:44:01:28:f4:01:68:67:78:cf:\n" + " 78:43:36:ac:b2:5c:13:0e:2a:94:59:88:9e:64:46:42:0a:9b:\n" + " be:7d:2d:10:11:fe:8b:64:01:fb:00:c5:2e:47:63:c0:93:3a:\n" + " 4a:f8:6c:fc:a9:16:58:ab:bc:7b:6b:20:31:9d:d7:d8:84:01:\n" + " cc:ce:52:7f:a1:18:2f:5c:c9:59:58:9a:98:b9:ef:54:d7:a0:\n" + " 56:79:28:ba:ad:f5:e5:fd:7e:d8:d6:be:dd:25:76:6f:fa:8a:\n" + " 07:f6:8e:0f:83:43:19:ee:96:c4:c9:54:df:19:5a:4c:ae:25:\n" + " 57:a2:5d:d5:e8:0a:66:d8:19:e9:c4:44:ba:6a:3b:b3:86:ae:\n" + " 44:c0:7c:6e:e5:a0:6c:45:bb:7f:34:94:e9:d3:d4:f4:04:0b:\n" + " eb:fc:9a:fa:67:d4:e5:83:5e:08:09:9c:70:a9:d3:0d:8a:08:\n" + " ed:3c:04:33:4f:ac:02:d9:5c:99:62:12:fc:0e:8d:55:8a:ce:\n" + " ca:28:5a:1a:9e:c9:59:8e:f0:f5:19:c7:30:1e:59:1f:3c:77:\n" + " 6d:fc:a2:31:ec:bf:83:fd:14:26:91:68:88:05:4c:87:82:e0:\n" + " 33:f4:ee:d8:56:97:23:3a:00:9b:e7:a2:10:c2:83:28:c6:c0:\n" + " c1:92:49:95:c1:d3:e1:43:e8:8f:0c:d0:ae:e3:50:17:1a:8d:\n" + " 0f:4a:60:71:76:8e:9e:fb:15:76:cd:cd:69:2c:59:24:69:d2:\n" + " 0f:f2:d5:0e:96:95:2b:2e:d7:81:ed:b3:7b:6f:ce:60:32:b5:\n" + " f0:f6:74:ea:27:3a:ee:2c:96:7b:e0:06:6c:33:25:c4:60:da:\n" + " 76:de:c4:a1:22:b6:b1:63:57:10:3c:62:60:98:47:39:9e:38:\n" + " ce:c7:ef:75:75:19:d3:26:2a:cf:46:e3:b0:72:38:49:ee:c3:\n" + " 4e:52:97:e5:e5:b8:bc:b1:45:56:98:54:0a:63:c8:87:ff:a0:\n" + " cb:28:12:5c:8f:a2:6e:a7:f9:50:98:2d:a5:26:08:df:16:29:\n" + " 19:63:7f:6c:b4:41:20:f7:5d:ef:6a:90:fd:1a:08:1c:c2:4c:\n" + " 3e:77:ea:e0:df:c0:dd:aa:a2:36:e7:e8:be:98:39:0a:68:59:\n" + " 8e:a0:71:2f:7c:92:ab:e0:c4:c1:c2:eb:89:b6:34:ce:44:ab:\n" + " f9:f6:a4:c8:7b:ad:a8:bc:c9:04:7c:d5:4c:a4:d2:8b:54:23:\n" + " 89:68:86:4e:07:36:d9:bc\n" "-----BEGIN CERTIFICATE-----\n" - "MIIGXDCCBESgAwIBAgIJAIIvj+uNBiSwMA0GCSqGSIb3DQEBCwUAMIG6MQswCQYD\n" + "MIIGSTCCBDGgAwIBAgIJAO9U2PfaGOgZMA0GCSqGSIb3DQEBCwUAMIG6MQswCQYD\n" "VQQGEwJVUzETMBEGA1UECAwKQ2FsaWZvcm5pYTEWMBQGA1UEBwwNU2FuIEZyYW5j\n" "aXNjbzETMBEGA1UECgwKTGluZGVuIExhYjEgMB4GA1UECwwXU2Vjb25kIExpZmUg\n" "RW5naW5lZXJpbmcxITAfBgNVBAMMGEludGVncmF0aW9uIFRlc3QgUm9vdCBDQTEk\n" - "MCIGCSqGSIb3DQEJARYVbm9yZXBseUBsaW5kZW5sYWIuY29tMB4XDTE4MDUyMjIy\n" - "MTk0NVoXDTM4MDUxNzIyMTk0NVowgboxCzAJBgNVBAYTAlVTMRMwEQYDVQQIDApD\n" + "MCIGCSqGSIb3DQEJARYVbm9yZXBseUBsaW5kZW5sYWIuY29tMB4XDTI0MDcyMzEx\n" + "NDYyNloXDTM0MDcyMTExNDYyNlowgboxCzAJBgNVBAYTAlVTMRMwEQYDVQQIDApD\n" "YWxpZm9ybmlhMRYwFAYDVQQHDA1TYW4gRnJhbmNpc2NvMRMwEQYDVQQKDApMaW5k\n" "ZW4gTGFiMSAwHgYDVQQLDBdTZWNvbmQgTGlmZSBFbmdpbmVlcmluZzEhMB8GA1UE\n" "AwwYSW50ZWdyYXRpb24gVGVzdCBSb290IENBMSQwIgYJKoZIhvcNAQkBFhVub3Jl\n" "cGx5QGxpbmRlbmxhYi5jb20wggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoIC\n" - "AQC94HndO6ash9A58FjHpEJC9l+TsDYEteLV9yrAbKAT0h4CgVcCUExXt+8nnvbx\n" - "8TAwch5XNOU/gjwhxGbSc2NskebdSZ6csTRqgUWhbsRQKPLY4/6AL4OqKJG0jFfJ\n" - "8RbZDIc8JYCggY1x8pbiFvGXxLDYU7sTbHNULymUhc+GbnVxrTnj/DkSU5Mczjng\n" - "M9pJtz2vsDfOdwkDJzJwwJx/nInOkEWwfZSL/xMnuoh/rsSqc9VHuIdpiYAMwSIY\n" - "eMINR9kQ/4B5DUZx7Nm6yfN3/ZJtHw/ZVBht9nIkXFw9Q0k1Phwo3n5E3CnDn2IE\n" - "RqrE5mlqFfjjdBwU6fSXfDBs1Cj8Kg4dbTkuHfkXQzVdI+e646jpl2s8PiPv2Lz7\n" - "elc3OZNZA/x4yrEx7yYZ7VbhY8OtmYBbR7UDNV/+aqYhY+xQ+07J+a6lZtBVM43m\n" - "xVBaxo9cNEWnctpQ9mZMGfXR5PsRi6G1TglDgT05KIY7/gcolwK1OgdfSiCAGn2k\n" - "jPds9sWb9mHlx7DD1Vg4e7tHHjTWFlXF0mywk3exkGkGsVPLG4Rxz7iHGx5ENbQr\n" - "uwRZWAvok9iuIZuxHIkwrhGAd8wW89Y17aGzcLNPzaFWme4OwACkCXDDWwu+oQcY\n" - "3cb0bYtYvPm7SwEs9swsm4cOsU+cEL78ReKk7H78/0W4UwIDAQABo2MwYTAdBgNV\n" - "HQ4EFgQUiiLGnC4R80AMzoIMIln/+H/QuRMwHwYDVR0jBBgwFoAUiiLGnC4R80AM\n" - "zoIMIln/+H/QuRMwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAYYwDQYJ\n" - "KoZIhvcNAQELBQADggIBALPLM+sOAmT0VZo9A5rPakwYQ/dCy2XcYVLlny9ClzyT\n" - "FiLUr66yD8Ob7+DM7raxaaPY2ibDrTvFZNyf1MJTS5FtxJIJC6zwmb5vuTwDSm2f\n" - "AV3sWprzp+U7LJlXfX4lFWggEjCWFob123SQYP6L35n292JJn7yNRSMKyHO4eYA8\n" - "ueVyhUuzgWZ0onKSTET9e0YuIaKpgaLzJk3jiX14sMZvtYfL7iXtJx91E/pt6Tdz\n" - "rQe7r9Nsh+oCAXC9U6rOOSzUZjkzqtGc7mfjqUXSey5UCa9wXz9aZy5scu/gnZIo\n" - "St+6C7cjylsEEUXRUenqyexU+jRGrvzcbPgeLJ70cVGNtaEmmhMwvh5BJVlYBSxk\n" - "yPleOK7ck7CK1jh0AsvOzpUxdvZ8v6Shjif9ynSC0eFNtkhR+sUXWSKjhL6CyIPs\n" - "YaD07izjo+rlUcnTT9uFvbp6UhS2A+1DF9jXHCJeyVbZ1oGWEeNeAUCRMAnao1/T\n" - "J2DlnWza0PA5ASNKphV6SoLr7HJKHTbcb4PEhYS1jc0J5RJj8yFWyGRr27jP1N/K\n" - "qCSO341jpZaEv/+LfkZ68MdzfHCK9RfQrMiJHteJQg9NZsTYuzaorsrhz+KI9s+w\n" - "REpfgVBL1iiBzWzw7OYJCPJZkaJprMeB+qthPttv9n/bGp65XczMM/qVxveNSzDz\n" + "AQDGzAf0CxcGTaYwtMcCa52kR6YJDmAaMtRrQojuxbnp+7ULYNyiRZKlu4gS/EIa\n" + "gDJ5FmJ6l6+EKFM8wfJowE5F5Apj+TQdoovMcN/GZcC6MTLSnQzIztwREqQR+tPI\n" + "VuIxiuP7kUDaJVXR8nWbTfq4H7Vtm+H+XejEAnkU731aszoettBgLJDcIuLFroUf\n" + "tJ16IPivY1YlGmTznD+az2gICjfb0KNlJtuAgv/gG1HI7vatwrTyq9LohYZ3KNBj\n" + "SnF4QeOMf3FRMa8kP/qN0NgL4n55M4q70gCeLsjN1VCSuFxaC5nvBTln2r5wNlE3\n" + "NyBvhKspEQB7ODK6C7w0prXGp/DAJS04C3JAq8/m/5d1/+KpPCpXzuRSIIze/mjO\n" + "VIU3urN/LlNY6puseWsWZbgRiFpG656egDyJkTXgxTNFyIZNJVE5sXKXK7PIyegR\n" + "zTJByMFWIn4zgYVhq9qebl8kHA+b+tqdhhpm9jIqEIDqcnpK78DyfEMC5nAZauEC\n" + "CgCAURyjA4ttiZ+RN5DW2JxzdwaevJWJZu5DQKPuQ6P2LUPde/AvCxI3SbeBWuJU\n" + "bXGI//5+QSU1TLS5YmXdnx96Bm4rIFh42ghmqPGJ3o9/XF7CcjN/to5BTCb2TNQO\n" + "EUTaxxT3i3lOUymHFbES6RkrVDPWLn+9QiC+/NectHoK2wIDAQABo1AwTjAdBgNV\n" + "HQ4EFgQUTX2uDaVeIlpqjxlhVLNYy3vAvdowHwYDVR0jBBgwFoAUTX2uDaVeIlpq\n" + "jxlhVLNYy3vAvdowDAYDVR0TBAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAgEAW0Bx\n" + "lsjRVz/88jx1+8mmp2OKIiOWD0B3d+J/dvxfexy96srwvhr9WeYOANF4RAEo9AFo\n" + "Z3jPeEM2rLJcEw4qlFmInmRGQgqbvn0tEBH+i2QB+wDFLkdjwJM6Svhs/KkWWKu8\n" + "e2sgMZ3X2IQBzM5Sf6EYL1zJWViamLnvVNegVnkouq315f1+2Na+3SV2b/qKB/aO\n" + "D4NDGe6WxMlU3xlaTK4lV6Jd1egKZtgZ6cREumo7s4auRMB8buWgbEW7fzSU6dPU\n" + "9AQL6/ya+mfU5YNeCAmccKnTDYoI7TwEM0+sAtlcmWIS/A6NVYrOyihaGp7JWY7w\n" + "9RnHMB5ZHzx3bfyiMey/g/0UJpFoiAVMh4LgM/Tu2FaXIzoAm+eiEMKDKMbAwZJJ\n" + "lcHT4UPojwzQruNQFxqND0pgcXaOnvsVds3NaSxZJGnSD/LVDpaVKy7Xge2ze2/O\n" + "YDK18PZ06ic67iyWe+AGbDMlxGDadt7EoSK2sWNXEDxiYJhHOZ44zsfvdXUZ0yYq\n" + "z0bjsHI4Se7DTlKX5eW4vLFFVphUCmPIh/+gyygSXI+ibqf5UJgtpSYI3xYpGWN/\n" + "bLRBIPdd72qQ/RoIHMJMPnfq4N/A3aqiNufovpg5CmhZjqBxL3ySq+DEwcLribY0\n" + "zkSr+fakyHutqLzJBHzVTKTSi1QjiWiGTgc22bw=\n" "-----END CERTIFICATE-----\n" - ); + ); + const std::string mPemIntermediateCert( "Certificate:\n" " Data:\n" " Version: 3 (0x2)\n" - " Serial Number: 4096 (0x1000)\n" + " Serial Number: 85:bb:4b:66:26:db:9a:c6\n" " Signature Algorithm: sha256WithRSAEncryption\n" " Issuer: C=US, ST=California, L=San Francisco, O=Linden Lab, OU=Second Life Engineering, CN=Integration Test Root CA/emailAddress=noreply@lindenlab.com\n" " Validity\n" - " Not Before: May 22 22:39:08 2018 GMT\n" - " Not After : May 19 22:39:08 2028 GMT\n" - " Subject: C=US, ST=California, O=Linden Lab, OU=Second Life Engineering, CN=Integration Test Intermediate CA/emailAddress=noreply@lindenlab.com\n" + " Not Before: Jul 23 11:46:33 2024 GMT\n" + " Not After : Jul 21 11:46:33 2034 GMT\n" + " Subject: C=US, ST=California, L=San Francisco, O=Linden Lab, OU=Second Life Engineering, CN=Integration Test Intermediate CA/emailAddress=noreply@lindenlab.com\n" " Subject Public Key Info:\n" " Public Key Algorithm: rsaEncryption\n" " Public-Key: (4096 bit)\n" " Modulus:\n" - " 00:ce:a3:70:e2:c4:fb:4b:97:90:a1:30:bb:c1:1b:\n" - " 13:b9:aa:7e:46:17:a3:26:8d:69:3f:5e:73:95:e8:\n" - " 6a:b1:0a:b4:8f:50:65:e3:c6:5c:39:24:34:df:0b:\n" - " b7:cc:ce:62:0c:36:5a:12:2c:fe:35:4c:e9:1c:ac:\n" - " 80:5e:24:99:d7:aa:bd:be:48:c0:62:64:77:36:88:\n" - " 66:ce:f4:a8:dd:d2:76:24:62:90:55:41:fc:1d:13:\n" - " 4e:a7:4e:57:bc:a8:a4:59:4b:2c:5a:1c:d8:cc:16:\n" - " de:e8:88:30:c9:95:df:2f:a6:14:28:0f:eb:34:46:\n" - " 12:58:ba:da:0e:e6:de:9c:15:f6:f4:e3:9f:74:aa:\n" - " 70:89:79:8b:e9:5a:7b:18:54:15:94:3a:23:0a:65:\n" - " 78:05:d9:33:90:2a:ce:15:18:0d:52:fc:5c:31:65:\n" - " 20:d0:12:37:8c:11:80:ba:d4:b0:82:73:00:4b:49:\n" - " be:cb:d6:bc:e7:cd:61:f3:00:98:99:74:5a:37:81:\n" - " 49:96:7e:14:01:1b:86:d2:d0:06:94:40:63:63:46:\n" - " 11:fc:33:5c:bd:3a:5e:d4:e5:44:47:64:50:bd:a6:\n" - " 97:55:70:64:9b:26:cc:de:20:82:90:6a:83:41:9c:\n" - " 6f:71:47:14:be:cb:68:7c:85:be:ef:2e:76:12:19:\n" - " d3:c9:87:32:b4:ac:60:20:16:28:2d:af:bc:e8:01:\n" - " c6:7f:fb:d8:11:d5:f4:b7:14:bd:27:08:5b:72:be:\n" - " 09:e0:91:c8:9c:7b:b4:b3:12:ef:32:36:be:b1:b9:\n" - " a2:b7:e3:69:47:30:76:ba:9c:9b:19:99:4d:53:dd:\n" - " 5c:e8:2c:f1:b2:64:69:cf:15:bd:f8:bb:58:95:73:\n" - " 58:38:95:b4:7a:cf:84:29:a6:c2:db:f0:bd:ef:97:\n" - " 26:d4:99:ac:d7:c7:be:b0:0d:11:f4:26:86:2d:77:\n" - " 42:52:25:d7:56:c7:e3:97:b1:36:5c:97:71:d0:9b:\n" - " f5:b5:50:8d:f9:ff:fb:10:77:3c:b5:53:6d:a1:43:\n" - " 35:a9:03:32:05:ab:d7:f5:d1:19:bd:5f:92:a3:00:\n" - " 2a:79:37:a4:76:4f:e9:32:0d:e4:86:bb:ea:c3:1a:\n" - " c5:33:e8:16:d4:a5:d8:e0:e8:bb:c2:f0:22:15:e2:\n" - " d9:8c:ae:ac:7d:2b:bf:eb:a3:4c:3b:29:1d:94:ac:\n" - " a3:bb:6d:ba:6d:03:91:03:cf:46:12:c4:66:21:c5:\n" - " c6:67:d8:11:19:79:01:0e:6e:84:1c:76:6f:11:3d:\n" - " eb:94:89:c5:6a:26:1f:cd:e0:11:8b:51:ee:99:35:\n" - " 69:e5:7f:0b:77:2a:94:e4:4b:64:b9:83:04:30:05:\n" - " e4:a2:e3\n" + " 00:be:f7:d2:cb:e4:5c:46:7b:e2:11:22:89:72:da:\n" + " 77:72:ec:05:87:19:f7:77:07:fd:67:d7:af:13:d5:\n" + " 76:12:92:dd:69:4d:22:47:b0:3d:94:8a:6a:95:85:\n" + " 34:b8:78:c3:9d:63:32:b1:4b:0a:b6:0e:05:7b:ab:\n" + " 06:23:fc:0d:21:b5:fc:c6:6a:5a:36:be:6e:fc:c7:\n" + " 47:97:a3:18:2e:33:cd:0e:8a:75:2b:b7:29:e9:68:\n" + " 4a:90:53:45:db:73:ff:b3:e5:c1:d4:6b:dd:3a:b1:\n" + " ef:53:9f:23:e9:c6:87:ce:67:b9:fb:a4:d5:76:21:\n" + " 03:cb:c5:72:6b:c5:a6:07:55:fb:47:90:e8:92:38:\n" + " 73:14:11:8e:ff:21:b9:35:64:5a:61:c7:fc:1f:e4:\n" + " 4d:47:e5:03:cc:0b:c3:69:66:71:84:0c:18:2f:61:\n" + " 7f:34:dd:f2:91:e3:b7:9d:a8:b8:db:3f:6e:6f:96:\n" + " fa:34:06:82:04:c8:18:cc:de:8b:7f:26:b5:48:53:\n" + " fb:fb:15:7b:0e:38:60:fe:da:21:98:8d:73:07:b2:\n" + " 6b:fd:ad:21:59:e7:84:66:e1:04:16:1c:be:13:34:\n" + " 28:43:2c:09:3d:e4:77:2a:a4:ad:6d:f9:26:04:f7:\n" + " 43:73:9b:d9:ea:1a:43:6a:b4:db:88:f8:f9:bd:34:\n" + " f8:a6:e8:7a:ab:b4:b2:e1:29:47:a6:ba:b8:65:9c:\n" + " c6:b3:af:13:43:38:ef:2a:05:77:9f:8f:f0:0c:56:\n" + " 21:c2:92:d2:2c:c3:32:50:d1:62:ae:51:fc:99:e6:\n" + " b8:38:f8:83:1d:8d:40:11:e0:1d:51:5d:3f:fa:55:\n" + " 61:b6:18:09:1e:71:af:95:64:9c:ea:c6:11:64:f0:\n" + " a8:02:7d:bb:c8:54:2e:57:48:32:7c:51:66:0d:d6:\n" + " 3e:0e:ed:5e:30:a8:a6:47:03:64:5c:89:21:45:90:\n" + " e1:4c:91:bc:bd:81:6e:73:a9:14:27:e6:0d:6d:38:\n" + " dc:50:9d:b2:56:66:60:6c:66:b9:5d:bb:8c:96:2d:\n" + " 89:5e:0d:2b:ed:b8:03:31:ce:0a:ff:82:03:f5:b2:\n" + " 3b:e5:27:de:61:d8:8f:bf:a2:6a:64:b0:4a:87:23:\n" + " 40:28:a3:f1:ec:96:50:cd:83:50:2d:78:71:92:f2:\n" + " 88:75:b0:9d:cd:0b:e4:62:a6:a5:63:11:fc:b4:ba:\n" + " 9f:c6:67:40:2c:ad:a4:ef:94:f0:f9:a0:ba:e1:52:\n" + " 2e:27:d9:6b:1d:82:23:ed:3c:0b:0b:d2:bc:14:be:\n" + " 6d:b1:69:ad:3e:25:3a:66:d2:d1:af:9f:88:45:25:\n" + " 6b:6e:be:1f:a0:e7:b2:9f:6d:24:94:0d:f4:c2:75:\n" + " f9:1f:5d\n" " Exponent: 65537 (0x10001)\n" " X509v3 extensions:\n" - " X509v3 Subject Key Identifier: \n" - " 83:21:DE:EC:C0:79:03:6D:1E:83:F3:E5:97:29:D5:5A:C0:96:40:FA\n" - " X509v3 Authority Key Identifier: \n" - " keyid:8A:22:C6:9C:2E:11:F3:40:0C:CE:82:0C:22:59:FF:F8:7F:D0:B9:13\n" - "\n" - " X509v3 Basic Constraints: critical\n" + " X509v3 Basic Constraints:\n" " CA:TRUE, pathlen:0\n" - " X509v3 Key Usage: critical\n" + " X509v3 Key Usage:\n" " Digital Signature, Certificate Sign, CRL Sign\n" + " X509v3 Subject Key Identifier:\n" + " 56:98:DC:45:25:11:E2:8C:2B:EA:D6:C6:E2:C8:BE:2C:C8:69:FF:FF\n" + " X509v3 Authority Key Identifier:\n" + " keyid:4D:7D:AE:0D:A5:5E:22:5A:6A:8F:19:61:54:B3:58:CB:7B:C0:BD:DA\n" + " DirName:/C=US/ST=California/L=San Francisco/O=Linden Lab/OU=Second Life Engineering/CN=Integration Test Root CA/emailAddress=noreply@lindenlab.com\n" + " serial:EF:54:D8:F7:DA:18:E8:19\n" " Signature Algorithm: sha256WithRSAEncryption\n" - " a3:6c:85:9a:2e:4e:7e:5d:83:63:0f:f5:4f:a9:7d:ec:0e:6f:\n" - " ae:d7:ba:df:64:e0:46:0e:3d:da:18:15:2c:f3:73:ca:81:b1:\n" - " 10:d9:53:14:21:7d:72:5c:94:88:a5:9d:ad:ab:45:42:c6:64:\n" - " a9:d9:2e:4e:29:47:2c:b1:95:07:b7:62:48:68:1f:68:13:1c:\n" - " d2:a0:fb:5e:38:24:4a:82:0a:87:c9:93:20:43:7e:e9:f9:79:\n" - " ef:03:a2:bd:9e:24:6b:0a:01:5e:4a:36:c5:7d:7a:fe:d6:aa:\n" - " 2f:c2:8c:38:8a:99:3c:b0:6a:e5:60:be:56:d6:eb:60:03:55:\n" - " 24:42:a0:1a:fa:91:24:a3:53:15:75:5d:c8:eb:7c:1e:68:5a:\n" - " 7e:13:34:e3:85:37:1c:76:3f:77:67:1b:ed:1b:52:17:fc:4a:\n" - " a3:e2:74:84:80:2c:69:fc:dd:7d:26:97:c4:2a:69:7d:9c:dc:\n" - " 61:97:70:29:a7:3f:2b:5b:2b:22:51:fd:fe:6a:5d:f9:e7:14:\n" - " 48:b7:2d:c8:33:58:fc:f2:5f:27:f7:26:16:be:be:b5:aa:a2:\n" - " 64:53:3c:69:e8:b5:61:eb:ab:91:a5:b4:09:9b:f6:98:b8:5c:\n" - " 5b:24:2f:93:f5:2b:9c:8c:58:fb:26:3f:67:53:d7:42:64:e8:\n" - " 79:77:73:41:4e:e3:02:39:0b:b6:68:97:8b:84:e8:1d:83:a8:\n" - " 15:f1:06:46:47:80:42:5e:14:e2:61:8a:76:84:d5:d4:71:7f:\n" - " 4e:ff:d9:74:87:ff:32:c5:87:20:0a:d4:59:40:3e:d8:17:ef:\n" - " da:65:e9:0a:51:fe:1e:c3:46:91:d2:ee:e4:23:57:97:87:d4:\n" - " a6:a5:eb:ef:81:6a:d8:8c:d6:1f:8e:b1:18:4c:6b:89:32:55:\n" - " 53:68:26:9e:bb:03:be:2c:e9:8b:ff:97:9c:1c:ac:28:c3:9f:\n" - " 0b:b7:93:23:24:31:63:e4:19:13:f2:bb:08:71:b7:c5:c5:c4:\n" - " 10:ff:dc:fc:33:54:a4:5e:ec:a3:fe:0a:80:ca:9c:bc:95:6f:\n" - " 5f:39:91:3b:61:69:16:94:0f:57:4b:fc:4b:b1:be:72:98:5d:\n" - " 10:f9:08:a7:d6:e0:e8:3d:5d:54:7d:fa:4b:6a:dd:98:41:ed:\n" - " 84:a1:39:67:5c:6c:7f:0c:b0:e1:98:c1:14:ed:fe:1e:e8:05:\n" - " 8d:7f:6a:24:cb:1b:05:42:0d:7f:13:ba:ca:b5:91:db:a5:f0:\n" - " 40:2b:70:7a:2a:a5:5d:ed:56:0c:f0:c2:72:ee:63:dd:cb:5d:\n" - " 76:f6:08:e6:e6:30:ef:3a:b2:16:34:41:a4:e1:30:14:bc:c7:\n" - " f9:23:3a:1a:70:df:b8:cc\n" + " ae:d0:30:ac:31:49:20:86:0b:34:01:58:08:94:68:cc:38:9c:\n" + " f7:13:5c:46:19:33:ed:54:5e:e4:43:f3:59:33:5c:50:d9:89:\n" + " 8b:ee:75:67:a8:c7:0e:d1:30:c2:4e:a3:2e:a8:64:2d:6a:a8:\n" + " f4:bd:b1:32:dc:bc:46:48:5d:1a:18:d8:e8:0b:8c:fe:7b:51:\n" + " d9:dd:b9:e3:4b:d1:f9:e0:22:46:dd:37:5b:b2:cb:72:8e:9c:\n" + " 4b:da:67:df:fd:ce:86:49:21:31:4e:99:b6:d4:38:0b:14:5d:\n" + " ad:97:ba:8f:e2:08:15:85:73:eb:4a:7d:01:49:af:63:ae:2d:\n" + " e3:9d:0a:d7:11:c2:03:d3:15:21:97:be:3d:d2:ea:ab:cc:93:\n" + " 16:98:64:80:72:eb:c2:78:0a:09:69:c4:2b:5d:df:30:7b:be:\n" + " 9b:02:34:73:62:9f:95:b1:cf:08:e8:9e:57:a8:37:31:cf:2c:\n" + " 8c:18:b1:d5:7a:25:90:d6:b6:76:28:1b:e2:b1:cf:1b:f1:ef:\n" + " dd:2f:d3:07:af:81:e3:5f:fc:5a:e7:3c:a9:37:0d:9c:78:5b:\n" + " 58:dc:89:54:70:a4:5b:ff:9f:64:30:a3:85:12:32:69:a5:02:\n" + " 73:d9:1d:ff:69:1f:d4:97:8f:d0:a8:90:8c:dd:2e:45:a1:b1:\n" + " e3:8a:82:fc:fc:08:41:01:51:92:87:9a:09:7b:35:c3:cc:48:\n" + " 81:39:30:a9:f4:41:3b:06:a3:06:21:cc:4b:bc:1b:76:58:94:\n" + " d1:e4:22:70:7f:20:7e:7a:b4:fa:7f:e8:79:c1:8c:89:9e:e9:\n" + " e3:72:2a:43:72:47:9e:bb:26:ed:64:2c:c8:54:f7:b4:95:c2:\n" + " c4:e9:8b:df:d5:10:a7:ed:a5:7a:94:97:c4:76:45:e3:6c:c0:\n" + " 0e:a6:2a:76:d5:1d:2f:ad:99:32:c6:7b:f6:41:e0:65:37:0f:\n" + " c0:1f:c5:99:4a:75:fd:6c:e0:f1:f0:58:49:2d:81:10:ca:d8:\n" + " eb:2b:c3:9b:a9:d9:a9:f5:6c:6d:26:fd:b8:32:92:58:f4:65:\n" + " 0b:d1:8e:03:1e:d5:6a:95:d4:46:9e:65:dd:e5:85:36:e6:31:\n" + " 77:3a:1a:20:2b:07:b7:f1:9a:4e:8d:54:22:5a:54:1c:72:5c:\n" + " 1f:b4:1a:5b:21:ed:06:5a:9a:e5:3c:01:c9:9b:af:50:61:f2:\n" + " 29:6b:ec:6d:19:bb:2e:02:94:ca:36:71:ef:45:39:f1:a5:25:\n" + " 10:0e:90:bc:a7:b3:5b:ab:af:f1:19:88:6a:09:2f:1f:d0:24:\n" + " a8:62:ed:d9:1a:65:89:65:16:a5:55:de:33:e8:7a:81:66:72:\n" + " 91:17:5e:1d:22:72:f7:b8\n" "-----BEGIN CERTIFICATE-----\n" - "MIIGSDCCBDCgAwIBAgICEAAwDQYJKoZIhvcNAQELBQAwgboxCzAJBgNVBAYTAlVT\n" - "MRMwEQYDVQQIDApDYWxpZm9ybmlhMRYwFAYDVQQHDA1TYW4gRnJhbmNpc2NvMRMw\n" - "EQYDVQQKDApMaW5kZW4gTGFiMSAwHgYDVQQLDBdTZWNvbmQgTGlmZSBFbmdpbmVl\n" - "cmluZzEhMB8GA1UEAwwYSW50ZWdyYXRpb24gVGVzdCBSb290IENBMSQwIgYJKoZI\n" - "hvcNAQkBFhVub3JlcGx5QGxpbmRlbmxhYi5jb20wHhcNMTgwNTIyMjIzOTA4WhcN\n" - "MjgwNTE5MjIzOTA4WjCBqjELMAkGA1UEBhMCVVMxEzARBgNVBAgMCkNhbGlmb3Ju\n" - "aWExEzARBgNVBAoMCkxpbmRlbiBMYWIxIDAeBgNVBAsMF1NlY29uZCBMaWZlIEVu\n" - "Z2luZWVyaW5nMSkwJwYDVQQDDCBJbnRlZ3JhdGlvbiBUZXN0IEludGVybWVkaWF0\n" - "ZSBDQTEkMCIGCSqGSIb3DQEJARYVbm9yZXBseUBsaW5kZW5sYWIuY29tMIICIjAN\n" - "BgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAzqNw4sT7S5eQoTC7wRsTuap+Rhej\n" - "Jo1pP15zlehqsQq0j1Bl48ZcOSQ03wu3zM5iDDZaEiz+NUzpHKyAXiSZ16q9vkjA\n" - "YmR3NohmzvSo3dJ2JGKQVUH8HRNOp05XvKikWUssWhzYzBbe6IgwyZXfL6YUKA/r\n" - "NEYSWLraDubenBX29OOfdKpwiXmL6Vp7GFQVlDojCmV4BdkzkCrOFRgNUvxcMWUg\n" - "0BI3jBGAutSwgnMAS0m+y9a8581h8wCYmXRaN4FJln4UARuG0tAGlEBjY0YR/DNc\n" - "vTpe1OVER2RQvaaXVXBkmybM3iCCkGqDQZxvcUcUvstofIW+7y52EhnTyYcytKxg\n" - "IBYoLa+86AHGf/vYEdX0txS9Jwhbcr4J4JHInHu0sxLvMja+sbmit+NpRzB2upyb\n" - "GZlNU91c6CzxsmRpzxW9+LtYlXNYOJW0es+EKabC2/C975cm1Jms18e+sA0R9CaG\n" - "LXdCUiXXVsfjl7E2XJdx0Jv1tVCN+f/7EHc8tVNtoUM1qQMyBavX9dEZvV+SowAq\n" - "eTekdk/pMg3khrvqwxrFM+gW1KXY4Oi7wvAiFeLZjK6sfSu/66NMOykdlKyju226\n" - "bQORA89GEsRmIcXGZ9gRGXkBDm6EHHZvET3rlInFaiYfzeARi1HumTVp5X8LdyqU\n" - "5EtkuYMEMAXkouMCAwEAAaNmMGQwHQYDVR0OBBYEFIMh3uzAeQNtHoPz5Zcp1VrA\n" - "lkD6MB8GA1UdIwQYMBaAFIoixpwuEfNADM6CDCJZ//h/0LkTMBIGA1UdEwEB/wQI\n" - "MAYBAf8CAQAwDgYDVR0PAQH/BAQDAgGGMA0GCSqGSIb3DQEBCwUAA4ICAQCjbIWa\n" - "Lk5+XYNjD/VPqX3sDm+u17rfZOBGDj3aGBUs83PKgbEQ2VMUIX1yXJSIpZ2tq0VC\n" - "xmSp2S5OKUcssZUHt2JIaB9oExzSoPteOCRKggqHyZMgQ37p+XnvA6K9niRrCgFe\n" - "SjbFfXr+1qovwow4ipk8sGrlYL5W1utgA1UkQqAa+pEko1MVdV3I63weaFp+EzTj\n" - "hTccdj93ZxvtG1IX/Eqj4nSEgCxp/N19JpfEKml9nNxhl3Appz8rWysiUf3+al35\n" - "5xRIty3IM1j88l8n9yYWvr61qqJkUzxp6LVh66uRpbQJm/aYuFxbJC+T9SucjFj7\n" - "Jj9nU9dCZOh5d3NBTuMCOQu2aJeLhOgdg6gV8QZGR4BCXhTiYYp2hNXUcX9O/9l0\n" - "h/8yxYcgCtRZQD7YF+/aZekKUf4ew0aR0u7kI1eXh9SmpevvgWrYjNYfjrEYTGuJ\n" - "MlVTaCaeuwO+LOmL/5ecHKwow58Lt5MjJDFj5BkT8rsIcbfFxcQQ/9z8M1SkXuyj\n" - "/gqAypy8lW9fOZE7YWkWlA9XS/xLsb5ymF0Q+Qin1uDoPV1UffpLat2YQe2EoTln\n" - "XGx/DLDhmMEU7f4e6AWNf2okyxsFQg1/E7rKtZHbpfBAK3B6KqVd7VYM8MJy7mPd\n" - "y1129gjm5jDvOrIWNEGk4TAUvMf5IzoacN+4zA==\n" + "MIIHNjCCBR6gAwIBAgIJAIW7S2Ym25rGMA0GCSqGSIb3DQEBCwUAMIG6MQswCQYD\n" + "VQQGEwJVUzETMBEGA1UECAwKQ2FsaWZvcm5pYTEWMBQGA1UEBwwNU2FuIEZyYW5j\n" + "aXNjbzETMBEGA1UECgwKTGluZGVuIExhYjEgMB4GA1UECwwXU2Vjb25kIExpZmUg\n" + "RW5naW5lZXJpbmcxITAfBgNVBAMMGEludGVncmF0aW9uIFRlc3QgUm9vdCBDQTEk\n" + "MCIGCSqGSIb3DQEJARYVbm9yZXBseUBsaW5kZW5sYWIuY29tMB4XDTI0MDcyMzEx\n" + "NDYzM1oXDTM0MDcyMTExNDYzM1owgcIxCzAJBgNVBAYTAlVTMRMwEQYDVQQIDApD\n" + "YWxpZm9ybmlhMRYwFAYDVQQHDA1TYW4gRnJhbmNpc2NvMRMwEQYDVQQKDApMaW5k\n" + "ZW4gTGFiMSAwHgYDVQQLDBdTZWNvbmQgTGlmZSBFbmdpbmVlcmluZzEpMCcGA1UE\n" + "AwwgSW50ZWdyYXRpb24gVGVzdCBJbnRlcm1lZGlhdGUgQ0ExJDAiBgkqhkiG9w0B\n" + "CQEWFW5vcmVwbHlAbGluZGVubGFiLmNvbTCCAiIwDQYJKoZIhvcNAQEBBQADggIP\n" + "ADCCAgoCggIBAL730svkXEZ74hEiiXLad3LsBYcZ93cH/WfXrxPVdhKS3WlNIkew\n" + "PZSKapWFNLh4w51jMrFLCrYOBXurBiP8DSG1/MZqWja+bvzHR5ejGC4zzQ6KdSu3\n" + "KeloSpBTRdtz/7PlwdRr3Tqx71OfI+nGh85nufuk1XYhA8vFcmvFpgdV+0eQ6JI4\n" + "cxQRjv8huTVkWmHH/B/kTUflA8wLw2lmcYQMGC9hfzTd8pHjt52ouNs/bm+W+jQG\n" + "ggTIGMzei38mtUhT+/sVew44YP7aIZiNcweya/2tIVnnhGbhBBYcvhM0KEMsCT3k\n" + "dyqkrW35JgT3Q3Ob2eoaQ2q024j4+b00+Kboequ0suEpR6a6uGWcxrOvE0M47yoF\n" + "d5+P8AxWIcKS0izDMlDRYq5R/JnmuDj4gx2NQBHgHVFdP/pVYbYYCR5xr5VknOrG\n" + "EWTwqAJ9u8hULldIMnxRZg3WPg7tXjCopkcDZFyJIUWQ4UyRvL2BbnOpFCfmDW04\n" + "3FCdslZmYGxmuV27jJYtiV4NK+24AzHOCv+CA/WyO+Un3mHYj7+iamSwSocjQCij\n" + "8eyWUM2DUC14cZLyiHWwnc0L5GKmpWMR/LS6n8ZnQCytpO+U8PmguuFSLifZax2C\n" + "I+08CwvSvBS+bbFprT4lOmbS0a+fiEUla26+H6Dnsp9tJJQN9MJ1+R9dAgMBAAGj\n" + "ggEzMIIBLzAPBgNVHRMECDAGAQH/AgEAMAsGA1UdDwQEAwIBhjAdBgNVHQ4EFgQU\n" + "VpjcRSUR4owr6tbG4si+LMhp//8wge8GA1UdIwSB5zCB5IAUTX2uDaVeIlpqjxlh\n" + "VLNYy3vAvdqhgcCkgb0wgboxCzAJBgNVBAYTAlVTMRMwEQYDVQQIDApDYWxpZm9y\n" + "bmlhMRYwFAYDVQQHDA1TYW4gRnJhbmNpc2NvMRMwEQYDVQQKDApMaW5kZW4gTGFi\n" + "MSAwHgYDVQQLDBdTZWNvbmQgTGlmZSBFbmdpbmVlcmluZzEhMB8GA1UEAwwYSW50\n" + "ZWdyYXRpb24gVGVzdCBSb290IENBMSQwIgYJKoZIhvcNAQkBFhVub3JlcGx5QGxp\n" + "bmRlbmxhYi5jb22CCQDvVNj32hjoGTANBgkqhkiG9w0BAQsFAAOCAgEArtAwrDFJ\n" + "IIYLNAFYCJRozDic9xNcRhkz7VRe5EPzWTNcUNmJi+51Z6jHDtEwwk6jLqhkLWqo\n" + "9L2xMty8RkhdGhjY6AuM/ntR2d2540vR+eAiRt03W7LLco6cS9pn3/3OhkkhMU6Z\n" + "ttQ4CxRdrZe6j+IIFYVz60p9AUmvY64t450K1xHCA9MVIZe+PdLqq8yTFphkgHLr\n" + "wngKCWnEK13fMHu+mwI0c2KflbHPCOieV6g3Mc8sjBix1XolkNa2digb4rHPG/Hv\n" + "3S/TB6+B41/8Wuc8qTcNnHhbWNyJVHCkW/+fZDCjhRIyaaUCc9kd/2kf1JeP0KiQ\n" + "jN0uRaGx44qC/PwIQQFRkoeaCXs1w8xIgTkwqfRBOwajBiHMS7wbdliU0eQicH8g\n" + "fnq0+n/oecGMiZ7p43IqQ3JHnrsm7WQsyFT3tJXCxOmL39UQp+2lepSXxHZF42zA\n" + "DqYqdtUdL62ZMsZ79kHgZTcPwB/FmUp1/Wzg8fBYSS2BEMrY6yvDm6nZqfVsbSb9\n" + "uDKSWPRlC9GOAx7VapXURp5l3eWFNuYxdzoaICsHt/GaTo1UIlpUHHJcH7QaWyHt\n" + "Blqa5TwByZuvUGHyKWvsbRm7LgKUyjZx70U58aUlEA6QvKezW6uv8RmIagkvH9Ak\n" + "qGLt2RpliWUWpVXeM+h6gWZykRdeHSJy97g=\n" "-----END CERTIFICATE-----\n" - ); + ); const std::string mPemChildCert( "Certificate:\n" " Data:\n" " Version: 3 (0x2)\n" - " Serial Number: 4096 (0x1000)\n" + " Serial Number: 9e:8d:34:13:e7:9b:f9:31\n" " Signature Algorithm: sha256WithRSAEncryption\n" - " Issuer: C=US, ST=California, O=Linden Lab, OU=Second Life Engineering, CN=Integration Test Intermediate CA/emailAddress=noreply@lindenlab.com\n" + " Issuer: C=US, ST=California, L=San Francisco, O=Linden Lab, OU=Second Life Engineering, CN=Integration Test Intermediate CA/emailAddress=noreply@lindenlab.com\n" " Validity\n" - " Not Before: May 22 22:58:15 2018 GMT\n" - " Not After : Jul 19 22:58:15 2024 GMT\n" + " Not Before: Jul 23 11:46:39 2024 GMT\n" + " Not After : Jul 21 11:46:39 2034 GMT\n" " Subject: C=US, ST=California, L=San Francisco, O=Linden Lab, OU=Second Life Engineering, CN=Integration Test Server Cert/emailAddress=noreply@lindenlab.com\n" " Subject Public Key Info:\n" " Public Key Algorithm: rsaEncryption\n" - " Public-Key: (2048 bit)\n" + " Public-Key: (4096 bit)\n" " Modulus:\n" - " 00:bf:a1:1c:76:82:4a:10:1d:25:0e:02:e2:7a:64:\n" - " 54:c7:94:c5:c0:98:d5:35:f3:cb:cb:30:ba:31:9c:\n" - " bd:4c:2f:4a:4e:24:03:4b:87:5c:c1:5c:fe:d9:89:\n" - " 3b:cb:01:bc:eb:a5:b7:78:dc:b3:58:e5:78:a7:15:\n" - " 34:50:30:aa:16:3a:b2:94:17:6d:1e:7f:b2:70:1e:\n" - " 96:41:bb:1d:e3:22:80:fa:dc:00:6a:fb:34:3e:67:\n" - " e7:c2:21:2f:1b:d3:af:04:49:91:eb:bb:60:e0:26:\n" - " 52:75:28:8a:08:5b:91:56:4e:51:50:40:51:70:af:\n" - " cb:80:66:c8:59:e9:e2:48:a8:62:d0:26:67:80:0a:\n" - " 12:16:d1:f6:15:9e:1f:f5:92:37:f3:c9:2f:03:9e:\n" - " 22:f6:60:5a:76:45:8c:01:2c:99:54:72:19:db:b7:\n" - " 72:e6:5a:69:f3:e9:31:65:5d:0f:c7:5c:9c:17:29:\n" - " 71:14:7f:db:47:c9:1e:65:a2:41:b0:2f:14:17:ec:\n" - " 4b:25:f2:43:8f:b4:a3:8d:37:1a:07:34:b3:29:bb:\n" - " 8a:44:8e:84:08:a2:1b:76:7a:cb:c2:39:2f:6e:e3:\n" - " fc:d6:91:b5:1f:ce:58:91:57:70:35:6e:25:a9:48:\n" - " 0e:07:cf:4e:dd:16:42:65:cf:8a:42:b3:27:e6:fe:\n" - " 6a:e3\n" + " 00:d8:ac:0c:27:8f:ea:c0:4d:21:e4:75:55:31:57:\n" + " 83:46:47:14:1e:f5:67:ae:98:60:c4:97:6d:e8:53:\n" + " f2:4d:3b:ec:6f:08:bc:1e:c0:e2:a6:75:b5:90:1d:\n" + " 30:a2:59:68:32:10:2b:29:67:fc:99:f1:24:6a:36:\n" + " 73:60:31:6b:c7:a0:b8:b0:38:60:b1:59:23:2c:ab:\n" + " 25:a2:c8:b0:bc:2c:c6:d7:4c:87:37:1b:5e:51:a4:\n" + " 63:3e:c4:6d:ed:da:5e:d3:ad:8a:6d:52:e4:87:38:\n" + " 33:76:cf:f2:86:58:b3:10:a4:91:8d:3d:4f:27:9a:\n" + " 8b:b4:d7:67:90:31:1c:f5:7f:78:af:6f:f2:dd:39:\n" + " d0:16:16:7b:46:ad:88:1b:3b:74:6b:10:29:8b:64:\n" + " ba:ed:9f:a7:69:99:55:8f:73:0d:18:a3:7f:40:20:\n" + " 3a:41:4a:94:39:62:8b:fe:c6:9d:79:d0:cd:1c:e2:\n" + " d4:74:bb:43:75:eb:86:8b:30:c1:8d:cc:14:ab:75:\n" + " 2e:f5:3e:0c:05:cb:e4:c3:92:d8:81:8c:df:a5:4e:\n" + " 2e:0b:ae:17:15:9b:e6:dd:9e:16:46:42:27:92:8a:\n" + " 0e:3a:74:1e:d1:3f:ee:7e:a5:d7:ec:1c:63:d4:96:\n" + " 5b:36:f9:15:ee:da:66:ac:5e:de:91:d9:08:24:fb:\n" + " 5d:fc:9b:77:dd:ff:20:a6:67:6f:48:41:5e:5a:ac:\n" + " 13:a4:2c:2a:f2:a3:15:86:e2:84:33:34:e3:91:27:\n" + " 8b:37:ba:b0:c7:5e:1a:0d:b9:f2:4e:0c:55:e6:bb:\n" + " d9:63:f5:05:7b:aa:19:e5:57:ce:a5:b1:46:4b:b3:\n" + " 04:f6:a0:97:26:ed:48:ed:97:93:a6:75:b1:a3:42:\n" + " fc:cc:57:89:da:44:e9:16:a6:30:2c:01:8e:f2:ed:\n" + " be:45:05:08:8a:af:1e:07:51:89:cf:51:4c:aa:f3:\n" + " b3:f0:6f:db:21:80:11:32:0a:23:e2:ff:cc:59:15:\n" + " eb:ff:d2:b8:d6:a1:c1:b4:96:12:82:bf:3f:68:ad:\n" + " c8:61:50:f8:88:4f:d0:be:8e:29:64:1a:16:a5:d9:\n" + " 29:76:16:cd:70:37:c4:f2:1f:4e:c6:57:36:dd:c1:\n" + " 27:19:72:ef:98:7e:34:25:3f:76:b1:ea:15:b2:38:\n" + " 6e:d3:43:03:7a:2b:78:91:9a:19:26:2a:31:b7:5e:\n" + " b7:22:c4:fd:bf:93:10:a4:23:3f:d7:79:53:28:5d:\n" + " 2e:ba:0c:b0:5e:0a:b4:c4:a1:71:75:88:1b:b2:0e:\n" + " 2c:67:08:7b:f0:f6:37:d3:aa:39:50:03:a3:7c:17:\n" + " 1d:52:52:2a:6b:d0:a2:54:2e:ba:11:bc:26:a9:16:\n" + " a6:1b:79\n" " Exponent: 65537 (0x10001)\n" " X509v3 extensions:\n" - " X509v3 Basic Constraints: \n" + " X509v3 Basic Constraints:\n" " CA:FALSE\n" - " Netscape Cert Type: \n" - " SSL Server\n" - " Netscape Comment: \n" - " OpenSSL Generated Server Certificate\n" - " X509v3 Subject Key Identifier: \n" - " BB:59:9F:DE:6B:51:A7:6C:B3:6D:5B:8B:42:F7:B1:65:77:17:A4:E4\n" - " X509v3 Authority Key Identifier: \n" - " keyid:83:21:DE:EC:C0:79:03:6D:1E:83:F3:E5:97:29:D5:5A:C0:96:40:FA\n" - " DirName:/C=US/ST=California/L=San Francisco/O=Linden Lab/OU=Second Life Engineering/CN=Integration Test Root CA/emailAddress=noreply@lindenlab.com\n" - " serial:10:00\n" - "\n" - " X509v3 Key Usage: critical\n" + " X509v3 Key Usage:\n" " Digital Signature, Key Encipherment\n" - " X509v3 Extended Key Usage: \n" + " X509v3 Extended Key Usage:\n" " TLS Web Server Authentication\n" + " X509v3 Subject Key Identifier:\n" + " 7B:1A:F9:2B:C4:B2:F6:AE:D6:F2:8E:B1:73:FB:DD:11:CA:DB:F8:87\n" + " X509v3 Authority Key Identifier:\n" + " keyid:56:98:DC:45:25:11:E2:8C:2B:EA:D6:C6:E2:C8:BE:2C:C8:69:FF:FF\n" + " DirName:/C=US/ST=California/L=San Francisco/O=Linden Lab/OU=Second Life Engineering/CN=Integration Test Root CA/emailAddress=noreply@lindenlab.com\n" + " serial:85:BB:4B:66:26:DB:9A:C6\n" " Signature Algorithm: sha256WithRSAEncryption\n" - " 18:a6:58:55:9b:d4:af:7d:8a:27:d3:28:3a:4c:4b:42:4e:f0:\n" - " 30:d6:d9:95:11:48:12:0a:96:40:d9:2b:21:39:c5:d4:8d:e5:\n" - " 10:bc:68:78:69:0b:9f:15:4a:0b:f1:ab:99:45:0c:20:5f:27:\n" - " df:e7:14:2d:4a:30:f2:c2:8d:37:73:36:1a:27:55:5a:08:5f:\n" - " 71:a1:5e:05:83:b2:59:fe:02:5e:d7:4a:30:15:23:58:04:cf:\n" - " 48:cc:b0:71:88:9c:6b:57:f0:04:0a:d3:a0:64:6b:ee:f3:5f:\n" - " ea:ac:e1:2b:b9:7f:79:b8:db:ce:72:48:72:db:c8:5c:38:72:\n" - " 31:55:d0:ff:6b:bd:73:23:a7:30:18:5d:ed:47:18:0a:67:8e:\n" - " 53:32:0e:99:9b:96:72:45:7f:c6:00:2c:5d:1a:97:53:75:3a:\n" - " 0b:49:3d:3a:00:37:14:67:0c:28:97:34:87:aa:c5:32:e4:ae:\n" - " 34:83:12:4a:10:f7:0e:74:d4:5f:73:bd:ef:0c:b7:d8:0a:7d:\n" - " 8e:8d:5a:48:bd:f4:8e:7b:f9:4a:15:3b:61:c9:5e:40:59:6e:\n" - " c7:a8:a4:02:28:72:c5:54:8c:77:f4:55:a7:86:c0:38:a0:68:\n" - " 19:da:0f:72:5a:a9:7e:69:9f:9c:3a:d6:66:aa:e1:f4:fd:f9:\n" - " b8:4b:6c:71:9e:f0:38:02:c7:6a:9e:dc:e6:fb:ef:23:59:4f:\n" - " 5c:84:0a:df:ea:86:1f:fd:0e:5c:fa:c4:e5:50:1c:10:cf:89:\n" - " 4e:08:0e:4c:4b:61:1a:49:12:f7:e9:4b:17:71:43:7b:6d:b6:\n" - " b5:9f:d4:3b:c7:88:53:48:63:b6:00:80:8f:49:0a:c5:7e:58:\n" - " ac:78:d8:b9:06:b0:bc:86:e2:2e:48:5b:c3:24:fa:aa:72:d8:\n" - " ec:f6:c7:91:9f:0f:c8:b5:fd:2b:b2:a7:bc:2f:40:20:2b:47:\n" - " e0:d1:1d:94:52:6f:6b:be:12:b6:8c:dc:11:db:71:e6:19:ef:\n" - " a8:71:8b:ad:d3:32:c0:1c:a4:3f:b3:0f:af:e5:50:e1:ff:41:\n" - " a4:b7:6f:57:71:af:fd:16:4c:e8:24:b3:99:1b:cf:12:8f:43:\n" - " 05:80:ba:18:19:0a:a5:ec:49:81:41:4c:7e:28:b2:21:f2:59:\n" - " 6e:4a:ed:de:f9:fa:99:85:60:1f:e6:c2:42:5c:08:00:3c:84:\n" - " 06:a9:24:d4:cf:7b:6e:1b:59:1d:f4:70:16:03:a1:e0:0b:00:\n" - " 95:5c:39:03:fc:9d:1c:8e:f7:59:0c:61:47:f6:7f:07:22:48:\n" - " 83:40:ac:e1:98:5f:c7:be:05:d5:29:2b:bf:0d:03:0e:e9:5e:\n" - " 2b:dd:09:18:fe:5e:30:61\n" + " ad:7c:50:12:24:62:62:83:e9:dd:81:1a:12:1c:6d:ae:1e:a6:\n" + " 01:cc:93:8b:ac:83:7c:3d:57:d7:7f:d2:13:40:82:c7:27:07:\n" + " 31:d8:c4:01:04:64:9c:dc:ae:7b:52:bd:f5:62:7a:d0:7c:13:\n" + " 1a:19:86:6a:ce:9a:ba:69:07:77:75:b6:67:56:d0:c3:8d:6f:\n" + " 59:5f:ac:31:83:32:2c:4f:8c:85:8c:f3:56:5b:e0:83:16:19:\n" + " c9:55:4d:56:2c:e0:06:f8:71:85:4b:7e:c6:20:b3:f6:5b:85:\n" + " 6a:b7:0f:0e:0c:75:38:6a:aa:53:cc:b0:bf:c1:fd:a1:01:8a:\n" + " 7e:5a:0b:4d:51:fc:1b:14:b0:8d:62:17:b7:5d:6a:64:30:80:\n" + " aa:50:9a:23:9e:19:46:11:9d:49:d1:35:81:87:80:8c:9c:71:\n" + " 61:26:07:23:5d:a7:ea:4e:0c:53:77:bd:eb:18:6d:63:8b:2c:\n" + " e1:83:bb:bb:f8:3e:7c:e8:0d:19:1e:be:35:aa:99:0f:c7:25:\n" + " 0c:a8:f9:74:02:c8:4c:8e:bb:13:18:fd:aa:21:34:bc:2d:9f:\n" + " 10:96:e2:99:e3:9a:d7:91:0e:1e:77:20:70:e9:b4:63:25:f8:\n" + " ea:14:1f:24:b0:6a:8b:2a:f4:61:b1:0d:7d:18:bc:1d:6d:04:\n" + " 11:b2:9f:a2:a7:55:be:2b:2c:2f:c1:d8:95:13:73:af:1c:96:\n" + " 49:30:9c:9c:94:81:6c:9b:a7:87:5c:cf:46:95:95:4a:6f:bf:\n" + " df:c9:3d:74:3e:24:6e:44:1e:14:8b:68:23:e4:00:b5:a5:b7:\n" + " 5b:a9:ea:16:5f:fa:b1:d3:1a:b1:9b:36:ef:a4:7a:6f:a3:b0:\n" + " 97:35:ac:70:c0:cc:8e:a2:d3:40:0e:c1:70:0b:d5:ce:cd:51:\n" + " 82:8a:40:72:04:8d:62:af:ba:a8:e7:a8:e9:b9:99:b7:5c:5d:\n" + " 27:96:b2:3d:f9:0d:26:8c:3f:db:ac:86:97:be:f1:2c:0b:ca:\n" + " 90:07:93:96:f4:75:c3:e8:4c:f6:a8:a2:3f:da:11:21:e7:b1:\n" + " 8c:62:36:ae:91:a9:2a:73:ba:67:f5:24:16:c3:ee:b7:b1:b4:\n" + " e3:8a:28:23:84:cf:38:c6:f0:8e:21:f6:b8:76:9a:6d:d1:e3:\n" + " 74:81:7a:22:20:a0:82:2a:31:8a:ba:44:0b:61:5a:aa:ba:c6:\n" + " 07:99:36:0a:24:06:2f:8e:c1:1c:4b:f0:65:72:fb:e9:b5:31:\n" + " 59:13:2c:c6:f8:5b:91:e2:d8:96:f3:1a:06:0b:2a:62:12:4d:\n" + " 5e:65:c9:e9:e4:00:99:a6:d3:60:1f:c3:d6:cc:a6:9b:a5:14:\n" + " 1b:4d:db:e7:3d:52:7e:2c\n" "-----BEGIN CERTIFICATE-----\n" - "MIIGbjCCBFagAwIBAgICEAAwDQYJKoZIhvcNAQELBQAwgaoxCzAJBgNVBAYTAlVT\n" - "MRMwEQYDVQQIDApDYWxpZm9ybmlhMRMwEQYDVQQKDApMaW5kZW4gTGFiMSAwHgYD\n" - "VQQLDBdTZWNvbmQgTGlmZSBFbmdpbmVlcmluZzEpMCcGA1UEAwwgSW50ZWdyYXRp\n" - "b24gVGVzdCBJbnRlcm1lZGlhdGUgQ0ExJDAiBgkqhkiG9w0BCQEWFW5vcmVwbHlA\n" - "bGluZGVubGFiLmNvbTAeFw0xODA1MjIyMjU4MTVaFw0yNDA3MTkyMjU4MTVaMIG+\n" - "MQswCQYDVQQGEwJVUzETMBEGA1UECAwKQ2FsaWZvcm5pYTEWMBQGA1UEBwwNU2Fu\n" - "IEZyYW5jaXNjbzETMBEGA1UECgwKTGluZGVuIExhYjEgMB4GA1UECwwXU2Vjb25k\n" - "IExpZmUgRW5naW5lZXJpbmcxJTAjBgNVBAMMHEludGVncmF0aW9uIFRlc3QgU2Vy\n" - "dmVyIENlcnQxJDAiBgkqhkiG9w0BCQEWFW5vcmVwbHlAbGluZGVubGFiLmNvbTCC\n" - "ASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAL+hHHaCShAdJQ4C4npkVMeU\n" - "xcCY1TXzy8swujGcvUwvSk4kA0uHXMFc/tmJO8sBvOult3jcs1jleKcVNFAwqhY6\n" - "spQXbR5/snAelkG7HeMigPrcAGr7ND5n58IhLxvTrwRJkeu7YOAmUnUoighbkVZO\n" - "UVBAUXCvy4BmyFnp4kioYtAmZ4AKEhbR9hWeH/WSN/PJLwOeIvZgWnZFjAEsmVRy\n" - "Gdu3cuZaafPpMWVdD8dcnBcpcRR/20fJHmWiQbAvFBfsSyXyQ4+0o403Ggc0sym7\n" - "ikSOhAiiG3Z6y8I5L27j/NaRtR/OWJFXcDVuJalIDgfPTt0WQmXPikKzJ+b+auMC\n" - "AwEAAaOCAYYwggGCMAkGA1UdEwQCMAAwEQYJYIZIAYb4QgEBBAQDAgZAMDMGCWCG\n" - "SAGG+EIBDQQmFiRPcGVuU1NMIEdlbmVyYXRlZCBTZXJ2ZXIgQ2VydGlmaWNhdGUw\n" - "HQYDVR0OBBYEFLtZn95rUadss21bi0L3sWV3F6TkMIHoBgNVHSMEgeAwgd2AFIMh\n" - "3uzAeQNtHoPz5Zcp1VrAlkD6oYHApIG9MIG6MQswCQYDVQQGEwJVUzETMBEGA1UE\n" - "CAwKQ2FsaWZvcm5pYTEWMBQGA1UEBwwNU2FuIEZyYW5jaXNjbzETMBEGA1UECgwK\n" - "TGluZGVuIExhYjEgMB4GA1UECwwXU2Vjb25kIExpZmUgRW5naW5lZXJpbmcxITAf\n" - "BgNVBAMMGEludGVncmF0aW9uIFRlc3QgUm9vdCBDQTEkMCIGCSqGSIb3DQEJARYV\n" - "bm9yZXBseUBsaW5kZW5sYWIuY29tggIQADAOBgNVHQ8BAf8EBAMCBaAwEwYDVR0l\n" - "BAwwCgYIKwYBBQUHAwEwDQYJKoZIhvcNAQELBQADggIBABimWFWb1K99iifTKDpM\n" - "S0JO8DDW2ZURSBIKlkDZKyE5xdSN5RC8aHhpC58VSgvxq5lFDCBfJ9/nFC1KMPLC\n" - "jTdzNhonVVoIX3GhXgWDsln+Al7XSjAVI1gEz0jMsHGInGtX8AQK06Bka+7zX+qs\n" - "4Su5f3m4285ySHLbyFw4cjFV0P9rvXMjpzAYXe1HGApnjlMyDpmblnJFf8YALF0a\n" - "l1N1OgtJPToANxRnDCiXNIeqxTLkrjSDEkoQ9w501F9zve8Mt9gKfY6NWki99I57\n" - "+UoVO2HJXkBZbseopAIocsVUjHf0VaeGwDigaBnaD3JaqX5pn5w61maq4fT9+bhL\n" - "bHGe8DgCx2qe3Ob77yNZT1yECt/qhh/9Dlz6xOVQHBDPiU4IDkxLYRpJEvfpSxdx\n" - "Q3tttrWf1DvHiFNIY7YAgI9JCsV+WKx42LkGsLyG4i5IW8Mk+qpy2Oz2x5GfD8i1\n" - "/Suyp7wvQCArR+DRHZRSb2u+EraM3BHbceYZ76hxi63TMsAcpD+zD6/lUOH/QaS3\n" - "b1dxr/0WTOgks5kbzxKPQwWAuhgZCqXsSYFBTH4osiHyWW5K7d75+pmFYB/mwkJc\n" - "CAA8hAapJNTPe24bWR30cBYDoeALAJVcOQP8nRyO91kMYUf2fwciSINArOGYX8e+\n" - "BdUpK78NAw7pXivdCRj+XjBh\n" + "MIIHSTCCBTGgAwIBAgIJAJ6NNBPnm/kxMA0GCSqGSIb3DQEBCwUAMIHCMQswCQYD\n" + "VQQGEwJVUzETMBEGA1UECAwKQ2FsaWZvcm5pYTEWMBQGA1UEBwwNU2FuIEZyYW5j\n" + "aXNjbzETMBEGA1UECgwKTGluZGVuIExhYjEgMB4GA1UECwwXU2Vjb25kIExpZmUg\n" + "RW5naW5lZXJpbmcxKTAnBgNVBAMMIEludGVncmF0aW9uIFRlc3QgSW50ZXJtZWRp\n" + "YXRlIENBMSQwIgYJKoZIhvcNAQkBFhVub3JlcGx5QGxpbmRlbmxhYi5jb20wHhcN\n" + "MjQwNzIzMTE0NjM5WhcNMzQwNzIxMTE0NjM5WjCBvjELMAkGA1UEBhMCVVMxEzAR\n" + "BgNVBAgMCkNhbGlmb3JuaWExFjAUBgNVBAcMDVNhbiBGcmFuY2lzY28xEzARBgNV\n" + "BAoMCkxpbmRlbiBMYWIxIDAeBgNVBAsMF1NlY29uZCBMaWZlIEVuZ2luZWVyaW5n\n" + "MSUwIwYDVQQDDBxJbnRlZ3JhdGlvbiBUZXN0IFNlcnZlciBDZXJ0MSQwIgYJKoZI\n" + "hvcNAQkBFhVub3JlcGx5QGxpbmRlbmxhYi5jb20wggIiMA0GCSqGSIb3DQEBAQUA\n" + "A4ICDwAwggIKAoICAQDYrAwnj+rATSHkdVUxV4NGRxQe9WeumGDEl23oU/JNO+xv\n" + "CLwewOKmdbWQHTCiWWgyECspZ/yZ8SRqNnNgMWvHoLiwOGCxWSMsqyWiyLC8LMbX\n" + "TIc3G15RpGM+xG3t2l7TrYptUuSHODN2z/KGWLMQpJGNPU8nmou012eQMRz1f3iv\n" + "b/LdOdAWFntGrYgbO3RrECmLZLrtn6dpmVWPcw0Yo39AIDpBSpQ5Yov+xp150M0c\n" + "4tR0u0N164aLMMGNzBSrdS71PgwFy+TDktiBjN+lTi4LrhcVm+bdnhZGQieSig46\n" + "dB7RP+5+pdfsHGPUlls2+RXu2masXt6R2Qgk+138m3fd/yCmZ29IQV5arBOkLCry\n" + "oxWG4oQzNOORJ4s3urDHXhoNufJODFXmu9lj9QV7qhnlV86lsUZLswT2oJcm7Ujt\n" + "l5OmdbGjQvzMV4naROkWpjAsAY7y7b5FBQiKrx4HUYnPUUyq87Pwb9shgBEyCiPi\n" + "/8xZFev/0rjWocG0lhKCvz9orchhUPiIT9C+jilkGhal2Sl2Fs1wN8TyH07GVzbd\n" + "wScZcu+YfjQlP3ax6hWyOG7TQwN6K3iRmhkmKjG3XrcixP2/kxCkIz/XeVMoXS66\n" + "DLBeCrTEoXF1iBuyDixnCHvw9jfTqjlQA6N8Fx1SUipr0KJULroRvCapFqYbeQID\n" + "AQABo4IBQjCCAT4wCQYDVR0TBAIwADALBgNVHQ8EBAMCBaAwEwYDVR0lBAwwCgYI\n" + "KwYBBQUHAwEwHQYDVR0OBBYEFHsa+SvEsvau1vKOsXP73RHK2/iHMIHvBgNVHSME\n" + "gecwgeSAFFaY3EUlEeKMK+rWxuLIvizIaf//oYHApIG9MIG6MQswCQYDVQQGEwJV\n" + "UzETMBEGA1UECAwKQ2FsaWZvcm5pYTEWMBQGA1UEBwwNU2FuIEZyYW5jaXNjbzET\n" + "MBEGA1UECgwKTGluZGVuIExhYjEgMB4GA1UECwwXU2Vjb25kIExpZmUgRW5naW5l\n" + "ZXJpbmcxITAfBgNVBAMMGEludGVncmF0aW9uIFRlc3QgUm9vdCBDQTEkMCIGCSqG\n" + "SIb3DQEJARYVbm9yZXBseUBsaW5kZW5sYWIuY29tggkAhbtLZibbmsYwDQYJKoZI\n" + "hvcNAQELBQADggIBAK18UBIkYmKD6d2BGhIcba4epgHMk4usg3w9V9d/0hNAgscn\n" + "BzHYxAEEZJzcrntSvfVietB8ExoZhmrOmrppB3d1tmdW0MONb1lfrDGDMixPjIWM\n" + "81Zb4IMWGclVTVYs4Ab4cYVLfsYgs/ZbhWq3Dw4MdThqqlPMsL/B/aEBin5aC01R\n" + "/BsUsI1iF7ddamQwgKpQmiOeGUYRnUnRNYGHgIyccWEmByNdp+pODFN3vesYbWOL\n" + "LOGDu7v4PnzoDRkevjWqmQ/HJQyo+XQCyEyOuxMY/aohNLwtnxCW4pnjmteRDh53\n" + "IHDptGMl+OoUHySwaosq9GGxDX0YvB1tBBGyn6KnVb4rLC/B2JUTc68clkkwnJyU\n" + "gWybp4dcz0aVlUpvv9/JPXQ+JG5EHhSLaCPkALWlt1up6hZf+rHTGrGbNu+kem+j\n" + "sJc1rHDAzI6i00AOwXAL1c7NUYKKQHIEjWKvuqjnqOm5mbdcXSeWsj35DSaMP9us\n" + "hpe+8SwLypAHk5b0dcPoTPaooj/aESHnsYxiNq6RqSpzumf1JBbD7rextOOKKCOE\n" + "zzjG8I4h9rh2mm3R43SBeiIgoIIqMYq6RAthWqq6xgeZNgokBi+OwRxL8GVy++m1\n" + "MVkTLMb4W5Hi2JbzGgYLKmISTV5lyenkAJmm02Afw9bMppulFBtN2+c9Un4s\n" "-----END CERTIFICATE-----\n" - ); + ); + // Test wrapper declaration : wrapping nothing for the moment struct sechandler_basic_test @@ -701,14 +722,13 @@ namespace tut //std::ostringstream llsd_value; //llsd_value << LLSDOStreamer(llsd_cert) << std::endl; LL_DEBUGS() << "test 1 cert " << llsd_cert << LL_ENDL; - ensure_equals("Issuer Name/commonName", (std::string)llsd_cert["issuer_name"]["commonName"], "Integration Test Intermediate CA"); ensure_equals("Issuer Name/countryName", (std::string)llsd_cert["issuer_name"]["countryName"], "US"); ensure_equals("Issuer Name/state", (std::string)llsd_cert["issuer_name"]["stateOrProvinceName"], "California"); ensure_equals("Issuer Name/org name", (std::string)llsd_cert["issuer_name"]["organizationName"], "Linden Lab"); ensure_equals("Issuer Name/org unit", (std::string)llsd_cert["issuer_name"]["organizationalUnitName"], "Second Life Engineering"); ensure_equals("Issuer name string", (std::string)llsd_cert["issuer_name_string"], - "emailAddress=noreply@lindenlab.com,CN=Integration Test Intermediate CA,OU=Second Life Engineering,O=Linden Lab,ST=California,C=US"); + "emailAddress=noreply@lindenlab.com,CN=Integration Test Intermediate CA,OU=Second Life Engineering,O=Linden Lab,L=San Francisco,ST=California,C=US"); ensure_equals("subject Name/commonName", (std::string)llsd_cert["subject_name"]["commonName"], "Integration Test Server Cert"); ensure_equals("subject Name/countryName", (std::string)llsd_cert["subject_name"]["countryName"], "US"); @@ -721,9 +741,9 @@ namespace tut ensure_equals("subject name string", (std::string)llsd_cert["subject_name_string"], "emailAddress=noreply@lindenlab.com,CN=Integration Test Server Cert,OU=Second Life Engineering,O=Linden Lab,L=San Francisco,ST=California,C=US"); - ensure_equals("serial number", (std::string)llsd_cert["serial_number"], "1000"); - ensure_equals("valid from", (std::string)llsd_cert["valid_from"], "2018-05-22T22:58:15Z"); - ensure_equals("valid to", (std::string)llsd_cert["valid_to"], "2024-07-19T22:58:15Z"); + ensure_equals("serial number", (std::string)llsd_cert["serial_number"], "9E8D3413E79BF931"); + ensure_equals("valid from", (std::string)llsd_cert["valid_from"], "2024-07-23T11:46:39Z"); + ensure_equals("valid to", (std::string)llsd_cert["valid_to"], "2034-07-21T11:46:39Z"); LLSD expectedKeyUsage = LLSD::emptyArray(); expectedKeyUsage.append(LLSD((std::string)"digitalSignature")); expectedKeyUsage.append(LLSD((std::string)"keyEncipherment")); @@ -1042,7 +1062,7 @@ namespace tut //validate find LLSD find_info = LLSD::emptyMap(); - find_info["subjectKeyIdentifier"] = "bb:59:9f:de:6b:51:a7:6c:b3:6d:5b:8b:42:f7:b1:65:77:17:a4:e4"; + find_info["subjectKeyIdentifier"] = "7b:1a:f9:2b:c4:b2:f6:ae:d6:f2:8e:b1:73:fb:dd:11:ca:db:f8:87"; LLBasicCertificateVector::iterator found_cert = test_vector->find(find_info); ensure("found some cert", found_cert != test_vector->end()); X509* found_x509 = (*found_cert).get()->getOpenSSLX509(); @@ -1225,7 +1245,7 @@ namespace tut X509_STORE_CTX_set0_untrusted(test_store, NULL); test_chain = new LLBasicCertificateChain(test_store); X509_STORE_CTX_free(test_store); - ensure_equals("two elements in store", test_chain->size(), 1); + ensure_equals("two elements in store [1]", test_chain->size(), 1); X509* test_cert = (*test_chain)[0]->getOpenSSLX509(); ensure("validate first element in store is expected cert", !X509_cmp(test_cert, mX509ChildCert)); X509_free(test_cert); @@ -1238,7 +1258,7 @@ namespace tut sk_X509_push(X509_STORE_CTX_get0_untrusted(test_store), mX509IntermediateCert); test_chain = new LLBasicCertificateChain(test_store); X509_STORE_CTX_free(test_store); - ensure_equals("two elements in store", test_chain->size(), 2); + ensure_equals("two elements in store [2]", test_chain->size(), 2); test_cert = (*test_chain)[0]->getOpenSSLX509(); ensure("validate first element in store is expected cert", !X509_cmp(test_cert, mX509ChildCert)); X509_free(test_cert); @@ -1254,7 +1274,7 @@ namespace tut sk_X509_push(X509_STORE_CTX_get0_untrusted(test_store), mX509TestCert); test_chain = new LLBasicCertificateChain(test_store); X509_STORE_CTX_free(test_store); - ensure_equals("two elements in store", test_chain->size(), 1); + ensure_equals("two elements in store [3]", test_chain->size(), 1); test_cert = (*test_chain)[0]->getOpenSSLX509(); ensure("validate first element in store is expected cert", !X509_cmp(test_cert, mX509ChildCert)); X509_free(test_cert); @@ -1267,7 +1287,7 @@ namespace tut sk_X509_push(X509_STORE_CTX_get0_untrusted(test_store), mX509TestCert); test_chain = new LLBasicCertificateChain(test_store); X509_STORE_CTX_free(test_store); - ensure_equals("two elements in store", test_chain->size(), 2); + ensure_equals("two elements in store [4]", test_chain->size(), 2); test_cert = (*test_chain)[0]->getOpenSSLX509(); ensure("validate first element in store is expected cert", !X509_cmp(test_cert, mX509ChildCert)); X509_free(test_cert); -- cgit v1.2.3 From 41ea8a61c247d915ebe53436e9cfc999a712b692 Mon Sep 17 00:00:00 2001 From: Mnikolenko Productengine Date: Fri, 26 Jul 2024 15:55:17 +0300 Subject: Add api for more script camera params --- indra/newview/llagentlistener.cpp | 54 ++++++++++++++++++++++++--- indra/newview/scripts/lua/require/LLAgent.lua | 19 +++++++++- 2 files changed, 66 insertions(+), 7 deletions(-) diff --git a/indra/newview/llagentlistener.cpp b/indra/newview/llagentlistener.cpp index 38a9d58c1f..045367dbdd 100644 --- a/indra/newview/llagentlistener.cpp +++ b/indra/newview/llagentlistener.cpp @@ -132,16 +132,25 @@ LLAgentListener::LLAgentListener(LLAgent &agent) "[\"contrib\"]: user's land contribution to this group\n", &LLAgentListener::getGroups, LLSDMap("reply", LLSD())); + //camera params are similar to LSL, see https://wiki.secondlife.com/wiki/LlSetCameraParams add("setCameraParams", "Set Follow camera params, and then activate it:\n" - "[\"camera_pos\"]: vector3\n" - "[\"focus_pos\"]: vector3\n" - "[\"focus_offset\"]: vector3\n" - "[\"camera_locked\"]: boolean\n" - "[\"focus_locked\"]: boolean", + "[\"camera_pos\"]: vector3, camera position in region coordinates\n" + "[\"focus_pos\"]: vector3, what the camera is aimed at (in region coordinates)\n" + "[\"focus_offset\"]: vector3, adjusts the camera focus position relative to the target, default is (1, 0, 0)\n" + "[\"distance\"]: float (meters), distance the camera wants to be from its target, default is 3\n" + "[\"focus_threshold\"]: float (meters), sets the radius of a sphere around the camera's target position within which its focus is not affected by target motion, default is 1\n" + "[\"camera_threshold\"]: float (meters), sets the radius of a sphere around the camera's ideal position within which it is not affected by target motion, default is 1\n" + "[\"focus_lag\"]: float (seconds), how much the camera lags as it tries to aim towards the target, default is 0.1\n" + "[\"camera_lag\"]: float (seconds), how much the camera lags as it tries to move towards its 'ideal' position, default is 0.1\n" + "[\"camera_pitch\"]: float (degrees), adjusts the angular amount that the camera aims straight ahead vs. straight down, maintaining the same distance, default is 0\n" + "[\"behindness_angle\"]: float (degrees), sets the angle in degrees within which the camera is not constrained by changes in target rotation, default is 10\n" + "[\"behindness_lag\"]: float (seconds), sets how strongly the camera is forced to stay behind the target if outside of behindness angle, default is 0\n" + "[\"camera_locked\"]: bool, locks the camera position so it will not move\n" + "[\"focus_locked\"]: bool, locks the camera focus so it will not move", &LLAgentListener::setFollowCamParams); add("setFollowCamActive", - "Set Follow camera active or deactivate it using boolean [\"active\"]", + "Turns on or off scripted control of the camera using boolean [\"active\"]", &LLAgentListener::setFollowCamActive, llsd::map("active", LLSD())); add("removeCameraParams", @@ -535,6 +544,39 @@ void LLAgentListener::setFollowCamParams(const LLSD& event) const { LLFollowCamMgr::getInstance()->setFocusLocked(gAgentID, event["focus_locked"]); } + if (event.has("distance")) + { + LLFollowCamMgr::getInstance()->setDistance(gAgentID, event["distance"].asReal()); + } + if (event.has("focus_threshold")) + { + LLFollowCamMgr::getInstance()->setFocusThreshold(gAgentID, event["focus_threshold"].asReal()); + } + if (event.has("camera_threshold")) + { + LLFollowCamMgr::getInstance()->setPositionThreshold(gAgentID, event["camera_threshold"].asReal()); + } + if (event.has("focus_lag")) + { + LLFollowCamMgr::getInstance()->setFocusLag(gAgentID, event["focus_lag"].asReal()); + } + if (event.has("camera_lag")) + { + LLFollowCamMgr::getInstance()->setPositionLag(gAgentID, event["camera_lag"].asReal()); + } + if (event.has("camera_pitch")) + { + LLFollowCamMgr::getInstance()->setPitch(gAgentID, event["camera_pitch"].asReal()); + } + if (event.has("behindness_lag")) + { + LLFollowCamMgr::getInstance()->setBehindnessLag(gAgentID, event["behindness_lag"].asReal()); + } + if (event.has("behindness_angle")) + { + LLFollowCamMgr::getInstance()->setBehindnessAngle(gAgentID, event["behindness_angle"].asReal()); + } + LLFollowCamMgr::getInstance()->setCameraActive(gAgentID, true); } diff --git a/indra/newview/scripts/lua/require/LLAgent.lua b/indra/newview/scripts/lua/require/LLAgent.lua index 7c6a842555..bc9a6b23a0 100644 --- a/indra/newview/scripts/lua/require/LLAgent.lua +++ b/indra/newview/scripts/lua/require/LLAgent.lua @@ -11,8 +11,25 @@ function LLAgent.getGlobalPosition() return leap.request('LLAgent', {op = 'getPosition'}).global end +-- Use LL.leaphelp('LLAgent') and see 'setCameraParams' to get more info about params +-- -- TYPE -- DEFAULT -- RANGE +-- LLAgent.setCamera{ [, camera_pos] -- vector3 +-- [, focus_pos] -- vector3 +-- [, focus_offset] -- vector3 -- {1,0,0} -- {-10,-10,-10} to {10,10,10} +-- [, distance] -- float (meters) -- 3 -- 0.5 to 50 +-- [, focus_threshold] -- float (meters) -- 1 -- 0 to 4 +-- [, camera_threshold] -- float (meters) -- 1 -- 0 to 4 +-- [, focus_lag] -- float (seconds) -- 0.1 -- 0 to 3 +-- [, camera_lag] -- float (seconds) -- 0.1 -- 0 to 3 +-- [, camera_pitch] -- float (degrees) -- 0 -- -45 to 80 +-- [, behindness_angle] -- float (degrees) -- 10 -- 0 to 180 +-- [, behindness_lag] -- float (seconds) -- 0 -- 0 to 3 +-- [, camera_locked] -- bool -- false +-- [, focus_locked]} -- bool -- false function LLAgent.setCamera(...) - local args = mapargs('camera_pos,focus_pos,focus_offset,camera_locked,focus_locked', ...) + local args = mapargs('camera_pos,focus_pos,focus_offset,focus_lag,camera_lag,' .. + 'distance,focus_threshold,camera_threshold,camera_pitch,' .. + 'camera_locked,focus_locked,behindness_angle,behindness_lag', ...) args.op = 'setCameraParams' leap.send('LLAgent', args) end -- cgit v1.2.3 From 8020079eb2bb50175f72fc7dde38346db7ee2477 Mon Sep 17 00:00:00 2001 From: Nicky Date: Fri, 26 Jul 2024 15:54:36 +0200 Subject: Add missing semicolon --- indra/llcommon/llerror.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/indra/llcommon/llerror.cpp b/indra/llcommon/llerror.cpp index 6c3b9c9542..237f07288e 100644 --- a/indra/llcommon/llerror.cpp +++ b/indra/llcommon/llerror.cpp @@ -109,7 +109,7 @@ namespace { virtual void recordMessage(LLError::ELevel level, const std::string& message) override { - LL_PROFILE_ZONE_SCOPED_CATEGORY_LOGGING + LL_PROFILE_ZONE_SCOPED_CATEGORY_LOGGING; int syslogPriority = LOG_CRIT; switch (level) { case LLError::LEVEL_DEBUG: syslogPriority = LOG_DEBUG; break; -- cgit v1.2.3 From 4225a04b444a3108439842b3ec629a55a5f7ba8b Mon Sep 17 00:00:00 2001 From: Nicky Date: Fri, 26 Jul 2024 16:23:32 +0200 Subject: Fix GCC being overly suspicious about a possible zero size allocation --- indra/llimage/llimagefilter.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/indra/llimage/llimagefilter.cpp b/indra/llimage/llimagefilter.cpp index bfcb1f76de..3b12aa39e4 100644 --- a/indra/llimage/llimagefilter.cpp +++ b/indra/llimage/llimagefilter.cpp @@ -389,6 +389,11 @@ void LLImageFilter::convolve(const LLMatrix3 &kernel, bool normalize, bool abs_v S32 buffer_size = width * components; llassert_always(buffer_size > 0); + + // ND: GCC womtimes is unable to figure out llassert_always (aka LLERROR_CRASH) will never return. + // This return here is just a dummy and will not be reached. + if( buffer_size == 0 ){return; } + std::vector even_buffer(buffer_size); std::vector odd_buffer(buffer_size); -- cgit v1.2.3 From c363e5bcf3e0cc60ee782d2ad82a8687e8a85cad Mon Sep 17 00:00:00 2001 From: Nicky Date: Fri, 26 Jul 2024 16:23:54 +0200 Subject: Post merge cleanup of Linux code --- indra/llwindow/llwindowsdl.cpp | 1646 ++++++++++++++++++-------------------- indra/llwindow/llwindowsdl.h | 334 +++++--- indra/newview/lldirpicker.cpp | 17 - indra/newview/viewer_manifest.py | 37 - 4 files changed, 1010 insertions(+), 1024 deletions(-) diff --git a/indra/llwindow/llwindowsdl.cpp b/indra/llwindow/llwindowsdl.cpp index 7433ad6bd2..e2d9d752ee 100644 --- a/indra/llwindow/llwindowsdl.cpp +++ b/indra/llwindow/llwindowsdl.cpp @@ -25,8 +25,6 @@ * $/LicenseInfo$ */ -#if LL_SDL - #include "linden_common.h" #include "llwindowsdl.h" @@ -40,12 +38,9 @@ #include "lldir.h" #include "llfindlocale.h" -#if LL_GTK -extern "C" { -# include "gtk/gtk.h" -} -#include -#endif // LL_GTK +#ifdef LL_GLIB +#include +#endif extern "C" { # include "fontconfig/fontconfig.h" @@ -57,6 +52,7 @@ extern "C" { # include # include # include +# include #endif // LL_LINUX extern bool gDebugWindowProc; @@ -97,100 +93,278 @@ void maybe_unlock_display(void) } -#if LL_GTK -// Lazily initialize and check the runtime GTK version for goodness. +#if LL_X11 // static -bool LLWindowSDL::ll_try_gtk_init(void) +Window LLWindowSDL::get_SDL_XWindowID(void) { - static bool done_gtk_diag = false; - static bool gtk_is_good = false; - static bool done_setlocale = false; - static bool tried_gtk_init = false; + if (gWindowImplementation) { + return gWindowImplementation->mSDL_XWindowID; + } + return None; +} - if (!done_setlocale) +//static +Display* LLWindowSDL::get_SDL_Display(void) +{ + if (gWindowImplementation) { + return gWindowImplementation->mSDL_Display; + } + return NULL; +} +#endif // LL_X11 + +#if LL_X11 + +// Clipboard handing via native X11, base on the implementation in Cool VL by Henri Beauchamp + +namespace +{ + std::array gSupportedAtoms; + + Atom XA_CLIPBOARD; + Atom XA_TARGETS; + Atom PVT_PASTE_BUFFER; + long const MAX_PASTE_BUFFER_SIZE = 16383; + + void filterSelectionRequest( XEvent aEvent ) { - LL_INFOS() << "Starting GTK Initialization." << LL_ENDL; - maybe_lock_display(); - gtk_disable_setlocale(); - maybe_unlock_display(); - done_setlocale = true; + auto *display = LLWindowSDL::getSDLDisplay(); + auto &request = aEvent.xselectionrequest; + + XSelectionEvent reply { SelectionNotify, aEvent.xany.serial, aEvent.xany.send_event, display, + request.requestor, request.selection, request.target, + request.property,request.time }; + + if (request.target == XA_TARGETS) + { + XChangeProperty(display, request.requestor, request.property, + XA_ATOM, 32, PropModeReplace, + (unsigned char *) &gSupportedAtoms.front(), gSupportedAtoms.size()); + } + else if (std::find(gSupportedAtoms.begin(), gSupportedAtoms.end(), request.target) != + gSupportedAtoms.end()) + { + std::string utf8; + if (request.selection == XA_PRIMARY) + utf8 = wstring_to_utf8str(gWindowImplementation->getPrimaryText()); + else + utf8 = wstring_to_utf8str(gWindowImplementation->getSecondaryText()); + + XChangeProperty(display, request.requestor, request.property, + request.target, 8, PropModeReplace, + (unsigned char *) utf8.c_str(), utf8.length()); + } + else if (request.selection == XA_CLIPBOARD) + { + // Did not have what they wanted, so no property set + reply.property = None; + } + else + return; + + XSendEvent(request.display, request.requestor, False, NoEventMask, (XEvent *) &reply); + XSync(display, False); } - if (!tried_gtk_init) + void filterSelectionClearRequest( XEvent aEvent ) { - tried_gtk_init = true; - if (!g_thread_supported ()) g_thread_init (NULL); - maybe_lock_display(); - gtk_is_good = gtk_init_check(NULL, NULL); - maybe_unlock_display(); - if (!gtk_is_good) - LL_WARNS() << "GTK Initialization failed." << LL_ENDL; + auto &request = aEvent.xselectionrequest; + if (request.selection == XA_PRIMARY) + gWindowImplementation->clearPrimaryText(); + else if (request.selection == XA_CLIPBOARD) + gWindowImplementation->clearSecondaryText(); + } + + int x11_clipboard_filter(void*, SDL_Event *evt) + { + Display *display = LLWindowSDL::getSDLDisplay(); + if (!display) + return 1; + + if (evt->type != SDL_SYSWMEVENT) + return 1; + + auto xevent = evt->syswm.msg->msg.x11.event; + + if (xevent.type == SelectionRequest) + filterSelectionRequest( xevent ); + else if (xevent.type == SelectionClear) + filterSelectionClearRequest( xevent ); + return 1; } - if (gtk_is_good && !done_gtk_diag) + bool grab_property(Display* display, Window window, Atom selection, Atom target) { - LL_INFOS() << "GTK Initialized." << LL_ENDL; - LL_INFOS() << "- Compiled against GTK version " - << GTK_MAJOR_VERSION << "." - << GTK_MINOR_VERSION << "." - << GTK_MICRO_VERSION << LL_ENDL; - LL_INFOS() << "- Running against GTK version " - << gtk_major_version << "." - << gtk_minor_version << "." - << gtk_micro_version << LL_ENDL; + if( !display ) + return false; + maybe_lock_display(); - const gchar* gtk_warning = gtk_check_version( - GTK_MAJOR_VERSION, - GTK_MINOR_VERSION, - GTK_MICRO_VERSION); + + XDeleteProperty(display, window, PVT_PASTE_BUFFER); + XFlush(display); + + XConvertSelection(display, selection, target, PVT_PASTE_BUFFER, window, CurrentTime); + + // Unlock the connection so that the SDL event loop may function maybe_unlock_display(); - if (gtk_warning) + + const auto start{ SDL_GetTicks() }; + const auto end{ start + 1000 }; + + XEvent xevent {}; + bool response = false; + + do { - LL_WARNS() << "- GTK COMPATIBILITY WARNING: " << - gtk_warning << LL_ENDL; - gtk_is_good = false; - } else { - LL_INFOS() << "- GTK version is good." << LL_ENDL; - } + SDL_Event event {}; + + // Wait for an event + SDL_WaitEvent(&event); + + // If the event is a window manager event + if (event.type == SDL_SYSWMEVENT) + { + xevent = event.syswm.msg->msg.x11.event; + + if (xevent.type == SelectionNotify && xevent.xselection.requestor == window) + response = true; + } + } while (!response && SDL_GetTicks() < end ); - done_gtk_diag = true; + return response && xevent.xselection.property != None; } +} + +void LLWindowSDL::initialiseX11Clipboard() +{ + if (!mSDL_Display) + return; + + SDL_EventState(SDL_SYSWMEVENT, SDL_ENABLE); + SDL_SetEventFilter(x11_clipboard_filter, nullptr); - return gtk_is_good; + maybe_lock_display(); + + XA_CLIPBOARD = XInternAtom(mSDL_Display, "CLIPBOARD", False); + + gSupportedAtoms[0] = XInternAtom(mSDL_Display, "UTF8_STRING", False); + gSupportedAtoms[1] = XInternAtom(mSDL_Display, "COMPOUND_TEXT", False); + gSupportedAtoms[2] = XA_STRING; + + // TARGETS atom + XA_TARGETS = XInternAtom(mSDL_Display, "TARGETS", False); + + // SL_PASTE_BUFFER atom + PVT_PASTE_BUFFER = XInternAtom(mSDL_Display, "FS_PASTE_BUFFER", False); + + maybe_unlock_display(); } -#endif // LL_GTK +bool LLWindowSDL::getSelectionText( Atom aSelection, Atom aType, LLWString &text ) +{ + if( !mSDL_Display ) + return false; -#if LL_X11 -// static -Window LLWindowSDL::get_SDL_XWindowID(void) + if( !grab_property(mSDL_Display, mSDL_XWindowID, aSelection,aType ) ) + return false; + + maybe_lock_display(); + + Atom type; + int format{}; + unsigned long len{},remaining {}; + unsigned char* data = nullptr; + int res = XGetWindowProperty(mSDL_Display, mSDL_XWindowID, + PVT_PASTE_BUFFER, 0, MAX_PASTE_BUFFER_SIZE, False, + AnyPropertyType, &type, &format, &len, + &remaining, &data); + if (data && len) + { + text = LLWString( + utf8str_to_wstring(reinterpret_cast< char const *>( data ) ) + ); + XFree(data); + } + + maybe_unlock_display(); + return res == Success; +} + +bool LLWindowSDL::getSelectionText(Atom selection, LLWString& text) { - if (gWindowImplementation) { - return gWindowImplementation->mSDL_XWindowID; + if (!mSDL_Display) + return false; + + maybe_lock_display(); + + Window owner = XGetSelectionOwner(mSDL_Display, selection); + if (owner == None) + { + if (selection == XA_PRIMARY) + { + owner = DefaultRootWindow(mSDL_Display); + selection = XA_CUT_BUFFER0; + } + else + { + maybe_unlock_display(); + return false; + } } - return None; + + maybe_unlock_display(); + + for( Atom atom : gSupportedAtoms ) + { + if(getSelectionText(selection, atom, text ) ) + return true; + } + + return false; } -//static -Display* LLWindowSDL::get_SDL_Display(void) +bool LLWindowSDL::setSelectionText(Atom selection, const LLWString& text) { - if (gWindowImplementation) { - return gWindowImplementation->mSDL_Display; + maybe_lock_display(); + + if (selection == XA_PRIMARY) + { + std::string utf8 = wstring_to_utf8str(text); + XStoreBytes(mSDL_Display, utf8.c_str(), utf8.length() + 1); + mPrimaryClipboard = text; } - return NULL; + else + mSecondaryClipboard = text; + + XSetSelectionOwner(mSDL_Display, selection, mSDL_XWindowID, CurrentTime); + + auto owner = XGetSelectionOwner(mSDL_Display, selection); + + maybe_unlock_display(); + + return owner == mSDL_XWindowID; } -#endif // LL_X11 + +Display* LLWindowSDL::getSDLDisplay() +{ + if (gWindowImplementation) + return gWindowImplementation->mSDL_Display; + return nullptr; +} + +#endif LLWindowSDL::LLWindowSDL(LLWindowCallbacks* callbacks, - const std::string& title, S32 x, S32 y, S32 width, - S32 height, U32 flags, - bool fullscreen, bool clearBg, - bool disable_vsync, bool use_gl, - bool ignore_pixel_depth, U32 fsaa_samples) - : LLWindow(callbacks, fullscreen, flags), - Lock_Display(NULL), - Unlock_Display(NULL), mGamma(1.0f) + const std::string& title, S32 x, S32 y, S32 width, + S32 height, U32 flags, + bool fullscreen, bool clearBg, + bool disable_vsync, bool use_gl, + bool ignore_pixel_depth, U32 fsaa_samples) + : LLWindow(callbacks, fullscreen, flags), + Lock_Display(NULL), + //Unlock_Display(NULL), mGamma(1.0f) + Unlock_Display(NULL), mGamma(1.0f) { // Initialize the keyboard gKeyboard = new LLKeyboardSDL(); @@ -199,6 +373,7 @@ LLWindowSDL::LLWindowSDL(LLWindowCallbacks* callbacks, // Ignore use_gl for now, only used for drones on PC mWindow = NULL; + mContext = {}; mNeedsResize = false; mOverrideAspectRatio = 0.f; mGrabbyKeyFlags = 0; @@ -209,16 +384,9 @@ LLWindowSDL::LLWindowSDL(LLWindowCallbacks* callbacks, #if LL_X11 mSDL_XWindowID = None; - mSDL_Display = NULL; + mSDL_Display = nullptr; #endif // LL_X11 -#if LL_GTK - // We MUST be the first to initialize GTK so that GTK doesn't get badly - // initialized with a non-C locale and cause lots of serious random - // weirdness. - ll_try_gtk_init(); -#endif // LL_GTK - // Assume 4:3 aspect ratio until we know better mOriginalAspectRatio = 1024.0 / 768.0; @@ -244,9 +412,9 @@ LLWindowSDL::LLWindowSDL(LLWindowCallbacks* callbacks, #if LL_X11 mFlashing = false; + initialiseX11Clipboard(); #endif // LL_X11 - mKeyScanCode = 0; mKeyVirtualKey = 0; mKeyModifiers = KMOD_NONE; } @@ -258,10 +426,10 @@ static SDL_Surface *Load_BMP_Resource(const char *basename) // Figure out where our BMP is living on the disk snprintf(path_buffer, PATH_BUFFER_SIZE-1, "%s%sres-sdl%s%s", - gDirUtilp->getAppRODataDir().c_str(), - gDirUtilp->getDirDelimiter().c_str(), - gDirUtilp->getDirDelimiter().c_str(), - basename); + gDirUtilp->getAppRODataDir().c_str(), + gDirUtilp->getDirDelimiter().c_str(), + gDirUtilp->getDirDelimiter().c_str(), + basename); path_buffer[PATH_BUFFER_SIZE-1] = '\0'; return SDL_LoadBMP(path_buffer); @@ -348,7 +516,7 @@ static int x11_detect_VRAM_kb() if (fp) { LL_INFOS() << "Looking in " << fname - << " for VRAM info..." << LL_ENDL; + << " for VRAM info..." << LL_ENDL; rtn = x11_detect_VRAM_kb_fp(fp, ": VideoRAM: "); fclose(fp); if (0 == rtn) @@ -373,7 +541,7 @@ static int x11_detect_VRAM_kb() else { LL_INFOS() << "Could not open " << fname - << " - skipped." << LL_ENDL; + << " - skipped." << LL_ENDL; // Try old XFree86 log otherwise fname = x_log_location; fname += "XFree86."; @@ -383,7 +551,7 @@ static int x11_detect_VRAM_kb() if (fp) { LL_INFOS() << "Looking in " << fname - << " for VRAM info..." << LL_ENDL; + << " for VRAM info..." << LL_ENDL; rtn = x11_detect_VRAM_kb_fp(fp, ": VideoRAM: "); fclose(fp); if (0 == rtn) @@ -399,95 +567,160 @@ static int x11_detect_VRAM_kb() else { LL_INFOS() << "Could not open " << fname - << " - skipped." << LL_ENDL; + << " - skipped." << LL_ENDL; } } return rtn; } #endif // LL_X11 +void LLWindowSDL::setTitle(const std::string title) +{ + SDL_SetWindowTitle( mWindow, title.c_str() ); +} + +void LLWindowSDL::tryFindFullscreenSize( int &width, int &height ) +{ + LL_INFOS() << "createContext: setting up fullscreen " << width << "x" << height << LL_ENDL; + + // If the requested width or height is 0, find the best default for the monitor. + if((width == 0) || (height == 0)) + { + // Scan through the list of modes, looking for one which has: + // height between 700 and 800 + // aspect ratio closest to the user's original mode + S32 resolutionCount = 0; + LLWindowResolution *resolutionList = getSupportedResolutions(resolutionCount); + + if(resolutionList != NULL) + { + F32 closestAspect = 0; + U32 closestHeight = 0; + U32 closestWidth = 0; + int i; + + LL_INFOS() << "createContext: searching for a display mode, original aspect is " << mOriginalAspectRatio << LL_ENDL; + + for(i=0; i < resolutionCount; i++) + { + F32 aspect = (F32)resolutionList[i].mWidth / (F32)resolutionList[i].mHeight; + + LL_INFOS() << "createContext: width " << resolutionList[i].mWidth << " height " << resolutionList[i].mHeight << " aspect " << aspect << LL_ENDL; + + if( (resolutionList[i].mHeight >= 700) && (resolutionList[i].mHeight <= 800) && + (fabs(aspect - mOriginalAspectRatio) < fabs(closestAspect - mOriginalAspectRatio))) + { + LL_INFOS() << " (new closest mode) " << LL_ENDL; + + // This is the closest mode we've seen yet. + closestWidth = resolutionList[i].mWidth; + closestHeight = resolutionList[i].mHeight; + closestAspect = aspect; + } + } + + width = closestWidth; + height = closestHeight; + } + } + + if((width == 0) || (height == 0)) + { + // Mode search failed for some reason. Use the old-school default. + width = 1024; + height = 768; + } +} + bool LLWindowSDL::createContext(int x, int y, int width, int height, int bits, bool fullscreen, bool disable_vsync) { //bool glneedsinit = false; LL_INFOS() << "createContext, fullscreen=" << fullscreen << - " size=" << width << "x" << height << LL_ENDL; + " size=" << width << "x" << height << LL_ENDL; // captures don't survive contexts mGrabbyKeyFlags = 0; mReallyCapturedCount = 0; - if (SDL_Init(SDL_INIT_VIDEO) < 0) + std::initializer_list > hintList = + { + {SDL_HINT_VIDEO_X11_NET_WM_BYPASS_COMPOSITOR,"0"}, + {SDL_HINT_MOUSE_FOCUS_CLICKTHROUGH,"1"}, + {SDL_HINT_IME_INTERNAL_EDITING,"1"} + }; + + for( auto hint: hintList ) { - LL_INFOS() << "sdl_init() failed! " << SDL_GetError() << LL_ENDL; - setupFailure("sdl_init() failure, window creation error", "error", OSMB_OK); - return false; + SDL_SetHint( std::get<0>(hint), std::get<1>(hint)); + } + + std::initializer_list> initList= + { {SDL_INIT_VIDEO,"SDL_INIT_VIDEO", true}, + {SDL_INIT_AUDIO,"SDL_INIT_AUDIO", false}, + {SDL_INIT_GAMECONTROLLER,"SDL_INIT_GAMECONTROLLER", false}, + {SDL_INIT_SENSOR,"SDL_INIT_SENSOR", false} + }; + + for( auto subSystem : initList) + { + if( SDL_InitSubSystem( std::get<0>(subSystem) ) < 0 ) + { + LL_WARNS() << "SDL_InitSubSystem for " << std::get<1>(subSystem) << " failed " << SDL_GetError() << LL_ENDL; + + if( std::get<2>(subSystem)) + setupFailure("SDL_Init() failure", "error", OSMB_OK); + + } } SDL_version c_sdl_version; SDL_VERSION(&c_sdl_version); LL_INFOS() << "Compiled against SDL " - << int(c_sdl_version.major) << "." - << int(c_sdl_version.minor) << "." - << int(c_sdl_version.patch) << LL_ENDL; - const SDL_version *r_sdl_version; - r_sdl_version = SDL_Linked_Version(); + << int(c_sdl_version.major) << "." + << int(c_sdl_version.minor) << "." + << int(c_sdl_version.patch) << LL_ENDL; + SDL_version r_sdl_version; + SDL_GetVersion(&r_sdl_version); LL_INFOS() << " Running against SDL " - << int(r_sdl_version->major) << "." - << int(r_sdl_version->minor) << "." - << int(r_sdl_version->patch) << LL_ENDL; + << int(r_sdl_version.major) << "." + << int(r_sdl_version.minor) << "." + << int(r_sdl_version.patch) << LL_ENDL; - const SDL_VideoInfo *video_info = SDL_GetVideoInfo( ); - if (!video_info) - { - LL_INFOS() << "SDL_GetVideoInfo() failed! " << SDL_GetError() << LL_ENDL; - setupFailure("SDL_GetVideoInfo() failed, Window creation error", "Error", OSMB_OK); - return false; - } + if (width == 0) + width = 1024; + if (height == 0) + width = 768; - if (video_info->current_h > 0) - { - mOriginalAspectRatio = (float)video_info->current_w / (float)video_info->current_h; - LL_INFOS() << "Original aspect ratio was " << video_info->current_w << ":" << video_info->current_h << "=" << mOriginalAspectRatio << LL_ENDL; - } + mFullscreen = fullscreen; - SDL_EnableUNICODE(1); - SDL_WM_SetCaption(mWindowTitle.c_str(), mWindowTitle.c_str()); + int sdlflags = SDL_WINDOW_OPENGL | SDL_WINDOW_RESIZABLE; - // Set the application icon. - SDL_Surface *bmpsurface; - bmpsurface = Load_BMP_Resource("ll_icon.BMP"); - if (bmpsurface) + if( mFullscreen ) { - // This attempts to give a black-keyed mask to the icon. - SDL_SetColorKey(bmpsurface, - SDL_SRCCOLORKEY, - SDL_MapRGB(bmpsurface->format, 0,0,0) ); - SDL_WM_SetIcon(bmpsurface, NULL); - // The SDL examples cheerfully avoid freeing the icon - // surface, but I'm betting that's leaky. - SDL_FreeSurface(bmpsurface); - bmpsurface = NULL; + sdlflags |= SDL_WINDOW_FULLSCREEN; + tryFindFullscreenSize( width, height ); } - // note: these SetAttributes make Tom's 9600-on-AMD64 fail to - // get a visual, but it's broken anyway when it does, and without - // these SetAttributes we might easily get an avoidable substandard - // visual to work with on most other machines. - SDL_GL_SetAttribute(SDL_GL_RED_SIZE, 8); - SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE,8); - SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, 8); - SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, (bits <= 16) ? 16 : 24); - // We need stencil support for a few (minor) things. - if (!getenv("LL_GL_NO_STENCIL")) - SDL_GL_SetAttribute(SDL_GL_STENCIL_SIZE, 8); - SDL_GL_SetAttribute(SDL_GL_ALPHA_SIZE, (bits <= 16) ? 1 : 8); + mSDLFlags = sdlflags; - // *FIX: try to toggle vsync here? + GLint redBits{8}, greenBits{8}, blueBits{8}, alphaBits{8}; - mFullscreen = fullscreen; + GLint depthBits{(bits <= 16) ? 16 : 24}, stencilBits{8}; + + if (getenv("LL_GL_NO_STENCIL")) + stencilBits = 0; - int sdlflags = SDL_OPENGL | SDL_RESIZABLE | SDL_ANYFORMAT; + SDL_GL_SetAttribute(SDL_GL_ALPHA_SIZE, alphaBits); + SDL_GL_SetAttribute(SDL_GL_RED_SIZE, redBits); + SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE, greenBits); + SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, blueBits); + SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, depthBits ); + + // We need stencil support for a few (minor) things. + if (stencilBits) + SDL_GL_SetAttribute(SDL_GL_STENCIL_SIZE, stencilBits); + // *FIX: try to toggle vsync here? SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1); @@ -497,80 +730,39 @@ bool LLWindowSDL::createContext(int x, int y, int width, int height, int bits, b SDL_GL_SetAttribute(SDL_GL_MULTISAMPLESAMPLES, mFSAASamples); } - mSDLFlags = sdlflags; + SDL_GL_SetAttribute(SDL_GL_SHARE_WITH_CURRENT_CONTEXT, 1); + mWindow = SDL_CreateWindow( mWindowTitle.c_str(), SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, width, height, mSDLFlags ); - if (mFullscreen) + if( mWindow ) { - LL_INFOS() << "createContext: setting up fullscreen " << width << "x" << height << LL_ENDL; - - // If the requested width or height is 0, find the best default for the monitor. - if((width == 0) || (height == 0)) - { - // Scan through the list of modes, looking for one which has: - // height between 700 and 800 - // aspect ratio closest to the user's original mode - S32 resolutionCount = 0; - LLWindowResolution *resolutionList = getSupportedResolutions(resolutionCount); - - if(resolutionList != NULL) - { - F32 closestAspect = 0; - U32 closestHeight = 0; - U32 closestWidth = 0; - int i; - - LL_INFOS() << "createContext: searching for a display mode, original aspect is " << mOriginalAspectRatio << LL_ENDL; - - for(i=0; i < resolutionCount; i++) - { - F32 aspect = (F32)resolutionList[i].mWidth / (F32)resolutionList[i].mHeight; - - LL_INFOS() << "createContext: width " << resolutionList[i].mWidth << " height " << resolutionList[i].mHeight << " aspect " << aspect << LL_ENDL; + mContext = SDL_GL_CreateContext( mWindow ); - if( (resolutionList[i].mHeight >= 700) && (resolutionList[i].mHeight <= 800) && - (fabs(aspect - mOriginalAspectRatio) < fabs(closestAspect - mOriginalAspectRatio))) - { - LL_INFOS() << " (new closest mode) " << LL_ENDL; - - // This is the closest mode we've seen yet. - closestWidth = resolutionList[i].mWidth; - closestHeight = resolutionList[i].mHeight; - closestAspect = aspect; - } - } - - width = closestWidth; - height = closestHeight; - } - } - - if((width == 0) || (height == 0)) + if( mContext == 0 ) { - // Mode search failed for some reason. Use the old-school default. - width = 1024; - height = 768; + LL_WARNS() << "Cannot create GL context " << SDL_GetError() << LL_ENDL; + setupFailure("GL Context creation error creation error", "Error", OSMB_OK); + return false; } + // SDL_GL_SetSwapInterval(1); + mSurface = SDL_GetWindowSurface( mWindow ); + } - mWindow = SDL_SetVideoMode(width, height, bits, sdlflags | SDL_FULLSCREEN); - if (!mWindow && bits > 16) - { - SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 16); - mWindow = SDL_SetVideoMode(width, height, bits, sdlflags | SDL_FULLSCREEN); - } - if (mWindow) + if( mFullscreen ) + { + if (mSurface) { mFullscreen = true; - mFullscreenWidth = mWindow->w; - mFullscreenHeight = mWindow->h; - mFullscreenBits = mWindow->format->BitsPerPixel; + mFullscreenWidth = mSurface->w; + mFullscreenHeight = mSurface->h; + mFullscreenBits = mSurface->format->BitsPerPixel; mFullscreenRefresh = -1; LL_INFOS() << "Running at " << mFullscreenWidth - << "x" << mFullscreenHeight - << "x" << mFullscreenBits - << " @ " << mFullscreenRefresh - << LL_ENDL; + << "x" << mFullscreenHeight + << "x" << mFullscreenBits + << " @ " << mFullscreenRefresh + << LL_ENDL; } else { @@ -584,33 +776,27 @@ bool LLWindowSDL::createContext(int x, int y, int width, int height, int bits, b std::string error = llformat("Unable to run fullscreen at %d x %d.\nRunning in window.", width, height); OSMessageBox(error, "Error", OSMB_OK); + return false; } } - - if(!mFullscreen && (mWindow == NULL)) + else { - if (width == 0) - width = 1024; - if (height == 0) - width = 768; - - LL_INFOS() << "createContext: creating window " << width << "x" << height << "x" << bits << LL_ENDL; - mWindow = SDL_SetVideoMode(width, height, bits, sdlflags); - if (!mWindow && bits > 16) - { - SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 16); - mWindow = SDL_SetVideoMode(width, height, bits, sdlflags); - } - if (!mWindow) { LL_WARNS() << "createContext: window creation failure. SDL: " << SDL_GetError() << LL_ENDL; setupFailure("Window creation error", "Error", OSMB_OK); return false; } - } else if (!mFullscreen && (mWindow != NULL)) + } + + // Set the application icon. + SDL_Surface *bmpsurface; + bmpsurface = Load_BMP_Resource("ll_icon.BMP"); + if (bmpsurface) { - LL_INFOS() << "createContext: SKIPPING - !fullscreen, but +mWindow " << width << "x" << height << "x" << bits << LL_ENDL; + SDL_SetWindowIcon(mWindow, bmpsurface); + SDL_FreeSurface(bmpsurface); + bmpsurface = NULL; } // Detect video memory size. @@ -625,7 +811,7 @@ bool LLWindowSDL::createContext(int x, int y, int width, int height, int bits, b // fallback to letting SDL detect VRAM. // note: I've not seen SDL's detection ever actually find // VRAM != 0, but if SDL *does* detect it then that's a bonus. - gGLManager.mVRAM = video_info->video_mem / 1024; + gGLManager.mVRAM = 0; if (gGLManager.mVRAM != 0) { LL_INFOS() << "SDL detected " << gGLManager.mVRAM << "MB VRAM." << LL_ENDL; @@ -637,22 +823,20 @@ bool LLWindowSDL::createContext(int x, int y, int width, int height, int bits, b // explicitly unsupported cards. //const char* RENDERER = (const char*) glGetString(GL_RENDERER); - GLint depthBits, stencilBits, redBits, greenBits, blueBits, alphaBits; - - glGetIntegerv(GL_RED_BITS, &redBits); - glGetIntegerv(GL_GREEN_BITS, &greenBits); - glGetIntegerv(GL_BLUE_BITS, &blueBits); - glGetIntegerv(GL_ALPHA_BITS, &alphaBits); - glGetIntegerv(GL_DEPTH_BITS, &depthBits); - glGetIntegerv(GL_STENCIL_BITS, &stencilBits); + SDL_GL_GetAttribute(SDL_GL_RED_SIZE, &redBits); + SDL_GL_GetAttribute(SDL_GL_GREEN_SIZE, &greenBits); + SDL_GL_GetAttribute(SDL_GL_BLUE_SIZE, &blueBits); + SDL_GL_GetAttribute(SDL_GL_ALPHA_SIZE, &alphaBits); + SDL_GL_GetAttribute(SDL_GL_DEPTH_SIZE, &depthBits); + SDL_GL_GetAttribute(SDL_GL_STENCIL_SIZE, &stencilBits); LL_INFOS() << "GL buffer:" << LL_ENDL; - LL_INFOS() << " Red Bits " << S32(redBits) << LL_ENDL; - LL_INFOS() << " Green Bits " << S32(greenBits) << LL_ENDL; - LL_INFOS() << " Blue Bits " << S32(blueBits) << LL_ENDL; - LL_INFOS() << " Alpha Bits " << S32(alphaBits) << LL_ENDL; - LL_INFOS() << " Depth Bits " << S32(depthBits) << LL_ENDL; - LL_INFOS() << " Stencil Bits " << S32(stencilBits) << LL_ENDL; + LL_INFOS() << " Red Bits " << S32(redBits) << LL_ENDL; + LL_INFOS() << " Green Bits " << S32(greenBits) << LL_ENDL; + LL_INFOS() << " Blue Bits " << S32(blueBits) << LL_ENDL; + LL_INFOS() << " Alpha Bits " << S32(alphaBits) << LL_ENDL; + LL_INFOS() << " Depth Bits " << S32(depthBits) << LL_ENDL; + LL_INFOS() << " Stencil Bits " << S32(stencilBits) << LL_ENDL; GLint colorBits = redBits + greenBits + blueBits + alphaBits; // fixme: actually, it's REALLY important for picking that we get at @@ -662,69 +846,46 @@ bool LLWindowSDL::createContext(int x, int y, int width, int height, int bits, b { close(); setupFailure( - "Second Life requires True Color (32-bit) to run in a window.\n" - "Please go to Control Panels -> Display -> Settings and\n" - "set the screen to 32-bit color.\n" - "Alternately, if you choose to run fullscreen, Second Life\n" - "will automatically adjust the screen each time it runs.", - "Error", - OSMB_OK); - return false; - } - -#if 0 // *FIX: we're going to brave it for now... - if (alphaBits < 8) - { - close(); - setupFailure( - "Second Life is unable to run because it can't get an 8 bit alpha\n" - "channel. Usually this is due to video card driver issues.\n" - "Please make sure you have the latest video card drivers installed.\n" - "Also be sure your monitor is set to True Color (32-bit) in\n" - "Control Panels -> Display -> Settings.\n" - "If you continue to receive this message, contact customer service.", - "Error", - OSMB_OK); + "Second Life requires True Color (32-bit) to run in a window.\n" + "Please go to Control Panels -> Display -> Settings and\n" + "set the screen to 32-bit color.\n" + "Alternately, if you choose to run fullscreen, Second Life\n" + "will automatically adjust the screen each time it runs.", + "Error", + OSMB_OK); return false; } -#endif #if LL_X11 /* Grab the window manager specific information */ SDL_SysWMinfo info; SDL_VERSION(&info.version); - if ( SDL_GetWMInfo(&info) ) + if ( SDL_GetWindowWMInfo(mWindow, &info) ) { /* Save the information for later use */ if ( info.subsystem == SDL_SYSWM_X11 ) { mSDL_Display = info.info.x11.display; - mSDL_XWindowID = info.info.x11.wmwindow; - Lock_Display = info.info.x11.lock_func; - Unlock_Display = info.info.x11.unlock_func; + mSDL_XWindowID = info.info.x11.window; } else { LL_WARNS() << "We're not running under X11? Wild." - << LL_ENDL; + << LL_ENDL; } } else { LL_WARNS() << "We're not running under any known WM. Wild." - << LL_ENDL; + << LL_ENDL; } #endif // LL_X11 + SDL_StartTextInput(); //make sure multisampling is disabled by default glDisable(GL_MULTISAMPLE_ARB); - // We need to do this here, once video is init'd - if (-1 == SDL_EnableKeyRepeat(SDL_DEFAULT_REPEAT_DELAY, - SDL_DEFAULT_REPEAT_INTERVAL)) - LL_WARNS() << "Couldn't enable key-repeat: " << SDL_GetError() <mX = mWindow->w; - size->mY = mWindow->h; - return (true); + size->mX = mSurface->w; + size->mY = mSurface->h; + return (true); } return (false); @@ -906,11 +1068,11 @@ bool LLWindowSDL::getSize(LLCoordScreen *size) bool LLWindowSDL::getSize(LLCoordWindow *size) { - if (mWindow) + if (mSurface) { - size->mX = mWindow->w; - size->mY = mWindow->h; - return (true); + size->mX = mSurface->w; + size->mY = mSurface->h; + return (true); } return (false); @@ -927,40 +1089,37 @@ bool LLWindowSDL::setPosition(const LLCoordScreen position) return true; } -bool LLWindowSDL::setSizeImpl(const LLCoordScreen size) +template< typename T > bool setSizeImpl( const T& newSize, SDL_Window *pWin ) { - if(mWindow) - { - // Push a resize event onto SDL's queue - we'll handle it - // when it comes out again. - SDL_Event event; - event.type = SDL_VIDEORESIZE; - event.resize.w = size.mX; - event.resize.h = size.mY; - SDL_PushEvent(&event); // copied into queue + if( !pWin ) + return false; - return true; - } + auto nFlags = SDL_GetWindowFlags( pWin ); - return false; + if( nFlags & SDL_WINDOW_MAXIMIZED ) + SDL_RestoreWindow( pWin ); + + + SDL_SetWindowSize( pWin, newSize.mX, newSize.mY ); + SDL_Event event; + event.type = SDL_WINDOWEVENT; + event.window.event = SDL_WINDOWEVENT_RESIZED; + event.window.windowID = SDL_GetWindowID( pWin ); + event.window.data1 = newSize.mX; + event.window.data2 = newSize.mY; + SDL_PushEvent( &event ); + + return true; } -bool LLWindowSDL::setSizeImpl(const LLCoordWindow size) +bool LLWindowSDL::setSizeImpl(const LLCoordScreen size) { - if(mWindow) - { - // Push a resize event onto SDL's queue - we'll handle it - // when it comes out again. - SDL_Event event; - event.type = SDL_VIDEORESIZE; - event.resize.w = size.mX; - event.resize.h = size.mY; - SDL_PushEvent(&event); // copied into queue - - return true; - } + return ::setSizeImpl( size, mWindow ); +} - return false; +bool LLWindowSDL::setSizeImpl(const LLCoordWindow size) +{ + return ::setSizeImpl( size, mWindow ); } @@ -968,7 +1127,7 @@ void LLWindowSDL::swapBuffers() { if (mWindow) { - SDL_GL_SwapBuffers(); + SDL_GL_SwapWindow( mWindow ); } } @@ -990,7 +1149,7 @@ F32 LLWindowSDL::getGamma() bool LLWindowSDL::restoreGamma() { //CGDisplayRestoreColorSyncSettings(); - SDL_SetGamma(1.0f, 1.0f, 1.0f); + // SDL_SetGamma(1.0f, 1.0f, 1.0f); return true; } @@ -999,7 +1158,7 @@ bool LLWindowSDL::setGamma(const F32 gamma) mGamma = gamma; if (mGamma == 0) mGamma = 0.1f; mGamma = 1/mGamma; - SDL_SetGamma(mGamma, mGamma, mGamma); + // SDL_SetGamma(mGamma, mGamma, mGamma); return true; } @@ -1048,7 +1207,7 @@ bool LLWindowSDL::setCursorPosition(const LLCoordWindow position) //LL_INFOS() << "setCursorPosition(" << screen_pos.mX << ", " << screen_pos.mY << ")" << LL_ENDL; // do the actual forced cursor move. - SDL_WarpMouse(screen_pos.mX, screen_pos.mY); + SDL_WarpMouseInWindow(mWindow, screen_pos.mX, screen_pos.mY); //LL_INFOS() << llformat("llcw %d,%d -> scr %d,%d", position.mX, position.mY, screen_pos.mX, screen_pos.mY) << LL_ENDL; @@ -1073,18 +1232,6 @@ bool LLWindowSDL::getCursorPosition(LLCoordWindow *position) F32 LLWindowSDL::getNativeAspectRatio() { -#if 0 - // RN: this hack presumes that the largest supported resolution is monitor-limited - // and that pixels in that mode are square, therefore defining the native aspect ratio - // of the monitor...this seems to work to a close approximation for most CRTs/LCDs - S32 num_resolutions; - LLWindowResolution* resolutions = getSupportedResolutions(num_resolutions); - - - return ((F32)resolutions[num_resolutions - 1].mWidth / (F32)resolutions[num_resolutions - 1].mHeight); - //rn: AC -#endif - // MBW -- there are a couple of bad assumptions here. One is that the display list won't include // ridiculous resolutions nobody would ever use. The other is that the list is in order. @@ -1146,7 +1293,7 @@ void LLWindowSDL::beforeDialog() // it only works in X11 if (running_x11 && mWindow) { - SDL_WM_ToggleFullScreen(mWindow); + SDL_SetWindowFullscreen( mWindow, 0 ); } } } @@ -1162,12 +1309,6 @@ void LLWindowSDL::beforeDialog() } #endif // LL_X11 -#if LL_GTK - // this is a good time to grab some GTK version information for - // diagnostics, if not already done. - ll_try_gtk_init(); -#endif // LL_GTK - maybe_lock_display(); } @@ -1188,7 +1329,7 @@ void LLWindowSDL::afterDialog() // in X11 if (running_x11 && mWindow) { - SDL_WM_ToggleFullScreen(mWindow); + SDL_SetWindowFullscreen( mWindow, 0 ); } } } @@ -1224,143 +1365,56 @@ void LLWindowSDL::x11_set_urgent(bool urgent) void LLWindowSDL::flashIcon(F32 seconds) { + if (getMinimized()) + { #if !LL_X11 - LL_INFOS() << "Stub LLWindowSDL::flashIcon(" << seconds << ")" << LL_ENDL; + LL_INFOS() << "Stub LLWindowSDL::flashIcon(" << seconds << ")" << LL_ENDL; #else - LL_INFOS() << "X11 LLWindowSDL::flashIcon(" << seconds << ")" << LL_ENDL; + LL_INFOS() << "X11 LLWindowSDL::flashIcon(" << seconds << ")" << LL_ENDL; - F32 remaining_time = mFlashTimer.getRemainingTimeF32(); - if (remaining_time < seconds) - remaining_time = seconds; - mFlashTimer.reset(); - mFlashTimer.setTimerExpirySec(remaining_time); + F32 remaining_time = mFlashTimer.getRemainingTimeF32(); + if (remaining_time < seconds) + remaining_time = seconds; + mFlashTimer.reset(); + mFlashTimer.setTimerExpirySec(remaining_time); - x11_set_urgent(true); - mFlashing = true; + x11_set_urgent(true); + mFlashing = true; #endif // LL_X11 -} - - -#if LL_GTK -bool LLWindowSDL::isClipboardTextAvailable() -{ - if (ll_try_gtk_init()) - { - GtkClipboard * const clipboard = - gtk_clipboard_get(GDK_NONE); - return gtk_clipboard_wait_is_text_available(clipboard) ? - true : false; - } - return false; // failure -} - -bool LLWindowSDL::pasteTextFromClipboard(LLWString &text) -{ - if (ll_try_gtk_init()) - { - GtkClipboard * const clipboard = - gtk_clipboard_get(GDK_NONE); - gchar * const data = gtk_clipboard_wait_for_text(clipboard); - if (data) - { - text = LLWString(utf8str_to_wstring(data)); - g_free(data); - return true; - } - } - return false; // failure -} - -bool LLWindowSDL::copyTextToClipboard(const LLWString &text) -{ - if (ll_try_gtk_init()) - { - const std::string utf8 = wstring_to_utf8str(text); - GtkClipboard * const clipboard = - gtk_clipboard_get(GDK_NONE); - gtk_clipboard_set_text(clipboard, utf8.c_str(), utf8.length()); - return true; - } - return false; // failure -} - - -bool LLWindowSDL::isPrimaryTextAvailable() -{ - if (ll_try_gtk_init()) - { - GtkClipboard * const clipboard = - gtk_clipboard_get(GDK_SELECTION_PRIMARY); - return gtk_clipboard_wait_is_text_available(clipboard) ? - true : false; - } - return false; // failure -} - -bool LLWindowSDL::pasteTextFromPrimary(LLWString &text) -{ - if (ll_try_gtk_init()) - { - GtkClipboard * const clipboard = - gtk_clipboard_get(GDK_SELECTION_PRIMARY); - gchar * const data = gtk_clipboard_wait_for_text(clipboard); - if (data) - { - text = LLWString(utf8str_to_wstring(data)); - g_free(data); - return true; - } - } - return false; // failure -} - -bool LLWindowSDL::copyTextToPrimary(const LLWString &text) -{ - if (ll_try_gtk_init()) - { - const std::string utf8 = wstring_to_utf8str(text); - GtkClipboard * const clipboard = - gtk_clipboard_get(GDK_SELECTION_PRIMARY); - gtk_clipboard_set_text(clipboard, utf8.c_str(), utf8.length()); - return true; } - return false; // failure } -#else - bool LLWindowSDL::isClipboardTextAvailable() { - return false; // unsupported + return mSDL_Display && XGetSelectionOwner(mSDL_Display, XA_CLIPBOARD) != None; } bool LLWindowSDL::pasteTextFromClipboard(LLWString &dst) { - return false; // unsupported + return getSelectionText(XA_CLIPBOARD, dst); } bool LLWindowSDL::copyTextToClipboard(const LLWString &s) { - return false; // unsupported + return setSelectionText(XA_CLIPBOARD, s); } bool LLWindowSDL::isPrimaryTextAvailable() { - return false; // unsupported + LLWString text; + return getSelectionText(XA_PRIMARY, text) && !text.empty(); } bool LLWindowSDL::pasteTextFromPrimary(LLWString &dst) { - return false; // unsupported + return getSelectionText(XA_PRIMARY, dst); } bool LLWindowSDL::copyTextToPrimary(const LLWString &s) { - return false; // unsupported + return setSelectionText(XA_PRIMARY, s); } -#endif // LL_GTK - LLWindow::LLWindowResolution* LLWindowSDL::getSupportedResolutions(S32 &num_resolutions) { if (!mSupportedResolutions) @@ -1368,33 +1422,30 @@ LLWindow::LLWindowResolution* LLWindowSDL::getSupportedResolutions(S32 &num_reso mSupportedResolutions = new LLWindowResolution[MAX_NUM_RESOLUTIONS]; mNumSupportedResolutions = 0; - SDL_Rect **modes = SDL_ListModes(NULL, SDL_OPENGL | SDL_FULLSCREEN); - if ( (modes != NULL) && (modes != ((SDL_Rect **) -1)) ) + // Use display no from mWindow/mSurface here? + int max = SDL_GetNumDisplayModes(0); + max = llclamp( max, 0, MAX_NUM_RESOLUTIONS ); + + for( int i =0; i < max; ++i ) { - int count = 0; - while (*modes && count= 800) && (h >= 600)) { - modes--; - SDL_Rect *r = *modes; - int w = r->w; - int h = r->h; - if ((w >= 800) && (h >= 600)) + // make sure we don't add the same resolution multiple times! + if ( (mNumSupportedResolutions == 0) || + ((mSupportedResolutions[mNumSupportedResolutions-1].mWidth != w) && + (mSupportedResolutions[mNumSupportedResolutions-1].mHeight != h)) ) { - // make sure we don't add the same resolution multiple times! - if ( (mNumSupportedResolutions == 0) || - ((mSupportedResolutions[mNumSupportedResolutions-1].mWidth != w) && - (mSupportedResolutions[mNumSupportedResolutions-1].mHeight != h)) ) - { - mSupportedResolutions[mNumSupportedResolutions].mWidth = w; - mSupportedResolutions[mNumSupportedResolutions].mHeight = h; - mNumSupportedResolutions++; - } + mSupportedResolutions[mNumSupportedResolutions].mWidth = w; + mSupportedResolutions[mNumSupportedResolutions].mHeight = h; + mNumSupportedResolutions++; } } } @@ -1410,7 +1461,7 @@ bool LLWindowSDL::convertCoords(LLCoordGL from, LLCoordWindow *to) return false; to->mX = from.mX; - to->mY = mWindow->h - from.mY - 1; + to->mY = mSurface->h - from.mY - 1; return true; } @@ -1421,7 +1472,7 @@ bool LLWindowSDL::convertCoords(LLCoordWindow from, LLCoordGL* to) return false; to->mX = from.mX; - to->mY = mWindow->h - from.mY - 1; + to->mY = mSurface->h - from.mY - 1; return true; } @@ -1482,13 +1533,13 @@ bool LLWindowSDL::SDLReallyCaptureInput(bool capture) else mReallyCapturedCount = 0; - SDL_GrabMode wantmode, newmode; + bool wantGrab; if (mReallyCapturedCount <= 0) // uncapture { - wantmode = SDL_GRAB_OFF; + wantGrab = false; } else // capture { - wantmode = SDL_GRAB_ON; + wantGrab = true; } if (mReallyCapturedCount < 0) // yuck, imbalance. @@ -1497,9 +1548,11 @@ bool LLWindowSDL::SDLReallyCaptureInput(bool capture) LL_WARNS() << "ReallyCapture count was < 0" << LL_ENDL; } + bool newGrab = wantGrab; + +#if LL_X11 if (!mFullscreen) /* only bother if we're windowed anyway */ { -#if LL_X11 if (mSDL_Display) { /* we dirtily mix raw X11 with SDL so that our pointer @@ -1512,49 +1565,37 @@ bool LLWindowSDL::SDLReallyCaptureInput(bool capture) *keyboard* input from the window manager, which was frustrating users. */ int result; - if (wantmode == SDL_GRAB_ON) + if (wantGrab == true) { - //LL_INFOS() << "X11 POINTER GRABBY" << LL_ENDL; - //newmode = SDL_WM_GrabInput(wantmode); maybe_lock_display(); result = XGrabPointer(mSDL_Display, mSDL_XWindowID, - True, 0, GrabModeAsync, - GrabModeAsync, - None, None, CurrentTime); + True, 0, GrabModeAsync, + GrabModeAsync, + None, None, CurrentTime); maybe_unlock_display(); if (GrabSuccess == result) - newmode = SDL_GRAB_ON; + newGrab = true; else - newmode = SDL_GRAB_OFF; - } else if (wantmode == SDL_GRAB_OFF) + newGrab = false; + } + else { - //LL_INFOS() << "X11 POINTER UNGRABBY" << LL_ENDL; - newmode = SDL_GRAB_OFF; - //newmode = SDL_WM_GrabInput(SDL_GRAB_OFF); + newGrab = false; maybe_lock_display(); XUngrabPointer(mSDL_Display, CurrentTime); // Make sure the ungrab happens RIGHT NOW. XSync(mSDL_Display, False); maybe_unlock_display(); - } else - { - newmode = SDL_GRAB_QUERY; // neutral } - } else // not actually running on X11, for some reason - newmode = wantmode; -#endif // LL_X11 - } else { - // pretend we got what we wanted, when really we don't care. - newmode = wantmode; + } } - +#endif // LL_X11 // return boolean success for whether we ended up in the desired state - return (capture && SDL_GRAB_ON==newmode) || - (!capture && SDL_GRAB_OFF==newmode); + return capture == newGrab; } -U32 LLWindowSDL::SDLCheckGrabbyKeys(SDLKey keysym, bool gain) +U32 LLWindowSDL::SDLCheckGrabbyKeys(U32 keysym, bool gain) { /* part of the fix for SL-13243: Some popular window managers like to totally eat alt-drag for the purposes of moving windows. We @@ -1572,16 +1613,16 @@ U32 LLWindowSDL::SDLCheckGrabbyKeys(SDLKey keysym, bool gain) U32 mask = 0; switch (keysym) { - case SDLK_LALT: - mask = 1U << 0; break; - case SDLK_RALT: - mask = 1U << 1; break; - case SDLK_LCTRL: - mask = 1U << 2; break; - case SDLK_RCTRL: - mask = 1U << 3; break; - default: - break; + case SDLK_LALT: + mask = 1U << 0; break; + case SDLK_RALT: + mask = 1U << 1; break; + case SDLK_LCTRL: + mask = 1U << 2; break; + case SDLK_RCTRL: + mask = 1U << 3; break; + default: + break; } if (gain) @@ -1679,7 +1720,7 @@ void check_vm_bloat() last_rss_size = this_rss_size; last_vm_size = this_vm_size; -finally: + finally: if (NULL != ptr) { free(ptr); @@ -1694,33 +1735,17 @@ finally: // virtual void LLWindowSDL::processMiscNativeEvents() { -#if LL_GTK - // Pump GTK events to avoid starvation for: - // * DBUS servicing - // * Anything else which quietly hooks into the default glib/GTK loop - if (ll_try_gtk_init()) - { - // Yuck, Mozilla's GTK callbacks play with the locale - push/pop - // the locale to protect it, as exotic/non-C locales - // causes our code lots of general critical weirdness - // and crashness. (SL-35450) - static std::string saved_locale; - saved_locale = ll_safe_string(setlocale(LC_ALL, NULL)); - - // Pump until we've nothing left to do or passed 1/15th of a - // second pumping for this frame. - static LLTimer pump_timer; - pump_timer.reset(); - pump_timer.setTimerExpirySec(1.0f / 15.0f); - do { - // Always do at least one non-blocking pump - gtk_main_iteration_do(false); - } while (gtk_events_pending() && - !pump_timer.hasExpired()); - - setlocale(LC_ALL, saved_locale.c_str() ); - } -#endif // LL_GTK +#if LL_GLIB + // Pump until we've nothing left to do or passed 1/15th of a + // second pumping for this frame. + static LLTimer pump_timer; + pump_timer.reset(); + pump_timer.setTimerExpirySec(1.0f / 15.0f); + do + { + g_main_context_iteration(g_main_context_default(), false); + } while( g_main_context_pending(g_main_context_default()) && !pump_timer.hasExpired()); +#endif // hack - doesn't belong here - but this is just for debugging if (getenv("LL_DEBUG_BLOAT")) @@ -1743,6 +1768,11 @@ void LLWindowSDL::gatherInput() { switch (event.type) { + case SDL_MOUSEWHEEL: + if( event.wheel.y != 0 ) + mCallbacks->handleScrollWheel(this, -event.wheel.y); + break; + case SDL_MOUSEMOTION: { LLCoordWindow winCoord(event.button.x, event.button.y); @@ -1753,33 +1783,68 @@ void LLWindowSDL::gatherInput() break; } + case SDL_TEXTINPUT: + { + auto string = utf8str_to_utf16str( event.text.text ); + mKeyModifiers = gKeyboard->currentMask( false ); + mInputType = "textinput"; + for( auto key: string ) + { + mKeyVirtualKey = key; + + if( (MASK_CONTROL|MASK_ALT)&mKeyModifiers ) + gKeyboard->handleKeyDown(mKeyVirtualKey, mKeyModifiers ); + else + handleUnicodeUTF16( key, mKeyModifiers ); + } + break; + } + case SDL_KEYDOWN: - mKeyScanCode = event.key.keysym.scancode; - mKeyVirtualKey = event.key.keysym.unicode; - mKeyModifiers = event.key.keysym.mod; + mKeyVirtualKey = event.key.keysym.sym; + mKeyModifiers = event.key.keysym.mod; + mInputType = "keydown"; - gKeyboard->handleKeyDown(event.key.keysym.sym, event.key.keysym.mod); - // part of the fix for SL-13243 - if (SDLCheckGrabbyKeys(event.key.keysym.sym, true) != 0) - SDLReallyCaptureInput(true); + // treat all possible Enter/Return keys the same + if (mKeyVirtualKey == SDLK_RETURN2 || mKeyVirtualKey == SDLK_KP_ENTER) + { + mKeyVirtualKey = SDLK_RETURN; + } + + gKeyboard->handleKeyDown(mKeyVirtualKey, mKeyModifiers ); + + // Slightly hacky :| To make the viewer honor enter (eg to accept form input) we've to not only send handleKeyDown but also send a + // invoke handleUnicodeUTF16 in case the user hits return. + // Note that we cannot blindly use handleUnicodeUTF16 for each SDL_KEYDOWN. Doing so will create bogus keyboard input (like % for cursor left). + if( mKeyVirtualKey == SDLK_RETURN ) + { + // fix return key not working when capslock, scrolllock or numlock are enabled + mKeyModifiers &= (~(KMOD_NUM | KMOD_CAPS | KMOD_MODE | KMOD_SCROLL)); + handleUnicodeUTF16( mKeyVirtualKey, mKeyModifiers ); + } + + // part of the fix for SL-13243 + if (SDLCheckGrabbyKeys(event.key.keysym.sym, true) != 0) + SDLReallyCaptureInput(true); - if (event.key.keysym.unicode) - { - handleUnicodeUTF16(event.key.keysym.unicode, - gKeyboard->currentMask(false)); - } break; case SDL_KEYUP: - mKeyScanCode = event.key.keysym.scancode; - mKeyVirtualKey = event.key.keysym.unicode; - mKeyModifiers = event.key.keysym.mod; + mKeyVirtualKey = event.key.keysym.sym; + mKeyModifiers = event.key.keysym.mod; + mInputType = "keyup"; - if (SDLCheckGrabbyKeys(event.key.keysym.sym, false) == 0) - SDLReallyCaptureInput(false); // part of the fix for SL-13243 + // treat all possible Enter/Return keys the same + if (mKeyVirtualKey == SDLK_RETURN2 || mKeyVirtualKey == SDLK_KP_ENTER) + { + mKeyVirtualKey = SDLK_RETURN; + } - gKeyboard->handleKeyUp(event.key.keysym.sym, event.key.keysym.mod); - break; + if (SDLCheckGrabbyKeys(mKeyVirtualKey, false) == 0) + SDLReallyCaptureInput(false); // part of the fix for SL-13243 + + gKeyboard->handleKeyUp(mKeyVirtualKey,mKeyModifiers); + break; case SDL_MOUSEBUTTONDOWN: { @@ -1787,7 +1852,7 @@ void LLWindowSDL::gatherInput() LLCoordWindow winCoord(event.button.x, event.button.y); LLCoordGL openGlCoord; convertCoords(winCoord, &openGlCoord); - MASK mask = gKeyboard->currentMask(true); + MASK mask = gKeyboard->currentMask(true); if (event.button.button == SDL_BUTTON_LEFT) // SDL doesn't manage double clicking... { @@ -1799,7 +1864,7 @@ void LLWindowSDL::gatherInput() if (++leftClick >= 2) { leftClick = 0; - isDoubleClick = true; + isDoubleClick = true; } } lastLeftDown = now; @@ -1830,7 +1895,7 @@ void LLWindowSDL::gatherInput() else if (event.button.button == SDL_BUTTON_RIGHT) // right { - mCallbacks->handleRightMouseDown(this, openGlCoord, mask); + mCallbacks->handleRightMouseDown(this, openGlCoord, mask); } else if (event.button.button == SDL_BUTTON_MIDDLE) // middle @@ -1850,86 +1915,69 @@ void LLWindowSDL::gatherInput() LLCoordWindow winCoord(event.button.x, event.button.y); LLCoordGL openGlCoord; convertCoords(winCoord, &openGlCoord); - MASK mask = gKeyboard->currentMask(true); + MASK mask = gKeyboard->currentMask(true); if (event.button.button == SDL_BUTTON_LEFT) // left - mCallbacks->handleMouseUp(this, openGlCoord, mask); + mCallbacks->handleMouseUp(this, openGlCoord, mask); else if (event.button.button == SDL_BUTTON_RIGHT) // right - mCallbacks->handleRightMouseUp(this, openGlCoord, mask); + mCallbacks->handleRightMouseUp(this, openGlCoord, mask); else if (event.button.button == SDL_BUTTON_MIDDLE) // middle - mCallbacks->handleMiddleMouseUp(this, openGlCoord, mask); + mCallbacks->handleMiddleMouseUp(this, openGlCoord, mask); // don't handle mousewheel here... break; } - case SDL_VIDEOEXPOSE: // VIDEOEXPOSE doesn't specify the damage, but hey, it's OpenGL...repaint the whole thing! - mCallbacks->handlePaint(this, 0, 0, mWindow->w, mWindow->h); - break; - - case SDL_VIDEORESIZE: // *FIX: handle this? + case SDL_WINDOWEVENT: // *FIX: handle this? { - LL_INFOS() << "Handling a resize event: " << event.resize.w << - "x" << event.resize.h << LL_ENDL; + if( event.window.event == SDL_WINDOWEVENT_RESIZED + /* || event.window.event == SDL_WINDOWEVENT_SIZE_CHANGED*/ ) // SDL_WINDOWEVENT_SIZE_CHANGED is followed by SDL_WINDOWEVENT_RESIZED, so handling one shall be enough + { + LL_INFOS() << "Handling a resize event: " << event.window.data1 << "x" << event.window.data2 << LL_ENDL; - S32 width = llmax(event.resize.w, (S32)mMinWindowWidth); - S32 height = llmax(event.resize.h, (S32)mMinWindowHeight); + S32 width = llmax(event.window.data1, (S32)mMinWindowWidth); + S32 height = llmax(event.window.data2, (S32)mMinWindowHeight); + mSurface = SDL_GetWindowSurface( mWindow ); - // *FIX: I'm not sure this is necessary! - mWindow = SDL_SetVideoMode(width, height, 32, mSDLFlags); - if (!mWindow) - { - // *FIX: More informative dialog? - LL_INFOS() << "Could not recreate context after resize! Quitting..." << LL_ENDL; - if(mCallbacks->handleCloseRequest(this)) - { - // Get the app to initiate cleanup. - mCallbacks->handleQuit(this); - // The app is responsible for calling destroyWindow when done with GL - } - break; - } + // *FIX: I'm not sure this is necessary! + // I think is is not + // SDL_SetWindowSize(mWindow, width, height); + // - mCallbacks->handleResize(this, width, height); - break; - } - case SDL_ACTIVEEVENT: - if (event.active.state & SDL_APPINPUTFOCUS) + mCallbacks->handleResize(this, width, height); + } + else if( event.window.event == SDL_WINDOWEVENT_FOCUS_GAINED ) // What about SDL_WINDOWEVENT_ENTER (mouse focus) { - // Note that for SDL (particularly on X11), keyboard - // and mouse focus are independent things. Here we are - // tracking keyboard focus state changes. - - // We have to do our own state massaging because SDL - // can send us two unfocus events in a row for example, - // which confuses the focus code [SL-24071]. - if (event.active.gain != mHaveInputFocus) - { - mHaveInputFocus = !!event.active.gain; + // We have to do our own state massaging because SDL + // can send us two unfocus events in a row for example, + // which confuses the focus code [SL-24071]. + mHaveInputFocus = true; - if (mHaveInputFocus) mCallbacks->handleFocus(this); - else + } + else if( event.window.event == SDL_WINDOWEVENT_FOCUS_LOST ) // What about SDL_WINDOWEVENT_LEAVE (mouse focus) + { + // We have to do our own state massaging because SDL + // can send us two unfocus events in a row for example, + // which confuses the focus code [SL-24071]. + mHaveInputFocus = false; + mCallbacks->handleFocusLost(this); - } } - if (event.active.state & SDL_APPACTIVE) + else if( event.window.event == SDL_WINDOWEVENT_MINIMIZED || + event.window.event == SDL_WINDOWEVENT_MAXIMIZED || + event.window.event == SDL_WINDOWEVENT_RESTORED || + event.window.event == SDL_WINDOWEVENT_EXPOSED || + event.window.event == SDL_WINDOWEVENT_SHOWN ) { - // Change in iconification/minimization state. - if ((!event.active.gain) != mIsMinimized) - { - mIsMinimized = (!event.active.gain); + mIsMinimized = (event.window.event == SDL_WINDOWEVENT_MINIMIZED); - mCallbacks->handleActivate(this, !mIsMinimized); - LL_INFOS() << "SDL deiconification state switched to " << bool(event.active.gain) << LL_ENDL; - } - else - { - LL_INFOS() << "Ignored bogus redundant SDL deiconification state switch to " << bool(event.active.gain) << LL_ENDL; - } + mCallbacks->handleActivate(this, !mIsMinimized); + LL_INFOS() << "SDL deiconification state switched to " << mIsMinimized << LL_ENDL; } - break; + break; + } case SDL_QUIT: if(mCallbacks->handleCloseRequest(this)) { @@ -1938,9 +1986,9 @@ void LLWindowSDL::gatherInput() // The app is responsible for calling destroyWindow when done with GL } break; - default: - //LL_INFOS() << "Unhandled SDL event type " << event.type << LL_ENDL; - break; + default: + //LL_INFOS() << "Unhandled SDL event type " << event.type << LL_ENDL; + break; } } @@ -1968,21 +2016,21 @@ static SDL_Cursor *makeSDLCursorFromBMP(const char *filename, int hotx, int hoty { SDL_Surface *cursurface; LL_DEBUGS() << "Loaded cursor file " << filename << " " - << bmpsurface->w << "x" << bmpsurface->h << LL_ENDL; + << bmpsurface->w << "x" << bmpsurface->h << LL_ENDL; cursurface = SDL_CreateRGBSurface (SDL_SWSURFACE, - bmpsurface->w, - bmpsurface->h, - 32, - SDL_SwapLE32(0xFFU), - SDL_SwapLE32(0xFF00U), - SDL_SwapLE32(0xFF0000U), - SDL_SwapLE32(0xFF000000U)); + bmpsurface->w, + bmpsurface->h, + 32, + SDL_SwapLE32(0xFFU), + SDL_SwapLE32(0xFF00U), + SDL_SwapLE32(0xFF0000U), + SDL_SwapLE32(0xFF000000U)); SDL_FillRect(cursurface, NULL, SDL_SwapLE32(0x00000000U)); // Blit the cursor pixel data onto a 32-bit RGBA surface so we // only have to cope with processing one type of pixel format. if (0 == SDL_BlitSurface(bmpsurface, NULL, - cursurface, NULL)) + cursurface, NULL)) { // n.b. we already checked that width is a multiple of 8. const int bitmap_bytes = (cursurface->w * cursurface->h) / 8; @@ -1997,26 +2045,26 @@ static SDL_Cursor *makeSDLCursorFromBMP(const char *filename, int hotx, int hoty for (i=0; ih; ++i) { for (j=0; jw; ++j) { U8 *pixelp = - ((U8*)cursurface->pixels) - + cursurface->pitch * i - + j*cursurface->format->BytesPerPixel; + ((U8*)cursurface->pixels) + + cursurface->pitch * i + + j*cursurface->format->BytesPerPixel; U8 srcred = pixelp[0]; U8 srcgreen = pixelp[1]; U8 srcblue = pixelp[2]; bool mask_bit = (srcred != 200) - || (srcgreen != 200) - || (srcblue != 200); + || (srcgreen != 200) + || (srcblue != 200); bool data_bit = mask_bit && (srcgreen <= 80);//not 0x80 unsigned char bit_offset = (cursurface->w/8) * i - + j/8; + + j/8; cursor_data[bit_offset] |= (data_bit) << (7 - (j&7)); cursor_mask[bit_offset] |= (mask_bit) << (7 - (j&7)); } } sdlcursor = SDL_CreateCursor((Uint8*)cursor_data, - (Uint8*)cursor_mask, - cursurface->w, cursurface->h, - hotx, hoty); + (Uint8*)cursor_mask, + cursurface->w, cursurface->h, + hotx, hoty); delete[] cursor_data; delete[] cursor_mask; } else { @@ -2207,8 +2255,6 @@ void LLWindowSDL::hideCursorUntilMouseMove() } } - - // // LLSplashScreenSDL - I don't think we'll bother to implement this; it's // fairly obsolete at this point. @@ -2233,133 +2279,51 @@ void LLSplashScreenSDL::hideImpl() { } - - -#if LL_GTK -static void response_callback (GtkDialog *dialog, - gint arg1, - gpointer user_data) -{ - gint *response = (gint*)user_data; - *response = arg1; - gtk_widget_destroy(GTK_WIDGET(dialog)); - gtk_main_quit(); -} - S32 OSMessageBoxSDL(const std::string& text, const std::string& caption, U32 type) { - S32 rtn = OSBTN_CANCEL; - - if(gWindowImplementation != NULL) - gWindowImplementation->beforeDialog(); + SDL_MessageBoxData oData = { SDL_MESSAGEBOX_INFORMATION, nullptr, caption.c_str(), text.c_str(), 0, nullptr, nullptr }; + SDL_MessageBoxButtonData btnOk[] = {{SDL_MESSAGEBOX_BUTTON_RETURNKEY_DEFAULT, OSBTN_OK, "OK" }}; + SDL_MessageBoxButtonData btnOkCancel [] = {{SDL_MESSAGEBOX_BUTTON_RETURNKEY_DEFAULT, OSBTN_OK, "OK" }, {SDL_MESSAGEBOX_BUTTON_ESCAPEKEY_DEFAULT, OSBTN_CANCEL, "Cancel"} }; + SDL_MessageBoxButtonData btnYesNo[] = { {SDL_MESSAGEBOX_BUTTON_RETURNKEY_DEFAULT, OSBTN_YES, "Yes" }, {SDL_MESSAGEBOX_BUTTON_ESCAPEKEY_DEFAULT, OSBTN_NO, "No"} }; - if (LLWindowSDL::ll_try_gtk_init()) + switch (type) { - GtkWidget *win = NULL; - - LL_INFOS() << "Creating a dialog because we're in windowed mode and GTK is happy." << LL_ENDL; - - GtkDialogFlags flags = GTK_DIALOG_MODAL; - GtkMessageType messagetype; - GtkButtonsType buttons; - switch (type) - { default: case OSMB_OK: - messagetype = GTK_MESSAGE_WARNING; - buttons = GTK_BUTTONS_OK; + oData.flags = SDL_MESSAGEBOX_WARNING; + oData.buttons = btnOk; + oData.numbuttons = 1; break; case OSMB_OKCANCEL: - messagetype = GTK_MESSAGE_QUESTION; - buttons = GTK_BUTTONS_OK_CANCEL; + oData.flags = SDL_MESSAGEBOX_INFORMATION; + oData.buttons = btnOkCancel; + oData.numbuttons = 2; break; case OSMB_YESNO: - messagetype = GTK_MESSAGE_QUESTION; - buttons = GTK_BUTTONS_YES_NO; + oData.flags = SDL_MESSAGEBOX_INFORMATION; + oData.buttons = btnYesNo; + oData.numbuttons = 2; break; - } - win = gtk_message_dialog_new(NULL, flags, messagetype, buttons, "%s", - text.c_str()); - -# if LL_X11 - // Make GTK tell the window manager to associate this - // dialog with our non-GTK SDL window, which should try - // to keep it on top etc. - if (gWindowImplementation && - gWindowImplementation->mSDL_XWindowID != None) - { - gtk_widget_realize(GTK_WIDGET(win)); // so we can get its gdkwin - GdkWindow *gdkwin = gdk_window_foreign_new(gWindowImplementation->mSDL_XWindowID); - gdk_window_set_transient_for(GTK_WIDGET(win)->window, - gdkwin); - } -# endif //LL_X11 - - gtk_window_set_position(GTK_WINDOW(win), - GTK_WIN_POS_CENTER_ON_PARENT); - - gtk_window_set_type_hint(GTK_WINDOW(win), - GDK_WINDOW_TYPE_HINT_DIALOG); - - if (!caption.empty()) - gtk_window_set_title(GTK_WINDOW(win), caption.c_str()); - - gint response = GTK_RESPONSE_NONE; - g_signal_connect (win, - "response", - G_CALLBACK (response_callback), - &response); - - // we should be able to use a gtk_dialog_run(), but it's - // apparently not written to exist in a world without a higher - // gtk_main(), so we manage its signal/destruction outselves. - gtk_widget_show_all (win); - gtk_main(); - - //LL_INFOS() << "response: " << response << LL_ENDL; - switch (response) - { - case GTK_RESPONSE_OK: rtn = OSBTN_OK; break; - case GTK_RESPONSE_YES: rtn = OSBTN_YES; break; - case GTK_RESPONSE_NO: rtn = OSBTN_NO; break; - case GTK_RESPONSE_APPLY: rtn = OSBTN_OK; break; - case GTK_RESPONSE_NONE: - case GTK_RESPONSE_CANCEL: - case GTK_RESPONSE_CLOSE: - case GTK_RESPONSE_DELETE_EVENT: - default: rtn = OSBTN_CANCEL; - } } - else - { - LL_INFOS() << "MSGBOX: " << caption << ": " << text << LL_ENDL; - LL_INFOS() << "Skipping dialog because we're in fullscreen mode or GTK is not happy." << LL_ENDL; - rtn = OSBTN_OK; - } - - if(gWindowImplementation != NULL) - gWindowImplementation->afterDialog(); - return rtn; + int btn{0}; + if( 0 == SDL_ShowMessageBox( &oData, &btn ) ) + return btn; + return OSBTN_CANCEL; } -static void color_changed_callback(GtkWidget *widget, - gpointer user_data) +bool LLWindowSDL::dialogColorPicker( F32 *r, F32 *g, F32 *b) { - GtkColorSelection *colorsel = GTK_COLOR_SELECTION(widget); - GdkColor *colorp = (GdkColor*)user_data; - - gtk_color_selection_get_current_color(colorsel, colorp); + return (false); } - /* Make the raw keyboard data available - used to poke through to LLQtWebKit so that Qt/Webkit has access to the virtual keycodes etc. that it needs */ LLSD LLWindowSDL::getNativeKeyData() { - LLSD result = LLSD::emptyMap(); + LLSD result = LLSD::emptyMap(); U32 modifiers = 0; // pretend-native modifiers... oh what a tangled web we weave! @@ -2377,99 +2341,14 @@ LLSD LLWindowSDL::getNativeKeyData() // *todo: test ALTs - I don't have a case for testing these. Do you? // *todo: NUM? - I don't care enough right now (and it's not a GDK modifier). - result["scan_code"] = (S32)mKeyScanCode; - result["virtual_key"] = (S32)mKeyVirtualKey; + result["virtual_key"] = (S32)mKeyVirtualKey; + result["virtual_key_win"] = (S32)LLKeyboardSDL::mapSDL2toWin( mKeyVirtualKey ); result["modifiers"] = (S32)modifiers; - - return result; -} - - -bool LLWindowSDL::dialogColorPicker( F32 *r, F32 *g, F32 *b) -{ - bool rtn = false; - - beforeDialog(); - - if (ll_try_gtk_init()) - { - GtkWidget *win = NULL; - - win = gtk_color_selection_dialog_new(NULL); - -# if LL_X11 - // Get GTK to tell the window manager to associate this - // dialog with our non-GTK SDL window, which should try - // to keep it on top etc. - if (mSDL_XWindowID != None) - { - gtk_widget_realize(GTK_WIDGET(win)); // so we can get its gdkwin - GdkWindow *gdkwin = gdk_window_foreign_new(mSDL_XWindowID); - gdk_window_set_transient_for(GTK_WIDGET(win)->window, - gdkwin); - } -# endif //LL_X11 - - GtkColorSelection *colorsel = GTK_COLOR_SELECTION (GTK_COLOR_SELECTION_DIALOG(win)->colorsel); - - GdkColor color, orig_color; - orig_color.pixel = 0; - orig_color.red = guint16(65535 * *r); - orig_color.green= guint16(65535 * *g); - orig_color.blue = guint16(65535 * *b); - color = orig_color; - - gtk_color_selection_set_previous_color (colorsel, &color); - gtk_color_selection_set_current_color (colorsel, &color); - gtk_color_selection_set_has_palette (colorsel, true); - gtk_color_selection_set_has_opacity_control(colorsel, false); - - gint response = GTK_RESPONSE_NONE; - g_signal_connect (win, - "response", - G_CALLBACK (response_callback), - &response); - - g_signal_connect (G_OBJECT (colorsel), "color_changed", - G_CALLBACK (color_changed_callback), - &color); - - gtk_window_set_modal(GTK_WINDOW(win), true); - gtk_widget_show_all(win); - // hide the help button - we don't service it. - gtk_widget_hide(GTK_COLOR_SELECTION_DIALOG(win)->help_button); - gtk_main(); - - if (response == GTK_RESPONSE_OK && - (orig_color.red != color.red - || orig_color.green != color.green - || orig_color.blue != color.blue) ) - { - *r = color.red / 65535.0f; - *g = color.green / 65535.0f; - *b = color.blue / 65535.0f; - rtn = true; - } - } - - afterDialog(); - - return rtn; -} -#else -S32 OSMessageBoxSDL(const std::string& text, const std::string& caption, U32 type) -{ - LL_INFOS() << "MSGBOX: " << caption << ": " << text << LL_ENDL; - return 0; -} - -bool LLWindowSDL::dialogColorPicker( F32 *r, F32 *g, F32 *b) -{ - return (false); + result["input_type"] = mInputType; + return result; } -#endif // LL_GTK -#if LL_LINUX +#if LL_LINUX || LL_SOLARIS // extracted from spawnWebBrowser for clarity and to eliminate // compiler confusion regarding close(int fd) vs. LLWindow::close() void exec_cmd(const std::string& cmd, const std::string& arg) @@ -2481,9 +2360,32 @@ void exec_cmd(const std::string& cmd, const std::string& arg) { // child // disconnect from stdin/stdout/stderr, or child will // keep our output pipe undesirably alive if it outlives us. - close(0); - close(1); - close(2); + // close(0); + // close(1); + // close(2); + // Reopen stdin, stdout, and stderr to /dev/null. + // It's good practice to always have those file + // descriptors open to something, lest the exec'd + // program actually try to use them. + FILE *result; + result = freopen("/dev/null","r",stdin); + if (result == NULL) + { + LL_WARNS() << "Error reopening stdin for web browser: " + << strerror(errno) << LL_ENDL; + } + result = freopen("/dev/null","w",stdout); + if (result == NULL) + { + LL_WARNS() << "Error reopening stdout for web browser: " + << strerror(errno) << LL_ENDL; + } + result = freopen("/dev/null","w",stderr); + if (result == NULL) + { + LL_WARNS() << "Error reopening stderr for web browser: " + << strerror(errno) << LL_ENDL; + } // end ourself by running the command execv(cmd.c_str(), argv); /* Flawfinder: ignore */ // if execv returns at all, there was a problem. @@ -2549,31 +2451,13 @@ void LLWindowSDL::spawnWebBrowser(const std::string& escaped_url, bool async) LL_INFOS() << "spawn_web_browser returning." << LL_ENDL; } +void LLWindowSDL::openFile(const std::string& file_name) +{ + spawnWebBrowser("file://"+file_name,true); +} void *LLWindowSDL::getPlatformWindow() { -#if LL_GTK && LL_LLMOZLIB_ENABLED - if (LLWindowSDL::ll_try_gtk_init()) - { - maybe_lock_display(); - - GtkWidget *owin = gtk_window_new(GTK_WINDOW_POPUP); - // Why a layout widget? A MozContainer would be ideal, but - // it involves exposing Mozilla headers to mozlib-using apps. - // A layout widget with a GtkWindow parent has the desired - // properties of being plain GTK, having a window, and being - // derived from a GtkContainer. - GtkWidget *rtnw = gtk_layout_new(NULL, NULL); - gtk_container_add(GTK_CONTAINER(owin), rtnw); - gtk_widget_realize(rtnw); - GTK_WIDGET_UNSET_FLAGS(GTK_WIDGET(rtnw), GTK_NO_WINDOW); - - maybe_unlock_display(); - - return rtnw; - } -#endif // LL_GTK && LL_LLMOZLIB_ENABLED - // Unixoid mozilla really needs GTK. return NULL; } @@ -2634,10 +2518,10 @@ std::vector LLWindowSDL::getDynamicFallbackFontList() LL_INFOS("AppInit") << "Variant " << locale->variant << LL_ENDL; LL_INFOS() << "Preferring fonts of language: " - << locale->lang - << LL_ENDL; + << locale->lang + << LL_ENDL; sort_order = "lang=" + std::string(locale->lang) + ":" - + sort_order; + + sort_order; } } FL_FreeLocale(&locale); @@ -2655,7 +2539,7 @@ std::vector LLWindowSDL::getDynamicFallbackFontList() // Sort the list of system fonts from most-to-least-desirable. FcResult result; fs = FcFontSort(NULL, sortpat, elide_unicode_coverage, - NULL, &result); + NULL, &result); FcPatternDestroy(sortpat); } @@ -2669,8 +2553,8 @@ std::vector LLWindowSDL::getDynamicFallbackFontList() { FcChar8 *filename; if (FcResultMatch == FcPatternGetString(fs->fonts[i], - FC_FILE, 0, - &filename) + FC_FILE, 0, + &filename) && filename) { rtns.push_back(std::string((const char*)filename)); @@ -2694,4 +2578,54 @@ std::vector LLWindowSDL::getDynamicFallbackFontList() return rtns; } -#endif // LL_SDL + +void* LLWindowSDL::createSharedContext() +{ + auto *pContext = SDL_GL_CreateContext(mWindow); + if ( pContext) + { + SDL_GL_SetSwapInterval(0); + SDL_GL_MakeCurrent(mWindow, mContext); + + LLCoordScreen size; + if (getSize(&size)) + setSize(size); + + LL_DEBUGS() << "Creating shared OpenGL context successful!" << LL_ENDL; + + return (void*)pContext; + } + + LL_WARNS() << "Creating shared OpenGL context failed!" << LL_ENDL; + + return nullptr; +} + +void LLWindowSDL::makeContextCurrent(void* contextPtr) +{ + LL_PROFILER_GPU_CONTEXT; + SDL_GL_MakeCurrent( mWindow, contextPtr ); +} + +void LLWindowSDL::destroySharedContext(void* contextPtr) +{ + SDL_GL_DeleteContext( contextPtr ); +} + +void LLWindowSDL::toggleVSync(bool enable_vsync) +{ +} + +void LLWindowSDL::setLanguageTextInput(const LLCoordGL& position) +{ + LLCoordWindow win_pos; + convertCoords( position, &win_pos ); + + SDL_Rect r; + r.x = win_pos.mX; + r.y = win_pos.mY; + r.w = 500; + r.h = 16; + + SDL_SetTextInputRect(&r); +} diff --git a/indra/llwindow/llwindowsdl.h b/indra/llwindow/llwindowsdl.h index 196ad2986d..5b29bfd973 100644 --- a/indra/llwindow/llwindowsdl.h +++ b/indra/llwindow/llwindowsdl.h @@ -24,20 +24,20 @@ * $/LicenseInfo$ */ -#ifndef LL_LLWINDOWSDL_H -#define LL_LLWINDOWSDL_H +#ifndef LL_LLWINDOWSDL2_H +#define LL_LLWINDOWSDL2_H // Simple Directmedia Layer (http://libsdl.org/) implementation of LLWindow class #include "llwindow.h" #include "lltimer.h" -#include "SDL/SDL.h" -#include "SDL/SDL_endian.h" +#include "SDL2/SDL.h" +#include "SDL2/SDL_endian.h" #if LL_X11 // get X11-specific headers for use in low-level stuff like copy-and-paste support -#include "SDL/SDL_syswm.h" +#include "SDL2/SDL_syswm.h" #endif // AssertMacros.h does bad things. @@ -46,83 +46,139 @@ #undef require -class LLWindowSDL : public LLWindow -{ +class LLWindowSDL : public LLWindow { public: - /*virtual*/ void show(); - /*virtual*/ void hide(); - /*virtual*/ void close(); - /*virtual*/ bool getVisible(); - /*virtual*/ bool getMinimized(); - /*virtual*/ bool getMaximized(); - /*virtual*/ bool maximize(); - /*virtual*/ void minimize(); - /*virtual*/ void restore(); - /*virtual*/ bool getFullscreen(); - /*virtual*/ bool getPosition(LLCoordScreen *position); - /*virtual*/ bool getSize(LLCoordScreen *size); - /*virtual*/ bool getSize(LLCoordWindow *size); - /*virtual*/ bool setPosition(LLCoordScreen position); - /*virtual*/ bool setSizeImpl(LLCoordScreen size); - /*virtual*/ bool setSizeImpl(LLCoordWindow size); - /*virtual*/ bool switchContext(bool fullscreen, const LLCoordScreen &size, bool disable_vsync, const LLCoordScreen * const posp = NULL); - /*virtual*/ bool setCursorPosition(LLCoordWindow position); - /*virtual*/ bool getCursorPosition(LLCoordWindow *position); - /*virtual*/ void showCursor(); - /*virtual*/ void hideCursor(); - /*virtual*/ void showCursorFromMouseMove(); - /*virtual*/ void hideCursorUntilMouseMove(); - /*virtual*/ bool isCursorHidden(); - /*virtual*/ void updateCursor(); - /*virtual*/ void captureMouse(); - /*virtual*/ void releaseMouse(); - /*virtual*/ void setMouseClipping( bool b ); - /*virtual*/ void setMinSize(U32 min_width, U32 min_height, bool enforce_immediately = true); - - /*virtual*/ bool isClipboardTextAvailable(); - /*virtual*/ bool pasteTextFromClipboard(LLWString &dst); - /*virtual*/ bool copyTextToClipboard(const LLWString & src); - - /*virtual*/ bool isPrimaryTextAvailable(); - /*virtual*/ bool pasteTextFromPrimary(LLWString &dst); - /*virtual*/ bool copyTextToPrimary(const LLWString & src); - - /*virtual*/ void flashIcon(F32 seconds); - /*virtual*/ F32 getGamma(); - /*virtual*/ bool setGamma(const F32 gamma); // Set the gamma - /*virtual*/ U32 getFSAASamples(); - /*virtual*/ void setFSAASamples(const U32 samples); - /*virtual*/ bool restoreGamma(); // Restore original gamma table (before updating gamma) - /*virtual*/ ESwapMethod getSwapMethod() { return mSwapMethod; } - /*virtual*/ void processMiscNativeEvents(); - /*virtual*/ void gatherInput(); - /*virtual*/ void swapBuffers(); - /*virtual*/ void restoreGLContext() {}; - - /*virtual*/ void delayInputProcessing() { }; + void show() override; + + void hide() override; + + void close() override; + + bool getVisible() override; + + bool getMinimized() override; + + bool getMaximized() override; + + bool maximize() override; + + void minimize() override; + + void restore() override; + + bool getFullscreen(); + + bool getPosition(LLCoordScreen *position) override; + + bool getSize(LLCoordScreen *size) override; + + bool getSize(LLCoordWindow *size) override; + + bool setPosition(LLCoordScreen position) override; + + bool setSizeImpl(LLCoordScreen size) override; + + bool setSizeImpl(LLCoordWindow size) override; + + bool switchContext(bool fullscreen, const LLCoordScreen &size, bool disable_vsync, + const LLCoordScreen *const posp = NULL) override; + + bool setCursorPosition(LLCoordWindow position) override; + + bool getCursorPosition(LLCoordWindow *position) override; + + void showCursor() override; + + void hideCursor() override; + + void showCursorFromMouseMove() override; + + void hideCursorUntilMouseMove() override; + + bool isCursorHidden() override; + + void updateCursor() override; + + void captureMouse() override; + + void releaseMouse() override; + + void setMouseClipping(bool b) override; + + void setMinSize(U32 min_width, U32 min_height, bool enforce_immediately = true) override; + + bool isClipboardTextAvailable() override; + + bool pasteTextFromClipboard(LLWString &dst) override; + + bool copyTextToClipboard(const LLWString &src) override; + + bool isPrimaryTextAvailable() override; + + bool pasteTextFromPrimary(LLWString &dst) override; + + bool copyTextToPrimary(const LLWString &src) override; + + void flashIcon(F32 seconds) override; + + F32 getGamma() override; + + bool setGamma(const F32 gamma) override; // Set the gamma + U32 getFSAASamples() override; + + void setFSAASamples(const U32 samples) override; + + bool restoreGamma() override; // Restore original gamma table (before updating gamma) + ESwapMethod getSwapMethod() override { return mSwapMethod; } + + void processMiscNativeEvents() override; + + void gatherInput() override; + + void swapBuffers() override; + + void restoreGLContext() {}; + + void delayInputProcessing() override {}; // handy coordinate space conversion routines - /*virtual*/ bool convertCoords(LLCoordScreen from, LLCoordWindow *to); - /*virtual*/ bool convertCoords(LLCoordWindow from, LLCoordScreen *to); - /*virtual*/ bool convertCoords(LLCoordWindow from, LLCoordGL *to); - /*virtual*/ bool convertCoords(LLCoordGL from, LLCoordWindow *to); - /*virtual*/ bool convertCoords(LLCoordScreen from, LLCoordGL *to); - /*virtual*/ bool convertCoords(LLCoordGL from, LLCoordScreen *to); + bool convertCoords(LLCoordScreen from, LLCoordWindow *to) override; + + bool convertCoords(LLCoordWindow from, LLCoordScreen *to) override; + + bool convertCoords(LLCoordWindow from, LLCoordGL *to) override; + + bool convertCoords(LLCoordGL from, LLCoordWindow *to) override; + + bool convertCoords(LLCoordScreen from, LLCoordGL *to) override; - /*virtual*/ LLWindowResolution* getSupportedResolutions(S32 &num_resolutions); - /*virtual*/ F32 getNativeAspectRatio(); - /*virtual*/ F32 getPixelAspectRatio(); - /*virtual*/ void setNativeAspectRatio(F32 ratio) { mOverrideAspectRatio = ratio; } + bool convertCoords(LLCoordGL from, LLCoordScreen *to) override; - /*virtual*/ void beforeDialog(); - /*virtual*/ void afterDialog(); + LLWindowResolution *getSupportedResolutions(S32 &num_resolutions) override; - /*virtual*/ bool dialogColorPicker(F32 *r, F32 *g, F32 *b); + F32 getNativeAspectRatio() override; - /*virtual*/ void *getPlatformWindow(); - /*virtual*/ void bringToFront(); + F32 getPixelAspectRatio() override; - /*virtual*/ void spawnWebBrowser(const std::string& escaped_url, bool async); + void setNativeAspectRatio(F32 ratio) override { mOverrideAspectRatio = ratio; } + + void beforeDialog() override; + + void afterDialog() override; + + bool dialogColorPicker(F32 *r, F32 *g, F32 *b) override; + + void *getPlatformWindow() override; + + void bringToFront() override; + + void setLanguageTextInput(const LLCoordGL& pos) override; + + void spawnWebBrowser(const std::string &escaped_url, bool async) override; + + void openFile(const std::string &file_name); + + void setTitle(const std::string title) override; static std::vector getDynamicFallbackFontList(); @@ -132,40 +188,52 @@ public: Window mSDL_XWindowID; Display *mSDL_Display; #endif + void (*Lock_Display)(void); - void (*Unlock_Display)(void); -#if LL_GTK - // Lazily initialize and check the runtime GTK version for goodness. - static bool ll_try_gtk_init(void); -#endif // LL_GTK + void (*Unlock_Display)(void); #if LL_X11 + static Window get_SDL_XWindowID(void); - static Display* get_SDL_Display(void); + + static Display *get_SDL_Display(void); + #endif // LL_X11 + void *createSharedContext() override; + + void makeContextCurrent(void *context) override; + + void destroySharedContext(void *context) override; + + void toggleVSync(bool enable_vsync) override; + protected: - LLWindowSDL(LLWindowCallbacks* callbacks, - const std::string& title, int x, int y, int width, int height, U32 flags, - bool fullscreen, bool clearBg, bool disable_vsync, bool use_gl, - bool ignore_pixel_depth, U32 fsaa_samples); + LLWindowSDL(LLWindowCallbacks *callbacks, + const std::string &title, int x, int y, int width, int height, U32 flags, + bool fullscreen, bool clearBg, bool disable_vsync, bool use_gl, + bool ignore_pixel_depth, U32 fsaa_samples); + ~LLWindowSDL(); - /*virtual*/ bool isValid(); - /*virtual*/ LLSD getNativeKeyData(); + bool isValid() override; + + LLSD getNativeKeyData() override; + + void initCursors(); - void initCursors(); - void quitCursors(); - void moveWindow(const LLCoordScreen& position,const LLCoordScreen& size); + void quitCursors(); + + void moveWindow(const LLCoordScreen &position, const LLCoordScreen &size); // Changes display resolution. Returns true if successful - bool setDisplayResolution(S32 width, S32 height, S32 bits, S32 refresh); + bool setDisplayResolution(S32 width, S32 height, S32 bits, S32 refresh); // Go back to last fullscreen display resolution. - bool setFullscreenResolution(); + bool setFullscreenResolution(); - bool shouldPostQuit() { return mPostQuit; } + bool shouldPostQuit() { return mPostQuit; } protected: // @@ -174,46 +242,84 @@ protected: // create or re-create the GL context/window. Called from the constructor and switchContext(). bool createContext(int x, int y, int width, int height, int bits, bool fullscreen, bool disable_vsync); + void destroyContext(); - void setupFailure(const std::string& text, const std::string& caption, U32 type); + + void setupFailure(const std::string &text, const std::string &caption, U32 type); + void fixWindowSize(void); - U32 SDLCheckGrabbyKeys(SDLKey keysym, bool gain); + + U32 SDLCheckGrabbyKeys(U32 keysym, bool gain); + bool SDLReallyCaptureInput(bool capture); // // Platform specific variables // - U32 mGrabbyKeyFlags; - int mReallyCapturedCount; - SDL_Surface * mWindow; + U32 mGrabbyKeyFlags; + int mReallyCapturedCount; + + SDL_Window *mWindow; + SDL_Surface *mSurface; + SDL_GLContext mContext; + SDL_Cursor *mSDLCursors[UI_CURSOR_COUNT]; + std::string mWindowTitle; - double mOriginalAspectRatio; - bool mNeedsResize; // Constructor figured out the window is too big, it needs a resize. - LLCoordScreen mNeedsResizeSize; - F32 mOverrideAspectRatio; - F32 mGamma; - U32 mFSAASamples; + double mOriginalAspectRatio; + bool mNeedsResize; // Constructor figured out the window is too big, it needs a resize. + LLCoordScreen mNeedsResizeSize; + F32 mOverrideAspectRatio; + F32 mGamma; + U32 mFSAASamples; - int mSDLFlags; + int mSDLFlags; - SDL_Cursor* mSDLCursors[UI_CURSOR_COUNT]; - int mHaveInputFocus; /* 0=no, 1=yes, else unknown */ - int mIsMinimized; /* 0=no, 1=yes, else unknown */ + int mHaveInputFocus; /* 0=no, 1=yes, else unknown */ + int mIsMinimized; /* 0=no, 1=yes, else unknown */ friend class LLWindowManager; private: #if LL_X11 + void x11_set_urgent(bool urgent); + bool mFlashing; LLTimer mFlashTimer; #endif //LL_X11 - U32 mKeyScanCode; - U32 mKeyVirtualKey; - SDLMod mKeyModifiers; -}; + U32 mKeyVirtualKey; + U32 mKeyModifiers; + std::string mInputType; + +public: +#if LL_X11 + + static Display *getSDLDisplay(); + LLWString const &getPrimaryText() const { return mPrimaryClipboard; } + + LLWString const &getSecondaryText() const { return mSecondaryClipboard; } + + void clearPrimaryText() { mPrimaryClipboard.clear(); } + + void clearSecondaryText() { mSecondaryClipboard.clear(); } + +private: + void tryFindFullscreenSize(int &aWidth, int &aHeight); + + void initialiseX11Clipboard(); + + bool getSelectionText(Atom selection, LLWString &text); + + bool getSelectionText(Atom selection, Atom type, LLWString &text); + + bool setSelectionText(Atom selection, const LLWString &text); + +#endif + LLWString mPrimaryClipboard; + LLWString mSecondaryClipboard; +}; class LLSplashScreenSDL : public LLSplashScreen { @@ -221,9 +327,9 @@ public: LLSplashScreenSDL(); virtual ~LLSplashScreenSDL(); - /*virtual*/ void showImpl(); - /*virtual*/ void updateImpl(const std::string& mesg); - /*virtual*/ void hideImpl(); + void showImpl(); + void updateImpl(const std::string& mesg); + void hideImpl(); }; S32 OSMessageBoxSDL(const std::string& text, const std::string& caption, U32 type); diff --git a/indra/newview/lldirpicker.cpp b/indra/newview/lldirpicker.cpp index 5df3ed1ba4..1d996b8c93 100644 --- a/indra/newview/lldirpicker.cpp +++ b/indra/newview/lldirpicker.cpp @@ -272,23 +272,6 @@ bool LLDirPicker::getDir(std::string* filename, bool blocking) } return !mDir.empty(); #endif -#if !LL_MESA_HEADLESS - - if (mFilePicker) - { - GtkWindow* picker = mFilePicker->buildFilePicker(false, true, - "dirpicker"); - - if (picker) - { - gtk_window_set_title(GTK_WINDOW(picker), LLTrans::getString("choose_the_directory").c_str()); - gtk_widget_show_all(GTK_WIDGET(picker)); - gtk_main(); - return (!mFilePicker->getFirstFile().empty()); - } - } -#endif // !LL_MESA_HEADLESS - return false; } diff --git a/indra/newview/viewer_manifest.py b/indra/newview/viewer_manifest.py index 5a4167a9e1..6be43f0021 100755 --- a/indra/newview/viewer_manifest.py +++ b/indra/newview/viewer_manifest.py @@ -1438,43 +1438,6 @@ class Linux_x86_64_Manifest(LinuxManifest): self.path("libalut.so*") self.path("libopenal.so*") self.path("libopenal.so", "libvivoxoal.so.1") # vivox's sdk expects this soname - if self.args['fmodstudio'] == 'ON': - try: - self.path("libfmod.so.11.7") - self.path("libfmod.so.11") - self.path("libfmod.so") - pass - except: - print("Skipping libfmod.so - not found") - - # KLUDGE: As of 2012-04-11, the 'fontconfig' package installs - # libfontconfig.so.1.4.4, along with symlinks libfontconfig.so.1 - # and libfontconfig.so. Before we added support for library-file - # wildcards, though, this self.path() call specifically named - # libfontconfig.so.1.4.4 WITHOUT also copying the symlinks. When I - # (nat) changed the call to self.path("libfontconfig.so.*"), we - # ended up with the libfontconfig.so.1 symlink in the target - # directory as well. But guess what! At least on Ubuntu 10.04, - # certain viewer fonts look terrible with libfontconfig.so.1 - # present in the target directory. Removing that symlink suffices - # to improve them. I suspect that means we actually do better when - # the viewer fails to find our packaged libfontconfig.so*, falling - # back on the system one instead -- but diagnosing and fixing that - # is a bit out of scope for the present project. Meanwhile, this - # particular wildcard specification gets us exactly what the - # previous call did, without having to explicitly state the - # version number. - self.path("libfontconfig.so.*.*") - - # Include libfreetype.so. but have it work as libfontconfig does. - self.path("libfreetype.so.*.*") - - try: - self.path("libtcmalloc.so*") #formerly called google perf tools - pass - except: - print("tcmalloc files not found, skipping") - pass # Vivox runtimes with self.prefix(src=relpkgdir, dst="bin"): -- cgit v1.2.3 From e6eb39b34cfaab5425dd57e88c734c1d6bb8460d Mon Sep 17 00:00:00 2001 From: Nicky Date: Fri, 26 Jul 2024 16:26:14 +0200 Subject: Re-enable Linux build --- .github/workflows/build.yaml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index 843d655832..4a831702d7 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -45,8 +45,7 @@ jobs: needs: setvar strategy: matrix: - # runner: [windows-large, macos-12-xl, linux-large] - runner: [windows-large, macos-12-xl] + runner: [windows-large, macos-12-xl, linux-large] configuration: [Release, ReleaseOS] Linden: [true] include: -- cgit v1.2.3 From 4ac06dd6a166fc63b0dda3eebbc33373192511f0 Mon Sep 17 00:00:00 2001 From: Nicky Date: Fri, 26 Jul 2024 19:15:58 +0200 Subject: Disable test_httprequest for Linux, same reason why it was already disabled for Windows --- indra/llcorehttp/tests/llcorehttp_test.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/indra/llcorehttp/tests/llcorehttp_test.cpp b/indra/llcorehttp/tests/llcorehttp_test.cpp index c0cc2c8030..8a788e5a93 100755 --- a/indra/llcorehttp/tests/llcorehttp_test.cpp +++ b/indra/llcorehttp/tests/llcorehttp_test.cpp @@ -43,9 +43,12 @@ #include "test_httpoperation.hpp" // As of 2019-06-28, test_httprequest.hpp consistently crashes on Mac Release // builds for reasons not yet diagnosed. -#if ! (LL_DARWIN && LL_RELEASE) +// On Linux too, this is likely to badly handling (p)thread creation and not waiting +// for threads to properly shutdown +#if LL_WINDOWS #include "test_httprequest.hpp" #endif + #include "test_httpheaders.hpp" #include "test_httprequestqueue.hpp" #include "_httpservice.h" -- cgit v1.2.3 From 42885c0d23d8efc47ad77e09bb327e4ec6a4fada Mon Sep 17 00:00:00 2001 From: Nicky Date: Fri, 26 Jul 2024 19:17:41 +0200 Subject: Replace hand rolled loop to create a unique temp-dir with a more standard implementation. --- indra/llfilesystem/tests/lldir_test.cpp | 31 +++++++++++++++---------------- 1 file changed, 15 insertions(+), 16 deletions(-) diff --git a/indra/llfilesystem/tests/lldir_test.cpp b/indra/llfilesystem/tests/lldir_test.cpp index 6e191ad096..15f2c0005d 100644 --- a/indra/llfilesystem/tests/lldir_test.cpp +++ b/indra/llfilesystem/tests/lldir_test.cpp @@ -27,12 +27,14 @@ #include "linden_common.h" +#include #include "llstring.h" #include "tests/StringVec.h" #include "../lldir.h" #include "../lldiriterator.h" #include "../test/lltut.h" +#include "../test/namedtempfile.h" #include "stringize.h" #include @@ -425,23 +427,19 @@ namespace tut return path; } - std::string makeTestDir( const std::string& dirbase ) + std::string makeTestDir( ) { - int counter; - std::string uniqueDir; - bool foundUnused; - std::string delim = gDirUtilp->getDirDelimiter(); + auto p = NamedTempFile::temp_path(); + std::filesystem::create_directories(p.native()); - for (counter=0, foundUnused=false; !foundUnused; counter++ ) - { - char counterStr[3]; - sprintf(counterStr, "%02d", counter); - uniqueDir = dirbase + counterStr; - foundUnused = ! ( LLFile::isdir(uniqueDir) || LLFile::isfile(uniqueDir) ); - } - ensure("test directory '" + uniqueDir + "' creation failed", !LLFile::mkdir(uniqueDir)); + auto ret { p.native() }; - return uniqueDir + delim; // HACK - apparently, the trailing delimiter is needed... + // There's an implicit assumtion all over this code that the returned path ends with "/" (or "\") + + if(ret.size() >= 1 && ret[ ret.size()-1 ] != std::filesystem::path::preferred_separator ) + ret += std::filesystem::path::preferred_separator; + + return ret; } static const char* DirScanFilename[5] = { "file1.abc", "file2.abc", "file1.xyz", "file2.xyz", "file1.mno" }; @@ -497,8 +495,9 @@ namespace tut // Create the same 5 file names of the two directories - std::string dir1 = makeTestDir(dirTemp + "LLDirIterator"); - std::string dir2 = makeTestDir(dirTemp + "LLDirIterator"); + std::string dir1 = makeTestDir(); + std::string dir2 = makeTestDir(); + std::string dir1files[5]; std::string dir2files[5]; for (int i=0; i<5; i++) -- cgit v1.2.3 From dd3e5e6b2bffdfa5511099f5c3a03034d493b1d8 Mon Sep 17 00:00:00 2001 From: Andrey Lihatskiy Date: Sat, 27 Jul 2024 18:01:29 +0300 Subject: Fix trailing whitespace --- indra/llwindow/llwindowsdl.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/indra/llwindow/llwindowsdl.h b/indra/llwindow/llwindowsdl.h index 5b29bfd973..4b2306da87 100644 --- a/indra/llwindow/llwindowsdl.h +++ b/indra/llwindow/llwindowsdl.h @@ -171,7 +171,7 @@ public: void *getPlatformWindow() override; void bringToFront() override; - + void setLanguageTextInput(const LLCoordGL& pos) override; void spawnWebBrowser(const std::string &escaped_url, bool async) override; -- cgit v1.2.3 From e008b44fd88be83426812e67707896e970947c49 Mon Sep 17 00:00:00 2001 From: Andrey Lihatskiy Date: Sat, 27 Jul 2024 18:26:31 +0300 Subject: Fix implicit conversion errors from int to float in llvoicewebrtc.cpp --- indra/newview/llvoicewebrtc.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/indra/newview/llvoicewebrtc.cpp b/indra/newview/llvoicewebrtc.cpp index d18a32cb05..123bd5e6ec 100644 --- a/indra/newview/llvoicewebrtc.cpp +++ b/indra/newview/llvoicewebrtc.cpp @@ -2096,7 +2096,7 @@ LLVoiceWebRTCConnection::LLVoiceWebRTCConnection(const LLUUID ®ionID, const s { // retries wait a short period...randomize it so // all clients don't try to reconnect at once. - mRetryWaitSecs = (F32)((F32) rand() / (RAND_MAX)) + 0.5f; + mRetryWaitSecs = static_cast(rand()) / static_cast(RAND_MAX) + 0.5f; mWebRTCPeerConnectionInterface = llwebrtc::newPeerConnection(); mWebRTCPeerConnectionInterface->setSignalingObserver(this); @@ -2678,7 +2678,7 @@ bool LLVoiceWebRTCConnection::connectionStateMachine() case VOICE_STATE_SESSION_UP: { mRetryWaitPeriod = 0; - mRetryWaitSecs = (F32)((F32) rand() / (RAND_MAX)) + 0.5f; + mRetryWaitSecs = static_cast(rand()) / static_cast(RAND_MAX) + 0.5f; // we'll stay here as long as the session remains up. if (mShutDown) @@ -2700,7 +2700,7 @@ bool LLVoiceWebRTCConnection::connectionStateMachine() { // back off the retry period, and do it by a small random // bit so all clients don't reconnect at once. - mRetryWaitSecs += (F32)((F32) rand() / (RAND_MAX)) + 0.5f; + mRetryWaitSecs += static_cast(rand()) / static_cast(RAND_MAX) + 0.5f; mRetryWaitPeriod = 0; } } -- cgit v1.2.3 From a9e1d0c781ffdddd5e04d48af604fcb014325b4c Mon Sep 17 00:00:00 2001 From: Nicky Dasmijn Date: Mon, 29 Jul 2024 11:02:30 +0200 Subject: Linux; Package lllibwebrtc.so (#2134) --- indra/newview/viewer_manifest.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/indra/newview/viewer_manifest.py b/indra/newview/viewer_manifest.py index 6be43f0021..876fbb3734 100755 --- a/indra/newview/viewer_manifest.py +++ b/indra/newview/viewer_manifest.py @@ -1253,11 +1253,13 @@ class LinuxManifest(ViewerManifest): with self.prefix(dst="res-sdl") : self.path("secondlife_256.BMP","ll_icon.BMP") + with self.prefix(src=os.path.join(self.args['build'], os.pardir, "llwebrtc" ), dst="lib"): + self.path("libllwebrtc.so") + # plugins with self.prefix(src=os.path.join(self.args['build'], os.pardir, 'media_plugins'), dst="bin/llplugin"): self.path("gstreamer10/libmedia_plugin_gstreamer10.so", "libmedia_plugin_gstreamer.so") - with self.prefix(src=os.path.join(self.args['build'], os.pardir, 'media_plugins'), dst="bin/llplugin"): self.path("cef/libmedia_plugin_cef.so", "libmedia_plugin_cef.so" ) with self.prefix(src=os.path.join(pkgdir, 'lib', 'release'), dst="lib"): -- cgit v1.2.3 From e687250b90e63e71c9115ed97c3efd60ceefbcb6 Mon Sep 17 00:00:00 2001 From: Andrey Lihatskiy Date: Mon, 29 Jul 2024 13:46:19 +0300 Subject: Build fix - string type conversion in lldir_test.cpp (#2133) --- indra/llfilesystem/tests/lldir_test.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/indra/llfilesystem/tests/lldir_test.cpp b/indra/llfilesystem/tests/lldir_test.cpp index 15f2c0005d..4fa5769f65 100644 --- a/indra/llfilesystem/tests/lldir_test.cpp +++ b/indra/llfilesystem/tests/lldir_test.cpp @@ -432,7 +432,7 @@ namespace tut auto p = NamedTempFile::temp_path(); std::filesystem::create_directories(p.native()); - auto ret { p.native() }; + std::string ret = p.string(); // There's an implicit assumtion all over this code that the returned path ends with "/" (or "\") -- cgit v1.2.3 From ed388f61bad7f6873ee3a2e4d673c9b991b4ef88 Mon Sep 17 00:00:00 2001 From: Maxim Nikolenko Date: Mon, 29 Jul 2024 16:51:37 +0300 Subject: Update expired cert in integration test (#2140) see fe8c976 for more info Co-authored-by: Andrey Lihatskiy --- indra/newview/tests/llsechandler_basic_test.cpp | 670 ++++++++++++------------ 1 file changed, 345 insertions(+), 325 deletions(-) diff --git a/indra/newview/tests/llsechandler_basic_test.cpp b/indra/newview/tests/llsechandler_basic_test.cpp index bfe32406cb..cf37a4713c 100644 --- a/indra/newview/tests/llsechandler_basic_test.cpp +++ b/indra/newview/tests/llsechandler_basic_test.cpp @@ -232,381 +232,402 @@ namespace tut "Certificate:\n" " Data:\n" " Version: 3 (0x2)\n" - " Serial Number:\n" - " 82:2f:8f:eb:8d:06:24:b0\n" + " Serial Number: ef:54:d8:f7:da:18:e8:19\n" " Signature Algorithm: sha256WithRSAEncryption\n" " Issuer: C=US, ST=California, L=San Francisco, O=Linden Lab, OU=Second Life Engineering, CN=Integration Test Root CA/emailAddress=noreply@lindenlab.com\n" " Validity\n" - " Not Before: May 22 22:19:45 2018 GMT\n" - " Not After : May 17 22:19:45 2038 GMT\n" + " Not Before: Jul 23 11:46:26 2024 GMT\n" + " Not After : Jul 21 11:46:26 2034 GMT\n" " Subject: C=US, ST=California, L=San Francisco, O=Linden Lab, OU=Second Life Engineering, CN=Integration Test Root CA/emailAddress=noreply@lindenlab.com\n" " Subject Public Key Info:\n" " Public Key Algorithm: rsaEncryption\n" " Public-Key: (4096 bit)\n" " Modulus:\n" - " 00:bd:e0:79:dd:3b:a6:ac:87:d0:39:f0:58:c7:a4:\n" - " 42:42:f6:5f:93:b0:36:04:b5:e2:d5:f7:2a:c0:6c:\n" - " a0:13:d2:1e:02:81:57:02:50:4c:57:b7:ef:27:9e:\n" - " f6:f1:f1:30:30:72:1e:57:34:e5:3f:82:3c:21:c4:\n" - " 66:d2:73:63:6c:91:e6:dd:49:9e:9c:b1:34:6a:81:\n" - " 45:a1:6e:c4:50:28:f2:d8:e3:fe:80:2f:83:aa:28:\n" - " 91:b4:8c:57:c9:f1:16:d9:0c:87:3c:25:80:a0:81:\n" - " 8d:71:f2:96:e2:16:f1:97:c4:b0:d8:53:bb:13:6c:\n" - " 73:54:2f:29:94:85:cf:86:6e:75:71:ad:39:e3:fc:\n" - " 39:12:53:93:1c:ce:39:e0:33:da:49:b7:3d:af:b0:\n" - " 37:ce:77:09:03:27:32:70:c0:9c:7f:9c:89:ce:90:\n" - " 45:b0:7d:94:8b:ff:13:27:ba:88:7f:ae:c4:aa:73:\n" - " d5:47:b8:87:69:89:80:0c:c1:22:18:78:c2:0d:47:\n" - " d9:10:ff:80:79:0d:46:71:ec:d9:ba:c9:f3:77:fd:\n" - " 92:6d:1f:0f:d9:54:18:6d:f6:72:24:5c:5c:3d:43:\n" - " 49:35:3e:1c:28:de:7e:44:dc:29:c3:9f:62:04:46:\n" - " aa:c4:e6:69:6a:15:f8:e3:74:1c:14:e9:f4:97:7c:\n" - " 30:6c:d4:28:fc:2a:0e:1d:6d:39:2e:1d:f9:17:43:\n" - " 35:5d:23:e7:ba:e3:a8:e9:97:6b:3c:3e:23:ef:d8:\n" - " bc:fb:7a:57:37:39:93:59:03:fc:78:ca:b1:31:ef:\n" - " 26:19:ed:56:e1:63:c3:ad:99:80:5b:47:b5:03:35:\n" - " 5f:fe:6a:a6:21:63:ec:50:fb:4e:c9:f9:ae:a5:66:\n" - " d0:55:33:8d:e6:c5:50:5a:c6:8f:5c:34:45:a7:72:\n" - " da:50:f6:66:4c:19:f5:d1:e4:fb:11:8b:a1:b5:4e:\n" - " 09:43:81:3d:39:28:86:3b:fe:07:28:97:02:b5:3a:\n" - " 07:5f:4a:20:80:1a:7d:a4:8c:f7:6c:f6:c5:9b:f6:\n" - " 61:e5:c7:b0:c3:d5:58:38:7b:bb:47:1e:34:d6:16:\n" - " 55:c5:d2:6c:b0:93:77:b1:90:69:06:b1:53:cb:1b:\n" - " 84:71:cf:b8:87:1b:1e:44:35:b4:2b:bb:04:59:58:\n" - " 0b:e8:93:d8:ae:21:9b:b1:1c:89:30:ae:11:80:77:\n" - " cc:16:f3:d6:35:ed:a1:b3:70:b3:4f:cd:a1:56:99:\n" - " ee:0e:c0:00:a4:09:70:c3:5b:0b:be:a1:07:18:dd:\n" - " c6:f4:6d:8b:58:bc:f9:bb:4b:01:2c:f6:cc:2c:9b:\n" - " 87:0e:b1:4f:9c:10:be:fc:45:e2:a4:ec:7e:fc:ff:\n" - " 45:b8:53\n" + " 00:c6:cc:07:f4:0b:17:06:4d:a6:30:b4:c7:02:6b:\n" + " 9d:a4:47:a6:09:0e:60:1a:32:d4:6b:42:88:ee:c5:\n" + " b9:e9:fb:b5:0b:60:dc:a2:45:92:a5:bb:88:12:fc:\n" + " 42:1a:80:32:79:16:62:7a:97:af:84:28:53:3c:c1:\n" + " f2:68:c0:4e:45:e4:0a:63:f9:34:1d:a2:8b:cc:70:\n" + " df:c6:65:c0:ba:31:32:d2:9d:0c:c8:ce:dc:11:12:\n" + " a4:11:fa:d3:c8:56:e2:31:8a:e3:fb:91:40:da:25:\n" + " 55:d1:f2:75:9b:4d:fa:b8:1f:b5:6d:9b:e1:fe:5d:\n" + " e8:c4:02:79:14:ef:7d:5a:b3:3a:1e:b6:d0:60:2c:\n" + " 90:dc:22:e2:c5:ae:85:1f:b4:9d:7a:20:f8:af:63:\n" + " 56:25:1a:64:f3:9c:3f:9a:cf:68:08:0a:37:db:d0:\n" + " a3:65:26:db:80:82:ff:e0:1b:51:c8:ee:f6:ad:c2:\n" + " b4:f2:ab:d2:e8:85:86:77:28:d0:63:4a:71:78:41:\n" + " e3:8c:7f:71:51:31:af:24:3f:fa:8d:d0:d8:0b:e2:\n" + " 7e:79:33:8a:bb:d2:00:9e:2e:c8:cd:d5:50:92:b8:\n" + " 5c:5a:0b:99:ef:05:39:67:da:be:70:36:51:37:37:\n" + " 20:6f:84:ab:29:11:00:7b:38:32:ba:0b:bc:34:a6:\n" + " b5:c6:a7:f0:c0:25:2d:38:0b:72:40:ab:cf:e6:ff:\n" + " 97:75:ff:e2:a9:3c:2a:57:ce:e4:52:20:8c:de:fe:\n" + " 68:ce:54:85:37:ba:b3:7f:2e:53:58:ea:9b:ac:79:\n" + " 6b:16:65:b8:11:88:5a:46:eb:9e:9e:80:3c:89:91:\n" + " 35:e0:c5:33:45:c8:86:4d:25:51:39:b1:72:97:2b:\n" + " b3:c8:c9:e8:11:cd:32:41:c8:c1:56:22:7e:33:81:\n" + " 85:61:ab:da:9e:6e:5f:24:1c:0f:9b:fa:da:9d:86:\n" + " 1a:66:f6:32:2a:10:80:ea:72:7a:4a:ef:c0:f2:7c:\n" + " 43:02:e6:70:19:6a:e1:02:0a:00:80:51:1c:a3:03:\n" + " 8b:6d:89:9f:91:37:90:d6:d8:9c:73:77:06:9e:bc:\n" + " 95:89:66:ee:43:40:a3:ee:43:a3:f6:2d:43:dd:7b:\n" + " f0:2f:0b:12:37:49:b7:81:5a:e2:54:6d:71:88:ff:\n" + " fe:7e:41:25:35:4c:b4:b9:62:65:dd:9f:1f:7a:06:\n" + " 6e:2b:20:58:78:da:08:66:a8:f1:89:de:8f:7f:5c:\n" + " 5e:c2:72:33:7f:b6:8e:41:4c:26:f6:4c:d4:0e:11:\n" + " 44:da:c7:14:f7:8b:79:4e:53:29:87:15:b1:12:e9:\n" + " 19:2b:54:33:d6:2e:7f:bd:42:20:be:fc:d7:9c:b4:\n" + " 7a:0a:db\n" " Exponent: 65537 (0x10001)\n" " X509v3 extensions:\n" - " X509v3 Subject Key Identifier: \n" - " 8A:22:C6:9C:2E:11:F3:40:0C:CE:82:0C:22:59:FF:F8:7F:D0:B9:13\n" - " X509v3 Authority Key Identifier: \n" - " keyid:8A:22:C6:9C:2E:11:F3:40:0C:CE:82:0C:22:59:FF:F8:7F:D0:B9:13\n" + " X509v3 Subject Key Identifier:\n" + " 4D:7D:AE:0D:A5:5E:22:5A:6A:8F:19:61:54:B3:58:CB:7B:C0:BD:DA\n" + " X509v3 Authority Key Identifier:\n" + " keyid:4D:7D:AE:0D:A5:5E:22:5A:6A:8F:19:61:54:B3:58:CB:7B:C0:BD:DA\n" "\n" - " X509v3 Basic Constraints: critical\n" + " X509v3 Basic Constraints:\n" " CA:TRUE\n" - " X509v3 Key Usage: critical\n" - " Digital Signature, Certificate Sign, CRL Sign\n" " Signature Algorithm: sha256WithRSAEncryption\n" - " b3:cb:33:eb:0e:02:64:f4:55:9a:3d:03:9a:cf:6a:4c:18:43:\n" - " f7:42:cb:65:dc:61:52:e5:9f:2f:42:97:3c:93:16:22:d4:af:\n" - " ae:b2:0f:c3:9b:ef:e0:cc:ee:b6:b1:69:a3:d8:da:26:c3:ad:\n" - " 3b:c5:64:dc:9f:d4:c2:53:4b:91:6d:c4:92:09:0b:ac:f0:99:\n" - " be:6f:b9:3c:03:4a:6d:9f:01:5d:ec:5a:9a:f3:a7:e5:3b:2c:\n" - " 99:57:7d:7e:25:15:68:20:12:30:96:16:86:f5:db:74:90:60:\n" - " fe:8b:df:99:f6:f7:62:49:9f:bc:8d:45:23:0a:c8:73:b8:79:\n" - " 80:3c:b9:e5:72:85:4b:b3:81:66:74:a2:72:92:4c:44:fd:7b:\n" - " 46:2e:21:a2:a9:81:a2:f3:26:4d:e3:89:7d:78:b0:c6:6f:b5:\n" - " 87:cb:ee:25:ed:27:1f:75:13:fa:6d:e9:37:73:ad:07:bb:af:\n" - " d3:6c:87:ea:02:01:70:bd:53:aa:ce:39:2c:d4:66:39:33:aa:\n" - " d1:9c:ee:67:e3:a9:45:d2:7b:2e:54:09:af:70:5f:3f:5a:67:\n" - " 2e:6c:72:ef:e0:9d:92:28:4a:df:ba:0b:b7:23:ca:5b:04:11:\n" - " 45:d1:51:e9:ea:c9:ec:54:fa:34:46:ae:fc:dc:6c:f8:1e:2c:\n" - " 9e:f4:71:51:8d:b5:a1:26:9a:13:30:be:1e:41:25:59:58:05:\n" - " 2c:64:c8:f9:5e:38:ae:dc:93:b0:8a:d6:38:74:02:cb:ce:ce:\n" - " 95:31:76:f6:7c:bf:a4:a1:8e:27:fd:ca:74:82:d1:e1:4d:b6:\n" - " 48:51:fa:c5:17:59:22:a3:84:be:82:c8:83:ec:61:a0:f4:ee:\n" - " 2c:e3:a3:ea:e5:51:c9:d3:4f:db:85:bd:ba:7a:52:14:b6:03:\n" - " ed:43:17:d8:d7:1c:22:5e:c9:56:d9:d6:81:96:11:e3:5e:01:\n" - " 40:91:30:09:da:a3:5f:d3:27:60:e5:9d:6c:da:d0:f0:39:01:\n" - " 23:4a:a6:15:7a:4a:82:eb:ec:72:4a:1d:36:dc:6f:83:c4:85:\n" - " 84:b5:8d:cd:09:e5:12:63:f3:21:56:c8:64:6b:db:b8:cf:d4:\n" - " df:ca:a8:24:8e:df:8d:63:a5:96:84:bf:ff:8b:7e:46:7a:f0:\n" - " c7:73:7c:70:8a:f5:17:d0:ac:c8:89:1e:d7:89:42:0f:4d:66:\n" - " c4:d8:bb:36:a8:ae:ca:e1:cf:e2:88:f6:cf:b0:44:4a:5f:81:\n" - " 50:4b:d6:28:81:cd:6c:f0:ec:e6:09:08:f2:59:91:a2:69:ac:\n" - " c7:81:fa:ab:61:3e:db:6f:f6:7f:db:1a:9e:b9:5d:cc:cc:33:\n" - " fa:95:c6:f7:8d:4b:30:f3\n" + " 5b:40:71:96:c8:d1:57:3f:fc:f2:3c:75:fb:c9:a6:a7:63:8a:\n" + " 22:23:96:0f:40:77:77:e2:7f:76:fc:5f:7b:1c:bd:ea:ca:f0:\n" + " be:1a:fd:59:e6:0e:00:d1:78:44:01:28:f4:01:68:67:78:cf:\n" + " 78:43:36:ac:b2:5c:13:0e:2a:94:59:88:9e:64:46:42:0a:9b:\n" + " be:7d:2d:10:11:fe:8b:64:01:fb:00:c5:2e:47:63:c0:93:3a:\n" + " 4a:f8:6c:fc:a9:16:58:ab:bc:7b:6b:20:31:9d:d7:d8:84:01:\n" + " cc:ce:52:7f:a1:18:2f:5c:c9:59:58:9a:98:b9:ef:54:d7:a0:\n" + " 56:79:28:ba:ad:f5:e5:fd:7e:d8:d6:be:dd:25:76:6f:fa:8a:\n" + " 07:f6:8e:0f:83:43:19:ee:96:c4:c9:54:df:19:5a:4c:ae:25:\n" + " 57:a2:5d:d5:e8:0a:66:d8:19:e9:c4:44:ba:6a:3b:b3:86:ae:\n" + " 44:c0:7c:6e:e5:a0:6c:45:bb:7f:34:94:e9:d3:d4:f4:04:0b:\n" + " eb:fc:9a:fa:67:d4:e5:83:5e:08:09:9c:70:a9:d3:0d:8a:08:\n" + " ed:3c:04:33:4f:ac:02:d9:5c:99:62:12:fc:0e:8d:55:8a:ce:\n" + " ca:28:5a:1a:9e:c9:59:8e:f0:f5:19:c7:30:1e:59:1f:3c:77:\n" + " 6d:fc:a2:31:ec:bf:83:fd:14:26:91:68:88:05:4c:87:82:e0:\n" + " 33:f4:ee:d8:56:97:23:3a:00:9b:e7:a2:10:c2:83:28:c6:c0:\n" + " c1:92:49:95:c1:d3:e1:43:e8:8f:0c:d0:ae:e3:50:17:1a:8d:\n" + " 0f:4a:60:71:76:8e:9e:fb:15:76:cd:cd:69:2c:59:24:69:d2:\n" + " 0f:f2:d5:0e:96:95:2b:2e:d7:81:ed:b3:7b:6f:ce:60:32:b5:\n" + " f0:f6:74:ea:27:3a:ee:2c:96:7b:e0:06:6c:33:25:c4:60:da:\n" + " 76:de:c4:a1:22:b6:b1:63:57:10:3c:62:60:98:47:39:9e:38:\n" + " ce:c7:ef:75:75:19:d3:26:2a:cf:46:e3:b0:72:38:49:ee:c3:\n" + " 4e:52:97:e5:e5:b8:bc:b1:45:56:98:54:0a:63:c8:87:ff:a0:\n" + " cb:28:12:5c:8f:a2:6e:a7:f9:50:98:2d:a5:26:08:df:16:29:\n" + " 19:63:7f:6c:b4:41:20:f7:5d:ef:6a:90:fd:1a:08:1c:c2:4c:\n" + " 3e:77:ea:e0:df:c0:dd:aa:a2:36:e7:e8:be:98:39:0a:68:59:\n" + " 8e:a0:71:2f:7c:92:ab:e0:c4:c1:c2:eb:89:b6:34:ce:44:ab:\n" + " f9:f6:a4:c8:7b:ad:a8:bc:c9:04:7c:d5:4c:a4:d2:8b:54:23:\n" + " 89:68:86:4e:07:36:d9:bc\n" "-----BEGIN CERTIFICATE-----\n" - "MIIGXDCCBESgAwIBAgIJAIIvj+uNBiSwMA0GCSqGSIb3DQEBCwUAMIG6MQswCQYD\n" + "MIIGSTCCBDGgAwIBAgIJAO9U2PfaGOgZMA0GCSqGSIb3DQEBCwUAMIG6MQswCQYD\n" "VQQGEwJVUzETMBEGA1UECAwKQ2FsaWZvcm5pYTEWMBQGA1UEBwwNU2FuIEZyYW5j\n" "aXNjbzETMBEGA1UECgwKTGluZGVuIExhYjEgMB4GA1UECwwXU2Vjb25kIExpZmUg\n" "RW5naW5lZXJpbmcxITAfBgNVBAMMGEludGVncmF0aW9uIFRlc3QgUm9vdCBDQTEk\n" - "MCIGCSqGSIb3DQEJARYVbm9yZXBseUBsaW5kZW5sYWIuY29tMB4XDTE4MDUyMjIy\n" - "MTk0NVoXDTM4MDUxNzIyMTk0NVowgboxCzAJBgNVBAYTAlVTMRMwEQYDVQQIDApD\n" + "MCIGCSqGSIb3DQEJARYVbm9yZXBseUBsaW5kZW5sYWIuY29tMB4XDTI0MDcyMzEx\n" + "NDYyNloXDTM0MDcyMTExNDYyNlowgboxCzAJBgNVBAYTAlVTMRMwEQYDVQQIDApD\n" "YWxpZm9ybmlhMRYwFAYDVQQHDA1TYW4gRnJhbmNpc2NvMRMwEQYDVQQKDApMaW5k\n" "ZW4gTGFiMSAwHgYDVQQLDBdTZWNvbmQgTGlmZSBFbmdpbmVlcmluZzEhMB8GA1UE\n" "AwwYSW50ZWdyYXRpb24gVGVzdCBSb290IENBMSQwIgYJKoZIhvcNAQkBFhVub3Jl\n" "cGx5QGxpbmRlbmxhYi5jb20wggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoIC\n" - "AQC94HndO6ash9A58FjHpEJC9l+TsDYEteLV9yrAbKAT0h4CgVcCUExXt+8nnvbx\n" - "8TAwch5XNOU/gjwhxGbSc2NskebdSZ6csTRqgUWhbsRQKPLY4/6AL4OqKJG0jFfJ\n" - "8RbZDIc8JYCggY1x8pbiFvGXxLDYU7sTbHNULymUhc+GbnVxrTnj/DkSU5Mczjng\n" - "M9pJtz2vsDfOdwkDJzJwwJx/nInOkEWwfZSL/xMnuoh/rsSqc9VHuIdpiYAMwSIY\n" - "eMINR9kQ/4B5DUZx7Nm6yfN3/ZJtHw/ZVBht9nIkXFw9Q0k1Phwo3n5E3CnDn2IE\n" - "RqrE5mlqFfjjdBwU6fSXfDBs1Cj8Kg4dbTkuHfkXQzVdI+e646jpl2s8PiPv2Lz7\n" - "elc3OZNZA/x4yrEx7yYZ7VbhY8OtmYBbR7UDNV/+aqYhY+xQ+07J+a6lZtBVM43m\n" - "xVBaxo9cNEWnctpQ9mZMGfXR5PsRi6G1TglDgT05KIY7/gcolwK1OgdfSiCAGn2k\n" - "jPds9sWb9mHlx7DD1Vg4e7tHHjTWFlXF0mywk3exkGkGsVPLG4Rxz7iHGx5ENbQr\n" - "uwRZWAvok9iuIZuxHIkwrhGAd8wW89Y17aGzcLNPzaFWme4OwACkCXDDWwu+oQcY\n" - "3cb0bYtYvPm7SwEs9swsm4cOsU+cEL78ReKk7H78/0W4UwIDAQABo2MwYTAdBgNV\n" - "HQ4EFgQUiiLGnC4R80AMzoIMIln/+H/QuRMwHwYDVR0jBBgwFoAUiiLGnC4R80AM\n" - "zoIMIln/+H/QuRMwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAYYwDQYJ\n" - "KoZIhvcNAQELBQADggIBALPLM+sOAmT0VZo9A5rPakwYQ/dCy2XcYVLlny9ClzyT\n" - "FiLUr66yD8Ob7+DM7raxaaPY2ibDrTvFZNyf1MJTS5FtxJIJC6zwmb5vuTwDSm2f\n" - "AV3sWprzp+U7LJlXfX4lFWggEjCWFob123SQYP6L35n292JJn7yNRSMKyHO4eYA8\n" - "ueVyhUuzgWZ0onKSTET9e0YuIaKpgaLzJk3jiX14sMZvtYfL7iXtJx91E/pt6Tdz\n" - "rQe7r9Nsh+oCAXC9U6rOOSzUZjkzqtGc7mfjqUXSey5UCa9wXz9aZy5scu/gnZIo\n" - "St+6C7cjylsEEUXRUenqyexU+jRGrvzcbPgeLJ70cVGNtaEmmhMwvh5BJVlYBSxk\n" - "yPleOK7ck7CK1jh0AsvOzpUxdvZ8v6Shjif9ynSC0eFNtkhR+sUXWSKjhL6CyIPs\n" - "YaD07izjo+rlUcnTT9uFvbp6UhS2A+1DF9jXHCJeyVbZ1oGWEeNeAUCRMAnao1/T\n" - "J2DlnWza0PA5ASNKphV6SoLr7HJKHTbcb4PEhYS1jc0J5RJj8yFWyGRr27jP1N/K\n" - "qCSO341jpZaEv/+LfkZ68MdzfHCK9RfQrMiJHteJQg9NZsTYuzaorsrhz+KI9s+w\n" - "REpfgVBL1iiBzWzw7OYJCPJZkaJprMeB+qthPttv9n/bGp65XczMM/qVxveNSzDz\n" + "AQDGzAf0CxcGTaYwtMcCa52kR6YJDmAaMtRrQojuxbnp+7ULYNyiRZKlu4gS/EIa\n" + "gDJ5FmJ6l6+EKFM8wfJowE5F5Apj+TQdoovMcN/GZcC6MTLSnQzIztwREqQR+tPI\n" + "VuIxiuP7kUDaJVXR8nWbTfq4H7Vtm+H+XejEAnkU731aszoettBgLJDcIuLFroUf\n" + "tJ16IPivY1YlGmTznD+az2gICjfb0KNlJtuAgv/gG1HI7vatwrTyq9LohYZ3KNBj\n" + "SnF4QeOMf3FRMa8kP/qN0NgL4n55M4q70gCeLsjN1VCSuFxaC5nvBTln2r5wNlE3\n" + "NyBvhKspEQB7ODK6C7w0prXGp/DAJS04C3JAq8/m/5d1/+KpPCpXzuRSIIze/mjO\n" + "VIU3urN/LlNY6puseWsWZbgRiFpG656egDyJkTXgxTNFyIZNJVE5sXKXK7PIyegR\n" + "zTJByMFWIn4zgYVhq9qebl8kHA+b+tqdhhpm9jIqEIDqcnpK78DyfEMC5nAZauEC\n" + "CgCAURyjA4ttiZ+RN5DW2JxzdwaevJWJZu5DQKPuQ6P2LUPde/AvCxI3SbeBWuJU\n" + "bXGI//5+QSU1TLS5YmXdnx96Bm4rIFh42ghmqPGJ3o9/XF7CcjN/to5BTCb2TNQO\n" + "EUTaxxT3i3lOUymHFbES6RkrVDPWLn+9QiC+/NectHoK2wIDAQABo1AwTjAdBgNV\n" + "HQ4EFgQUTX2uDaVeIlpqjxlhVLNYy3vAvdowHwYDVR0jBBgwFoAUTX2uDaVeIlpq\n" + "jxlhVLNYy3vAvdowDAYDVR0TBAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAgEAW0Bx\n" + "lsjRVz/88jx1+8mmp2OKIiOWD0B3d+J/dvxfexy96srwvhr9WeYOANF4RAEo9AFo\n" + "Z3jPeEM2rLJcEw4qlFmInmRGQgqbvn0tEBH+i2QB+wDFLkdjwJM6Svhs/KkWWKu8\n" + "e2sgMZ3X2IQBzM5Sf6EYL1zJWViamLnvVNegVnkouq315f1+2Na+3SV2b/qKB/aO\n" + "D4NDGe6WxMlU3xlaTK4lV6Jd1egKZtgZ6cREumo7s4auRMB8buWgbEW7fzSU6dPU\n" + "9AQL6/ya+mfU5YNeCAmccKnTDYoI7TwEM0+sAtlcmWIS/A6NVYrOyihaGp7JWY7w\n" + "9RnHMB5ZHzx3bfyiMey/g/0UJpFoiAVMh4LgM/Tu2FaXIzoAm+eiEMKDKMbAwZJJ\n" + "lcHT4UPojwzQruNQFxqND0pgcXaOnvsVds3NaSxZJGnSD/LVDpaVKy7Xge2ze2/O\n" + "YDK18PZ06ic67iyWe+AGbDMlxGDadt7EoSK2sWNXEDxiYJhHOZ44zsfvdXUZ0yYq\n" + "z0bjsHI4Se7DTlKX5eW4vLFFVphUCmPIh/+gyygSXI+ibqf5UJgtpSYI3xYpGWN/\n" + "bLRBIPdd72qQ/RoIHMJMPnfq4N/A3aqiNufovpg5CmhZjqBxL3ySq+DEwcLribY0\n" + "zkSr+fakyHutqLzJBHzVTKTSi1QjiWiGTgc22bw=\n" "-----END CERTIFICATE-----\n" - ); + ); + const std::string mPemIntermediateCert( "Certificate:\n" " Data:\n" " Version: 3 (0x2)\n" - " Serial Number: 4096 (0x1000)\n" + " Serial Number: 85:bb:4b:66:26:db:9a:c6\n" " Signature Algorithm: sha256WithRSAEncryption\n" " Issuer: C=US, ST=California, L=San Francisco, O=Linden Lab, OU=Second Life Engineering, CN=Integration Test Root CA/emailAddress=noreply@lindenlab.com\n" " Validity\n" - " Not Before: May 22 22:39:08 2018 GMT\n" - " Not After : May 19 22:39:08 2028 GMT\n" - " Subject: C=US, ST=California, O=Linden Lab, OU=Second Life Engineering, CN=Integration Test Intermediate CA/emailAddress=noreply@lindenlab.com\n" + " Not Before: Jul 23 11:46:33 2024 GMT\n" + " Not After : Jul 21 11:46:33 2034 GMT\n" + " Subject: C=US, ST=California, L=San Francisco, O=Linden Lab, OU=Second Life Engineering, CN=Integration Test Intermediate CA/emailAddress=noreply@lindenlab.com\n" " Subject Public Key Info:\n" " Public Key Algorithm: rsaEncryption\n" " Public-Key: (4096 bit)\n" " Modulus:\n" - " 00:ce:a3:70:e2:c4:fb:4b:97:90:a1:30:bb:c1:1b:\n" - " 13:b9:aa:7e:46:17:a3:26:8d:69:3f:5e:73:95:e8:\n" - " 6a:b1:0a:b4:8f:50:65:e3:c6:5c:39:24:34:df:0b:\n" - " b7:cc:ce:62:0c:36:5a:12:2c:fe:35:4c:e9:1c:ac:\n" - " 80:5e:24:99:d7:aa:bd:be:48:c0:62:64:77:36:88:\n" - " 66:ce:f4:a8:dd:d2:76:24:62:90:55:41:fc:1d:13:\n" - " 4e:a7:4e:57:bc:a8:a4:59:4b:2c:5a:1c:d8:cc:16:\n" - " de:e8:88:30:c9:95:df:2f:a6:14:28:0f:eb:34:46:\n" - " 12:58:ba:da:0e:e6:de:9c:15:f6:f4:e3:9f:74:aa:\n" - " 70:89:79:8b:e9:5a:7b:18:54:15:94:3a:23:0a:65:\n" - " 78:05:d9:33:90:2a:ce:15:18:0d:52:fc:5c:31:65:\n" - " 20:d0:12:37:8c:11:80:ba:d4:b0:82:73:00:4b:49:\n" - " be:cb:d6:bc:e7:cd:61:f3:00:98:99:74:5a:37:81:\n" - " 49:96:7e:14:01:1b:86:d2:d0:06:94:40:63:63:46:\n" - " 11:fc:33:5c:bd:3a:5e:d4:e5:44:47:64:50:bd:a6:\n" - " 97:55:70:64:9b:26:cc:de:20:82:90:6a:83:41:9c:\n" - " 6f:71:47:14:be:cb:68:7c:85:be:ef:2e:76:12:19:\n" - " d3:c9:87:32:b4:ac:60:20:16:28:2d:af:bc:e8:01:\n" - " c6:7f:fb:d8:11:d5:f4:b7:14:bd:27:08:5b:72:be:\n" - " 09:e0:91:c8:9c:7b:b4:b3:12:ef:32:36:be:b1:b9:\n" - " a2:b7:e3:69:47:30:76:ba:9c:9b:19:99:4d:53:dd:\n" - " 5c:e8:2c:f1:b2:64:69:cf:15:bd:f8:bb:58:95:73:\n" - " 58:38:95:b4:7a:cf:84:29:a6:c2:db:f0:bd:ef:97:\n" - " 26:d4:99:ac:d7:c7:be:b0:0d:11:f4:26:86:2d:77:\n" - " 42:52:25:d7:56:c7:e3:97:b1:36:5c:97:71:d0:9b:\n" - " f5:b5:50:8d:f9:ff:fb:10:77:3c:b5:53:6d:a1:43:\n" - " 35:a9:03:32:05:ab:d7:f5:d1:19:bd:5f:92:a3:00:\n" - " 2a:79:37:a4:76:4f:e9:32:0d:e4:86:bb:ea:c3:1a:\n" - " c5:33:e8:16:d4:a5:d8:e0:e8:bb:c2:f0:22:15:e2:\n" - " d9:8c:ae:ac:7d:2b:bf:eb:a3:4c:3b:29:1d:94:ac:\n" - " a3:bb:6d:ba:6d:03:91:03:cf:46:12:c4:66:21:c5:\n" - " c6:67:d8:11:19:79:01:0e:6e:84:1c:76:6f:11:3d:\n" - " eb:94:89:c5:6a:26:1f:cd:e0:11:8b:51:ee:99:35:\n" - " 69:e5:7f:0b:77:2a:94:e4:4b:64:b9:83:04:30:05:\n" - " e4:a2:e3\n" + " 00:be:f7:d2:cb:e4:5c:46:7b:e2:11:22:89:72:da:\n" + " 77:72:ec:05:87:19:f7:77:07:fd:67:d7:af:13:d5:\n" + " 76:12:92:dd:69:4d:22:47:b0:3d:94:8a:6a:95:85:\n" + " 34:b8:78:c3:9d:63:32:b1:4b:0a:b6:0e:05:7b:ab:\n" + " 06:23:fc:0d:21:b5:fc:c6:6a:5a:36:be:6e:fc:c7:\n" + " 47:97:a3:18:2e:33:cd:0e:8a:75:2b:b7:29:e9:68:\n" + " 4a:90:53:45:db:73:ff:b3:e5:c1:d4:6b:dd:3a:b1:\n" + " ef:53:9f:23:e9:c6:87:ce:67:b9:fb:a4:d5:76:21:\n" + " 03:cb:c5:72:6b:c5:a6:07:55:fb:47:90:e8:92:38:\n" + " 73:14:11:8e:ff:21:b9:35:64:5a:61:c7:fc:1f:e4:\n" + " 4d:47:e5:03:cc:0b:c3:69:66:71:84:0c:18:2f:61:\n" + " 7f:34:dd:f2:91:e3:b7:9d:a8:b8:db:3f:6e:6f:96:\n" + " fa:34:06:82:04:c8:18:cc:de:8b:7f:26:b5:48:53:\n" + " fb:fb:15:7b:0e:38:60:fe:da:21:98:8d:73:07:b2:\n" + " 6b:fd:ad:21:59:e7:84:66:e1:04:16:1c:be:13:34:\n" + " 28:43:2c:09:3d:e4:77:2a:a4:ad:6d:f9:26:04:f7:\n" + " 43:73:9b:d9:ea:1a:43:6a:b4:db:88:f8:f9:bd:34:\n" + " f8:a6:e8:7a:ab:b4:b2:e1:29:47:a6:ba:b8:65:9c:\n" + " c6:b3:af:13:43:38:ef:2a:05:77:9f:8f:f0:0c:56:\n" + " 21:c2:92:d2:2c:c3:32:50:d1:62:ae:51:fc:99:e6:\n" + " b8:38:f8:83:1d:8d:40:11:e0:1d:51:5d:3f:fa:55:\n" + " 61:b6:18:09:1e:71:af:95:64:9c:ea:c6:11:64:f0:\n" + " a8:02:7d:bb:c8:54:2e:57:48:32:7c:51:66:0d:d6:\n" + " 3e:0e:ed:5e:30:a8:a6:47:03:64:5c:89:21:45:90:\n" + " e1:4c:91:bc:bd:81:6e:73:a9:14:27:e6:0d:6d:38:\n" + " dc:50:9d:b2:56:66:60:6c:66:b9:5d:bb:8c:96:2d:\n" + " 89:5e:0d:2b:ed:b8:03:31:ce:0a:ff:82:03:f5:b2:\n" + " 3b:e5:27:de:61:d8:8f:bf:a2:6a:64:b0:4a:87:23:\n" + " 40:28:a3:f1:ec:96:50:cd:83:50:2d:78:71:92:f2:\n" + " 88:75:b0:9d:cd:0b:e4:62:a6:a5:63:11:fc:b4:ba:\n" + " 9f:c6:67:40:2c:ad:a4:ef:94:f0:f9:a0:ba:e1:52:\n" + " 2e:27:d9:6b:1d:82:23:ed:3c:0b:0b:d2:bc:14:be:\n" + " 6d:b1:69:ad:3e:25:3a:66:d2:d1:af:9f:88:45:25:\n" + " 6b:6e:be:1f:a0:e7:b2:9f:6d:24:94:0d:f4:c2:75:\n" + " f9:1f:5d\n" " Exponent: 65537 (0x10001)\n" " X509v3 extensions:\n" - " X509v3 Subject Key Identifier: \n" - " 83:21:DE:EC:C0:79:03:6D:1E:83:F3:E5:97:29:D5:5A:C0:96:40:FA\n" - " X509v3 Authority Key Identifier: \n" - " keyid:8A:22:C6:9C:2E:11:F3:40:0C:CE:82:0C:22:59:FF:F8:7F:D0:B9:13\n" - "\n" - " X509v3 Basic Constraints: critical\n" + " X509v3 Basic Constraints:\n" " CA:TRUE, pathlen:0\n" - " X509v3 Key Usage: critical\n" + " X509v3 Key Usage:\n" " Digital Signature, Certificate Sign, CRL Sign\n" + " X509v3 Subject Key Identifier:\n" + " 56:98:DC:45:25:11:E2:8C:2B:EA:D6:C6:E2:C8:BE:2C:C8:69:FF:FF\n" + " X509v3 Authority Key Identifier:\n" + " keyid:4D:7D:AE:0D:A5:5E:22:5A:6A:8F:19:61:54:B3:58:CB:7B:C0:BD:DA\n" + " DirName:/C=US/ST=California/L=San Francisco/O=Linden Lab/OU=Second Life Engineering/CN=Integration Test Root CA/emailAddress=noreply@lindenlab.com\n" + " serial:EF:54:D8:F7:DA:18:E8:19\n" " Signature Algorithm: sha256WithRSAEncryption\n" - " a3:6c:85:9a:2e:4e:7e:5d:83:63:0f:f5:4f:a9:7d:ec:0e:6f:\n" - " ae:d7:ba:df:64:e0:46:0e:3d:da:18:15:2c:f3:73:ca:81:b1:\n" - " 10:d9:53:14:21:7d:72:5c:94:88:a5:9d:ad:ab:45:42:c6:64:\n" - " a9:d9:2e:4e:29:47:2c:b1:95:07:b7:62:48:68:1f:68:13:1c:\n" - " d2:a0:fb:5e:38:24:4a:82:0a:87:c9:93:20:43:7e:e9:f9:79:\n" - " ef:03:a2:bd:9e:24:6b:0a:01:5e:4a:36:c5:7d:7a:fe:d6:aa:\n" - " 2f:c2:8c:38:8a:99:3c:b0:6a:e5:60:be:56:d6:eb:60:03:55:\n" - " 24:42:a0:1a:fa:91:24:a3:53:15:75:5d:c8:eb:7c:1e:68:5a:\n" - " 7e:13:34:e3:85:37:1c:76:3f:77:67:1b:ed:1b:52:17:fc:4a:\n" - " a3:e2:74:84:80:2c:69:fc:dd:7d:26:97:c4:2a:69:7d:9c:dc:\n" - " 61:97:70:29:a7:3f:2b:5b:2b:22:51:fd:fe:6a:5d:f9:e7:14:\n" - " 48:b7:2d:c8:33:58:fc:f2:5f:27:f7:26:16:be:be:b5:aa:a2:\n" - " 64:53:3c:69:e8:b5:61:eb:ab:91:a5:b4:09:9b:f6:98:b8:5c:\n" - " 5b:24:2f:93:f5:2b:9c:8c:58:fb:26:3f:67:53:d7:42:64:e8:\n" - " 79:77:73:41:4e:e3:02:39:0b:b6:68:97:8b:84:e8:1d:83:a8:\n" - " 15:f1:06:46:47:80:42:5e:14:e2:61:8a:76:84:d5:d4:71:7f:\n" - " 4e:ff:d9:74:87:ff:32:c5:87:20:0a:d4:59:40:3e:d8:17:ef:\n" - " da:65:e9:0a:51:fe:1e:c3:46:91:d2:ee:e4:23:57:97:87:d4:\n" - " a6:a5:eb:ef:81:6a:d8:8c:d6:1f:8e:b1:18:4c:6b:89:32:55:\n" - " 53:68:26:9e:bb:03:be:2c:e9:8b:ff:97:9c:1c:ac:28:c3:9f:\n" - " 0b:b7:93:23:24:31:63:e4:19:13:f2:bb:08:71:b7:c5:c5:c4:\n" - " 10:ff:dc:fc:33:54:a4:5e:ec:a3:fe:0a:80:ca:9c:bc:95:6f:\n" - " 5f:39:91:3b:61:69:16:94:0f:57:4b:fc:4b:b1:be:72:98:5d:\n" - " 10:f9:08:a7:d6:e0:e8:3d:5d:54:7d:fa:4b:6a:dd:98:41:ed:\n" - " 84:a1:39:67:5c:6c:7f:0c:b0:e1:98:c1:14:ed:fe:1e:e8:05:\n" - " 8d:7f:6a:24:cb:1b:05:42:0d:7f:13:ba:ca:b5:91:db:a5:f0:\n" - " 40:2b:70:7a:2a:a5:5d:ed:56:0c:f0:c2:72:ee:63:dd:cb:5d:\n" - " 76:f6:08:e6:e6:30:ef:3a:b2:16:34:41:a4:e1:30:14:bc:c7:\n" - " f9:23:3a:1a:70:df:b8:cc\n" + " ae:d0:30:ac:31:49:20:86:0b:34:01:58:08:94:68:cc:38:9c:\n" + " f7:13:5c:46:19:33:ed:54:5e:e4:43:f3:59:33:5c:50:d9:89:\n" + " 8b:ee:75:67:a8:c7:0e:d1:30:c2:4e:a3:2e:a8:64:2d:6a:a8:\n" + " f4:bd:b1:32:dc:bc:46:48:5d:1a:18:d8:e8:0b:8c:fe:7b:51:\n" + " d9:dd:b9:e3:4b:d1:f9:e0:22:46:dd:37:5b:b2:cb:72:8e:9c:\n" + " 4b:da:67:df:fd:ce:86:49:21:31:4e:99:b6:d4:38:0b:14:5d:\n" + " ad:97:ba:8f:e2:08:15:85:73:eb:4a:7d:01:49:af:63:ae:2d:\n" + " e3:9d:0a:d7:11:c2:03:d3:15:21:97:be:3d:d2:ea:ab:cc:93:\n" + " 16:98:64:80:72:eb:c2:78:0a:09:69:c4:2b:5d:df:30:7b:be:\n" + " 9b:02:34:73:62:9f:95:b1:cf:08:e8:9e:57:a8:37:31:cf:2c:\n" + " 8c:18:b1:d5:7a:25:90:d6:b6:76:28:1b:e2:b1:cf:1b:f1:ef:\n" + " dd:2f:d3:07:af:81:e3:5f:fc:5a:e7:3c:a9:37:0d:9c:78:5b:\n" + " 58:dc:89:54:70:a4:5b:ff:9f:64:30:a3:85:12:32:69:a5:02:\n" + " 73:d9:1d:ff:69:1f:d4:97:8f:d0:a8:90:8c:dd:2e:45:a1:b1:\n" + " e3:8a:82:fc:fc:08:41:01:51:92:87:9a:09:7b:35:c3:cc:48:\n" + " 81:39:30:a9:f4:41:3b:06:a3:06:21:cc:4b:bc:1b:76:58:94:\n" + " d1:e4:22:70:7f:20:7e:7a:b4:fa:7f:e8:79:c1:8c:89:9e:e9:\n" + " e3:72:2a:43:72:47:9e:bb:26:ed:64:2c:c8:54:f7:b4:95:c2:\n" + " c4:e9:8b:df:d5:10:a7:ed:a5:7a:94:97:c4:76:45:e3:6c:c0:\n" + " 0e:a6:2a:76:d5:1d:2f:ad:99:32:c6:7b:f6:41:e0:65:37:0f:\n" + " c0:1f:c5:99:4a:75:fd:6c:e0:f1:f0:58:49:2d:81:10:ca:d8:\n" + " eb:2b:c3:9b:a9:d9:a9:f5:6c:6d:26:fd:b8:32:92:58:f4:65:\n" + " 0b:d1:8e:03:1e:d5:6a:95:d4:46:9e:65:dd:e5:85:36:e6:31:\n" + " 77:3a:1a:20:2b:07:b7:f1:9a:4e:8d:54:22:5a:54:1c:72:5c:\n" + " 1f:b4:1a:5b:21:ed:06:5a:9a:e5:3c:01:c9:9b:af:50:61:f2:\n" + " 29:6b:ec:6d:19:bb:2e:02:94:ca:36:71:ef:45:39:f1:a5:25:\n" + " 10:0e:90:bc:a7:b3:5b:ab:af:f1:19:88:6a:09:2f:1f:d0:24:\n" + " a8:62:ed:d9:1a:65:89:65:16:a5:55:de:33:e8:7a:81:66:72:\n" + " 91:17:5e:1d:22:72:f7:b8\n" "-----BEGIN CERTIFICATE-----\n" - "MIIGSDCCBDCgAwIBAgICEAAwDQYJKoZIhvcNAQELBQAwgboxCzAJBgNVBAYTAlVT\n" - "MRMwEQYDVQQIDApDYWxpZm9ybmlhMRYwFAYDVQQHDA1TYW4gRnJhbmNpc2NvMRMw\n" - "EQYDVQQKDApMaW5kZW4gTGFiMSAwHgYDVQQLDBdTZWNvbmQgTGlmZSBFbmdpbmVl\n" - "cmluZzEhMB8GA1UEAwwYSW50ZWdyYXRpb24gVGVzdCBSb290IENBMSQwIgYJKoZI\n" - "hvcNAQkBFhVub3JlcGx5QGxpbmRlbmxhYi5jb20wHhcNMTgwNTIyMjIzOTA4WhcN\n" - "MjgwNTE5MjIzOTA4WjCBqjELMAkGA1UEBhMCVVMxEzARBgNVBAgMCkNhbGlmb3Ju\n" - "aWExEzARBgNVBAoMCkxpbmRlbiBMYWIxIDAeBgNVBAsMF1NlY29uZCBMaWZlIEVu\n" - "Z2luZWVyaW5nMSkwJwYDVQQDDCBJbnRlZ3JhdGlvbiBUZXN0IEludGVybWVkaWF0\n" - "ZSBDQTEkMCIGCSqGSIb3DQEJARYVbm9yZXBseUBsaW5kZW5sYWIuY29tMIICIjAN\n" - "BgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAzqNw4sT7S5eQoTC7wRsTuap+Rhej\n" - "Jo1pP15zlehqsQq0j1Bl48ZcOSQ03wu3zM5iDDZaEiz+NUzpHKyAXiSZ16q9vkjA\n" - "YmR3NohmzvSo3dJ2JGKQVUH8HRNOp05XvKikWUssWhzYzBbe6IgwyZXfL6YUKA/r\n" - "NEYSWLraDubenBX29OOfdKpwiXmL6Vp7GFQVlDojCmV4BdkzkCrOFRgNUvxcMWUg\n" - "0BI3jBGAutSwgnMAS0m+y9a8581h8wCYmXRaN4FJln4UARuG0tAGlEBjY0YR/DNc\n" - "vTpe1OVER2RQvaaXVXBkmybM3iCCkGqDQZxvcUcUvstofIW+7y52EhnTyYcytKxg\n" - "IBYoLa+86AHGf/vYEdX0txS9Jwhbcr4J4JHInHu0sxLvMja+sbmit+NpRzB2upyb\n" - "GZlNU91c6CzxsmRpzxW9+LtYlXNYOJW0es+EKabC2/C975cm1Jms18e+sA0R9CaG\n" - "LXdCUiXXVsfjl7E2XJdx0Jv1tVCN+f/7EHc8tVNtoUM1qQMyBavX9dEZvV+SowAq\n" - "eTekdk/pMg3khrvqwxrFM+gW1KXY4Oi7wvAiFeLZjK6sfSu/66NMOykdlKyju226\n" - "bQORA89GEsRmIcXGZ9gRGXkBDm6EHHZvET3rlInFaiYfzeARi1HumTVp5X8LdyqU\n" - "5EtkuYMEMAXkouMCAwEAAaNmMGQwHQYDVR0OBBYEFIMh3uzAeQNtHoPz5Zcp1VrA\n" - "lkD6MB8GA1UdIwQYMBaAFIoixpwuEfNADM6CDCJZ//h/0LkTMBIGA1UdEwEB/wQI\n" - "MAYBAf8CAQAwDgYDVR0PAQH/BAQDAgGGMA0GCSqGSIb3DQEBCwUAA4ICAQCjbIWa\n" - "Lk5+XYNjD/VPqX3sDm+u17rfZOBGDj3aGBUs83PKgbEQ2VMUIX1yXJSIpZ2tq0VC\n" - "xmSp2S5OKUcssZUHt2JIaB9oExzSoPteOCRKggqHyZMgQ37p+XnvA6K9niRrCgFe\n" - "SjbFfXr+1qovwow4ipk8sGrlYL5W1utgA1UkQqAa+pEko1MVdV3I63weaFp+EzTj\n" - "hTccdj93ZxvtG1IX/Eqj4nSEgCxp/N19JpfEKml9nNxhl3Appz8rWysiUf3+al35\n" - "5xRIty3IM1j88l8n9yYWvr61qqJkUzxp6LVh66uRpbQJm/aYuFxbJC+T9SucjFj7\n" - "Jj9nU9dCZOh5d3NBTuMCOQu2aJeLhOgdg6gV8QZGR4BCXhTiYYp2hNXUcX9O/9l0\n" - "h/8yxYcgCtRZQD7YF+/aZekKUf4ew0aR0u7kI1eXh9SmpevvgWrYjNYfjrEYTGuJ\n" - "MlVTaCaeuwO+LOmL/5ecHKwow58Lt5MjJDFj5BkT8rsIcbfFxcQQ/9z8M1SkXuyj\n" - "/gqAypy8lW9fOZE7YWkWlA9XS/xLsb5ymF0Q+Qin1uDoPV1UffpLat2YQe2EoTln\n" - "XGx/DLDhmMEU7f4e6AWNf2okyxsFQg1/E7rKtZHbpfBAK3B6KqVd7VYM8MJy7mPd\n" - "y1129gjm5jDvOrIWNEGk4TAUvMf5IzoacN+4zA==\n" + "MIIHNjCCBR6gAwIBAgIJAIW7S2Ym25rGMA0GCSqGSIb3DQEBCwUAMIG6MQswCQYD\n" + "VQQGEwJVUzETMBEGA1UECAwKQ2FsaWZvcm5pYTEWMBQGA1UEBwwNU2FuIEZyYW5j\n" + "aXNjbzETMBEGA1UECgwKTGluZGVuIExhYjEgMB4GA1UECwwXU2Vjb25kIExpZmUg\n" + "RW5naW5lZXJpbmcxITAfBgNVBAMMGEludGVncmF0aW9uIFRlc3QgUm9vdCBDQTEk\n" + "MCIGCSqGSIb3DQEJARYVbm9yZXBseUBsaW5kZW5sYWIuY29tMB4XDTI0MDcyMzEx\n" + "NDYzM1oXDTM0MDcyMTExNDYzM1owgcIxCzAJBgNVBAYTAlVTMRMwEQYDVQQIDApD\n" + "YWxpZm9ybmlhMRYwFAYDVQQHDA1TYW4gRnJhbmNpc2NvMRMwEQYDVQQKDApMaW5k\n" + "ZW4gTGFiMSAwHgYDVQQLDBdTZWNvbmQgTGlmZSBFbmdpbmVlcmluZzEpMCcGA1UE\n" + "AwwgSW50ZWdyYXRpb24gVGVzdCBJbnRlcm1lZGlhdGUgQ0ExJDAiBgkqhkiG9w0B\n" + "CQEWFW5vcmVwbHlAbGluZGVubGFiLmNvbTCCAiIwDQYJKoZIhvcNAQEBBQADggIP\n" + "ADCCAgoCggIBAL730svkXEZ74hEiiXLad3LsBYcZ93cH/WfXrxPVdhKS3WlNIkew\n" + "PZSKapWFNLh4w51jMrFLCrYOBXurBiP8DSG1/MZqWja+bvzHR5ejGC4zzQ6KdSu3\n" + "KeloSpBTRdtz/7PlwdRr3Tqx71OfI+nGh85nufuk1XYhA8vFcmvFpgdV+0eQ6JI4\n" + "cxQRjv8huTVkWmHH/B/kTUflA8wLw2lmcYQMGC9hfzTd8pHjt52ouNs/bm+W+jQG\n" + "ggTIGMzei38mtUhT+/sVew44YP7aIZiNcweya/2tIVnnhGbhBBYcvhM0KEMsCT3k\n" + "dyqkrW35JgT3Q3Ob2eoaQ2q024j4+b00+Kboequ0suEpR6a6uGWcxrOvE0M47yoF\n" + "d5+P8AxWIcKS0izDMlDRYq5R/JnmuDj4gx2NQBHgHVFdP/pVYbYYCR5xr5VknOrG\n" + "EWTwqAJ9u8hULldIMnxRZg3WPg7tXjCopkcDZFyJIUWQ4UyRvL2BbnOpFCfmDW04\n" + "3FCdslZmYGxmuV27jJYtiV4NK+24AzHOCv+CA/WyO+Un3mHYj7+iamSwSocjQCij\n" + "8eyWUM2DUC14cZLyiHWwnc0L5GKmpWMR/LS6n8ZnQCytpO+U8PmguuFSLifZax2C\n" + "I+08CwvSvBS+bbFprT4lOmbS0a+fiEUla26+H6Dnsp9tJJQN9MJ1+R9dAgMBAAGj\n" + "ggEzMIIBLzAPBgNVHRMECDAGAQH/AgEAMAsGA1UdDwQEAwIBhjAdBgNVHQ4EFgQU\n" + "VpjcRSUR4owr6tbG4si+LMhp//8wge8GA1UdIwSB5zCB5IAUTX2uDaVeIlpqjxlh\n" + "VLNYy3vAvdqhgcCkgb0wgboxCzAJBgNVBAYTAlVTMRMwEQYDVQQIDApDYWxpZm9y\n" + "bmlhMRYwFAYDVQQHDA1TYW4gRnJhbmNpc2NvMRMwEQYDVQQKDApMaW5kZW4gTGFi\n" + "MSAwHgYDVQQLDBdTZWNvbmQgTGlmZSBFbmdpbmVlcmluZzEhMB8GA1UEAwwYSW50\n" + "ZWdyYXRpb24gVGVzdCBSb290IENBMSQwIgYJKoZIhvcNAQkBFhVub3JlcGx5QGxp\n" + "bmRlbmxhYi5jb22CCQDvVNj32hjoGTANBgkqhkiG9w0BAQsFAAOCAgEArtAwrDFJ\n" + "IIYLNAFYCJRozDic9xNcRhkz7VRe5EPzWTNcUNmJi+51Z6jHDtEwwk6jLqhkLWqo\n" + "9L2xMty8RkhdGhjY6AuM/ntR2d2540vR+eAiRt03W7LLco6cS9pn3/3OhkkhMU6Z\n" + "ttQ4CxRdrZe6j+IIFYVz60p9AUmvY64t450K1xHCA9MVIZe+PdLqq8yTFphkgHLr\n" + "wngKCWnEK13fMHu+mwI0c2KflbHPCOieV6g3Mc8sjBix1XolkNa2digb4rHPG/Hv\n" + "3S/TB6+B41/8Wuc8qTcNnHhbWNyJVHCkW/+fZDCjhRIyaaUCc9kd/2kf1JeP0KiQ\n" + "jN0uRaGx44qC/PwIQQFRkoeaCXs1w8xIgTkwqfRBOwajBiHMS7wbdliU0eQicH8g\n" + "fnq0+n/oecGMiZ7p43IqQ3JHnrsm7WQsyFT3tJXCxOmL39UQp+2lepSXxHZF42zA\n" + "DqYqdtUdL62ZMsZ79kHgZTcPwB/FmUp1/Wzg8fBYSS2BEMrY6yvDm6nZqfVsbSb9\n" + "uDKSWPRlC9GOAx7VapXURp5l3eWFNuYxdzoaICsHt/GaTo1UIlpUHHJcH7QaWyHt\n" + "Blqa5TwByZuvUGHyKWvsbRm7LgKUyjZx70U58aUlEA6QvKezW6uv8RmIagkvH9Ak\n" + "qGLt2RpliWUWpVXeM+h6gWZykRdeHSJy97g=\n" "-----END CERTIFICATE-----\n" - ); + ); const std::string mPemChildCert( "Certificate:\n" " Data:\n" " Version: 3 (0x2)\n" - " Serial Number: 4096 (0x1000)\n" + " Serial Number: 9e:8d:34:13:e7:9b:f9:31\n" " Signature Algorithm: sha256WithRSAEncryption\n" - " Issuer: C=US, ST=California, O=Linden Lab, OU=Second Life Engineering, CN=Integration Test Intermediate CA/emailAddress=noreply@lindenlab.com\n" + " Issuer: C=US, ST=California, L=San Francisco, O=Linden Lab, OU=Second Life Engineering, CN=Integration Test Intermediate CA/emailAddress=noreply@lindenlab.com\n" " Validity\n" - " Not Before: May 22 22:58:15 2018 GMT\n" - " Not After : Jul 19 22:58:15 2024 GMT\n" + " Not Before: Jul 23 11:46:39 2024 GMT\n" + " Not After : Jul 21 11:46:39 2034 GMT\n" " Subject: C=US, ST=California, L=San Francisco, O=Linden Lab, OU=Second Life Engineering, CN=Integration Test Server Cert/emailAddress=noreply@lindenlab.com\n" " Subject Public Key Info:\n" " Public Key Algorithm: rsaEncryption\n" - " Public-Key: (2048 bit)\n" + " Public-Key: (4096 bit)\n" " Modulus:\n" - " 00:bf:a1:1c:76:82:4a:10:1d:25:0e:02:e2:7a:64:\n" - " 54:c7:94:c5:c0:98:d5:35:f3:cb:cb:30:ba:31:9c:\n" - " bd:4c:2f:4a:4e:24:03:4b:87:5c:c1:5c:fe:d9:89:\n" - " 3b:cb:01:bc:eb:a5:b7:78:dc:b3:58:e5:78:a7:15:\n" - " 34:50:30:aa:16:3a:b2:94:17:6d:1e:7f:b2:70:1e:\n" - " 96:41:bb:1d:e3:22:80:fa:dc:00:6a:fb:34:3e:67:\n" - " e7:c2:21:2f:1b:d3:af:04:49:91:eb:bb:60:e0:26:\n" - " 52:75:28:8a:08:5b:91:56:4e:51:50:40:51:70:af:\n" - " cb:80:66:c8:59:e9:e2:48:a8:62:d0:26:67:80:0a:\n" - " 12:16:d1:f6:15:9e:1f:f5:92:37:f3:c9:2f:03:9e:\n" - " 22:f6:60:5a:76:45:8c:01:2c:99:54:72:19:db:b7:\n" - " 72:e6:5a:69:f3:e9:31:65:5d:0f:c7:5c:9c:17:29:\n" - " 71:14:7f:db:47:c9:1e:65:a2:41:b0:2f:14:17:ec:\n" - " 4b:25:f2:43:8f:b4:a3:8d:37:1a:07:34:b3:29:bb:\n" - " 8a:44:8e:84:08:a2:1b:76:7a:cb:c2:39:2f:6e:e3:\n" - " fc:d6:91:b5:1f:ce:58:91:57:70:35:6e:25:a9:48:\n" - " 0e:07:cf:4e:dd:16:42:65:cf:8a:42:b3:27:e6:fe:\n" - " 6a:e3\n" + " 00:d8:ac:0c:27:8f:ea:c0:4d:21:e4:75:55:31:57:\n" + " 83:46:47:14:1e:f5:67:ae:98:60:c4:97:6d:e8:53:\n" + " f2:4d:3b:ec:6f:08:bc:1e:c0:e2:a6:75:b5:90:1d:\n" + " 30:a2:59:68:32:10:2b:29:67:fc:99:f1:24:6a:36:\n" + " 73:60:31:6b:c7:a0:b8:b0:38:60:b1:59:23:2c:ab:\n" + " 25:a2:c8:b0:bc:2c:c6:d7:4c:87:37:1b:5e:51:a4:\n" + " 63:3e:c4:6d:ed:da:5e:d3:ad:8a:6d:52:e4:87:38:\n" + " 33:76:cf:f2:86:58:b3:10:a4:91:8d:3d:4f:27:9a:\n" + " 8b:b4:d7:67:90:31:1c:f5:7f:78:af:6f:f2:dd:39:\n" + " d0:16:16:7b:46:ad:88:1b:3b:74:6b:10:29:8b:64:\n" + " ba:ed:9f:a7:69:99:55:8f:73:0d:18:a3:7f:40:20:\n" + " 3a:41:4a:94:39:62:8b:fe:c6:9d:79:d0:cd:1c:e2:\n" + " d4:74:bb:43:75:eb:86:8b:30:c1:8d:cc:14:ab:75:\n" + " 2e:f5:3e:0c:05:cb:e4:c3:92:d8:81:8c:df:a5:4e:\n" + " 2e:0b:ae:17:15:9b:e6:dd:9e:16:46:42:27:92:8a:\n" + " 0e:3a:74:1e:d1:3f:ee:7e:a5:d7:ec:1c:63:d4:96:\n" + " 5b:36:f9:15:ee:da:66:ac:5e:de:91:d9:08:24:fb:\n" + " 5d:fc:9b:77:dd:ff:20:a6:67:6f:48:41:5e:5a:ac:\n" + " 13:a4:2c:2a:f2:a3:15:86:e2:84:33:34:e3:91:27:\n" + " 8b:37:ba:b0:c7:5e:1a:0d:b9:f2:4e:0c:55:e6:bb:\n" + " d9:63:f5:05:7b:aa:19:e5:57:ce:a5:b1:46:4b:b3:\n" + " 04:f6:a0:97:26:ed:48:ed:97:93:a6:75:b1:a3:42:\n" + " fc:cc:57:89:da:44:e9:16:a6:30:2c:01:8e:f2:ed:\n" + " be:45:05:08:8a:af:1e:07:51:89:cf:51:4c:aa:f3:\n" + " b3:f0:6f:db:21:80:11:32:0a:23:e2:ff:cc:59:15:\n" + " eb:ff:d2:b8:d6:a1:c1:b4:96:12:82:bf:3f:68:ad:\n" + " c8:61:50:f8:88:4f:d0:be:8e:29:64:1a:16:a5:d9:\n" + " 29:76:16:cd:70:37:c4:f2:1f:4e:c6:57:36:dd:c1:\n" + " 27:19:72:ef:98:7e:34:25:3f:76:b1:ea:15:b2:38:\n" + " 6e:d3:43:03:7a:2b:78:91:9a:19:26:2a:31:b7:5e:\n" + " b7:22:c4:fd:bf:93:10:a4:23:3f:d7:79:53:28:5d:\n" + " 2e:ba:0c:b0:5e:0a:b4:c4:a1:71:75:88:1b:b2:0e:\n" + " 2c:67:08:7b:f0:f6:37:d3:aa:39:50:03:a3:7c:17:\n" + " 1d:52:52:2a:6b:d0:a2:54:2e:ba:11:bc:26:a9:16:\n" + " a6:1b:79\n" " Exponent: 65537 (0x10001)\n" " X509v3 extensions:\n" - " X509v3 Basic Constraints: \n" + " X509v3 Basic Constraints:\n" " CA:FALSE\n" - " Netscape Cert Type: \n" - " SSL Server\n" - " Netscape Comment: \n" - " OpenSSL Generated Server Certificate\n" - " X509v3 Subject Key Identifier: \n" - " BB:59:9F:DE:6B:51:A7:6C:B3:6D:5B:8B:42:F7:B1:65:77:17:A4:E4\n" - " X509v3 Authority Key Identifier: \n" - " keyid:83:21:DE:EC:C0:79:03:6D:1E:83:F3:E5:97:29:D5:5A:C0:96:40:FA\n" - " DirName:/C=US/ST=California/L=San Francisco/O=Linden Lab/OU=Second Life Engineering/CN=Integration Test Root CA/emailAddress=noreply@lindenlab.com\n" - " serial:10:00\n" - "\n" - " X509v3 Key Usage: critical\n" + " X509v3 Key Usage:\n" " Digital Signature, Key Encipherment\n" - " X509v3 Extended Key Usage: \n" + " X509v3 Extended Key Usage:\n" " TLS Web Server Authentication\n" + " X509v3 Subject Key Identifier:\n" + " 7B:1A:F9:2B:C4:B2:F6:AE:D6:F2:8E:B1:73:FB:DD:11:CA:DB:F8:87\n" + " X509v3 Authority Key Identifier:\n" + " keyid:56:98:DC:45:25:11:E2:8C:2B:EA:D6:C6:E2:C8:BE:2C:C8:69:FF:FF\n" + " DirName:/C=US/ST=California/L=San Francisco/O=Linden Lab/OU=Second Life Engineering/CN=Integration Test Root CA/emailAddress=noreply@lindenlab.com\n" + " serial:85:BB:4B:66:26:DB:9A:C6\n" " Signature Algorithm: sha256WithRSAEncryption\n" - " 18:a6:58:55:9b:d4:af:7d:8a:27:d3:28:3a:4c:4b:42:4e:f0:\n" - " 30:d6:d9:95:11:48:12:0a:96:40:d9:2b:21:39:c5:d4:8d:e5:\n" - " 10:bc:68:78:69:0b:9f:15:4a:0b:f1:ab:99:45:0c:20:5f:27:\n" - " df:e7:14:2d:4a:30:f2:c2:8d:37:73:36:1a:27:55:5a:08:5f:\n" - " 71:a1:5e:05:83:b2:59:fe:02:5e:d7:4a:30:15:23:58:04:cf:\n" - " 48:cc:b0:71:88:9c:6b:57:f0:04:0a:d3:a0:64:6b:ee:f3:5f:\n" - " ea:ac:e1:2b:b9:7f:79:b8:db:ce:72:48:72:db:c8:5c:38:72:\n" - " 31:55:d0:ff:6b:bd:73:23:a7:30:18:5d:ed:47:18:0a:67:8e:\n" - " 53:32:0e:99:9b:96:72:45:7f:c6:00:2c:5d:1a:97:53:75:3a:\n" - " 0b:49:3d:3a:00:37:14:67:0c:28:97:34:87:aa:c5:32:e4:ae:\n" - " 34:83:12:4a:10:f7:0e:74:d4:5f:73:bd:ef:0c:b7:d8:0a:7d:\n" - " 8e:8d:5a:48:bd:f4:8e:7b:f9:4a:15:3b:61:c9:5e:40:59:6e:\n" - " c7:a8:a4:02:28:72:c5:54:8c:77:f4:55:a7:86:c0:38:a0:68:\n" - " 19:da:0f:72:5a:a9:7e:69:9f:9c:3a:d6:66:aa:e1:f4:fd:f9:\n" - " b8:4b:6c:71:9e:f0:38:02:c7:6a:9e:dc:e6:fb:ef:23:59:4f:\n" - " 5c:84:0a:df:ea:86:1f:fd:0e:5c:fa:c4:e5:50:1c:10:cf:89:\n" - " 4e:08:0e:4c:4b:61:1a:49:12:f7:e9:4b:17:71:43:7b:6d:b6:\n" - " b5:9f:d4:3b:c7:88:53:48:63:b6:00:80:8f:49:0a:c5:7e:58:\n" - " ac:78:d8:b9:06:b0:bc:86:e2:2e:48:5b:c3:24:fa:aa:72:d8:\n" - " ec:f6:c7:91:9f:0f:c8:b5:fd:2b:b2:a7:bc:2f:40:20:2b:47:\n" - " e0:d1:1d:94:52:6f:6b:be:12:b6:8c:dc:11:db:71:e6:19:ef:\n" - " a8:71:8b:ad:d3:32:c0:1c:a4:3f:b3:0f:af:e5:50:e1:ff:41:\n" - " a4:b7:6f:57:71:af:fd:16:4c:e8:24:b3:99:1b:cf:12:8f:43:\n" - " 05:80:ba:18:19:0a:a5:ec:49:81:41:4c:7e:28:b2:21:f2:59:\n" - " 6e:4a:ed:de:f9:fa:99:85:60:1f:e6:c2:42:5c:08:00:3c:84:\n" - " 06:a9:24:d4:cf:7b:6e:1b:59:1d:f4:70:16:03:a1:e0:0b:00:\n" - " 95:5c:39:03:fc:9d:1c:8e:f7:59:0c:61:47:f6:7f:07:22:48:\n" - " 83:40:ac:e1:98:5f:c7:be:05:d5:29:2b:bf:0d:03:0e:e9:5e:\n" - " 2b:dd:09:18:fe:5e:30:61\n" + " ad:7c:50:12:24:62:62:83:e9:dd:81:1a:12:1c:6d:ae:1e:a6:\n" + " 01:cc:93:8b:ac:83:7c:3d:57:d7:7f:d2:13:40:82:c7:27:07:\n" + " 31:d8:c4:01:04:64:9c:dc:ae:7b:52:bd:f5:62:7a:d0:7c:13:\n" + " 1a:19:86:6a:ce:9a:ba:69:07:77:75:b6:67:56:d0:c3:8d:6f:\n" + " 59:5f:ac:31:83:32:2c:4f:8c:85:8c:f3:56:5b:e0:83:16:19:\n" + " c9:55:4d:56:2c:e0:06:f8:71:85:4b:7e:c6:20:b3:f6:5b:85:\n" + " 6a:b7:0f:0e:0c:75:38:6a:aa:53:cc:b0:bf:c1:fd:a1:01:8a:\n" + " 7e:5a:0b:4d:51:fc:1b:14:b0:8d:62:17:b7:5d:6a:64:30:80:\n" + " aa:50:9a:23:9e:19:46:11:9d:49:d1:35:81:87:80:8c:9c:71:\n" + " 61:26:07:23:5d:a7:ea:4e:0c:53:77:bd:eb:18:6d:63:8b:2c:\n" + " e1:83:bb:bb:f8:3e:7c:e8:0d:19:1e:be:35:aa:99:0f:c7:25:\n" + " 0c:a8:f9:74:02:c8:4c:8e:bb:13:18:fd:aa:21:34:bc:2d:9f:\n" + " 10:96:e2:99:e3:9a:d7:91:0e:1e:77:20:70:e9:b4:63:25:f8:\n" + " ea:14:1f:24:b0:6a:8b:2a:f4:61:b1:0d:7d:18:bc:1d:6d:04:\n" + " 11:b2:9f:a2:a7:55:be:2b:2c:2f:c1:d8:95:13:73:af:1c:96:\n" + " 49:30:9c:9c:94:81:6c:9b:a7:87:5c:cf:46:95:95:4a:6f:bf:\n" + " df:c9:3d:74:3e:24:6e:44:1e:14:8b:68:23:e4:00:b5:a5:b7:\n" + " 5b:a9:ea:16:5f:fa:b1:d3:1a:b1:9b:36:ef:a4:7a:6f:a3:b0:\n" + " 97:35:ac:70:c0:cc:8e:a2:d3:40:0e:c1:70:0b:d5:ce:cd:51:\n" + " 82:8a:40:72:04:8d:62:af:ba:a8:e7:a8:e9:b9:99:b7:5c:5d:\n" + " 27:96:b2:3d:f9:0d:26:8c:3f:db:ac:86:97:be:f1:2c:0b:ca:\n" + " 90:07:93:96:f4:75:c3:e8:4c:f6:a8:a2:3f:da:11:21:e7:b1:\n" + " 8c:62:36:ae:91:a9:2a:73:ba:67:f5:24:16:c3:ee:b7:b1:b4:\n" + " e3:8a:28:23:84:cf:38:c6:f0:8e:21:f6:b8:76:9a:6d:d1:e3:\n" + " 74:81:7a:22:20:a0:82:2a:31:8a:ba:44:0b:61:5a:aa:ba:c6:\n" + " 07:99:36:0a:24:06:2f:8e:c1:1c:4b:f0:65:72:fb:e9:b5:31:\n" + " 59:13:2c:c6:f8:5b:91:e2:d8:96:f3:1a:06:0b:2a:62:12:4d:\n" + " 5e:65:c9:e9:e4:00:99:a6:d3:60:1f:c3:d6:cc:a6:9b:a5:14:\n" + " 1b:4d:db:e7:3d:52:7e:2c\n" "-----BEGIN CERTIFICATE-----\n" - "MIIGbjCCBFagAwIBAgICEAAwDQYJKoZIhvcNAQELBQAwgaoxCzAJBgNVBAYTAlVT\n" - "MRMwEQYDVQQIDApDYWxpZm9ybmlhMRMwEQYDVQQKDApMaW5kZW4gTGFiMSAwHgYD\n" - "VQQLDBdTZWNvbmQgTGlmZSBFbmdpbmVlcmluZzEpMCcGA1UEAwwgSW50ZWdyYXRp\n" - "b24gVGVzdCBJbnRlcm1lZGlhdGUgQ0ExJDAiBgkqhkiG9w0BCQEWFW5vcmVwbHlA\n" - "bGluZGVubGFiLmNvbTAeFw0xODA1MjIyMjU4MTVaFw0yNDA3MTkyMjU4MTVaMIG+\n" - "MQswCQYDVQQGEwJVUzETMBEGA1UECAwKQ2FsaWZvcm5pYTEWMBQGA1UEBwwNU2Fu\n" - "IEZyYW5jaXNjbzETMBEGA1UECgwKTGluZGVuIExhYjEgMB4GA1UECwwXU2Vjb25k\n" - "IExpZmUgRW5naW5lZXJpbmcxJTAjBgNVBAMMHEludGVncmF0aW9uIFRlc3QgU2Vy\n" - "dmVyIENlcnQxJDAiBgkqhkiG9w0BCQEWFW5vcmVwbHlAbGluZGVubGFiLmNvbTCC\n" - "ASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAL+hHHaCShAdJQ4C4npkVMeU\n" - "xcCY1TXzy8swujGcvUwvSk4kA0uHXMFc/tmJO8sBvOult3jcs1jleKcVNFAwqhY6\n" - "spQXbR5/snAelkG7HeMigPrcAGr7ND5n58IhLxvTrwRJkeu7YOAmUnUoighbkVZO\n" - "UVBAUXCvy4BmyFnp4kioYtAmZ4AKEhbR9hWeH/WSN/PJLwOeIvZgWnZFjAEsmVRy\n" - "Gdu3cuZaafPpMWVdD8dcnBcpcRR/20fJHmWiQbAvFBfsSyXyQ4+0o403Ggc0sym7\n" - "ikSOhAiiG3Z6y8I5L27j/NaRtR/OWJFXcDVuJalIDgfPTt0WQmXPikKzJ+b+auMC\n" - "AwEAAaOCAYYwggGCMAkGA1UdEwQCMAAwEQYJYIZIAYb4QgEBBAQDAgZAMDMGCWCG\n" - "SAGG+EIBDQQmFiRPcGVuU1NMIEdlbmVyYXRlZCBTZXJ2ZXIgQ2VydGlmaWNhdGUw\n" - "HQYDVR0OBBYEFLtZn95rUadss21bi0L3sWV3F6TkMIHoBgNVHSMEgeAwgd2AFIMh\n" - "3uzAeQNtHoPz5Zcp1VrAlkD6oYHApIG9MIG6MQswCQYDVQQGEwJVUzETMBEGA1UE\n" - "CAwKQ2FsaWZvcm5pYTEWMBQGA1UEBwwNU2FuIEZyYW5jaXNjbzETMBEGA1UECgwK\n" - "TGluZGVuIExhYjEgMB4GA1UECwwXU2Vjb25kIExpZmUgRW5naW5lZXJpbmcxITAf\n" - "BgNVBAMMGEludGVncmF0aW9uIFRlc3QgUm9vdCBDQTEkMCIGCSqGSIb3DQEJARYV\n" - "bm9yZXBseUBsaW5kZW5sYWIuY29tggIQADAOBgNVHQ8BAf8EBAMCBaAwEwYDVR0l\n" - "BAwwCgYIKwYBBQUHAwEwDQYJKoZIhvcNAQELBQADggIBABimWFWb1K99iifTKDpM\n" - "S0JO8DDW2ZURSBIKlkDZKyE5xdSN5RC8aHhpC58VSgvxq5lFDCBfJ9/nFC1KMPLC\n" - "jTdzNhonVVoIX3GhXgWDsln+Al7XSjAVI1gEz0jMsHGInGtX8AQK06Bka+7zX+qs\n" - "4Su5f3m4285ySHLbyFw4cjFV0P9rvXMjpzAYXe1HGApnjlMyDpmblnJFf8YALF0a\n" - "l1N1OgtJPToANxRnDCiXNIeqxTLkrjSDEkoQ9w501F9zve8Mt9gKfY6NWki99I57\n" - "+UoVO2HJXkBZbseopAIocsVUjHf0VaeGwDigaBnaD3JaqX5pn5w61maq4fT9+bhL\n" - "bHGe8DgCx2qe3Ob77yNZT1yECt/qhh/9Dlz6xOVQHBDPiU4IDkxLYRpJEvfpSxdx\n" - "Q3tttrWf1DvHiFNIY7YAgI9JCsV+WKx42LkGsLyG4i5IW8Mk+qpy2Oz2x5GfD8i1\n" - "/Suyp7wvQCArR+DRHZRSb2u+EraM3BHbceYZ76hxi63TMsAcpD+zD6/lUOH/QaS3\n" - "b1dxr/0WTOgks5kbzxKPQwWAuhgZCqXsSYFBTH4osiHyWW5K7d75+pmFYB/mwkJc\n" - "CAA8hAapJNTPe24bWR30cBYDoeALAJVcOQP8nRyO91kMYUf2fwciSINArOGYX8e+\n" - "BdUpK78NAw7pXivdCRj+XjBh\n" + "MIIHSTCCBTGgAwIBAgIJAJ6NNBPnm/kxMA0GCSqGSIb3DQEBCwUAMIHCMQswCQYD\n" + "VQQGEwJVUzETMBEGA1UECAwKQ2FsaWZvcm5pYTEWMBQGA1UEBwwNU2FuIEZyYW5j\n" + "aXNjbzETMBEGA1UECgwKTGluZGVuIExhYjEgMB4GA1UECwwXU2Vjb25kIExpZmUg\n" + "RW5naW5lZXJpbmcxKTAnBgNVBAMMIEludGVncmF0aW9uIFRlc3QgSW50ZXJtZWRp\n" + "YXRlIENBMSQwIgYJKoZIhvcNAQkBFhVub3JlcGx5QGxpbmRlbmxhYi5jb20wHhcN\n" + "MjQwNzIzMTE0NjM5WhcNMzQwNzIxMTE0NjM5WjCBvjELMAkGA1UEBhMCVVMxEzAR\n" + "BgNVBAgMCkNhbGlmb3JuaWExFjAUBgNVBAcMDVNhbiBGcmFuY2lzY28xEzARBgNV\n" + "BAoMCkxpbmRlbiBMYWIxIDAeBgNVBAsMF1NlY29uZCBMaWZlIEVuZ2luZWVyaW5n\n" + "MSUwIwYDVQQDDBxJbnRlZ3JhdGlvbiBUZXN0IFNlcnZlciBDZXJ0MSQwIgYJKoZI\n" + "hvcNAQkBFhVub3JlcGx5QGxpbmRlbmxhYi5jb20wggIiMA0GCSqGSIb3DQEBAQUA\n" + "A4ICDwAwggIKAoICAQDYrAwnj+rATSHkdVUxV4NGRxQe9WeumGDEl23oU/JNO+xv\n" + "CLwewOKmdbWQHTCiWWgyECspZ/yZ8SRqNnNgMWvHoLiwOGCxWSMsqyWiyLC8LMbX\n" + "TIc3G15RpGM+xG3t2l7TrYptUuSHODN2z/KGWLMQpJGNPU8nmou012eQMRz1f3iv\n" + "b/LdOdAWFntGrYgbO3RrECmLZLrtn6dpmVWPcw0Yo39AIDpBSpQ5Yov+xp150M0c\n" + "4tR0u0N164aLMMGNzBSrdS71PgwFy+TDktiBjN+lTi4LrhcVm+bdnhZGQieSig46\n" + "dB7RP+5+pdfsHGPUlls2+RXu2masXt6R2Qgk+138m3fd/yCmZ29IQV5arBOkLCry\n" + "oxWG4oQzNOORJ4s3urDHXhoNufJODFXmu9lj9QV7qhnlV86lsUZLswT2oJcm7Ujt\n" + "l5OmdbGjQvzMV4naROkWpjAsAY7y7b5FBQiKrx4HUYnPUUyq87Pwb9shgBEyCiPi\n" + "/8xZFev/0rjWocG0lhKCvz9orchhUPiIT9C+jilkGhal2Sl2Fs1wN8TyH07GVzbd\n" + "wScZcu+YfjQlP3ax6hWyOG7TQwN6K3iRmhkmKjG3XrcixP2/kxCkIz/XeVMoXS66\n" + "DLBeCrTEoXF1iBuyDixnCHvw9jfTqjlQA6N8Fx1SUipr0KJULroRvCapFqYbeQID\n" + "AQABo4IBQjCCAT4wCQYDVR0TBAIwADALBgNVHQ8EBAMCBaAwEwYDVR0lBAwwCgYI\n" + "KwYBBQUHAwEwHQYDVR0OBBYEFHsa+SvEsvau1vKOsXP73RHK2/iHMIHvBgNVHSME\n" + "gecwgeSAFFaY3EUlEeKMK+rWxuLIvizIaf//oYHApIG9MIG6MQswCQYDVQQGEwJV\n" + "UzETMBEGA1UECAwKQ2FsaWZvcm5pYTEWMBQGA1UEBwwNU2FuIEZyYW5jaXNjbzET\n" + "MBEGA1UECgwKTGluZGVuIExhYjEgMB4GA1UECwwXU2Vjb25kIExpZmUgRW5naW5l\n" + "ZXJpbmcxITAfBgNVBAMMGEludGVncmF0aW9uIFRlc3QgUm9vdCBDQTEkMCIGCSqG\n" + "SIb3DQEJARYVbm9yZXBseUBsaW5kZW5sYWIuY29tggkAhbtLZibbmsYwDQYJKoZI\n" + "hvcNAQELBQADggIBAK18UBIkYmKD6d2BGhIcba4epgHMk4usg3w9V9d/0hNAgscn\n" + "BzHYxAEEZJzcrntSvfVietB8ExoZhmrOmrppB3d1tmdW0MONb1lfrDGDMixPjIWM\n" + "81Zb4IMWGclVTVYs4Ab4cYVLfsYgs/ZbhWq3Dw4MdThqqlPMsL/B/aEBin5aC01R\n" + "/BsUsI1iF7ddamQwgKpQmiOeGUYRnUnRNYGHgIyccWEmByNdp+pODFN3vesYbWOL\n" + "LOGDu7v4PnzoDRkevjWqmQ/HJQyo+XQCyEyOuxMY/aohNLwtnxCW4pnjmteRDh53\n" + "IHDptGMl+OoUHySwaosq9GGxDX0YvB1tBBGyn6KnVb4rLC/B2JUTc68clkkwnJyU\n" + "gWybp4dcz0aVlUpvv9/JPXQ+JG5EHhSLaCPkALWlt1up6hZf+rHTGrGbNu+kem+j\n" + "sJc1rHDAzI6i00AOwXAL1c7NUYKKQHIEjWKvuqjnqOm5mbdcXSeWsj35DSaMP9us\n" + "hpe+8SwLypAHk5b0dcPoTPaooj/aESHnsYxiNq6RqSpzumf1JBbD7rextOOKKCOE\n" + "zzjG8I4h9rh2mm3R43SBeiIgoIIqMYq6RAthWqq6xgeZNgokBi+OwRxL8GVy++m1\n" + "MVkTLMb4W5Hi2JbzGgYLKmISTV5lyenkAJmm02Afw9bMppulFBtN2+c9Un4s\n" "-----END CERTIFICATE-----\n" - ); + ); + // Test wrapper declaration : wrapping nothing for the moment struct sechandler_basic_test @@ -701,14 +722,13 @@ namespace tut //std::ostringstream llsd_value; //llsd_value << LLSDOStreamer(llsd_cert) << std::endl; LL_DEBUGS() << "test 1 cert " << llsd_cert << LL_ENDL; - ensure_equals("Issuer Name/commonName", (std::string)llsd_cert["issuer_name"]["commonName"], "Integration Test Intermediate CA"); ensure_equals("Issuer Name/countryName", (std::string)llsd_cert["issuer_name"]["countryName"], "US"); ensure_equals("Issuer Name/state", (std::string)llsd_cert["issuer_name"]["stateOrProvinceName"], "California"); ensure_equals("Issuer Name/org name", (std::string)llsd_cert["issuer_name"]["organizationName"], "Linden Lab"); ensure_equals("Issuer Name/org unit", (std::string)llsd_cert["issuer_name"]["organizationalUnitName"], "Second Life Engineering"); ensure_equals("Issuer name string", (std::string)llsd_cert["issuer_name_string"], - "emailAddress=noreply@lindenlab.com,CN=Integration Test Intermediate CA,OU=Second Life Engineering,O=Linden Lab,ST=California,C=US"); + "emailAddress=noreply@lindenlab.com,CN=Integration Test Intermediate CA,OU=Second Life Engineering,O=Linden Lab,L=San Francisco,ST=California,C=US"); ensure_equals("subject Name/commonName", (std::string)llsd_cert["subject_name"]["commonName"], "Integration Test Server Cert"); ensure_equals("subject Name/countryName", (std::string)llsd_cert["subject_name"]["countryName"], "US"); @@ -721,9 +741,9 @@ namespace tut ensure_equals("subject name string", (std::string)llsd_cert["subject_name_string"], "emailAddress=noreply@lindenlab.com,CN=Integration Test Server Cert,OU=Second Life Engineering,O=Linden Lab,L=San Francisco,ST=California,C=US"); - ensure_equals("serial number", (std::string)llsd_cert["serial_number"], "1000"); - ensure_equals("valid from", (std::string)llsd_cert["valid_from"], "2018-05-22T22:58:15Z"); - ensure_equals("valid to", (std::string)llsd_cert["valid_to"], "2024-07-19T22:58:15Z"); + ensure_equals("serial number", (std::string)llsd_cert["serial_number"], "9E8D3413E79BF931"); + ensure_equals("valid from", (std::string)llsd_cert["valid_from"], "2024-07-23T11:46:39Z"); + ensure_equals("valid to", (std::string)llsd_cert["valid_to"], "2034-07-21T11:46:39Z"); LLSD expectedKeyUsage = LLSD::emptyArray(); expectedKeyUsage.append(LLSD((std::string)"digitalSignature")); expectedKeyUsage.append(LLSD((std::string)"keyEncipherment")); @@ -1042,7 +1062,7 @@ namespace tut //validate find LLSD find_info = LLSD::emptyMap(); - find_info["subjectKeyIdentifier"] = "bb:59:9f:de:6b:51:a7:6c:b3:6d:5b:8b:42:f7:b1:65:77:17:a4:e4"; + find_info["subjectKeyIdentifier"] = "7b:1a:f9:2b:c4:b2:f6:ae:d6:f2:8e:b1:73:fb:dd:11:ca:db:f8:87"; LLBasicCertificateVector::iterator found_cert = test_vector->find(find_info); ensure("found some cert", found_cert != test_vector->end()); X509* found_x509 = (*found_cert).get()->getOpenSSLX509(); @@ -1225,7 +1245,7 @@ namespace tut X509_STORE_CTX_set0_untrusted(test_store, NULL); test_chain = new LLBasicCertificateChain(test_store); X509_STORE_CTX_free(test_store); - ensure_equals("two elements in store", test_chain->size(), 1); + ensure_equals("two elements in store [1]", test_chain->size(), 1); X509* test_cert = (*test_chain)[0]->getOpenSSLX509(); ensure("validate first element in store is expected cert", !X509_cmp(test_cert, mX509ChildCert)); X509_free(test_cert); @@ -1238,7 +1258,7 @@ namespace tut sk_X509_push(X509_STORE_CTX_get0_untrusted(test_store), mX509IntermediateCert); test_chain = new LLBasicCertificateChain(test_store); X509_STORE_CTX_free(test_store); - ensure_equals("two elements in store", test_chain->size(), 2); + ensure_equals("two elements in store [2]", test_chain->size(), 2); test_cert = (*test_chain)[0]->getOpenSSLX509(); ensure("validate first element in store is expected cert", !X509_cmp(test_cert, mX509ChildCert)); X509_free(test_cert); @@ -1254,7 +1274,7 @@ namespace tut sk_X509_push(X509_STORE_CTX_get0_untrusted(test_store), mX509TestCert); test_chain = new LLBasicCertificateChain(test_store); X509_STORE_CTX_free(test_store); - ensure_equals("two elements in store", test_chain->size(), 1); + ensure_equals("two elements in store [3]", test_chain->size(), 1); test_cert = (*test_chain)[0]->getOpenSSLX509(); ensure("validate first element in store is expected cert", !X509_cmp(test_cert, mX509ChildCert)); X509_free(test_cert); @@ -1267,7 +1287,7 @@ namespace tut sk_X509_push(X509_STORE_CTX_get0_untrusted(test_store), mX509TestCert); test_chain = new LLBasicCertificateChain(test_store); X509_STORE_CTX_free(test_store); - ensure_equals("two elements in store", test_chain->size(), 2); + ensure_equals("two elements in store [4]", test_chain->size(), 2); test_cert = (*test_chain)[0]->getOpenSSLX509(); ensure("validate first element in store is expected cert", !X509_cmp(test_cert, mX509ChildCert)); X509_free(test_cert); -- cgit v1.2.3 From ffeb738afda99fca98a9c1ba7704d7f6144da70c Mon Sep 17 00:00:00 2001 From: nat-goodspeed Date: Wed, 31 Jul 2024 06:30:19 -0400 Subject: Represent the many "LLAgent" "setCameraParams" args in an array. This encapsulates the boilerplate associated with passing each distinct parameter to its corresponding LLFollowCamMgr method. --- indra/newview/llagentlistener.cpp | 106 +++++++++++----------- indra/newview/scripts/lua/test_camera_control.lua | 2 +- 2 files changed, 55 insertions(+), 53 deletions(-) diff --git a/indra/newview/llagentlistener.cpp b/indra/newview/llagentlistener.cpp index 045367dbdd..80460666a5 100644 --- a/indra/newview/llagentlistener.cpp +++ b/indra/newview/llagentlistener.cpp @@ -45,6 +45,7 @@ #include "lltoolgrab.h" #include "llhudeffectlookat.h" #include "llagentcamera.h" +#include LLAgentListener::LLAgentListener(LLAgent &agent) : LLEventAPI("LLAgent", @@ -522,62 +523,63 @@ void LLAgentListener::getGroups(const LLSD& event) const sendReply(LLSDMap("groups", reply), event); } +/*----------------------------- camera control -----------------------------*/ +// specialize LLSDParam to support (const LLVector3&) arguments -- this +// wouldn't even be necessary except that the relevant LLVector3 constructor +// is explicitly explicit +template <> +class LLSDParam: public LLSDParamBase +{ +public: + LLSDParam(const LLSD& value): value(LLVector3(value)) {} + + operator const LLVector3&() const { return value; } + +private: + LLVector3 value; +}; + +// accept any of a number of similar LLFollowCamMgr methods with different +// argument types, and return a wrapper lambda that accepts LLSD and converts +// to the target argument type +template +auto wrap(void (LLFollowCamMgr::*method)(const LLUUID& source, T arg)) +{ + return [method](LLFollowCamMgr& followcam, const LLUUID& source, const LLSD& arg) + { (followcam.*method)(source, LLSDParam(arg)); }; +} + +// table of supported LLFollowCamMgr methods, +// with the corresponding setFollowCamParams() argument keys +static std::pair> +cam_params[] = +{ + { "camera_pos", wrap(&LLFollowCamMgr::setPosition) }, + { "focus_pos", wrap(&LLFollowCamMgr::setFocus) }, + { "focus_offset", wrap(&LLFollowCamMgr::setFocusOffset) }, + { "camera_locked", wrap(&LLFollowCamMgr::setPositionLocked) }, + { "focus_locked", wrap(&LLFollowCamMgr::setFocusLocked) }, + { "distance", wrap(&LLFollowCamMgr::setDistance) }, + { "focus_threshold", wrap(&LLFollowCamMgr::setFocusThreshold) }, + { "camera_threshold", wrap(&LLFollowCamMgr::setPositionThreshold) }, + { "focus_lag", wrap(&LLFollowCamMgr::setFocusLag) }, + { "camera_lag", wrap(&LLFollowCamMgr::setPositionLag) }, + { "camera_pitch", wrap(&LLFollowCamMgr::setPitch) }, + { "behindness_lag", wrap(&LLFollowCamMgr::setBehindnessLag) }, + { "behindness_angle", wrap(&LLFollowCamMgr::setBehindnessAngle) }, +}; + void LLAgentListener::setFollowCamParams(const LLSD& event) const { - if (event.has("camera_pos")) + auto& followcam{ LLFollowCamMgr::instance() }; + for (const auto& pair : cam_params) { - LLFollowCamMgr::getInstance()->setPosition(gAgentID, LLVector3(event["camera_pos"])); - } - if (event.has("focus_pos")) - { - LLFollowCamMgr::getInstance()->setFocus(gAgentID, LLVector3(event["focus_pos"])); - } - if (event.has("focus_offset")) - { - LLFollowCamMgr::getInstance()->setFocusOffset(gAgentID, LLVector3(event["focus_offset"])); - } - if (event.has("camera_locked")) - { - LLFollowCamMgr::getInstance()->setPositionLocked(gAgentID, event["camera_locked"]); - } - if (event.has("focus_locked")) - { - LLFollowCamMgr::getInstance()->setFocusLocked(gAgentID, event["focus_locked"]); - } - if (event.has("distance")) - { - LLFollowCamMgr::getInstance()->setDistance(gAgentID, event["distance"].asReal()); - } - if (event.has("focus_threshold")) - { - LLFollowCamMgr::getInstance()->setFocusThreshold(gAgentID, event["focus_threshold"].asReal()); - } - if (event.has("camera_threshold")) - { - LLFollowCamMgr::getInstance()->setPositionThreshold(gAgentID, event["camera_threshold"].asReal()); - } - if (event.has("focus_lag")) - { - LLFollowCamMgr::getInstance()->setFocusLag(gAgentID, event["focus_lag"].asReal()); - } - if (event.has("camera_lag")) - { - LLFollowCamMgr::getInstance()->setPositionLag(gAgentID, event["camera_lag"].asReal()); - } - if (event.has("camera_pitch")) - { - LLFollowCamMgr::getInstance()->setPitch(gAgentID, event["camera_pitch"].asReal()); - } - if (event.has("behindness_lag")) - { - LLFollowCamMgr::getInstance()->setBehindnessLag(gAgentID, event["behindness_lag"].asReal()); - } - if (event.has("behindness_angle")) - { - LLFollowCamMgr::getInstance()->setBehindnessAngle(gAgentID, event["behindness_angle"].asReal()); + if (event.has(pair.first)) + { + pair.second(followcam, gAgentID, event[pair.first]); + } } - - LLFollowCamMgr::getInstance()->setCameraActive(gAgentID, true); + followcam.setCameraActive(gAgentID, true); } void LLAgentListener::setFollowCamActive(LLSD const & event) const diff --git a/indra/newview/scripts/lua/test_camera_control.lua b/indra/newview/scripts/lua/test_camera_control.lua index e7ad69b473..9b35cdf8cd 100644 --- a/indra/newview/scripts/lua/test_camera_control.lua +++ b/indra/newview/scripts/lua/test_camera_control.lua @@ -45,5 +45,5 @@ function flt:commit_reset_btn(event_data) LLAgent.setFollowCamActive(false) end -startup.wait('STATE_LOGIN_WAIT') +startup.wait('STATE_STARTED') flt:show() -- cgit v1.2.3 From 3214c7bd7e77fdd458d64ec101a8a67287b59ffa Mon Sep 17 00:00:00 2001 From: Nat Goodspeed Date: Thu, 1 Aug 2024 16:09:11 -0400 Subject: Add lua_push(), lua_to(), lua_[gs]etfieldv(), lua_raw[gs]etfield(). Leverage C++ overloads to allow use of generic function names disambiguated by argument type. This allows using templates for certain common operation sequences. --- indra/llcommon/lua_function.cpp | 17 ++--- indra/llcommon/lua_function.h | 162 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 168 insertions(+), 11 deletions(-) diff --git a/indra/llcommon/lua_function.cpp b/indra/llcommon/lua_function.cpp index 42bba80ed5..b173d17ede 100644 --- a/indra/llcommon/lua_function.cpp +++ b/indra/llcommon/lua_function.cpp @@ -32,6 +32,8 @@ #include "lualistener.h" #include "stringize.h" +using namespace std::literals; // e.g. std::string_view literals: "this"sv + const S32 INTERRUPTS_MAX_LIMIT = 20000; const S32 INTERRUPTS_SUSPEND_LIMIT = 100; @@ -41,7 +43,7 @@ const S32 INTERRUPTS_SUSPEND_LIMIT = 100; int DistinctInt::mValues{0}; /***************************************************************************** -* luau namespace +* lluau namespace *****************************************************************************/ namespace { @@ -93,21 +95,14 @@ fsyspath lluau::source_path(lua_State* L) void lluau::set_interrupts_counter(lua_State *L, S32 counter) { - luaL_checkstack(L, 2, nullptr); - lua_pushstring(L, "_INTERRUPTS"); - lua_pushinteger(L, counter); - lua_rawset(L, LUA_REGISTRYINDEX); + lua_rawsetfield(L, LUA_REGISTRYINDEX, "_INTERRUPTS"sv, lua_Integer(counter)); } void lluau::check_interrupts_counter(lua_State* L) { - luaL_checkstack(L, 1, nullptr); - lua_pushstring(L, "_INTERRUPTS"); - lua_rawget(L, LUA_REGISTRYINDEX); - S32 counter = lua_tointeger(L, -1); - lua_pop(L, 1); + auto counter = lua_rawgetfield(L, LUA_REGISTRYINDEX, "_INTERRUPTS"sv); - lluau::set_interrupts_counter(L, ++counter); + set_interrupts_counter(L, ++counter); if (counter > INTERRUPTS_MAX_LIMIT) { lluau::error(L, "Possible infinite loop, terminated."); diff --git a/indra/llcommon/lua_function.h b/indra/llcommon/lua_function.h index 7f7a7566f3..7b59af30f5 100644 --- a/indra/llcommon/lua_function.h +++ b/indra/llcommon/lua_function.h @@ -18,6 +18,7 @@ #include "luau/lualib.h" #include "fsyspath.h" #include "llerror.h" +#include "llsd.h" #include "stringize.h" #include // std::uncaught_exceptions() #include // std::shared_ptr @@ -168,6 +169,167 @@ private: int mIndex; }; +/***************************************************************************** +* lua_push() wrappers for generic code +*****************************************************************************/ +inline +void lua_push(lua_State* L, bool b) +{ + lua_pushboolean(L, int(b)); +} + +inline +void lua_push(lua_State* L, lua_CFunction fn) +{ + lua_pushcfunction(L, fn, ""); +} + +inline +void lua_push(lua_State* L, lua_Integer n) +{ + lua_pushinteger(L, n); +} + +inline +void lua_push(lua_State* L, void* p) +{ + lua_pushlightuserdata(L, p); +} + +inline +void lua_push(lua_State* L, const LLSD& data) +{ + lua_pushllsd(L, data); +} + +inline +void lua_push(lua_State* L, const char* s, size_t len) +{ + lua_pushlstring(L, s, len); +} + +inline +void lua_push(lua_State* L) +{ + lua_pushnil(L); +} + +inline +void lua_push(lua_State* L, lua_Number n) +{ + lua_pushnumber(L, n); +} + +inline +void lua_push(lua_State* L, const std::string& s) +{ + lua_pushstdstring(L, s); +} + +inline +void lua_push(lua_State* L, const char* s) +{ + lua_pushstring(L, s); +} + +/***************************************************************************** +* lua_to() wrappers for generic code +*****************************************************************************/ +template +auto lua_to(lua_State* L, int index); + +template <> +inline +auto lua_to(lua_State* L, int index) +{ + return lua_toboolean(L, index); +} + +template <> +inline +auto lua_to(lua_State* L, int index) +{ + return lua_tocfunction(L, index); +} + +template <> +inline +auto lua_to(lua_State* L, int index) +{ + return lua_tointeger(L, index); +} + +template <> +inline +auto lua_to(lua_State* L, int index) +{ + return lua_tollsd(L, index); +} + +template <> +inline +auto lua_to(lua_State* L, int index) +{ + return lua_tonumber(L, index); +} + +template <> +inline +auto lua_to(lua_State* L, int index) +{ + return lua_tostdstring(L, index); +} + +template <> +inline +auto lua_to(lua_State* L, int index) +{ + return lua_touserdata(L, index); +} + +/***************************************************************************** +* field operations +*****************************************************************************/ +// return to C++, from table at index, the value of field k +template +auto lua_getfieldv(lua_State* L, int index, const char* k) +{ + luaL_checkstack(L, 1, nullptr); + lua_getfield(L, index, k); + LuaPopper pop(L, 1); + return lua_to(L, -1); +} + +// set in table at index, as field k, the specified C++ value +template +auto lua_setfieldv(lua_State* L, int index, const char* k, const T& value) +{ + luaL_checkstack(L, 1, nullptr); + lua_push(L, value); + lua_setfield(L, index, k); +} + +// return to C++, from table at index, the value of field k (without metamethods) +template +auto lua_rawgetfield(lua_State* L, int index, const std::string_view& k) +{ + luaL_checkstack(L, 1, nullptr); + lua_pushlstring(L, k.data(), k.length()); + lua_rawget(L, index); + LuaPopper pop(L, 1); + return lua_to(L, -1); +} + +// set in table at index, as field k, the specified C++ value (without metamethods) +template +void lua_rawsetfield(lua_State* L, int index, const std::string_view& k, const T& value) +{ + luaL_checkstack(L, 2, nullptr); + lua_pushlstring(L, k.data(), k.length()); + lua_push(L, value); + lua_rawset(L, index); +} + /***************************************************************************** * lua_function (and helper class LuaFunction) *****************************************************************************/ -- cgit v1.2.3 From fdb7207aa0d1f25ed3e14fbc4e8615e8383e508c Mon Sep 17 00:00:00 2001 From: Nat Goodspeed Date: Thu, 1 Aug 2024 16:52:44 -0400 Subject: Add UI.callables() and corresponding entry point. --- indra/newview/lluilistener.cpp | 43 ++++++++++++++++++++++++++++ indra/newview/lluilistener.h | 1 + indra/newview/scripts/lua/require/UI.lua | 6 ++++ indra/newview/scripts/lua/test_callables.lua | 6 ++++ 4 files changed, 56 insertions(+) create mode 100644 indra/newview/scripts/lua/test_callables.lua diff --git a/indra/newview/lluilistener.cpp b/indra/newview/lluilistener.cpp index c73c93859a..2b2187a73b 100644 --- a/indra/newview/lluilistener.cpp +++ b/indra/newview/lluilistener.cpp @@ -55,6 +55,13 @@ LLUIListener::LLUIListener(): &LLUIListener::call, llsd::map("function", LLSD(), "reply", LLSD())); + add("callables", + "Return a list [\"callables\"] of dicts {name, access} of functions registered to\n" + "invoke with \"call\".\n" + "access has values \"allow\", \"block\" or \"throttle\".", + &LLUIListener::callables, + llsd::map("reply", LLSD::String())); + add("getValue", "For the UI control identified by the path in [\"path\"], return the control's\n" "current value as [\"value\"] reply.", @@ -131,6 +138,42 @@ void LLUIListener::call(const LLSD& event) (info->callback_func)(NULL, event["parameter"]); } +void LLUIListener::callables(const LLSD& event) const +{ + Response response(LLSD(), event); + + using Registry = LLUICtrl::CommitCallbackRegistry; + using Method = Registry::Registrar& (*)(); + static Method registrars[] = + { + &Registry::defaultRegistrar, + &Registry::currentRegistrar, + }; + LLSD list; + for (auto method : registrars) + { + auto& registrar{ (*method)() }; + for (auto it = registrar.beginItems(), end = registrar.endItems(); it != end; ++it) + { + LLSD entry{ llsd::map("name", it->first) }; + switch (it->second.handle_untrusted) + { + case LLUICtrl::CommitCallbackInfo::UNTRUSTED_ALLOW: + entry["access"] = "allow"; + break; + case LLUICtrl::CommitCallbackInfo::UNTRUSTED_BLOCK: + entry["access"] = "block"; + break; + case LLUICtrl::CommitCallbackInfo::UNTRUSTED_THROTTLE: + entry["access"] = "throttle"; + break; + } + list.append(entry); + } + } + response["callables"] = list; +} + void LLUIListener::getValue(const LLSD&event) const { Response response(LLSD(), event); diff --git a/indra/newview/lluilistener.h b/indra/newview/lluilistener.h index cbb5014300..c90253ab59 100644 --- a/indra/newview/lluilistener.h +++ b/indra/newview/lluilistener.h @@ -41,6 +41,7 @@ public: private: void call(const LLSD& event); + void callables(const LLSD& event) const; void getValue(const LLSD&event) const; void addMenu(const LLSD&event) const; diff --git a/indra/newview/scripts/lua/require/UI.lua b/indra/newview/scripts/lua/require/UI.lua index 28488ff3e1..06b49c6269 100644 --- a/indra/newview/scripts/lua/require/UI.lua +++ b/indra/newview/scripts/lua/require/UI.lua @@ -14,6 +14,10 @@ function UI.call(func, parameter) leap.request('UI', {op='call', ['function']=func, parameter=parameter}) end +function UI.callables() + return leap.request('UI', {op='callables'}).callables +end + function UI.getValue(path) return leap.request('UI', {op='getValue', path=path})['value'] end @@ -152,6 +156,7 @@ function UI.addMenuBranch(...) return leap.request('UI', args) end +-- see UI.callables() for valid values of 'func' function UI.addMenuItem(...) local args = mapargs('name,label,parent_menu,func,param', ...) args.op = 'addMenuItem' @@ -163,4 +168,5 @@ function UI.addMenuSeparator(...) args.op = 'addMenuSeparator' return leap.request('UI', args) end + return UI diff --git a/indra/newview/scripts/lua/test_callables.lua b/indra/newview/scripts/lua/test_callables.lua new file mode 100644 index 0000000000..1bee062db8 --- /dev/null +++ b/indra/newview/scripts/lua/test_callables.lua @@ -0,0 +1,6 @@ +startup=require 'startup' +UI=require 'UI' +startup.wait('STATE_LOGIN_WAIT') +for _, cbl in pairs(UI.callables()) do + print(`{cbl.name} ({cbl.access})`) +end -- cgit v1.2.3 From c8ba633a4564da29d9b58b0e08ffb590bb3bce5f Mon Sep 17 00:00:00 2001 From: Nat Goodspeed Date: Fri, 2 Aug 2024 10:12:57 -0400 Subject: Add 'UI' 'getParents' op to list top-menu 'parent_menu' names. --- indra/newview/lluilistener.cpp | 15 ++++++++++++++- indra/newview/lluilistener.h | 3 ++- 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/indra/newview/lluilistener.cpp b/indra/newview/lluilistener.cpp index 2b2187a73b..b81859a764 100644 --- a/indra/newview/lluilistener.cpp +++ b/indra/newview/lluilistener.cpp @@ -68,6 +68,11 @@ LLUIListener::LLUIListener(): &LLUIListener::getValue, llsd::map("path", LLSD(), "reply", LLSD())); + add("getParents", + "List names of Top menus suitable for passing as \"parent_menu\"", + &LLUIListener::getParents, + llsd::map("reply", LLSD::String())); + LLSD required_args = llsd::map("name", LLSD(), "label", LLSD(), "reply", LLSD()); add("addMenu", "Add new drop-down menu [\"name\"] with displayed [\"label\"] to the Top menu.", @@ -174,7 +179,7 @@ void LLUIListener::callables(const LLSD& event) const response["callables"] = list; } -void LLUIListener::getValue(const LLSD&event) const +void LLUIListener::getValue(const LLSD& event) const { Response response(LLSD(), event); @@ -192,6 +197,14 @@ void LLUIListener::getValue(const LLSD&event) const } } +void LLUIListener::getParents(const LLSD& event) const +{ + Response response(LLSD(), event); + response["parents"] = llsd::toArray( + *gMenuBarView->getChildList(), + [](auto childp) {return childp->getName(); }); +} + LLMenuGL::Params get_params(const LLSD&event) { LLMenuGL::Params item_params; diff --git a/indra/newview/lluilistener.h b/indra/newview/lluilistener.h index c90253ab59..671eb5f29b 100644 --- a/indra/newview/lluilistener.h +++ b/indra/newview/lluilistener.h @@ -42,7 +42,8 @@ public: private: void call(const LLSD& event); void callables(const LLSD& event) const; - void getValue(const LLSD&event) const; + void getValue(const LLSD& event) const; + void getParents(const LLSD& event) const; void addMenu(const LLSD&event) const; void addMenuBranch(const LLSD&event) const; -- cgit v1.2.3 From 99307b619a5aa27bef3bb67027c4cb5e54f21ad4 Mon Sep 17 00:00:00 2001 From: Mnikolenko Productengine Date: Fri, 2 Aug 2024 19:46:52 +0300 Subject: Lua api for adjusting toolbars --- indra/llui/llcommandmanager.cpp | 10 +++++ indra/llui/llcommandmanager.h | 2 + indra/newview/lluilistener.cpp | 68 ++++++++++++++++++++++++++++++++ indra/newview/lluilistener.h | 6 +++ indra/newview/scripts/lua/require/UI.lua | 30 ++++++++++++++ 5 files changed, 116 insertions(+) diff --git a/indra/llui/llcommandmanager.cpp b/indra/llui/llcommandmanager.cpp index 270ec86e01..0c27e519dd 100644 --- a/indra/llui/llcommandmanager.cpp +++ b/indra/llui/llcommandmanager.cpp @@ -189,3 +189,13 @@ bool LLCommandManager::load() return true; } + +LLSD LLCommandManager::getCommandNames() +{ + LLSD cmd_names; + for (auto &it : mCommands) + { + cmd_names.append(it->name()); + } + return cmd_names; + } diff --git a/indra/llui/llcommandmanager.h b/indra/llui/llcommandmanager.h index 3b2586a5a1..cf2270e658 100644 --- a/indra/llui/llcommandmanager.h +++ b/indra/llui/llcommandmanager.h @@ -192,6 +192,8 @@ public: LLCommand * getCommand(const LLCommandId& commandId); LLCommand * getCommand(const std::string& name); + LLSD getCommandNames(); + static bool load(); protected: diff --git a/indra/newview/lluilistener.cpp b/indra/newview/lluilistener.cpp index b81859a764..6d1d5ac735 100644 --- a/indra/newview/lluilistener.cpp +++ b/indra/newview/lluilistener.cpp @@ -35,6 +35,7 @@ // external library headers // other Linden headers #include "llmenugl.h" +#include "lltoolbarview.h" #include "llui.h" // getRootView(), resolvePath() #include "lluictrl.h" #include "llerror.h" @@ -97,6 +98,32 @@ LLUIListener::LLUIListener(): "Add menu separator to the [\"parent_menu\"] within the Top menu.", &LLUIListener::addMenuSeparator, llsd::map("parent_menu", LLSD(), "reply", LLSD())); + + add("defaultToolbars", + "todo: defaultToolbars desc", + &LLUIListener::restoreDefaultToolbars); + + add("clearToolbars", + "Clear all buttons off the toolbars", + &LLUIListener::clearAllToolbars); + + add("addToolbarBtn", + "Add [\"btn_name\"] toolbar button to the [\"toolbar\"]\n" + "[1 (left toolbar), 2 (right toolbar), 3 (bottom toolbar)]\n" + "Position of the command in the original list can be specified as [\"rank\"]", + &LLUIListener::addToolbarBtn, + llsd::map("btn_name", LLSD(), "reply", LLSD())); + + add("removeToolbarBtn", + "Remove [\"btn_name\"] toolbar button off the toolbar,\n" + "return [\"rank\"] (old position) of the command in the original list", + &LLUIListener::removeToolbarBtn, + llsd::map("btn_name", LLSD(), "reply", LLSD())); + + add("getToolbarBtnNames", + "Return the table of Toolbar buttons names", + &LLUIListener::getToolbarBtnNames, + llsd::map("reply", LLSD())); } typedef LLUICtrl::CommitCallbackInfo cb_info; @@ -280,3 +307,44 @@ void LLUIListener::addMenuSeparator(const LLSD&event) const } } } + +void LLUIListener::restoreDefaultToolbars(const LLSD &event) const +{ + LLToolBarView::loadDefaultToolbars(); +} + +void LLUIListener::clearAllToolbars(const LLSD &event) const +{ + LLToolBarView::clearAllToolbars(); +} + +void LLUIListener::addToolbarBtn(const LLSD &event) const +{ + Response response(LLSD(), event); + + typedef LLToolBarEnums::EToolBarLocation ToolBarLocation; + ToolBarLocation toolbar = ToolBarLocation::TOOLBAR_BOTTOM; + if (event.has("toolbar")) + { + toolbar = llclamp((ToolBarLocation)event["toolbar"].asInteger(), ToolBarLocation::TOOLBAR_NONE, ToolBarLocation::TOOLBAR_BOTTOM); + } + S32 rank = event.has("rank") ? event["rank"].asInteger() : - 1; + if(!gToolBarView->addCommand(event["btn_name"].asString(), toolbar, rank)) + { + response.error(stringize("Toolbar button ", std::quoted(event["btn_name"].asString()), " was not found")); + } +} + +void LLUIListener::removeToolbarBtn(const LLSD &event) const +{ + Response response(LLSD(), event); + + S32 old_rank = LLToolBar::RANK_NONE; + gToolBarView->removeCommand(event["btn_name"].asString(), old_rank); + response["rank"] = old_rank; +} + +void LLUIListener::getToolbarBtnNames(const LLSD &event) const +{ + Response response(llsd::map("cmd_names", LLCommandManager::instance().getCommandNames()), event); +} diff --git a/indra/newview/lluilistener.h b/indra/newview/lluilistener.h index 671eb5f29b..f4bee00807 100644 --- a/indra/newview/lluilistener.h +++ b/indra/newview/lluilistener.h @@ -50,6 +50,12 @@ private: void addMenuItem(const LLSD&event) const; void addMenuSeparator(const LLSD&event) const; + void restoreDefaultToolbars(const LLSD &event) const; + void clearAllToolbars(const LLSD &event) const; + void addToolbarBtn(const LLSD &event) const; + void removeToolbarBtn(const LLSD &event) const; + void getToolbarBtnNames(const LLSD &event) const; + F64 mLastUntrustedThrottle {0}; F64 mLastMinThrottle {0}; }; diff --git a/indra/newview/scripts/lua/require/UI.lua b/indra/newview/scripts/lua/require/UI.lua index 06b49c6269..30b7189c3c 100644 --- a/indra/newview/scripts/lua/require/UI.lua +++ b/indra/newview/scripts/lua/require/UI.lua @@ -169,4 +169,34 @@ function UI.addMenuSeparator(...) return leap.request('UI', args) end +-- *************************************************************************** +-- Toolbar buttons +-- *************************************************************************** +-- Clears all buttons off the toolbars +function UI.clearToolbars() + leap.send('UI', {op='clearToolbars'}) +end + +function UI.defaultToolbars() + leap.send('UI', {op='defaultToolbars'}) +end + +-- UI.addToolbarBtn{btn_name=btn_name +-- [, toolbar= 3] -- 1 [TOOLBAR_LEFT], 2 [TOOLBAR_RIGHT], 3 [TOOLBAR_BOTTOM] +-- [, rank=1]} -- position on the toolbar +function UI.addToolbarBtn(...) + local args = mapargs('btn_name,toolbar,rank', ...) + args.op = 'addToolbarBtn' + return leap.request('UI', args) +end + +-- Returns the rank(position) of the command in the original list +function UI.removeToolbarBtn(btn_name) + return leap.request('UI', {op = 'removeToolbarBtn', btn_name=btn_name}).rank +end + +function UI.getToolbarBtnNames() + return leap.request('UI', {op = 'getToolbarBtnNames'}).cmd_names +end + return UI -- cgit v1.2.3 From a67a01240cbd58a1800290294be83e7b874fefb6 Mon Sep 17 00:00:00 2001 From: Mnikolenko Productengine Date: Fri, 2 Aug 2024 20:19:53 +0300 Subject: Lua api for showing/hiding floater; rename demo scripts --- indra/newview/lluilistener.cpp | 10 ++++++ indra/newview/lluilistener.h | 2 ++ indra/newview/scripts/lua/require/UI.lua | 23 +++++++++++++ indra/newview/scripts/lua/test_luafloater_demo.lua | 39 ++++++++++++++++++++++ .../newview/scripts/lua/test_luafloater_demo2.lua | 39 ---------------------- .../scripts/lua/test_luafloater_gesture_list.lua | 27 +++++++++++++++ .../scripts/lua/test_luafloater_gesture_list2.lua | 27 --------------- 7 files changed, 101 insertions(+), 66 deletions(-) create mode 100644 indra/newview/scripts/lua/test_luafloater_demo.lua delete mode 100644 indra/newview/scripts/lua/test_luafloater_demo2.lua create mode 100644 indra/newview/scripts/lua/test_luafloater_gesture_list.lua delete mode 100644 indra/newview/scripts/lua/test_luafloater_gesture_list2.lua diff --git a/indra/newview/lluilistener.cpp b/indra/newview/lluilistener.cpp index 6d1d5ac735..07a8b45f89 100644 --- a/indra/newview/lluilistener.cpp +++ b/indra/newview/lluilistener.cpp @@ -39,6 +39,7 @@ #include "llui.h" // getRootView(), resolvePath() #include "lluictrl.h" #include "llerror.h" +#include "llviewermenufile.h" // close_all_windows() extern LLMenuBarGL* gMenuBarView; @@ -124,6 +125,10 @@ LLUIListener::LLUIListener(): "Return the table of Toolbar buttons names", &LLUIListener::getToolbarBtnNames, llsd::map("reply", LLSD())); + + add("closeAllFloaters", + "Close all the floaters", + &LLUIListener::closeAllFloaters); } typedef LLUICtrl::CommitCallbackInfo cb_info; @@ -348,3 +353,8 @@ void LLUIListener::getToolbarBtnNames(const LLSD &event) const { Response response(llsd::map("cmd_names", LLCommandManager::instance().getCommandNames()), event); } + +void LLUIListener::closeAllFloaters(const LLSD &event) const +{ + close_all_windows(); +} diff --git a/indra/newview/lluilistener.h b/indra/newview/lluilistener.h index f4bee00807..bae6724b3d 100644 --- a/indra/newview/lluilistener.h +++ b/indra/newview/lluilistener.h @@ -56,6 +56,8 @@ private: void removeToolbarBtn(const LLSD &event) const; void getToolbarBtnNames(const LLSD &event) const; + void closeAllFloaters(const LLSD &event) const; + F64 mLastUntrustedThrottle {0}; F64 mLastMinThrottle {0}; }; diff --git a/indra/newview/scripts/lua/require/UI.lua b/indra/newview/scripts/lua/require/UI.lua index 30b7189c3c..df77eb2b56 100644 --- a/indra/newview/scripts/lua/require/UI.lua +++ b/indra/newview/scripts/lua/require/UI.lua @@ -199,4 +199,27 @@ function UI.getToolbarBtnNames() return leap.request('UI', {op = 'getToolbarBtnNames'}).cmd_names end +-- *************************************************************************** +-- Floaters +-- *************************************************************************** +function UI.showFloater(floater_name) + leap.send("LLFloaterReg", {op = "showInstance", name = floater_name}) +end + +function UI.hideFloater(floater_name) + leap.send("LLFloaterReg", {op = "hideInstance", name = floater_name}) +end + +function UI.toggleFloater(floater_name) + leap.send("LLFloaterReg", {op = "toggleInstance", name = floater_name}) +end + +function UI.isFloaterVisible(floater_name) + return leap.request("LLFloaterReg", {op = "instanceVisible", name = floater_name}).visible +end + +function UI.closeAllFloaters() + return leap.send("UI", {op = "closeAllFloaters"}) +end + return UI diff --git a/indra/newview/scripts/lua/test_luafloater_demo.lua b/indra/newview/scripts/lua/test_luafloater_demo.lua new file mode 100644 index 0000000000..3903d01e65 --- /dev/null +++ b/indra/newview/scripts/lua/test_luafloater_demo.lua @@ -0,0 +1,39 @@ +local Floater = require 'Floater' +local leap = require 'leap' +local startup = require 'startup' + +local flt = Floater( + 'luafloater_demo.xml', + {show_time_lbl = {"right_mouse_down", "double_click"}}) + +-- override base-class handleEvents() to report the event data in the floater's display field +function flt:handleEvents(event_data) + self:post({action="add_text", ctrl_name="events_editor", value = event_data}) + -- forward the call to base-class handleEvents() + return Floater.handleEvents(self, event_data) +end + +function flt:commit_disable_ctrl(event_data) + self:post({action="set_enabled", ctrl_name="open_btn", value = (1 - event_data.value)}) +end + +function flt:commit_title_cmb(event_data) + self:post({action="set_title", value=event_data.value}) +end + +function flt:commit_open_btn(event_data) + floater_name = self:request({action="get_value", ctrl_name='openfloater_cmd'}).value + leap.send("LLFloaterReg", {name = floater_name, op = "showInstance"}) +end + +local function getCurrentTime() + local currentTime = os.date("*t") + return string.format("%02d:%02d:%02d", currentTime.hour, currentTime.min, currentTime.sec) +end + +function flt:double_click_show_time_lbl(event_data) + self:post({action="set_value", ctrl_name="time_lbl", value=getCurrentTime()}) +end + +startup.wait('STATE_LOGIN_WAIT') +flt:show() diff --git a/indra/newview/scripts/lua/test_luafloater_demo2.lua b/indra/newview/scripts/lua/test_luafloater_demo2.lua deleted file mode 100644 index 3903d01e65..0000000000 --- a/indra/newview/scripts/lua/test_luafloater_demo2.lua +++ /dev/null @@ -1,39 +0,0 @@ -local Floater = require 'Floater' -local leap = require 'leap' -local startup = require 'startup' - -local flt = Floater( - 'luafloater_demo.xml', - {show_time_lbl = {"right_mouse_down", "double_click"}}) - --- override base-class handleEvents() to report the event data in the floater's display field -function flt:handleEvents(event_data) - self:post({action="add_text", ctrl_name="events_editor", value = event_data}) - -- forward the call to base-class handleEvents() - return Floater.handleEvents(self, event_data) -end - -function flt:commit_disable_ctrl(event_data) - self:post({action="set_enabled", ctrl_name="open_btn", value = (1 - event_data.value)}) -end - -function flt:commit_title_cmb(event_data) - self:post({action="set_title", value=event_data.value}) -end - -function flt:commit_open_btn(event_data) - floater_name = self:request({action="get_value", ctrl_name='openfloater_cmd'}).value - leap.send("LLFloaterReg", {name = floater_name, op = "showInstance"}) -end - -local function getCurrentTime() - local currentTime = os.date("*t") - return string.format("%02d:%02d:%02d", currentTime.hour, currentTime.min, currentTime.sec) -end - -function flt:double_click_show_time_lbl(event_data) - self:post({action="set_value", ctrl_name="time_lbl", value=getCurrentTime()}) -end - -startup.wait('STATE_LOGIN_WAIT') -flt:show() diff --git a/indra/newview/scripts/lua/test_luafloater_gesture_list.lua b/indra/newview/scripts/lua/test_luafloater_gesture_list.lua new file mode 100644 index 0000000000..bd397ef2a6 --- /dev/null +++ b/indra/newview/scripts/lua/test_luafloater_gesture_list.lua @@ -0,0 +1,27 @@ +local Floater = require 'Floater' +local LLGesture = require 'LLGesture' +local startup = require 'startup' + +local flt = Floater( + "luafloater_gesture_list.xml", + {gesture_list = {"double_click"}}) + +function flt:post_build(event_data) + local gestures_uuid = LLGesture.getActiveGestures() + local action_data = {} + action_data.action = "add_list_element" + action_data.ctrl_name = "gesture_list" + local gestures = {} + for uuid, info in pairs(gestures_uuid) do + table.insert(gestures, {value = uuid, columns={column = "gesture_name", value = info.name}}) + end + action_data.value = gestures + self:post(action_data) +end + +function flt:double_click_gesture_list(event_data) + LLGesture.startGesture(event_data.value) +end + +startup.wait('STATE_STARTED') +flt:show() diff --git a/indra/newview/scripts/lua/test_luafloater_gesture_list2.lua b/indra/newview/scripts/lua/test_luafloater_gesture_list2.lua deleted file mode 100644 index bd397ef2a6..0000000000 --- a/indra/newview/scripts/lua/test_luafloater_gesture_list2.lua +++ /dev/null @@ -1,27 +0,0 @@ -local Floater = require 'Floater' -local LLGesture = require 'LLGesture' -local startup = require 'startup' - -local flt = Floater( - "luafloater_gesture_list.xml", - {gesture_list = {"double_click"}}) - -function flt:post_build(event_data) - local gestures_uuid = LLGesture.getActiveGestures() - local action_data = {} - action_data.action = "add_list_element" - action_data.ctrl_name = "gesture_list" - local gestures = {} - for uuid, info in pairs(gestures_uuid) do - table.insert(gestures, {value = uuid, columns={column = "gesture_name", value = info.name}}) - end - action_data.value = gestures - self:post(action_data) -end - -function flt:double_click_gesture_list(event_data) - LLGesture.startGesture(event_data.value) -end - -startup.wait('STATE_STARTED') -flt:show() -- cgit v1.2.3 From 1913ea4ec2f46458e2971e6e02afacf4da670af2 Mon Sep 17 00:00:00 2001 From: Nat Goodspeed Date: Fri, 2 Aug 2024 19:07:02 -0400 Subject: Search --luafile script on LuaCommandPath. --- indra/newview/llappviewer.cpp | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/indra/newview/llappviewer.cpp b/indra/newview/llappviewer.cpp index ef12fe0bd3..ff29f64aeb 100644 --- a/indra/newview/llappviewer.cpp +++ b/indra/newview/llappviewer.cpp @@ -113,6 +113,7 @@ #include "llgltfmateriallist.h" // Linden library includes +#include "fsyspath.h" #include "llavatarnamecache.h" #include "lldiriterator.h" #include "llexperiencecache.h" @@ -1216,8 +1217,23 @@ bool LLAppViewer::init() "--luafile", "LuaScript", [](const LLSD& script) { - // no completion callback: we don't need to know - LLLUAmanager::runScriptFile(script); + LLSD paths(gSavedSettings.getLLSD("LuaCommandPath")); + LL_DEBUGS("Lua") << "LuaCommandPath = " << paths << LL_ENDL; + for (const auto& path : llsd::inArray(paths)) + { + // if script path is already absolute, operator/() preserves it + auto abspath(fsyspath(gDirUtilp->getAppRODataDir()) / path.asString()); + auto absscript{ (abspath / script.asString()) }; + std::error_code ec; + if (std::filesystem::exists(absscript, ec)) + { + // no completion callback: we don't need to know + LLLUAmanager::runScriptFile(absscript.u8string()); + return; // from lambda + } + } + LL_WARNS("Lua") << "--luafile " << std::quoted(script.asString()) + << " not found on " << paths << LL_ENDL; }); processComposeSwitch( "LuaAutorunPath", "LuaAutorunPath", -- cgit v1.2.3 From af947de6928daa70098edb8346effb9dc8146f47 Mon Sep 17 00:00:00 2001 From: Nat Goodspeed Date: Fri, 2 Aug 2024 19:24:07 -0400 Subject: Add 'LLPanelLogin' 'login', 'savedLogins' operations. 'login' accepts optional 'username', 'slurl', 'grid'. 'savedLogins' returns the list of saved usernames in both display form and internal form. Make LLPanelLogin::getUserName() accept (const LLPointer&). There's a whole separate discussion pending as to whether const LLPointer should provide access to non-const T methods. Similarly, make LLCredential::getIdentifier() a const method. These two changes enable read-only access to credentials. Make LLPanelLogin methods capture and reuse LLGridManager::instance() as appropriate. Add require/login.lua and test_login.lua. --- indra/newview/llpanellogin.cpp | 57 +++++---- indra/newview/llpanellogin.h | 2 +- indra/newview/llpanelloginlistener.cpp | 178 ++++++++++++++++++++++++++++ indra/newview/llpanelloginlistener.h | 2 + indra/newview/llsecapi.h | 2 +- indra/newview/scripts/lua/require/login.lua | 21 ++-- indra/newview/scripts/lua/test_login.lua | 10 +- 7 files changed, 226 insertions(+), 46 deletions(-) diff --git a/indra/newview/llpanellogin.cpp b/indra/newview/llpanellogin.cpp index 86f56f0949..a6dbcedc4b 100644 --- a/indra/newview/llpanellogin.cpp +++ b/indra/newview/llpanellogin.cpp @@ -81,15 +81,16 @@ BOOL LLPanelLogin::sCredentialSet = FALSE; LLPointer load_user_credentials(std::string &user_key) { - if (gSecAPIHandler->hasCredentialMap("login_list", LLGridManager::getInstance()->getGrid())) + std::string grid{ LLGridManager::instance().getGrid() }; + if (gSecAPIHandler->hasCredentialMap("login_list", grid)) { // user_key should be of "name Resident" format - return gSecAPIHandler->loadFromCredentialMap("login_list", LLGridManager::getInstance()->getGrid(), user_key); + return gSecAPIHandler->loadFromCredentialMap("login_list", grid, user_key); } else { // legacy (or legacy^2, since it also tries to load from settings) - return gSecAPIHandler->loadCredential(LLGridManager::getInstance()->getGrid()); + return gSecAPIHandler->loadCredential(grid); } } @@ -223,7 +224,8 @@ LLPanelLogin::LLPanelLogin(const LLRect &rect, sendChildToBack(getChildView("forgot_password_text")); sendChildToBack(getChildView("sign_up_text")); - std::string current_grid = LLGridManager::getInstance()->getGrid(); + LLGridManager& gridmgr{ LLGridManager::instance() }; + std::string current_grid = gridmgr.getGrid(); if (!mFirstLoginThisInstall) { LLComboBox* favorites_combo = getChild("start_location_combo"); @@ -237,7 +239,7 @@ LLPanelLogin::LLPanelLogin(const LLRect &rect, // Load all of the grids, sorted, and then add a bar and the current grid at the top server_choice_combo->removeall(); - std::map known_grids = LLGridManager::getInstance()->getKnownGrids(); + std::map known_grids = gridmgr.getKnownGrids(); for (std::map::iterator grid_choice = known_grids.begin(); grid_choice != known_grids.end(); grid_choice++) @@ -251,7 +253,7 @@ LLPanelLogin::LLPanelLogin(const LLRect &rect, server_choice_combo->sortByName(); LL_DEBUGS("AppInit") << "adding current " << current_grid << LL_ENDL; - server_choice_combo->add(LLGridManager::getInstance()->getGridLabel(), + server_choice_combo->add(gridmgr.getGridLabel(), current_grid, ADD_TOP); server_choice_combo->selectFirstItem(); @@ -760,6 +762,7 @@ void LLPanelLogin::onUpdateStartSLURL(const LLSLURL& new_start_slurl) LL_DEBUGS("AppInit")<getChild("start_location_combo"); + LLGridManager& gridmgr{ LLGridManager::instance() }; /* * Determine whether or not the new_start_slurl modifies the grid. * @@ -774,17 +777,17 @@ void LLPanelLogin::onUpdateStartSLURL(const LLSLURL& new_start_slurl) { case LLSLURL::LOCATION: { - std::string slurl_grid = LLGridManager::getInstance()->getGrid(new_start_slurl.getGrid()); + std::string slurl_grid = gridmgr.getGrid(new_start_slurl.getGrid()); if ( ! slurl_grid.empty() ) // is that a valid grid? { - if ( slurl_grid != LLGridManager::getInstance()->getGrid() ) // new grid? + if ( slurl_grid != gridmgr.getGrid() ) // new grid? { // the slurl changes the grid, so update everything to match - LLGridManager::getInstance()->setGridChoice(slurl_grid); + gridmgr.setGridChoice(slurl_grid); // update the grid selector to match the slurl LLComboBox* server_combo = sInstance->getChild("server_combo"); - std::string server_label(LLGridManager::getInstance()->getGridLabel(slurl_grid)); + std::string server_label(gridmgr.getGridLabel(slurl_grid)); server_combo->setSimple(server_label); updateServer(); // to change the links and splash screen @@ -831,8 +834,7 @@ void LLPanelLogin::autologinToLocation(const LLSLURL& slurl) if ( LLPanelLogin::sInstance != NULL ) { - void* unused_parameter = 0; - LLPanelLogin::sInstance->onClickConnect(unused_parameter); + LLPanelLogin::sInstance->onClickConnect(false); } } @@ -872,7 +874,8 @@ void LLPanelLogin::loadLoginPage() { if (!sInstance) return; - LLURI login_page = LLURI(LLGridManager::getInstance()->getLoginPage()); + LLGridManager& gridmgr{ LLGridManager::instance() }; + LLURI login_page = LLURI(gridmgr.getLoginPage()); LLSD params(login_page.queryMap()); LL_DEBUGS("AppInit") << "login_page: " << login_page << LL_ENDL; @@ -899,7 +902,7 @@ void LLPanelLogin::loadLoginPage() params["channel"] = LLVersionInfo::instance().getChannel(); // Grid - params["grid"] = LLGridManager::getInstance()->getGridId(); + params["grid"] = gridmgr.getGridId(); // add OS info params["os"] = LLOSInfo::instance().getOSStringSimple(); @@ -916,7 +919,7 @@ void LLPanelLogin::loadLoginPage() login_page.path(), params)); - gViewerWindow->setMenuBackgroundColor(false, !LLGridManager::getInstance()->isInProductionGrid()); + gViewerWindow->setMenuBackgroundColor(false, !gridmgr.isInProductionGrid()); LLMediaCtrl* web_browser = sInstance->getChild("login_html"); if (web_browser->getCurrentNavUrl() != login_uri.asString()) @@ -949,9 +952,10 @@ void LLPanelLogin::onClickConnect(bool commit_fields) // the grid definitions may come from a user-supplied grids.xml, so they may not be good LL_DEBUGS("AppInit")<<"grid "<setGridChoice(combo_val.asString()); + gridmgr.setGridChoice(combo_val.asString()); } catch (LLInvalidGridName ex) { @@ -984,7 +988,7 @@ void LLPanelLogin::onClickConnect(bool commit_fields) std::string identifier_type; cred->identifierType(identifier_type); LLSD allowed_credential_types; - LLGridManager::getInstance()->getLoginIdentifierTypes(allowed_credential_types); + gridmgr.getLoginIdentifierTypes(allowed_credential_types); // check the typed in credential type against the credential types expected by the server. for(LLSD::array_iterator i = allowed_credential_types.beginArray(); @@ -1133,6 +1137,7 @@ void LLPanelLogin::updateServer() { if (sInstance) { + LLGridManager& gridmgr{ LLGridManager::instance() }; try { // if they've selected another grid, we should load the credentials @@ -1152,7 +1157,7 @@ void LLPanelLogin::updateServer() // populate dropbox and setFields // Note: following call is related to initializeLoginInfo() - LLPointer credential = gSecAPIHandler->loadCredential(LLGridManager::getInstance()->getGrid()); + LLPointer credential = gSecAPIHandler->loadCredential(gridmgr.getGrid()); sInstance->populateUserList(credential); // restore creds @@ -1165,12 +1170,12 @@ void LLPanelLogin::updateServer() { // populate dropbox and setFields // Note: following call is related to initializeLoginInfo() - LLPointer credential = gSecAPIHandler->loadCredential(LLGridManager::getInstance()->getGrid()); + LLPointer credential = gSecAPIHandler->loadCredential(gridmgr.getGrid()); sInstance->populateUserList(credential); } // update the login panel links - bool system_grid = LLGridManager::getInstance()->isSystemGrid(); + bool system_grid = gridmgr.isSystemGrid(); // Want to vanish not only create_new_account_btn, but also the // title text over it, so turn on/off the whole layout_panel element. @@ -1219,11 +1224,12 @@ void LLPanelLogin::populateUserList(LLPointer credential) getChild("password_edit")->setValue(std::string()); mUsernameLength = 0; mPasswordLength = 0; + std::string grid{ LLGridManager::instance().getGrid() }; - if (gSecAPIHandler->hasCredentialMap("login_list", LLGridManager::getInstance()->getGrid())) + if (gSecAPIHandler->hasCredentialMap("login_list", grid)) { LLSecAPIHandler::credential_map_t credencials; - gSecAPIHandler->loadCredentialMap("login_list", LLGridManager::getInstance()->getGrid(), credencials); + gSecAPIHandler->loadCredentialMap("login_list", grid, credencials); LLSecAPIHandler::credential_map_t::iterator cr_iter = credencials.begin(); LLSecAPIHandler::credential_map_t::iterator cr_end = credencials.end(); @@ -1278,7 +1284,8 @@ void LLPanelLogin::onSelectServer() LLComboBox* server_combo = getChild("server_combo"); LLSD server_combo_val = server_combo->getSelectedValue(); LL_INFOS("AppInit") << "grid "<setGridChoice(server_combo_val.asString()); + auto& gridmgr{ LLGridManager::instance() }; + gridmgr.setGridChoice(server_combo_val.asString()); addFavoritesToStartLocation(); /* @@ -1308,7 +1315,7 @@ void LLPanelLogin::onSelectServer() LLSLURL slurl(location); // generata a slurl from the location combo contents if (location.empty() || (slurl.getType() == LLSLURL::LOCATION - && slurl.getGrid() != LLGridManager::getInstance()->getGrid()) + && slurl.getGrid() != gridmgr.getGrid()) ) { // the grid specified by the location is not this one, so clear the combo @@ -1338,7 +1345,7 @@ bool LLPanelLogin::getShowFavorites() } // static -std::string LLPanelLogin::getUserName(LLPointer &cred) +std::string LLPanelLogin::getUserName(const LLPointer &cred) { if (cred.isNull()) { diff --git a/indra/newview/llpanellogin.h b/indra/newview/llpanellogin.h index 7f4c2487bf..a7019ea2a3 100644 --- a/indra/newview/llpanellogin.h +++ b/indra/newview/llpanellogin.h @@ -88,7 +88,7 @@ public: static bool getShowFavorites(); // extract name from cred in a format apropriate for username field - static std::string getUserName(LLPointer &cred); + static std::string getUserName(const LLPointer &cred); private: friend class LLPanelLoginListener; diff --git a/indra/newview/llpanelloginlistener.cpp b/indra/newview/llpanelloginlistener.cpp index fb6f034b7f..a4418145d7 100644 --- a/indra/newview/llpanelloginlistener.cpp +++ b/indra/newview/llpanelloginlistener.cpp @@ -32,9 +32,19 @@ #include "llpanelloginlistener.h" // STL headers // std headers +#include +#include // external library headers // other Linden headers +#include "llcombobox.h" #include "llpanellogin.h" +#include "llsdutil.h" +#include "llsecapi.h" +#include "llslurl.h" +#include "llstartup.h" +#include "lluictrl.h" +#include "llviewernetwork.h" +#include "stringize.h" LLPanelLoginListener::LLPanelLoginListener(LLPanelLogin* instance): LLEventAPI("LLPanelLogin", "Access to LLPanelLogin methods"), @@ -43,9 +53,177 @@ LLPanelLoginListener::LLPanelLoginListener(LLPanelLogin* instance): add("onClickConnect", "Pretend user clicked the \"Log In\" button", &LLPanelLoginListener::onClickConnect); + add("login", + "Login to Second Life with saved credentials.\n" + "Pass [\"username\"] to select credentials previously saved with that username.\n" + "Pass [\"slurl\"] to specify target location.\n" + "Pass [\"grid\"] to select one of the valid grids in grids.xml.", + &LLPanelLoginListener::login, + llsd::map("reply", LLSD::String())); + add("savedLogins", + "Return \"logins\" as a list of {} saved login names.\n" + "Pass [\"grid\"] to select one of the valid grids in grids.xml.", + &LLPanelLoginListener::savedLogins, + llsd::map("reply", LLSD::String())); } void LLPanelLoginListener::onClickConnect(const LLSD&) const { mPanel->onClickConnect(false); } + +void LLPanelLoginListener::login(const LLSD& args) const +{ + // Although we require a "reply" key, don't instantiate a Response object: + // if things go well, response to our invoker will come later, once the + // viewer gets the response from the login server. + std::string username(args["username"]), slurlstr(args["slurl"]), grid(args["grid"]); + LL_DEBUGS("AppInit") << "Login with saved credentials"; + if (! username.empty()) + { + LL_CONT << " for user " << std::quoted(username); + } + if (! slurlstr.empty()) + { + LL_CONT << " to location " << std::quoted(slurlstr); + } + if (! grid.empty()) + { + LL_CONT << " on grid " << std::quoted(grid); + } + LL_CONT << LL_ENDL; + + // change grid first, allowing slurl to override + auto& gridmgr{ LLGridManager::instance() }; + if (! grid.empty()) + { + // setGridChoice() can throw LLInvalidGridName -- but if so, let it + // propagate, trusting that LLEventAPI will catch it and send an + // appropriate reply. + auto server_combo = mPanel->getChild("server_combo"); + server_combo->setSimple(gridmgr.getGridLabel(grid)); + LLPanelLogin::updateServer(); + } + if (! slurlstr.empty()) + { + LLSLURL slurl(slurlstr); + if (! slurl.isSpatial()) + { + // don't bother with LLTHROW() because we expect LLEventAPI to + // catch and report back to invoker + throw DispatchError(stringize("Invalid start SLURL ", std::quoted(slurlstr))); + } + // Bypass LLStartUp::setStartSLURL() because, after validating as + // above, it bounces right back to LLPanelLogin::onUpdateStartSLURL(). + // It also sets the "NextLoginLocation" user setting, but as with grid, + // we don't yet know whether that's desirable for scripted login. + LLPanelLogin::onUpdateStartSLURL(slurl); + } + if (! username.empty()) + { + // Transform (e.g.) "Nat Linden" to the internal form expected by + // loadFromCredentialMap(), e.g. "nat_linden" + LLStringUtil::toLower(username); + LLStringUtil::replaceChar(username, ' ', '_'); + LLStringUtil::replaceChar(username, '.', '_'); + + // call gridmgr.getGrid() because our caller didn't necessarily pass + // ["grid"] -- or it might have been overridden by the SLURL + auto cred = gSecAPIHandler->loadFromCredentialMap("login_list", + gridmgr.getGrid(), + username); + LLPanelLogin::setFields(cred); + } + + if (mPanel->getChild("username_combo")->getValue().asString().empty() || + mPanel->getChild("password_edit") ->getValue().asString().empty()) + { + // as above, let LLEventAPI report back to invoker + throw DispatchError(stringize("Unrecognized username ", std::quoted(username))); + } + + // Here we're about to trigger actual login, which is all we can do in + // this method. All subsequent responses must come via the "login" + // LLEventPump. + LLEventPumps::instance().obtain("login").listen( + "Lua login", + [args] + (const LLBoundListener& conn, const LLSD& update) + { + LLSD response{ llsd::map("ok", false) }; + if (update["change"] == "connect") + { + // success! + response["ok"] = true; + } + else if (update["change"] == "disconnect") + { + // Does this ever actually happen? + // LLLoginInstance::handleDisconnect() says it's a placeholder. + response["error"] = "login disconnect"; + } + else if (update["change"] == "fail.login") + { + // why? + // LLLoginInstance::handleLoginFailure() has special code to + // handle "tos", "update" and "mfa_challenge". Do not respond + // automatically because these things are SUPPOSED to engage + // the user. + // Copy specific response fields because there's an enormous + // chunk of stuff that comes back on success. + LLSD data{ update["data"] }; + const char* fields[] = { + "reason", + "error_code" + }; + for (auto field : fields) + { + response[field] = data[field]; + } + response["error"] = update["message"]; + } + else + { + // Ignore all other "change" values: LLLogin sends multiple update + // events. Only a few of them (above) indicate completion. + return; + } + // For all the completion cases, disconnect from the "login" + // LLEventPump. + conn.disconnect(); + // and at last, send response to the original invoker + sendReply(response, args); + }); + + // Turn the Login crank. + mPanel->onClickConnect(false); +} + +LLSD LLPanelLoginListener::savedLogins(const LLSD& args) const +{ + LL_INFOS() << "called with " << args << LL_ENDL; + std::string grid = (args.has("grid")? args["grid"].asString() + : LLGridManager::instance().getGrid()); + if (! gSecAPIHandler->hasCredentialMap("login_list", grid)) + { + LL_INFOS() << "no creds for " << grid << LL_ENDL; + return llsd::map("error", + stringize("No saved logins for grid ", std::quoted(grid))); + } + LLSecAPIHandler::credential_map_t creds; + gSecAPIHandler->loadCredentialMap("login.list", grid, creds); + auto result = llsd::map( + "logins", + llsd::toArray( + creds, + [](const auto& pair) + { + return llsd::map( + "name", + LLPanelLogin::getUserName(pair.second), + "key", + pair.first); + })); + LL_INFOS() << "returning creds " << result << LL_ENDL; + return result; +} diff --git a/indra/newview/llpanelloginlistener.h b/indra/newview/llpanelloginlistener.h index b552ccd5b0..f2f4d70ff9 100644 --- a/indra/newview/llpanelloginlistener.h +++ b/indra/newview/llpanelloginlistener.h @@ -40,6 +40,8 @@ public: private: void onClickConnect(const LLSD&) const; + void login(const LLSD&) const; + LLSD savedLogins(const LLSD&) const; LLPanelLogin* mPanel; }; diff --git a/indra/newview/llsecapi.h b/indra/newview/llsecapi.h index 5cc78d09dc..367bfe1469 100644 --- a/indra/newview/llsecapi.h +++ b/indra/newview/llsecapi.h @@ -311,7 +311,7 @@ public: mIdentifier = identifier; mAuthenticator = authenticator; } - virtual LLSD getIdentifier() { return mIdentifier; } + virtual LLSD getIdentifier() const { return mIdentifier; } virtual void identifierType(std::string& idType); virtual LLSD getAuthenticator() { return mAuthenticator; } virtual void authenticatorType(std::string& authType); diff --git a/indra/newview/scripts/lua/require/login.lua b/indra/newview/scripts/lua/require/login.lua index 0d8591cace..f4e65b1606 100644 --- a/indra/newview/scripts/lua/require/login.lua +++ b/indra/newview/scripts/lua/require/login.lua @@ -1,19 +1,12 @@ -local UI = require 'UI' local leap = require 'leap' +local startup = require 'startup' +local mapargs = require 'mapargs' -local function login(username, password) - if username and password then - local userpath = '//username_combo/Combo Text Entry' - local passpath = '//password_edit' - -- first clear anything presently in those text fields - for _, path in pairs({userpath, passpath}) do - UI.click(path) - UI.keypress{keysym='Backsp', path=path} - end - UI.type{path=userpath, text=username} - UI.type{path=passpath, text=password} - end - leap.send('LLPanelLogin', {op='onClickConnect'}) +local function login(...) + startup.wait('STATE_LOGIN_WAIT') + local args = mapargs('username,grid,slurl', ...) + args.op = 'login' + return leap.request('LLPanelLogin', args) end return login diff --git a/indra/newview/scripts/lua/test_login.lua b/indra/newview/scripts/lua/test_login.lua index a8c31807bc..f263940f79 100644 --- a/indra/newview/scripts/lua/test_login.lua +++ b/indra/newview/scripts/lua/test_login.lua @@ -1,7 +1,7 @@ -startup = require 'startup' +inspect = require 'inspect' login = require 'login' -startup.wait('STATE_LOGIN_WAIT') -login() --- Fill in valid credentials as they would be entered on the login screen --- login('My Username', 'password') +print(inspect(login{ + username='Nat Linden', +-- grid='agni' + })) -- cgit v1.2.3 From 9c4b6a04166c0053b147f9064056a50644be4d6b Mon Sep 17 00:00:00 2001 From: Nicky Date: Sat, 3 Aug 2024 14:20:17 +0200 Subject: Remove -wno-dangling-pointer, compiling with GCC 13.3, 12, 11 and clang did not yield such a warning. Furthermore this warning should not just be disabled but rather dealt with when it comes up --- indra/cmake/00-Common.cmake | 1 - 1 file changed, 1 deletion(-) diff --git a/indra/cmake/00-Common.cmake b/indra/cmake/00-Common.cmake index 25841d9d78..4ae9377500 100644 --- a/indra/cmake/00-Common.cmake +++ b/indra/cmake/00-Common.cmake @@ -175,7 +175,6 @@ if (LINUX) set(GCC_WARNINGS ${GCC_CLANG_COMPATIBLE_WARNINGS} - -Wno-dangling-pointer ) add_link_options( -- cgit v1.2.3 From 6cd306e9bf3698551beb70ec6ddfde7f6cec12bd Mon Sep 17 00:00:00 2001 From: Nicky Date: Sat, 3 Aug 2024 14:23:57 +0200 Subject: Use python raw string literals to remove any ambiguity with standard escape sequences like \n --- indra/lib/python/indra/util/llmanifest.py | 2 +- indra/newview/viewer_manifest.py | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/indra/lib/python/indra/util/llmanifest.py b/indra/lib/python/indra/util/llmanifest.py index 38de9c7cf1..b5fdccc9ba 100755 --- a/indra/lib/python/indra/util/llmanifest.py +++ b/indra/lib/python/indra/util/llmanifest.py @@ -309,7 +309,7 @@ def main(extra=[]): class LLManifestRegistry(type): def __init__(cls, name, bases, dct): super(LLManifestRegistry, cls).__init__(name, bases, dct) - match = re.match("(\w+)Manifest", name) + match = re.match(r"(\w+)Manifest", name) if match: cls.manifests[match.group(1).lower()] = cls diff --git a/indra/newview/viewer_manifest.py b/indra/newview/viewer_manifest.py index 6be43f0021..d94bdc31d2 100755 --- a/indra/newview/viewer_manifest.py +++ b/indra/newview/viewer_manifest.py @@ -277,13 +277,13 @@ class ViewerManifest(LLManifest): # All lines up to and including the first blank line are the file header; skip them lines.reverse() # so that pop will pull from first to last line - while not re.match("\s*$", lines.pop()) : + while not re.match(r"\s*$", lines.pop()) : pass # do nothing # A line that starts with a non-whitespace character is a name; all others describe contributions, so collect the names names = [] for line in lines : - if re.match("\S", line) : + if re.match(r"\S", line) : names.append(line.rstrip()) # It's not fair to always put the same people at the head of the list random.shuffle(names) -- cgit v1.2.3 From f480075e1ffbcc026843a3d14fd6533aad1e7126 Mon Sep 17 00:00:00 2001 From: Nicky Date: Sat, 3 Aug 2024 14:27:37 +0200 Subject: Cast RAND_MAX into F32 to avoid any implicit casting --- indra/newview/llvoicewebrtc.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/indra/newview/llvoicewebrtc.cpp b/indra/newview/llvoicewebrtc.cpp index d18a32cb05..59158922d0 100644 --- a/indra/newview/llvoicewebrtc.cpp +++ b/indra/newview/llvoicewebrtc.cpp @@ -2096,7 +2096,7 @@ LLVoiceWebRTCConnection::LLVoiceWebRTCConnection(const LLUUID ®ionID, const s { // retries wait a short period...randomize it so // all clients don't try to reconnect at once. - mRetryWaitSecs = (F32)((F32) rand() / (RAND_MAX)) + 0.5f; + mRetryWaitSecs = (F32)((F32) rand() / ((F32)RAND_MAX)) + 0.5f; mWebRTCPeerConnectionInterface = llwebrtc::newPeerConnection(); mWebRTCPeerConnectionInterface->setSignalingObserver(this); @@ -2678,7 +2678,7 @@ bool LLVoiceWebRTCConnection::connectionStateMachine() case VOICE_STATE_SESSION_UP: { mRetryWaitPeriod = 0; - mRetryWaitSecs = (F32)((F32) rand() / (RAND_MAX)) + 0.5f; + mRetryWaitSecs = (F32)((F32) rand() / ((F32)RAND_MAX)) + 0.5f; // we'll stay here as long as the session remains up. if (mShutDown) @@ -2700,7 +2700,7 @@ bool LLVoiceWebRTCConnection::connectionStateMachine() { // back off the retry period, and do it by a small random // bit so all clients don't reconnect at once. - mRetryWaitSecs += (F32)((F32) rand() / (RAND_MAX)) + 0.5f; + mRetryWaitSecs += (F32)((F32) rand() / ((F32)RAND_MAX)) + 0.5f; mRetryWaitPeriod = 0; } } -- cgit v1.2.3 From 34e48e686200c80bcdbdde309a8901cfef2a5543 Mon Sep 17 00:00:00 2001 From: Nicky Date: Sat, 3 Aug 2024 15:42:06 +0200 Subject: Change another case of a regex sequence needing to be a raw string --- scripts/packages-formatter.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/packages-formatter.py b/scripts/packages-formatter.py index 4449111e46..5d31702e76 100755 --- a/scripts/packages-formatter.py +++ b/scripts/packages-formatter.py @@ -42,7 +42,7 @@ _autobuild_env=os.environ.copy() # Coerce stdout encoding to utf-8 as cygwin's will be detected as cp1252 otherwise. _autobuild_env["PYTHONIOENCODING"] = "utf-8" -pkg_line=re.compile('^([\w-]+):\s+(.*)$') +pkg_line=re.compile(r'^([\w-]+):\s+(.*)$') def autobuild(*args): """ -- cgit v1.2.3 From 24854b4dd74edabf67efe533ef191553442a9bad Mon Sep 17 00:00:00 2001 From: Nicky Date: Sat, 3 Aug 2024 18:55:40 +0200 Subject: Covert gRandomGenerator into a unique_ptr. Having a static object of type LLRandFlagFib2281 needs a lot of space in the TLB, which is usually fine. Unless libcef gets loaded... CEF is compiled with static TLS/TLS model initial-exec and then having gRandomGenerator allocation so much space in the TLB will exhaust the available space and CEF cannot be loaded. --- indra/llcommon/llrand.cpp | 25 +++++++++++++++++++++---- 1 file changed, 21 insertions(+), 4 deletions(-) diff --git a/indra/llcommon/llrand.cpp b/indra/llcommon/llrand.cpp index 2c51e6f07f..513613f543 100644 --- a/indra/llcommon/llrand.cpp +++ b/indra/llcommon/llrand.cpp @@ -58,9 +58,26 @@ * to restore uniform distribution. */ -// gRandomGenerator is a stateful static object, which is therefore not +// pRandomGenerator is a stateful static object, which is therefore not // inherently thread-safe. -static thread_local LLRandLagFib2281 gRandomGenerator(LLUUID::getRandomSeed()); +//We use a pointer to not construct a huge object in the TLS space, sadly this is necessary +// due to libcef.so on Linux being compiled with TLS model initial-exec (resulting in +// FLAG STATIC_TLS, see readelf libcef.so). CEFs own TLS objects + LLRandLagFib2281 then will exhaust the +// available TLS space, causing media failure. + +static thread_local std::unique_ptr< LLRandLagFib2281 > pRandomGenerator = nullptr; + +namespace { + F64 ll_internal_get_rand() + { + if( !pRandomGenerator ) + { + pRandomGenerator.reset(new LLRandLagFib2281(LLUUID::getRandomSeed( ) )); + } + + return(*pRandomGenerator)(); + } +} // no default implementation, only specific F64 and F32 specializations template @@ -73,7 +90,7 @@ inline F64 ll_internal_random() // CPUs (or at least multi-threaded processes) seem to // occasionally give an obviously incorrect random number -- like // 5^15 or something. Sooooo, clamp it as described above. - F64 rv{ gRandomGenerator() }; + F64 rv{ ll_internal_get_rand() }; if(!((rv >= 0.0) && (rv < 1.0))) return fmod(rv, 1.0); return rv; } @@ -85,7 +102,7 @@ inline F32 ll_internal_random() // Per Monty, it's important to clamp using the correct fmodf() rather // than expanding to F64 for fmod() and then truncating back to F32. Prior // to this change, we were getting sporadic ll_frand() == 1.0 results. - F32 rv{ narrow(gRandomGenerator()) }; + F32 rv{ narrow(ll_internal_get_rand()) }; if(!((rv >= 0.0f) && (rv < 1.0f))) return fmodf(rv, 1.0f); return rv; } -- cgit v1.2.3 From b12d135c38e327774c01594acfa96e37a9e67a64 Mon Sep 17 00:00:00 2001 From: Nat Goodspeed Date: Mon, 5 Aug 2024 11:24:01 -0400 Subject: Fix a couple problems with "LLPanelLogin" listener (thanks Maxim!). Convert plain grid (e.g. "agni") to domain form (e.g. "util.agni.lindenlab.com"). Fix a typo in `savedLogins()`: "login_list", not "login.list". login.lua now returns a table with two functions: `login.login()` and `login.savedLogins()`. Defend Lua caller against trying to engage login features too late in startup sequence: in addition to waiting for "STATE_LOGIN_WAIT", produce an error if `startup.state()` is beyond that state. Since by then `LLPanelLogin` is destroyed, `leap.request("LLPanelLogin", ...)` would get no response, causing the calling Lua script to hang until viewer shutdown. --- indra/newview/llpanelloginlistener.cpp | 22 ++++++++++++++++++++-- indra/newview/scripts/lua/require/login.lua | 23 ++++++++++++++++++++++- indra/newview/scripts/lua/test_login.lua | 9 ++++++--- 3 files changed, 48 insertions(+), 6 deletions(-) diff --git a/indra/newview/llpanelloginlistener.cpp b/indra/newview/llpanelloginlistener.cpp index a4418145d7..aeaf2d1d00 100644 --- a/indra/newview/llpanelloginlistener.cpp +++ b/indra/newview/llpanelloginlistener.cpp @@ -72,6 +72,23 @@ void LLPanelLoginListener::onClickConnect(const LLSD&) const mPanel->onClickConnect(false); } +namespace +{ + + std::string gridkey(const std::string& grid) + { + if (grid.find('.') != std::string::npos) + { + return grid; + } + else + { + return stringize("util.", grid, ".lindenlab.com"); + } + } + +} // anonymous namespace + void LLPanelLoginListener::login(const LLSD& args) const { // Although we require a "reply" key, don't instantiate a Response object: @@ -97,6 +114,7 @@ void LLPanelLoginListener::login(const LLSD& args) const auto& gridmgr{ LLGridManager::instance() }; if (! grid.empty()) { + grid = gridkey(grid); // setGridChoice() can throw LLInvalidGridName -- but if so, let it // propagate, trusting that LLEventAPI will catch it and send an // appropriate reply. @@ -202,7 +220,7 @@ void LLPanelLoginListener::login(const LLSD& args) const LLSD LLPanelLoginListener::savedLogins(const LLSD& args) const { LL_INFOS() << "called with " << args << LL_ENDL; - std::string grid = (args.has("grid")? args["grid"].asString() + std::string grid = (args.has("grid")? gridkey(args["grid"].asString()) : LLGridManager::instance().getGrid()); if (! gSecAPIHandler->hasCredentialMap("login_list", grid)) { @@ -211,7 +229,7 @@ LLSD LLPanelLoginListener::savedLogins(const LLSD& args) const stringize("No saved logins for grid ", std::quoted(grid))); } LLSecAPIHandler::credential_map_t creds; - gSecAPIHandler->loadCredentialMap("login.list", grid, creds); + gSecAPIHandler->loadCredentialMap("login_list", grid, creds); auto result = llsd::map( "logins", llsd::toArray( diff --git a/indra/newview/scripts/lua/require/login.lua b/indra/newview/scripts/lua/require/login.lua index f4e65b1606..50fc9e3793 100644 --- a/indra/newview/scripts/lua/require/login.lua +++ b/indra/newview/scripts/lua/require/login.lua @@ -2,11 +2,32 @@ local leap = require 'leap' local startup = require 'startup' local mapargs = require 'mapargs' -local function login(...) +local login = {} + +local function ensure_login_state(op) + -- no point trying to login until the viewer is ready startup.wait('STATE_LOGIN_WAIT') + -- Once we've actually started login, LLPanelLogin is destroyed, and so is + -- its "LLPanelLogin" listener. At that point, + -- leap.request("LLPanelLogin", ...) will hang indefinitely because no one + -- is listening on that LLEventPump any more. Intercept that case and + -- produce a sensible error. + local state = startup.state() + if startup.before('STATE_LOGIN_WAIT', state) then + error(`Can't engage login operation {op} once we've reached state {state}`, 2) + end +end + +function login.login(...) + ensure_login_state('login') local args = mapargs('username,grid,slurl', ...) args.op = 'login' return leap.request('LLPanelLogin', args) end +function login.savedLogins(grid) + ensure_login_state('savedLogins') + return leap.request('LLPanelLogin', {op='savedLogins'})['logins'] +end + return login diff --git a/indra/newview/scripts/lua/test_login.lua b/indra/newview/scripts/lua/test_login.lua index f263940f79..54d3635a41 100644 --- a/indra/newview/scripts/lua/test_login.lua +++ b/indra/newview/scripts/lua/test_login.lua @@ -1,7 +1,10 @@ inspect = require 'inspect' login = require 'login' -print(inspect(login{ - username='Nat Linden', --- grid='agni' +local grid = 'agni' +print(inspect(login.savedLogins(grid))) + +print(inspect(login.login{ + username='Your Username', + grid=grid })) -- cgit v1.2.3 From cf29b701b19644062a1428a64023c3cf9829e2de Mon Sep 17 00:00:00 2001 From: Mnikolenko Productengine Date: Mon, 5 Aug 2024 20:37:03 +0300 Subject: Allow getting the list of floater names, hide top menu items; add demo script --- indra/llui/llfloaterreg.cpp | 10 +++++++++ indra/llui/llfloaterreg.h | 2 ++ indra/llui/llfloaterreglistener.cpp | 11 +++++++++ indra/llui/llfloaterreglistener.h | 1 + indra/newview/lluilistener.cpp | 16 ++++++++++++++ indra/newview/lluilistener.h | 1 + indra/newview/scripts/lua/require/UI.lua | 8 +++++++ indra/newview/scripts/lua/test_LLChatListener.lua | 19 ++++++++++++---- indra/newview/scripts/lua/test_toolbars.lua | 27 +++++++++++++++++++++++ 9 files changed, 91 insertions(+), 4 deletions(-) create mode 100644 indra/newview/scripts/lua/test_toolbars.lua diff --git a/indra/llui/llfloaterreg.cpp b/indra/llui/llfloaterreg.cpp index 989ce12d09..a9ed678973 100644 --- a/indra/llui/llfloaterreg.cpp +++ b/indra/llui/llfloaterreg.cpp @@ -607,3 +607,13 @@ U32 LLFloaterReg::getVisibleFloaterInstanceCount() return count; } + +LLSD LLFloaterReg::getFloaterNames() +{ + LLSD names; + for (auto &it : sGroupMap) + { + names.append(it.first); + } + return names; +} diff --git a/indra/llui/llfloaterreg.h b/indra/llui/llfloaterreg.h index 43f3f7b170..31a334b89c 100644 --- a/indra/llui/llfloaterreg.h +++ b/indra/llui/llfloaterreg.h @@ -153,6 +153,8 @@ public: static void blockShowFloaters(bool value) { sBlockShowFloaters = value;} static U32 getVisibleFloaterInstanceCount(); + + static LLSD getFloaterNames(); }; #endif diff --git a/indra/llui/llfloaterreglistener.cpp b/indra/llui/llfloaterreglistener.cpp index 8316101264..e17f9f4dd6 100644 --- a/indra/llui/llfloaterreglistener.cpp +++ b/indra/llui/llfloaterreglistener.cpp @@ -80,6 +80,11 @@ LLFloaterRegListener::LLFloaterRegListener(): add("getFloaterEvents", "Return the table of Lua Floater events which are send to the script", &LLFloaterRegListener::getLuaFloaterEvents); + + add("getFloaterNames", + "Return the table of all registered floaters", + &LLFloaterRegListener::getFloaterNames, + llsd::map("reply", LLSD())); } void LLFloaterRegListener::getBuildMap(const LLSD& event) const @@ -121,6 +126,12 @@ void LLFloaterRegListener::instanceVisible(const LLSD& event) const event); } + +void LLFloaterRegListener::getFloaterNames(const LLSD &event) const +{ + Response response(llsd::map("floaters", LLFloaterReg::getFloaterNames()), event); +} + void LLFloaterRegListener::clickButton(const LLSD& event) const { // If the caller requests a reply, build the reply. diff --git a/indra/llui/llfloaterreglistener.h b/indra/llui/llfloaterreglistener.h index 9cb0af2de5..2165b1b62f 100644 --- a/indra/llui/llfloaterreglistener.h +++ b/indra/llui/llfloaterreglistener.h @@ -49,6 +49,7 @@ private: void toggleInstance(const LLSD& event) const; void instanceVisible(const LLSD& event) const; void clickButton(const LLSD& event) const; + void getFloaterNames(const LLSD &event) const; void getLuaFloaterEvents(const LLSD &event) const; }; diff --git a/indra/newview/lluilistener.cpp b/indra/newview/lluilistener.cpp index 07a8b45f89..bcc13c5fe9 100644 --- a/indra/newview/lluilistener.cpp +++ b/indra/newview/lluilistener.cpp @@ -100,6 +100,11 @@ LLUIListener::LLUIListener(): &LLUIListener::addMenuSeparator, llsd::map("parent_menu", LLSD(), "reply", LLSD())); + add("setMenuVisible", + "Set menu [\"name\"] visibility to [\"visible\"]", + &LLUIListener::setMenuVisible, + llsd::map("name", LLSD(), "visible", LLSD(), "reply", LLSD())); + add("defaultToolbars", "todo: defaultToolbars desc", &LLUIListener::restoreDefaultToolbars); @@ -313,6 +318,17 @@ void LLUIListener::addMenuSeparator(const LLSD&event) const } } +void LLUIListener::setMenuVisible(const LLSD &event) const +{ + Response response(LLSD(), event); + std::string menu_name(event["name"]); + if (!gMenuBarView->getItem(menu_name)) + { + return response.error(stringize("Menu ", std::quoted(menu_name), " was not found")); + } + gMenuBarView->setItemVisible(menu_name, event["visible"].asBoolean()); +} + void LLUIListener::restoreDefaultToolbars(const LLSD &event) const { LLToolBarView::loadDefaultToolbars(); diff --git a/indra/newview/lluilistener.h b/indra/newview/lluilistener.h index bae6724b3d..98e4754306 100644 --- a/indra/newview/lluilistener.h +++ b/indra/newview/lluilistener.h @@ -49,6 +49,7 @@ private: void addMenuBranch(const LLSD&event) const; void addMenuItem(const LLSD&event) const; void addMenuSeparator(const LLSD&event) const; + void setMenuVisible(const LLSD &event) const; void restoreDefaultToolbars(const LLSD &event) const; void clearAllToolbars(const LLSD &event) const; diff --git a/indra/newview/scripts/lua/require/UI.lua b/indra/newview/scripts/lua/require/UI.lua index df77eb2b56..df76b1501c 100644 --- a/indra/newview/scripts/lua/require/UI.lua +++ b/indra/newview/scripts/lua/require/UI.lua @@ -150,6 +150,10 @@ function UI.addMenu(...) return leap.request('UI', args) end +function UI.setMenuVisible(name, visible) + return leap.request('UI', {op='setMenuVisible', name=name, visible=visible}) +end + function UI.addMenuBranch(...) local args = mapargs('name,label,parent_menu', ...) args.op = 'addMenuBranch' @@ -222,4 +226,8 @@ function UI.closeAllFloaters() return leap.send("UI", {op = "closeAllFloaters"}) end +function UI.getFloaterNames() + return leap.request("LLFloaterReg", {op = "getFloaterNames"}).floaters +end + return UI diff --git a/indra/newview/scripts/lua/test_LLChatListener.lua b/indra/newview/scripts/lua/test_LLChatListener.lua index 18363ed43b..4a4d40bee5 100644 --- a/indra/newview/scripts/lua/test_LLChatListener.lua +++ b/indra/newview/scripts/lua/test_LLChatListener.lua @@ -1,11 +1,22 @@ local LLChatListener = require 'LLChatListener' local LLChat = require 'LLChat' -local leap = require 'leap' +local UI = require 'UI' +-- Chat listener script allows to use the following commands in Nearby chat: +-- open inventory -- open defined floater by name +-- close inventory -- close defined floater by name +-- closeall -- close all floaters +-- stop -- close the script +-- any other messages will be echoed. function openOrEcho(message) - local floater_name = string.match(message, "^open%s+(%w+)") - if floater_name then - leap.send("LLFloaterReg", {name = floater_name, op = "showInstance"}) + local open_floater_name = string.match(message, "^open%s+(%w+)") + local close_floater_name = string.match(message, "^close%s+(%w+)") + if open_floater_name then + UI.showFloater(open_floater_name) + elseif close_floater_name then + UI.hideFloater(close_floater_name) + elseif message == 'closeall' then + UI.closeAllFloaters() else LLChat.sendNearby('Echo: ' .. message) end diff --git a/indra/newview/scripts/lua/test_toolbars.lua b/indra/newview/scripts/lua/test_toolbars.lua new file mode 100644 index 0000000000..70035db775 --- /dev/null +++ b/indra/newview/scripts/lua/test_toolbars.lua @@ -0,0 +1,27 @@ +popup = require 'popup' +UI = require 'UI' + +local OK = 'OK_okcancelbuttons' +local BUTTONS = UI.getToolbarBtnNames() + +-- Clear the toolbars and then add the toolbar buttons to the random toolbar +response = popup:alertYesCancel('Toolbars will be randomly reshuffled. Proceed?') +if next(response) == OK then + UI.clearToolbars() + math.randomseed(os.time()) + + -- add the buttons to the random toolbar + for i = 1, #BUTTONS do + UI.addToolbarBtn(BUTTONS[i], math.random(3)) + end + + -- remove some of the added buttons from the toolbars + for i = 1, #BUTTONS do + if math.random(100) < 30 then + UI.removeToolbarBtn(BUTTONS[i]) + end + end + popup:tip('Toolbars were reshuffled') +else + popup:tip('Canceled') +end -- cgit v1.2.3 From fd15b4309b126fd5f83f2091c060a6043e18fdbf Mon Sep 17 00:00:00 2001 From: Nicky Dasmijn Date: Tue, 6 Aug 2024 15:20:05 +0200 Subject: Ignore VmallocTotal as it's fixed, huge Number. (#2200) Not ignoring it will cause a lot of log spam due to conversion/parse errors. --- indra/cmake/Linking.cmake | 1 + indra/llcommon/llsys.cpp | 8 ++++++++ 2 files changed, 9 insertions(+) diff --git a/indra/cmake/Linking.cmake b/indra/cmake/Linking.cmake index 0ab30d0800..e6ce902b61 100644 --- a/indra/cmake/Linking.cmake +++ b/indra/cmake/Linking.cmake @@ -7,6 +7,7 @@ set(ARCH_PREBUILT_DIRS ${AUTOBUILD_INSTALL_DIR}/lib) set(ARCH_PREBUILT_DIRS_PLUGINS ${AUTOBUILD_INSTALL_DIR}/plugins) set(ARCH_PREBUILT_DIRS_RELEASE ${AUTOBUILD_INSTALL_DIR}/lib/release) set(ARCH_PREBUILT_DIRS_DEBUG ${AUTOBUILD_INSTALL_DIR}/lib/debug) + if (WINDOWS OR DARWIN ) # Kludge for older cmake versions, 3.20+ is needed to use a genex in add_custom_command( OUTPUT ... ) # Using this will work okay-ish, as Debug is not supported anyway. But for property multi config and also diff --git a/indra/llcommon/llsys.cpp b/indra/llcommon/llsys.cpp index 06b1855785..add8f18a62 100644 --- a/indra/llcommon/llsys.cpp +++ b/indra/llcommon/llsys.cpp @@ -1107,6 +1107,14 @@ LLSD LLMemoryInfo::loadStatsMap() LLSD::String key(matched[1].first, matched[1].second); LLSD::String value_str(matched[2].first, matched[2].second); LLSD::Integer value(0); + + // Skip over VmallocTotal. It's just a fixed and huge number on (modern) systems. "34359738367 kB" + // https://unix.stackexchange.com/questions/700724/why-is-vmalloctotal-34359738367-kb + // If not skipped converting it to a LLSD::integer (32 bit) will fail and spam the logs (this function + // is called quite frequently). + if( key == "VmallocTotal") + continue; + try { value = boost::lexical_cast(value_str); -- cgit v1.2.3 From 143168f9d95afeac2058efa4f54435a654c770f8 Mon Sep 17 00:00:00 2001 From: Andrey Lihatskiy Date: Tue, 6 Aug 2024 16:58:06 +0300 Subject: Force ReleaseOS builds on Linux --- .github/workflows/build.yaml | 6 ++++++ indra/cmake/Copy3rdPartyLibs.cmake | 1 - indra/newview/viewer_manifest.py | 1 - 3 files changed, 6 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index b060ba35e3..4594355732 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -48,6 +48,12 @@ jobs: matrix: runner: [windows-large, macos-12-xl, linux-large] configuration: ${{ fromJSON(needs.setup.outputs.configurations) }} + include: + - runner: linux-large + configuration: ReleaseOS + exclude: + - runner: linux-large + configuration: Release runs-on: ${{ matrix.runner }} outputs: viewer_channel: ${{ steps.build.outputs.viewer_channel }} diff --git a/indra/cmake/Copy3rdPartyLibs.cmake b/indra/cmake/Copy3rdPartyLibs.cmake index bed365fdda..4626e9d0d9 100644 --- a/indra/cmake/Copy3rdPartyLibs.cmake +++ b/indra/cmake/Copy3rdPartyLibs.cmake @@ -226,7 +226,6 @@ elseif(LINUX) list( APPEND release_files libapr-1.so.0 libaprutil-1.so.0 - libhunspell-1.3.so.0.0.0 ) endif() diff --git a/indra/newview/viewer_manifest.py b/indra/newview/viewer_manifest.py index 8600efc91e..24b43192a9 100755 --- a/indra/newview/viewer_manifest.py +++ b/indra/newview/viewer_manifest.py @@ -1428,7 +1428,6 @@ class Linux_x86_64_Manifest(LinuxManifest): self.path_optional("libjemalloc*.so") - self.path("libhunspell-1.3.so*") self.path("libalut.so*") self.path("libopenal.so*") self.path("libopenal.so", "libvivoxoal.so.1") # vivox's sdk expects this soname -- cgit v1.2.3 From eb82c78b071d71a0fd2d7be1c573997e41bab51e Mon Sep 17 00:00:00 2001 From: Mnikolenko Productengine Date: Tue, 6 Aug 2024 20:38:06 +0300 Subject: code clean up --- indra/llui/llcommandmanager.cpp | 8 ++------ indra/llui/llfloaterreg.cpp | 7 +------ indra/newview/lluilistener.cpp | 26 +++++++++++++++++++------- indra/newview/scripts/lua/require/UI.lua | 8 ++++---- indra/newview/scripts/lua/test_toolbars.lua | 5 +++-- 5 files changed, 29 insertions(+), 25 deletions(-) diff --git a/indra/llui/llcommandmanager.cpp b/indra/llui/llcommandmanager.cpp index 0c27e519dd..6c3e676c9b 100644 --- a/indra/llui/llcommandmanager.cpp +++ b/indra/llui/llcommandmanager.cpp @@ -32,6 +32,7 @@ #include "llcommandmanager.h" #include "lldir.h" #include "llerror.h" +#include "llsdutil.h" #include "llxuiparser.h" @@ -192,10 +193,5 @@ bool LLCommandManager::load() LLSD LLCommandManager::getCommandNames() { - LLSD cmd_names; - for (auto &it : mCommands) - { - cmd_names.append(it->name()); - } - return cmd_names; + return llsd::toArray(mCommands, [](const auto &cmd) { return cmd->name(); }); } diff --git a/indra/llui/llfloaterreg.cpp b/indra/llui/llfloaterreg.cpp index a9ed678973..8f9268ffcb 100644 --- a/indra/llui/llfloaterreg.cpp +++ b/indra/llui/llfloaterreg.cpp @@ -610,10 +610,5 @@ U32 LLFloaterReg::getVisibleFloaterInstanceCount() LLSD LLFloaterReg::getFloaterNames() { - LLSD names; - for (auto &it : sGroupMap) - { - names.append(it.first); - } - return names; + return llsd::toArray(sGroupMap, [](const auto &pair) { return pair.first; }); } diff --git a/indra/newview/lluilistener.cpp b/indra/newview/lluilistener.cpp index bcc13c5fe9..3e67531388 100644 --- a/indra/newview/lluilistener.cpp +++ b/indra/newview/lluilistener.cpp @@ -106,23 +106,24 @@ LLUIListener::LLUIListener(): llsd::map("name", LLSD(), "visible", LLSD(), "reply", LLSD())); add("defaultToolbars", - "todo: defaultToolbars desc", + "Restore default toolbar buttons", &LLUIListener::restoreDefaultToolbars); - add("clearToolbars", + add("clearAllToolbars", "Clear all buttons off the toolbars", &LLUIListener::clearAllToolbars); add("addToolbarBtn", - "Add [\"btn_name\"] toolbar button to the [\"toolbar\"]\n" - "[1 (left toolbar), 2 (right toolbar), 3 (bottom toolbar)]\n" + "Add [\"btn_name\"] toolbar button to the [\"toolbar\"]:\n" + "\"left\", \"right\", \"bottom\" (default is \"bottom\")\n" "Position of the command in the original list can be specified as [\"rank\"]", &LLUIListener::addToolbarBtn, llsd::map("btn_name", LLSD(), "reply", LLSD())); add("removeToolbarBtn", "Remove [\"btn_name\"] toolbar button off the toolbar,\n" - "return [\"rank\"] (old position) of the command in the original list", + "return [\"rank\"] (old position) of the command in the original list,\n" + "rank -1 means that [\"btn_name\"] was not found", &LLUIListener::removeToolbarBtn, llsd::map("btn_name", LLSD(), "reply", LLSD())); @@ -347,9 +348,20 @@ void LLUIListener::addToolbarBtn(const LLSD &event) const ToolBarLocation toolbar = ToolBarLocation::TOOLBAR_BOTTOM; if (event.has("toolbar")) { - toolbar = llclamp((ToolBarLocation)event["toolbar"].asInteger(), ToolBarLocation::TOOLBAR_NONE, ToolBarLocation::TOOLBAR_BOTTOM); + if (event["toolbar"] == "left") + { + toolbar = ToolBarLocation::TOOLBAR_LEFT; + } + else if (event["toolbar"] == "right") + { + toolbar = ToolBarLocation::TOOLBAR_RIGHT; + } + else if (event["toolbar"] != "bottom") + { + return response.error(stringize("Toolbar name ", std::quoted(event["toolbar"].asString()), " is not correct. Toolbar names are: left, right, bottom")); + } } - S32 rank = event.has("rank") ? event["rank"].asInteger() : - 1; + S32 rank = event.has("rank") ? event["rank"].asInteger() : LLToolBar::RANK_NONE; if(!gToolBarView->addCommand(event["btn_name"].asString(), toolbar, rank)) { response.error(stringize("Toolbar button ", std::quoted(event["btn_name"].asString()), " was not found")); diff --git a/indra/newview/scripts/lua/require/UI.lua b/indra/newview/scripts/lua/require/UI.lua index df76b1501c..9bc9a3685d 100644 --- a/indra/newview/scripts/lua/require/UI.lua +++ b/indra/newview/scripts/lua/require/UI.lua @@ -177,8 +177,8 @@ end -- Toolbar buttons -- *************************************************************************** -- Clears all buttons off the toolbars -function UI.clearToolbars() - leap.send('UI', {op='clearToolbars'}) +function UI.clearAllToolbars() + leap.send('UI', {op='clearAllToolbars'}) end function UI.defaultToolbars() @@ -186,8 +186,8 @@ function UI.defaultToolbars() end -- UI.addToolbarBtn{btn_name=btn_name --- [, toolbar= 3] -- 1 [TOOLBAR_LEFT], 2 [TOOLBAR_RIGHT], 3 [TOOLBAR_BOTTOM] --- [, rank=1]} -- position on the toolbar +-- [, toolbar= bottom] -- left, right, bottom -- default is bottom +-- [, rank=1]} -- position on the toolbar, starts at 0 (0 - first position, 1 - second position etc.) function UI.addToolbarBtn(...) local args = mapargs('btn_name,toolbar,rank', ...) args.op = 'addToolbarBtn' diff --git a/indra/newview/scripts/lua/test_toolbars.lua b/indra/newview/scripts/lua/test_toolbars.lua index 70035db775..9a832c5644 100644 --- a/indra/newview/scripts/lua/test_toolbars.lua +++ b/indra/newview/scripts/lua/test_toolbars.lua @@ -3,16 +3,17 @@ UI = require 'UI' local OK = 'OK_okcancelbuttons' local BUTTONS = UI.getToolbarBtnNames() +local TOOLBARS = {'left','right','bottom'} -- Clear the toolbars and then add the toolbar buttons to the random toolbar response = popup:alertYesCancel('Toolbars will be randomly reshuffled. Proceed?') if next(response) == OK then - UI.clearToolbars() + UI.clearAllToolbars() math.randomseed(os.time()) -- add the buttons to the random toolbar for i = 1, #BUTTONS do - UI.addToolbarBtn(BUTTONS[i], math.random(3)) + UI.addToolbarBtn(BUTTONS[i], TOOLBARS[math.random(3)]) end -- remove some of the added buttons from the toolbars -- cgit v1.2.3 From d3b1859ca3d6c2c2bcc92edba994b522cf9d4e99 Mon Sep 17 00:00:00 2001 From: Nat Goodspeed Date: Tue, 6 Aug 2024 15:55:58 -0400 Subject: Introduce a custom coroutine/fiber scheduler to prioritize UI. The viewer's main thread's main fiber is responsible for coordinating just about everything. With the default round_robin fiber scheduling algorithm, launching too many additional fibers could starve the main fiber, resulting in visible lag. This custom scheduler tracks when it switches to and from the main fiber, and at each context switch, how long it's been since the last time the main fiber ran. If that exceeds a certain timeslice, it jumps the main fiber to the head of the queue and resumes that instead of any other ready fiber. --- indra/llcommon/CMakeLists.txt | 2 + indra/llcommon/coro_scheduler.cpp | 164 ++++++++++++++++++++++++++++++++++++++ indra/llcommon/coro_scheduler.h | 73 +++++++++++++++++ indra/newview/llstartup.cpp | 5 +- 4 files changed, 243 insertions(+), 1 deletion(-) create mode 100644 indra/llcommon/coro_scheduler.cpp create mode 100644 indra/llcommon/coro_scheduler.h diff --git a/indra/llcommon/CMakeLists.txt b/indra/llcommon/CMakeLists.txt index 20670d7ebe..221fc2bb3f 100644 --- a/indra/llcommon/CMakeLists.txt +++ b/indra/llcommon/CMakeLists.txt @@ -18,6 +18,7 @@ include(Tracy) set(llcommon_SOURCE_FILES apply.cpp commoncontrol.cpp + coro_scheduler.cpp hbxxh.cpp indra_constants.cpp lazyeventapi.cpp @@ -125,6 +126,7 @@ set(llcommon_HEADER_FILES chrono.h classic_callback.h commoncontrol.h + coro_scheduler.h ctype_workaround.h fix_macros.h fsyspath.h diff --git a/indra/llcommon/coro_scheduler.cpp b/indra/llcommon/coro_scheduler.cpp new file mode 100644 index 0000000000..337162cbd5 --- /dev/null +++ b/indra/llcommon/coro_scheduler.cpp @@ -0,0 +1,164 @@ +/** + * @file coro_scheduler.cpp + * @author Nat Goodspeed + * @date 2024-08-05 + * @brief Implementation for llcoro::scheduler. + * + * $LicenseInfo:firstyear=2024&license=viewerlgpl$ + * Copyright (c) 2024, Linden Research, Inc. + * $/LicenseInfo$ + */ + +// Precompiled header +#include "linden_common.h" +// associated header +#include "coro_scheduler.h" +// STL headers +// std headers +#include +// external library headers +#include +// other Linden headers +#include "llcallbacklist.h" +#include "lldate.h" +#include "llerror.h" + +namespace llcoro +{ + +const F32 scheduler::DEFAULT_TIMESLICE{ LL::Timers::DEFAULT_TIMESLICE }; + +const std::string qname("General"); + +scheduler::scheduler(): + // Since use_scheduling_algorithm() must be called before any other + // Boost.Fibers operations, we can assume that the calling fiber is in + // fact the main fiber. + mMainID(boost::this_fiber::get_id()), + mStart(LLDate::now().secondsSinceEpoch()), + mQueue(LL::WorkQueue::getInstance(qname)) +{} + +void scheduler::awakened( boost::fibers::context* ctx) noexcept +{ + if (ctx->get_id() == mMainID) + { + // If the fiber that just came ready is the main fiber, record its + // pointer. + llassert(! mMainCtx); + mMainCtx = ctx; + } + // Delegate to round_robin::awakened() as usual, even for the main fiber. + // This way, as long as other fibers don't take too long, we can just let + // normal round_robin processing pass control to the main fiber. + super::awakened(ctx); +} + +boost::fibers::context* scheduler::pick_next() noexcept +{ + // count calls to pick_next() + ++mSwitches; + // pick_next() is called when the previous fiber has suspended, and we + // need to pick another. Did the previous pick_next() call pick the main + // fiber? If so, it's the main fiber that just suspended. + auto now = LLDate::now().secondsSinceEpoch(); + if (mMainRunning) + { + mMainRunning = false; + mMainLast = now; + } + + boost::fibers::context* next; + + // When the main fiber is ready, and it's been more than mTimeslice since + // the main fiber last ran, it's time to intervene. + F32 elapsed(now - mMainLast); + if (mMainCtx && elapsed > mTimeslice) + { + // We claim that the main fiber is not only stored in mMainCtx, but is + // also queued (somewhere) in our ready list. + llassert(mMainCtx->ready_is_linked()); + // The usefulness of a doubly-linked list is that, given only a + // pointer to an item, we can unlink it. + mMainCtx->ready_unlink(); + // Instead of delegating to round_robin::pick_next() to pop the head + // of the queue, override by returning mMainCtx. + next = mMainCtx; + + /*------------------------- logging stuff --------------------------*/ + // Unless this log tag is enabled, don't even bother posting. + LL_DEBUGS("LLCoros.scheduler"); + // This feature is inherently hard to verify. The logging in the + // lambda below seems useful, but also seems like a lot of overhead + // for a coroutine context switch. Try posting the logging lambda to a + // ThreadPool to offload that overhead. However, if this is still + // taking an unreasonable amount of context-switch time, this whole + // passage could be skipped. + + // Record this event for logging, but push it off to a thread pool to + // perform that work. Presumably std::weak_ptr::lock() is cheaper than + // WorkQueue::getInstance(). + LL::WorkQueue::ptr_t queue{ mQueue.lock() }; + // We probably started before the relevant WorkQueue was created. + if (! queue) + { + // Try again to locate the specified WorkQueue. + queue = LL::WorkQueue::getInstance(qname); + mQueue = queue; + } + // Both the lock() call and the getInstance() call might have failed. + if (queue) + { + // Bind values. Do NOT bind 'this' to avoid cross-thread access! + // It would be interesting to know from what queue position we + // unlinked the main fiber, out of how many in the ready list. + // Unfortunately round_robin::rqueue_ is private, not protected, + // so we have no access. + queue->post( + [switches=mSwitches, start=mStart, elapsed, now] + () + { + U32 runtime(U32(now) - U32(start)); + U32 minutes(runtime / 60u); + U32 seconds(runtime % 60u); + // use stringize to avoid lasting side effects to the + // logging ostream + LL_DEBUGS("LLCoros.scheduler") + << "At time " + << stringize(minutes, ":", std::setw(2), std::setfill('0'), seconds) + << " (" << switches << " switches), coroutines took " + << stringize(std::setprecision(4), elapsed) + << " sec, main coroutine jumped queue" + << LL_ENDL; + }); + } + LL_ENDL; + /*----------------------- end logging stuff ------------------------*/ + } + else + { + // Either the main fiber isn't yet ready, or it hasn't yet been + // mTimeslice seconds since the last time the main fiber ran. Business + // as usual. + next = super::pick_next(); + } + + // super::pick_next() could also have returned the main fiber, which is + // why this is a separate test instead of being folded into the override + // case above. + if (next && next->get_id() == mMainID) + { + // we're about to resume the main fiber: it's no longer "ready" + mMainCtx = nullptr; + // instead, it's "running" + mMainRunning = true; + } + return next; +} + +void scheduler::use() +{ + boost::fibers::use_scheduling_algorithm(); +} + +} // namespace llcoro diff --git a/indra/llcommon/coro_scheduler.h b/indra/llcommon/coro_scheduler.h new file mode 100644 index 0000000000..a7572ccf4d --- /dev/null +++ b/indra/llcommon/coro_scheduler.h @@ -0,0 +1,73 @@ +/** + * @file coro_scheduler.h + * @author Nat Goodspeed + * @date 2024-08-05 + * @brief Custom scheduler for viewer's Boost.Fibers (aka coroutines) + * + * $LicenseInfo:firstyear=2024&license=viewerlgpl$ + * Copyright (c) 2024, Linden Research, Inc. + * $/LicenseInfo$ + */ + +#if ! defined(LL_CORO_SCHEDULER_H) +#define LL_CORO_SCHEDULER_H + +#include "workqueue.h" +#include +#include + +/** + * llcoro::scheduler is specifically intended for the viewer's main thread. + * Its role is to ensure that the main coroutine, responsible for UI + * operations and coordinating everything else, doesn't get starved by + * secondary coroutines -- however many of those there might be. + * + * The simple boost::fibers::algo::round_robin scheduler could result in + * arbitrary time lag between resumptions of the main coroutine. Of course + * every well-behaved viewer coroutine must be coded to yield before too much + * real time has elapsed, but sheer volume of secondary coroutines could still + * consume unreasonable real time before cycling back to the main coroutine. + */ + +namespace llcoro +{ + +class scheduler: public boost::fibers::algo::round_robin +{ + using super = boost::fibers::algo::round_robin; +public: + // If the main fiber is ready, and it's been at least this long since the + // main fiber last ran, jump the main fiber to the head of the queue. + static const F32 DEFAULT_TIMESLICE; + + scheduler(); + void awakened( boost::fibers::context*) noexcept override; + boost::fibers::context* pick_next() noexcept override; + + static void use(); + +private: + // This is the fiber::id of the main fiber. We use this to discover + // whether the fiber passed to awakened() is in fact the main fiber. + boost::fibers::fiber::id mMainID; + // This context* is nullptr until awakened() notices that the main fiber + // has become ready, at which point it contains the main fiber's context*. + boost::fibers::context* mMainCtx{}; + // Set when pick_next() returns the main fiber. + bool mMainRunning{ false }; + // If it's been at least this long since the last time the main fiber got + // control, jump it to the head of the queue. + F32 mTimeslice{ DEFAULT_TIMESLICE }; + // Timestamp as of the last time we suspended the main fiber. + F32 mMainLast{ 0 }; + // Timestamp of start time + F32 mStart{ 0 }; + // count context switches + U64 mSwitches{ 0 }; + // WorkQueue for deferred logging + LL::WorkQueue::weak_t mQueue; +}; + +} // namespace llcoro + +#endif /* ! defined(LL_CORO_SCHEDULER_H) */ diff --git a/indra/newview/llstartup.cpp b/indra/newview/llstartup.cpp index 3cf0def66e..c7fb734440 100644 --- a/indra/newview/llstartup.cpp +++ b/indra/newview/llstartup.cpp @@ -48,6 +48,7 @@ #include "llaudioengine_openal.h" #endif +#include "coro_scheduler.h" #include "llavatarnamecache.h" #include "llexperiencecache.h" #include "lllandmark.h" @@ -400,10 +401,12 @@ bool idle_startup() static bool first_call = true; if (first_call) { + first_call = false; // Other phases get handled when startup state changes, // need to capture the initial state as well. LLStartUp::getPhases().startPhase(LLStartUp::getStartupStateString()); - first_call = false; + // Use our custom scheduler for coroutine scheduling. + llcoro::scheduler::use(); } gViewerWindow->showCursor(); -- cgit v1.2.3 From d28089d5f1ca0de07a704da977bf90fbf2ad119a Mon Sep 17 00:00:00 2001 From: Andrey Lihatskiy Date: Wed, 7 Aug 2024 02:16:45 +0300 Subject: Make build.yaml look slightly better --- .github/workflows/build.yaml | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index 4594355732..3df3ff4c9e 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -46,14 +46,11 @@ jobs: needs: setup strategy: matrix: - runner: [windows-large, macos-12-xl, linux-large] + runner: [windows-large, macos-12-xl] configuration: ${{ fromJSON(needs.setup.outputs.configurations) }} include: - - runner: linux-large - configuration: ReleaseOS - exclude: - - runner: linux-large - configuration: Release + - runner: linux-large + configuration: ReleaseOS runs-on: ${{ matrix.runner }} outputs: viewer_channel: ${{ steps.build.outputs.viewer_channel }} -- cgit v1.2.3 From 5fde031b876e3b8ce794286e5796ffb8f0614cd3 Mon Sep 17 00:00:00 2001 From: Nat Goodspeed Date: Wed, 7 Aug 2024 10:39:30 -0400 Subject: Rename 'UI' 'getParents' op to 'getTopMenus', add UI.lua function. Also update the 'UI' help text to reflect its more general nature. Mention 0-relative rank in the xxToolbarBtn operation help text. --- indra/newview/lluilistener.cpp | 23 ++++++++++++----------- indra/newview/lluilistener.h | 2 +- indra/newview/scripts/lua/require/UI.lua | 4 ++++ 3 files changed, 17 insertions(+), 12 deletions(-) diff --git a/indra/newview/lluilistener.cpp b/indra/newview/lluilistener.cpp index 3e67531388..0280836075 100644 --- a/indra/newview/lluilistener.cpp +++ b/indra/newview/lluilistener.cpp @@ -48,8 +48,7 @@ extern LLMenuBarGL* gMenuBarView; LLUIListener::LLUIListener(): LLEventAPI("UI", - "LLUICtrl::CommitCallbackRegistry listener.\n" - "Capable of invoking any function (with parameter) you can specify in XUI.") + "Operations to manipulate the viewer's user interface.") { add("call", "Invoke the operation named by [\"function\"], passing [\"parameter\"],\n" @@ -70,9 +69,9 @@ LLUIListener::LLUIListener(): &LLUIListener::getValue, llsd::map("path", LLSD(), "reply", LLSD())); - add("getParents", + add("getTopMenus", "List names of Top menus suitable for passing as \"parent_menu\"", - &LLUIListener::getParents, + &LLUIListener::getTopMenus, llsd::map("reply", LLSD::String())); LLSD required_args = llsd::map("name", LLSD(), "label", LLSD(), "reply", LLSD()); @@ -116,13 +115,15 @@ LLUIListener::LLUIListener(): add("addToolbarBtn", "Add [\"btn_name\"] toolbar button to the [\"toolbar\"]:\n" "\"left\", \"right\", \"bottom\" (default is \"bottom\")\n" - "Position of the command in the original list can be specified as [\"rank\"]", + "Position of the command in the original list can be specified as [\"rank\"],\n" + "where 0 means the first item", &LLUIListener::addToolbarBtn, llsd::map("btn_name", LLSD(), "reply", LLSD())); add("removeToolbarBtn", "Remove [\"btn_name\"] toolbar button off the toolbar,\n" "return [\"rank\"] (old position) of the command in the original list,\n" + "rank 0 is the first position,\n" "rank -1 means that [\"btn_name\"] was not found", &LLUIListener::removeToolbarBtn, llsd::map("btn_name", LLSD(), "reply", LLSD())); @@ -141,7 +142,7 @@ typedef LLUICtrl::CommitCallbackInfo cb_info; void LLUIListener::call(const LLSD& event) { Response response(LLSD(), event); - LLUICtrl::CommitCallbackInfo *info = LLUICtrl::CommitCallbackRegistry::getValue(event["function"]); + cb_info *info = LLUICtrl::CommitCallbackRegistry::getValue(event["function"]); if (!info || !info->callback_func) { return response.error(stringize("Function ", std::quoted(event["function"].asString()), " was not found")); @@ -201,13 +202,13 @@ void LLUIListener::callables(const LLSD& event) const LLSD entry{ llsd::map("name", it->first) }; switch (it->second.handle_untrusted) { - case LLUICtrl::CommitCallbackInfo::UNTRUSTED_ALLOW: + case cb_info::UNTRUSTED_ALLOW: entry["access"] = "allow"; break; - case LLUICtrl::CommitCallbackInfo::UNTRUSTED_BLOCK: + case cb_info::UNTRUSTED_BLOCK: entry["access"] = "block"; break; - case LLUICtrl::CommitCallbackInfo::UNTRUSTED_THROTTLE: + case cb_info::UNTRUSTED_THROTTLE: entry["access"] = "throttle"; break; } @@ -235,10 +236,10 @@ void LLUIListener::getValue(const LLSD& event) const } } -void LLUIListener::getParents(const LLSD& event) const +void LLUIListener::getTopMenus(const LLSD& event) const { Response response(LLSD(), event); - response["parents"] = llsd::toArray( + response["menus"] = llsd::toArray( *gMenuBarView->getChildList(), [](auto childp) {return childp->getName(); }); } diff --git a/indra/newview/lluilistener.h b/indra/newview/lluilistener.h index 98e4754306..c839c03a57 100644 --- a/indra/newview/lluilistener.h +++ b/indra/newview/lluilistener.h @@ -43,7 +43,7 @@ private: void call(const LLSD& event); void callables(const LLSD& event) const; void getValue(const LLSD& event) const; - void getParents(const LLSD& event) const; + void getTopMenus(const LLSD& event) const; void addMenu(const LLSD&event) const; void addMenuBranch(const LLSD&event) const; diff --git a/indra/newview/scripts/lua/require/UI.lua b/indra/newview/scripts/lua/require/UI.lua index 9bc9a3685d..464e6547ea 100644 --- a/indra/newview/scripts/lua/require/UI.lua +++ b/indra/newview/scripts/lua/require/UI.lua @@ -144,6 +144,10 @@ end -- Top menu -- *************************************************************************** +function UI.getTopMenus() + return leap.request('UI', {op='getTopMenus'}).menus +end + function UI.addMenu(...) local args = mapargs('name,label', ...) args.op = 'addMenu' -- cgit v1.2.3 From 2df6ee631507bd3c9bff783d57b96d4546405a8a Mon Sep 17 00:00:00 2001 From: Nat Goodspeed Date: Wed, 7 Aug 2024 12:51:47 -0400 Subject: Fix omission in login.savedLogins(). Also add Region.lua. --- indra/newview/scripts/lua/require/Region.lua | 17 +++++++++++++++++ indra/newview/scripts/lua/require/login.lua | 2 +- 2 files changed, 18 insertions(+), 1 deletion(-) create mode 100644 indra/newview/scripts/lua/require/Region.lua diff --git a/indra/newview/scripts/lua/require/Region.lua b/indra/newview/scripts/lua/require/Region.lua new file mode 100644 index 0000000000..e4eefece33 --- /dev/null +++ b/indra/newview/scripts/lua/require/Region.lua @@ -0,0 +1,17 @@ +LLFloaterAbout = require 'LLFloaterAbout' + +local Region = {} + +function Region.getInfo() + info = LLFloaterAbout.getInfo() + return { + HOSTNAME=info.HOSTNAME, + POSITION=info.POSITION, + POSITION_LOCAL=info.POSITION_LOCAL, + REGION=info.REGION, + SERVER_VERSION=info.SERVER_VERSION, + SLURL=info.SLURL, + } +end + +return Region diff --git a/indra/newview/scripts/lua/require/login.lua b/indra/newview/scripts/lua/require/login.lua index 50fc9e3793..919434f3a5 100644 --- a/indra/newview/scripts/lua/require/login.lua +++ b/indra/newview/scripts/lua/require/login.lua @@ -27,7 +27,7 @@ end function login.savedLogins(grid) ensure_login_state('savedLogins') - return leap.request('LLPanelLogin', {op='savedLogins'})['logins'] + return leap.request('LLPanelLogin', {op='savedLogins', grid=grid})['logins'] end return login -- cgit v1.2.3 From 08def53d8bf07beaa56db80e95aa76f3682c557c Mon Sep 17 00:00:00 2001 From: Nat Goodspeed Date: Wed, 7 Aug 2024 16:01:45 -0400 Subject: Move llcoro::scheduler::use() call from llstartup to llappviewer. Thanks, Maxim. --- indra/newview/llappviewer.cpp | 3 ++- indra/newview/llstartup.cpp | 2 -- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/indra/newview/llappviewer.cpp b/indra/newview/llappviewer.cpp index ff29f64aeb..3d53200eda 100644 --- a/indra/newview/llappviewer.cpp +++ b/indra/newview/llappviewer.cpp @@ -765,7 +765,8 @@ bool LLAppViewer::init() //set the max heap size. initMaxHeapSize() ; LLCoros::instance().setStackSize(gSavedSettings.getS32("CoroutineStackSize")); - + // Use our custom scheduler for coroutine scheduling. + llcoro::scheduler::use(); // Although initLoggingAndGetLastDuration() is the right place to mess with // setFatalFunction(), we can't query gSavedSettings until after diff --git a/indra/newview/llstartup.cpp b/indra/newview/llstartup.cpp index c7fb734440..82eb59c002 100644 --- a/indra/newview/llstartup.cpp +++ b/indra/newview/llstartup.cpp @@ -405,8 +405,6 @@ bool idle_startup() // Other phases get handled when startup state changes, // need to capture the initial state as well. LLStartUp::getPhases().startPhase(LLStartUp::getStartupStateString()); - // Use our custom scheduler for coroutine scheduling. - llcoro::scheduler::use(); } gViewerWindow->showCursor(); -- cgit v1.2.3 From 3102dc0db472acac7c4007e7423e405a889d665a Mon Sep 17 00:00:00 2001 From: Nat Goodspeed Date: Wed, 7 Aug 2024 16:12:23 -0400 Subject: Allow smaller minimum timer intervals. Add test_flycam.lua to exercise the smaller intervals. --- indra/llcommon/llcallbacklist.cpp | 2 +- indra/newview/scripts/lua/test_flycam.lua | 38 +++++++++++++++++++++++++++++++ 2 files changed, 39 insertions(+), 1 deletion(-) create mode 100644 indra/newview/scripts/lua/test_flycam.lua diff --git a/indra/llcommon/llcallbacklist.cpp b/indra/llcommon/llcallbacklist.cpp index 555c793333..647b268b8b 100644 --- a/indra/llcommon/llcallbacklist.cpp +++ b/indra/llcommon/llcallbacklist.cpp @@ -391,7 +391,7 @@ public: TimersListener(const LazyEventAPIParams& params): LLEventAPI(params) {} // Forbid a script from requesting callbacks too quickly. - static constexpr LLSD::Real MINTIMER{ 1.0 }; + static constexpr LLSD::Real MINTIMER{ 0.010 }; void scheduleAfter(const LLSD& params); void scheduleEvery(const LLSD& params); diff --git a/indra/newview/scripts/lua/test_flycam.lua b/indra/newview/scripts/lua/test_flycam.lua new file mode 100644 index 0000000000..4fb5608b33 --- /dev/null +++ b/indra/newview/scripts/lua/test_flycam.lua @@ -0,0 +1,38 @@ +-- Make camera fly around the subject avatar for a few seconds. + +local LLAgent = require 'LLAgent' +local startup = require 'startup' +local timers = require 'timers' + +local initial = LLAgent.getRegionPosition() +local height = 2.0 -- meters +local radius = 4.0 -- meters +local speed = 1.0 -- meters/second along circle +local start = os.clock() +local stop = os.clock() + 30 -- seconds + +local function cameraPos(t) + local radians = speed * t + return { + initial[1] + radius * math.cos(radians), + initial[2] + radius * math.sin(radians), + initial[3] + height + } +end + +local function moveCamera() + if os.clock() < stop then + -- usual case + LLAgent.setCamera{ camera_pos=cameraPos(os.clock() - start), camera_locked=true } + return nil + else + -- last time + LLAgent.removeCamParams() + LLAgent.setFollowCamActive(false) + return true + end +end + +startup.wait('STATE_STARTED') +-- call moveCamera() repeatedly until it returns true +local timer = timers.Timer(0.1, moveCamera, true) -- cgit v1.2.3 From cdc4f5dbd69526906724dc298594906826f6b8b0 Mon Sep 17 00:00:00 2001 From: Nat Goodspeed Date: Wed, 7 Aug 2024 16:34:14 -0400 Subject: Move #include "coro_scheduler.h" from llstartup to llappviewer. --- indra/newview/llappviewer.cpp | 1 + indra/newview/llstartup.cpp | 1 - 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/indra/newview/llappviewer.cpp b/indra/newview/llappviewer.cpp index 3d53200eda..c259275d8f 100644 --- a/indra/newview/llappviewer.cpp +++ b/indra/newview/llappviewer.cpp @@ -29,6 +29,7 @@ #include "llappviewer.h" // Viewer includes +#include "coro_scheduler.h" #include "llversioninfo.h" #include "llfeaturemanager.h" #include "lluictrlfactory.h" diff --git a/indra/newview/llstartup.cpp b/indra/newview/llstartup.cpp index 82eb59c002..d49e0d9ba2 100644 --- a/indra/newview/llstartup.cpp +++ b/indra/newview/llstartup.cpp @@ -48,7 +48,6 @@ #include "llaudioengine_openal.h" #endif -#include "coro_scheduler.h" #include "llavatarnamecache.h" #include "llexperiencecache.h" #include "lllandmark.h" -- cgit v1.2.3 From b4fe47a5c0abac02d161640e04a9a78afb1c5987 Mon Sep 17 00:00:00 2001 From: Nat Goodspeed Date: Thu, 8 Aug 2024 09:07:56 -0400 Subject: Ensure that the flycam stays near moving avatar. --- indra/newview/scripts/lua/test_flycam.lua | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/indra/newview/scripts/lua/test_flycam.lua b/indra/newview/scripts/lua/test_flycam.lua index 4fb5608b33..05c3c37b93 100644 --- a/indra/newview/scripts/lua/test_flycam.lua +++ b/indra/newview/scripts/lua/test_flycam.lua @@ -4,7 +4,6 @@ local LLAgent = require 'LLAgent' local startup = require 'startup' local timers = require 'timers' -local initial = LLAgent.getRegionPosition() local height = 2.0 -- meters local radius = 4.0 -- meters local speed = 1.0 -- meters/second along circle @@ -12,11 +11,12 @@ local start = os.clock() local stop = os.clock() + 30 -- seconds local function cameraPos(t) + local agent = LLAgent.getRegionPosition() local radians = speed * t return { - initial[1] + radius * math.cos(radians), - initial[2] + radius * math.sin(radians), - initial[3] + height + agent[1] + radius * math.cos(radians), + agent[2] + radius * math.sin(radians), + agent[3] + height } end -- cgit v1.2.3 From 087cbe553e5bac6fe702200c33acc42baf4eef4f Mon Sep 17 00:00:00 2001 From: Mnikolenko Productengine Date: Fri, 9 Aug 2024 15:00:04 +0300 Subject: Lua api for sending group messages --- indra/newview/groupchatlistener.cpp | 100 +++++++++++++++++++-------- indra/newview/groupchatlistener.h | 21 ++++-- indra/newview/llgroupactions.cpp | 8 ++- indra/newview/lltoastimpanel.cpp | 4 +- indra/newview/scripts/lua/require/LLChat.lua | 25 +++++++ 5 files changed, 119 insertions(+), 39 deletions(-) diff --git a/indra/newview/groupchatlistener.cpp b/indra/newview/groupchatlistener.cpp index 43507f13e9..ab367d9fa1 100644 --- a/indra/newview/groupchatlistener.cpp +++ b/indra/newview/groupchatlistener.cpp @@ -2,11 +2,11 @@ * @file groupchatlistener.cpp * @author Nat Goodspeed * @date 2011-04-11 - * @brief Implementation for groupchatlistener. + * @brief Implementation for LLGroupChatListener. * - * $LicenseInfo:firstyear=2011&license=viewerlgpl$ + * $LicenseInfo:firstyear=2024&license=viewerlgpl$ * Second Life Viewer Source Code - * Copyright (C) 2011, Linden Research, Inc. + * Copyright (C) 2024, Linden Research, Inc. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -34,43 +34,85 @@ // std headers // external library headers // other Linden headers +#include "llchat.h" #include "llgroupactions.h" #include "llimview.h" +static const F32 GROUP_CHAT_THROTTLE_PERIOD = 1.f; -namespace { - void startIm_wrapper(LLSD const & event) +LLGroupChatListener::LLGroupChatListener(): + LLEventAPI("GroupChat", + "API to enter, leave, send and intercept group chat messages"), + mLastThrottleTime(0) +{ + add("startGroupChat", + "Enter a group chat in group with UUID [\"group_id\"]\n" + "Assumes the logged-in agent is already a member of this group.", + &LLGroupChatListener::startGroupChat, + llsd::map("group_id", LLSD())); + add("leaveGroupChat", + "Leave a group chat in group with UUID [\"group_id\"]\n" + "Assumes a prior successful startIM request.", + &LLGroupChatListener::leaveGroupChat, + llsd::map("group_id", LLSD())); + add("sendGroupIM", + "send a groupchat IM", + &LLGroupChatListener::sendGroupIM, + llsd::map("message", LLSD(), "group_id", LLSD())); +} + +bool is_in_group(LLEventAPI::Response &response, const LLSD &data) +{ + if (!LLGroupActions::isInGroup(data["group_id"])) { - LLUUID session_id = LLGroupActions::startIM(event["id"].asUUID()); - sendReply(LLSDMap("session_id", LLSD(session_id)), event); + response.error(stringize("You are not the member of the group:", std::quoted(data["group_id"].asString()))); + return false; } + return true; +} - void send_message_wrapper(const std::string& text, const LLUUID& session_id, const LLUUID& group_id) +void LLGroupChatListener::startGroupChat(LLSD const &data) +{ + Response response(LLSD(), data); + if (!is_in_group(response, data)) { - LLIMModel::sendMessage(text, session_id, group_id, IM_SESSION_GROUP_START); + return; + } + if (LLGroupActions::startIM(data["group_id"]).isNull()) + { + return response.error(stringize("Failed to start group chat session ", std::quoted(data["group_id"].asString()))); } } +void LLGroupChatListener::leaveGroupChat(LLSD const &data) +{ + Response response(LLSD(), data); + if (is_in_group(response, data)) + { + LLGroupActions::endIM(data["group_id"].asUUID()); + } +} -GroupChatListener::GroupChatListener(): - LLEventAPI("GroupChat", - "API to enter, leave, send and intercept group chat messages") +void LLGroupChatListener::sendGroupIM(LLSD const &data) { - add("startIM", - "Enter a group chat in group with UUID [\"id\"]\n" - "Assumes the logged-in agent is already a member of this group.", - &startIm_wrapper); - add("endIM", - "Leave a group chat in group with UUID [\"id\"]\n" - "Assumes a prior successful startIM request.", - &LLGroupActions::endIM, - llsd::array("id")); - add("sendIM", - "send a groupchat IM", - &send_message_wrapper, - llsd::array("text", "session_id", "group_id")); + Response response(LLSD(), data); + if (!is_in_group(response, data)) + { + return; + } + + F64 cur_time = LLTimer::getElapsedSeconds(); + + if (cur_time < mLastThrottleTime + GROUP_CHAT_THROTTLE_PERIOD) + { + LL_DEBUGS("LLGroupChatListener") << "'sendGroupIM' was throttled" << LL_ENDL; + return; + } + mLastThrottleTime = cur_time; + + LLUUID group_id(data["group_id"]); + LLIMModel::sendMessage(LUA_PREFIX + data["message"].asString(), + gIMMgr->computeSessionID(IM_SESSION_GROUP_START, group_id), + group_id, + IM_SESSION_GROUP_START); } -/* - static void sendMessage(const std::string& utf8_text, const LLUUID& im_session_id, - const LLUUID& other_participant_id, EInstantMessage dialog); -*/ diff --git a/indra/newview/groupchatlistener.h b/indra/newview/groupchatlistener.h index 3819ac59b7..35afc5766c 100644 --- a/indra/newview/groupchatlistener.h +++ b/indra/newview/groupchatlistener.h @@ -4,9 +4,9 @@ * @date 2011-04-11 * @brief * - * $LicenseInfo:firstyear=2011&license=viewerlgpl$ + * $LicenseInfo:firstyear=2024&license=viewerlgpl$ * Second Life Viewer Source Code - * Copyright (C) 2011, Linden Research, Inc. + * Copyright (C) 2024, Linden Research, Inc. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -26,15 +26,22 @@ * $/LicenseInfo$ */ -#if ! defined(LL_GROUPCHATLISTENER_H) -#define LL_GROUPCHATLISTENER_H +#if ! defined(LL_LLGROUPCHATLISTENER_H) +#define LL_LLGROUPCHATLISTENER_H #include "lleventapi.h" -class GroupChatListener: public LLEventAPI +class LLGroupChatListener: public LLEventAPI { public: - GroupChatListener(); + LLGroupChatListener(); + +private: + void startGroupChat(LLSD const &data); + void leaveGroupChat(LLSD const &data); + void sendGroupIM(LLSD const &data); + + F64 mLastThrottleTime {0.0}; }; -#endif /* ! defined(LL_GROUPCHATLISTENER_H) */ +#endif /* ! defined(LL_LLGROUPCHATLISTENER_H) */ diff --git a/indra/newview/llgroupactions.cpp b/indra/newview/llgroupactions.cpp index 24ae90e3ae..e901631d94 100644 --- a/indra/newview/llgroupactions.cpp +++ b/indra/newview/llgroupactions.cpp @@ -37,6 +37,7 @@ #include "llfloatersidepanelcontainer.h" #include "llgroupmgr.h" #include "llfloaterimcontainer.h" +#include "llfloaterimsession.h" #include "llimview.h" // for gIMMgr #include "llnotificationsutil.h" #include "llstartup.h" @@ -46,7 +47,7 @@ // // Globals // -static GroupChatListener sGroupChatListener; +static LLGroupChatListener sGroupChatListener; class LLGroupHandler : public LLCommandHandler { @@ -519,7 +520,10 @@ void LLGroupActions::endIM(const LLUUID& group_id) LLUUID session_id = gIMMgr->computeSessionID(IM_SESSION_GROUP_START, group_id); if (session_id != LLUUID::null) { - gIMMgr->leaveSession(session_id); + if (LLFloaterIMSession *conversationFloater = LLFloaterIMSession::findInstance(session_id)) + { + LLFloater::onClickClose(conversationFloater); + } } } diff --git a/indra/newview/lltoastimpanel.cpp b/indra/newview/lltoastimpanel.cpp index f7e2d49e13..7fadd8773b 100644 --- a/indra/newview/lltoastimpanel.cpp +++ b/indra/newview/lltoastimpanel.cpp @@ -87,7 +87,9 @@ LLToastIMPanel::LLToastIMPanel(LLToastIMPanel::Params &p) : LLToastPanel(p.notif { LLAvatarName avatar_name; LLAvatarNameCache::get(p.avatar_id, &avatar_name); - p.message = "[From " + avatar_name.getDisplayName() + "]\n" + p.message; + auto [message, is_lua] = LLStringUtil::withoutPrefix(p.message, LUA_PREFIX); + std::string prefix = is_lua ? "LUA - " : ""; + p.message = "[From " + prefix + avatar_name.getDisplayName() + "]\n" + message; } style_params.font.style = "NORMAL"; mMessage->setText(p.message, style_params); diff --git a/indra/newview/scripts/lua/require/LLChat.lua b/indra/newview/scripts/lua/require/LLChat.lua index 78dca765e8..ea6329d574 100644 --- a/indra/newview/scripts/lua/require/LLChat.lua +++ b/indra/newview/scripts/lua/require/LLChat.lua @@ -2,6 +2,10 @@ local leap = require 'leap' local LLChat = {} +-- *************************************************************************** +-- Nearby chat +-- *************************************************************************** + function LLChat.sendNearby(msg) leap.send('LLChatBar', {op='sendChat', message=msg}) end @@ -14,4 +18,25 @@ function LLChat.sendShout(msg) leap.send('LLChatBar', {op='sendChat', type='shout', message=msg}) end +-- 0 is public nearby channel, other channels are used to communicate with LSL scripts +function LLChat.sendChannel(msg, channel) + leap.send('LLChatBar', {op='sendChat', channel=channel, message=msg}) +end + +-- *************************************************************************** +-- Group chat +-- *************************************************************************** + +function LLChat.startGroupChat(group_id) + return leap.request('GroupChat', {op='startGroupChat', group_id=group_id}) +end + +function LLChat.leaveGroupChat(group_id) + leap.send('GroupChat', {op='leaveGroupChat', group_id=group_id}) +end + +function LLChat.sendGroupIM(msg, group_id) + leap.send('GroupChat', {op='sendGroupIM', message=msg, group_id=group_id}) +end + return LLChat -- cgit v1.2.3 From 926a32aa0a9518fe7f19d0c63b30b12a66469f2d Mon Sep 17 00:00:00 2001 From: Mnikolenko Productengine Date: Fri, 9 Aug 2024 18:24:25 +0300 Subject: add demo script for sending group chat messages --- indra/newview/groupchatlistener.cpp | 4 ++-- indra/newview/scripts/lua/require/LLAgent.lua | 11 +++++++++++ indra/newview/scripts/lua/test_group_chat.lua | 16 ++++++++++++++++ 3 files changed, 29 insertions(+), 2 deletions(-) create mode 100644 indra/newview/scripts/lua/test_group_chat.lua diff --git a/indra/newview/groupchatlistener.cpp b/indra/newview/groupchatlistener.cpp index ab367d9fa1..09951ba1cc 100644 --- a/indra/newview/groupchatlistener.cpp +++ b/indra/newview/groupchatlistener.cpp @@ -56,7 +56,7 @@ LLGroupChatListener::LLGroupChatListener(): &LLGroupChatListener::leaveGroupChat, llsd::map("group_id", LLSD())); add("sendGroupIM", - "send a groupchat IM", + "send a [\"message\"] to group with UUID [\"group_id\"]", &LLGroupChatListener::sendGroupIM, llsd::map("message", LLSD(), "group_id", LLSD())); } @@ -114,5 +114,5 @@ void LLGroupChatListener::sendGroupIM(LLSD const &data) LLIMModel::sendMessage(LUA_PREFIX + data["message"].asString(), gIMMgr->computeSessionID(IM_SESSION_GROUP_START, group_id), group_id, - IM_SESSION_GROUP_START); + IM_SESSION_SEND); } diff --git a/indra/newview/scripts/lua/require/LLAgent.lua b/indra/newview/scripts/lua/require/LLAgent.lua index bc9a6b23a0..5ee092f2f6 100644 --- a/indra/newview/scripts/lua/require/LLAgent.lua +++ b/indra/newview/scripts/lua/require/LLAgent.lua @@ -11,6 +11,17 @@ function LLAgent.getGlobalPosition() return leap.request('LLAgent', {op = 'getPosition'}).global end +-- Return array information about the agent's groups +-- id: group id\n" +-- name: group name\n" +-- insignia: group insignia texture id +-- notices: bool indicating if this user accepts notices from this group +-- display: bool indicating if this group is listed in the user's profile +-- contrib: user's land contribution to this group +function LLAgent.getGroups() + return leap.request('LLAgent', {op = 'getGroups'}).groups +end + -- Use LL.leaphelp('LLAgent') and see 'setCameraParams' to get more info about params -- -- TYPE -- DEFAULT -- RANGE -- LLAgent.setCamera{ [, camera_pos] -- vector3 diff --git a/indra/newview/scripts/lua/test_group_chat.lua b/indra/newview/scripts/lua/test_group_chat.lua new file mode 100644 index 0000000000..6299ba535f --- /dev/null +++ b/indra/newview/scripts/lua/test_group_chat.lua @@ -0,0 +1,16 @@ +LLChat = require 'LLChat' +inspect = require 'inspect' +LLAgent = require 'LLAgent' +popup = require 'popup' + +local OK = 'OK_okcancelbuttons' +local GROUPS = LLAgent.getGroups() + +-- Choose one of the groups randomly and send group message +math.randomseed(os.time()) +group_info = GROUPS[math.random(#GROUPS)] +LLChat.startGroupChat(group_info.id) +response = popup:alertYesCancel('Started group chat with ' .. group_info.name .. ' group. Send greetings?') +if next(response) == OK then + LLChat.sendGroupIM('Greetings', group_info.id) +end -- cgit v1.2.3 From 34c13264f33d9bbfc73302b84c8fade1b115fb93 Mon Sep 17 00:00:00 2001 From: AiraYumi Date: Sun, 11 Aug 2024 03:26:33 +0900 Subject: bump sdl2 2.30.6 (#2246) --- autobuild.xml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/autobuild.xml b/autobuild.xml index 7ce8372559..8eadb11c61 100644 --- a/autobuild.xml +++ b/autobuild.xml @@ -16,11 +16,11 @@ archive hash - ceb0392106c2f50d79dc724fd5a6d8ec82b92cdb + 6bbad9b30ee93749e4701313c537d5bac0850e2b hash_algorithm sha1 url - https://github.com/secondlife/3p-sdl2/releases/download/v2.28.0-r3/SDL2-2.28.0-linux64-8663899652.tar.zst + https://github.com/secondlife/3p-sdl2/releases/download/v2.30.6-r1/SDL2-2.30.6-linux64-10323260130.tar.zst name linux64 @@ -2114,11 +2114,11 @@ Copyright (c) 2012, 2014, 2015, 2016 nghttp2 contributors archive hash - f215c7e1a10f04a2c18cbb837e0039521fd150b6 + da16445129cc82d23d01709a912c99fd74388395 hash_algorithm sha1 url - https://github.com/secondlife/3p-open-libndofdev/releases/download/v1.14-r2/open_libndofdev-0.14.8730039102-linux64-8730039102.tar.zst + https://github.com/secondlife/3p-open-libndofdev/releases/download/v1.14-r3/open_libndofdev-0.14.10326946482-linux64-10326946482.tar.zst name linux64 -- cgit v1.2.3 From a596c1ad2864d639bbab13e7f13fd9f029cf5d93 Mon Sep 17 00:00:00 2001 From: Nat Goodspeed Date: Mon, 12 Aug 2024 15:58:03 -0400 Subject: Add Throttle and LogThrottle classes to manage throttled APIs. --- indra/llcommon/CMakeLists.txt | 2 + indra/llcommon/throttle.cpp | 34 +++++++++ indra/llcommon/throttle.h | 137 ++++++++++++++++++++++++++++++++++++ indra/newview/groupchatlistener.cpp | 19 +++-- indra/newview/groupchatlistener.h | 4 +- 5 files changed, 184 insertions(+), 12 deletions(-) create mode 100644 indra/llcommon/throttle.cpp create mode 100644 indra/llcommon/throttle.h diff --git a/indra/llcommon/CMakeLists.txt b/indra/llcommon/CMakeLists.txt index 20670d7ebe..4c4a676531 100644 --- a/indra/llcommon/CMakeLists.txt +++ b/indra/llcommon/CMakeLists.txt @@ -112,6 +112,7 @@ set(llcommon_SOURCE_FILES lua_function.cpp lualistener.cpp threadpool.cpp + throttle.cpp u64.cpp workqueue.cpp StackWalker.cpp @@ -263,6 +264,7 @@ set(llcommon_HEADER_FILES threadpool.h threadpool_fwd.h threadsafeschedule.h + throttle.h timer.h tuple.h u64.h diff --git a/indra/llcommon/throttle.cpp b/indra/llcommon/throttle.cpp new file mode 100644 index 0000000000..d5f7d5dcab --- /dev/null +++ b/indra/llcommon/throttle.cpp @@ -0,0 +1,34 @@ +/** + * @file throttle.cpp + * @author Nat Goodspeed + * @date 2024-08-12 + * @brief Implementation for ThrottleBase. + * + * $LicenseInfo:firstyear=2024&license=viewerlgpl$ + * Copyright (c) 2024, Linden Research, Inc. + * $/LicenseInfo$ + */ + +// Precompiled header +#include "linden_common.h" +// associated header +#include "throttle.h" +// STL headers +// std headers +// external library headers +// other Linden headers +#include "lltimer.h" + +bool ThrottleBase::too_fast() +{ + F64 now = LLTimer::getElapsedSeconds(); + if (now < mNext) + { + return true; + } + else + { + mNext = now + mInterval; + return false; + } +} diff --git a/indra/llcommon/throttle.h b/indra/llcommon/throttle.h new file mode 100644 index 0000000000..abf595e3c4 --- /dev/null +++ b/indra/llcommon/throttle.h @@ -0,0 +1,137 @@ +/** + * @file throttle.h + * @author Nat Goodspeed + * @date 2024-08-12 + * @brief Throttle class + * + * $LicenseInfo:firstyear=2024&license=viewerlgpl$ + * Copyright (c) 2024, Linden Research, Inc. + * $/LicenseInfo$ + */ + +#if ! defined(LL_THROTTLE_H) +#define LL_THROTTLE_H + +#include "apply.h" // LL::bind_front() +#include "llerror.h" +#include +#include // std::quoted() + +class ThrottleBase +{ +public: + ThrottleBase(F64 interval): + mInterval(interval) + {} + +protected: + bool too_fast(); // not const: we update mNext + F64 mInterval, mNext{ 0. }; +}; + +/** + * An instance of Throttle mediates calls to some other specified function, + * ensuring that it's called no more often than the specified time interval. + * Throttle is an abstract base class that delegates the behavior when the + * specified interval is exceeded. + */ +template +class Throttle: public ThrottleBase +{ +public: + Throttle(const std::string& desc, + const std::function& func, + F64 interval): + ThrottleBase(interval), + mDesc(desc), + mFunc(func) + {} + // Constructing Throttle with a member function pointer but without an + // instance pointer requires you to pass the instance pointer/reference as + // the first argument to operator()(). + template + Throttle(const std::string& desc, R C::* method, F64 interval): + Throttle(desc, std::mem_fn(method), interval) + {} + template + Throttle(const std::string& desc, R C::* method, C* instance, F64 interval): + Throttle(desc, LL::bind_front(method, instance), interval) + {} + template + Throttle(const std::string& desc, R C::* method, const C* instance, F64 interval): + Throttle(desc, LL::bind_front(method, instance), interval) + {} + + template + auto operator()(ARGS... args) + { + if (too_fast()) + { + suppress(); + using rtype = decltype(mFunc(std::forward(args)...)); + if constexpr (! std::is_same_v) + { + return rtype{}; + } + } + else + { + return mFunc(std::forward(args)...); + } + } + +protected: + // override with desired behavior when calls come too often + virtual void suppress() = 0; + const std::string mDesc; + +private: + std::function mFunc; +}; + +/** + * An instance of LogThrottle mediates calls to some other specified function, + * ensuring that it's called no more often than the specified time interval. + * When that interval is exceeded, it logs a message at the specified log + * level. It uses LL_MUMBLES_ONCE() logic to prevent spamming, since a too- + * frequent call may well be spammy. + */ +template +class LogThrottle: public Throttle +{ + using super = Throttle; +public: + LogThrottle(const std::string& desc, + const std::function& func, + F64 interval): + super(desc, func, interval) + {} + template + LogThrottle(const std::string& desc, R C::* method, F64 interval): + super(desc, method, interval) + {} + template + LogThrottle(const std::string& desc, R C::* method, C* instance, F64 interval): + super(desc, method, instance, interval) + {} + template + LogThrottle(const std::string& desc, R C::* method, const C* instance, F64 interval): + super(desc, method, instance, interval) + {} + +private: + void suppress() override + { + // Using lllog(), the macro underlying LL_WARNS() et al., allows + // specifying compile-time LOGLEVEL. It does NOT support a variable + // LOGLEVEL, which is why LOGLEVEL is a non-type template parameter. + // See llvlog() for variable support, which is a bit more expensive. + // true = only print the log message once + lllog(LOGLEVEL, true, "LogThrottle") << std::quoted(super::mDesc) + << " called more than once per " + << super::mInterval + << LL_ENDL; + } +}; + +#endif /* ! defined(LL_THROTTLE_H) */ diff --git a/indra/newview/groupchatlistener.cpp b/indra/newview/groupchatlistener.cpp index 09951ba1cc..298f41ff8c 100644 --- a/indra/newview/groupchatlistener.cpp +++ b/indra/newview/groupchatlistener.cpp @@ -43,7 +43,8 @@ static const F32 GROUP_CHAT_THROTTLE_PERIOD = 1.f; LLGroupChatListener::LLGroupChatListener(): LLEventAPI("GroupChat", "API to enter, leave, send and intercept group chat messages"), - mLastThrottleTime(0) + mIMThrottle("sendGroupIM", &LLGroupChatListener::sendGroupIM_, this, + GROUP_CHAT_THROTTLE_PERIOD) { add("startGroupChat", "Enter a group chat in group with UUID [\"group_id\"]\n" @@ -101,18 +102,14 @@ void LLGroupChatListener::sendGroupIM(LLSD const &data) return; } - F64 cur_time = LLTimer::getElapsedSeconds(); - - if (cur_time < mLastThrottleTime + GROUP_CHAT_THROTTLE_PERIOD) - { - LL_DEBUGS("LLGroupChatListener") << "'sendGroupIM' was throttled" << LL_ENDL; - return; - } - mLastThrottleTime = cur_time; + mIMThrottle(data["group_id"], data["message"]); +} - LLUUID group_id(data["group_id"]); - LLIMModel::sendMessage(LUA_PREFIX + data["message"].asString(), +void LLGroupChatListener::sendGroupIM_(const LLUUID& group_id, const std::string& message) +{ + LLIMModel::sendMessage(LUA_PREFIX + message, gIMMgr->computeSessionID(IM_SESSION_GROUP_START, group_id), group_id, IM_SESSION_SEND); } + diff --git a/indra/newview/groupchatlistener.h b/indra/newview/groupchatlistener.h index 35afc5766c..a75fecb254 100644 --- a/indra/newview/groupchatlistener.h +++ b/indra/newview/groupchatlistener.h @@ -30,6 +30,7 @@ #define LL_LLGROUPCHATLISTENER_H #include "lleventapi.h" +#include "throttle.h" class LLGroupChatListener: public LLEventAPI { @@ -40,8 +41,9 @@ private: void startGroupChat(LLSD const &data); void leaveGroupChat(LLSD const &data); void sendGroupIM(LLSD const &data); + void sendGroupIM_(const LLUUID& group_id, const std::string& message); - F64 mLastThrottleTime {0.0}; + LogThrottle mIMThrottle; }; #endif /* ! defined(LL_LLGROUPCHATLISTENER_H) */ -- cgit v1.2.3 From 153b0573cd91361a0f674c0ce1b9d7cb47e2bbc0 Mon Sep 17 00:00:00 2001 From: Nat Goodspeed Date: Mon, 12 Aug 2024 16:28:44 -0400 Subject: Add virtual destructor to Throttle class. --- indra/llcommon/throttle.h | 1 + 1 file changed, 1 insertion(+) diff --git a/indra/llcommon/throttle.h b/indra/llcommon/throttle.h index abf595e3c4..481d1d8f72 100644 --- a/indra/llcommon/throttle.h +++ b/indra/llcommon/throttle.h @@ -61,6 +61,7 @@ public: Throttle(const std::string& desc, R C::* method, const C* instance, F64 interval): Throttle(desc, LL::bind_front(method, instance), interval) {} + virtual ~Throttle() {} template auto operator()(ARGS... args) -- cgit v1.2.3 From 9f2e322c7eea6830d372943d74f986d299cd314a Mon Sep 17 00:00:00 2001 From: Mnikolenko Productengine Date: Tue, 13 Aug 2024 14:50:04 +0300 Subject: clean up and add comment --- indra/newview/groupchatlistener.cpp | 2 +- indra/newview/lltoastimpanel.cpp | 1 + indra/newview/scripts/lua/require/LLChat.lua | 10 +++------- indra/newview/scripts/lua/test_group_chat.lua | 1 - 4 files changed, 5 insertions(+), 9 deletions(-) diff --git a/indra/newview/groupchatlistener.cpp b/indra/newview/groupchatlistener.cpp index 298f41ff8c..e48cbe824a 100644 --- a/indra/newview/groupchatlistener.cpp +++ b/indra/newview/groupchatlistener.cpp @@ -38,7 +38,7 @@ #include "llgroupactions.h" #include "llimview.h" -static const F32 GROUP_CHAT_THROTTLE_PERIOD = 1.f; +static const F64 GROUP_CHAT_THROTTLE_PERIOD = 1.f; LLGroupChatListener::LLGroupChatListener(): LLEventAPI("GroupChat", diff --git a/indra/newview/lltoastimpanel.cpp b/indra/newview/lltoastimpanel.cpp index 7fadd8773b..b7353d6960 100644 --- a/indra/newview/lltoastimpanel.cpp +++ b/indra/newview/lltoastimpanel.cpp @@ -87,6 +87,7 @@ LLToastIMPanel::LLToastIMPanel(LLToastIMPanel::Params &p) : LLToastPanel(p.notif { LLAvatarName avatar_name; LLAvatarNameCache::get(p.avatar_id, &avatar_name); + // move Lua prefix from the message field to the [From] field auto [message, is_lua] = LLStringUtil::withoutPrefix(p.message, LUA_PREFIX); std::string prefix = is_lua ? "LUA - " : ""; p.message = "[From " + prefix + avatar_name.getDisplayName() + "]\n" + message; diff --git a/indra/newview/scripts/lua/require/LLChat.lua b/indra/newview/scripts/lua/require/LLChat.lua index ea6329d574..bc0fc86d22 100644 --- a/indra/newview/scripts/lua/require/LLChat.lua +++ b/indra/newview/scripts/lua/require/LLChat.lua @@ -6,8 +6,9 @@ local LLChat = {} -- Nearby chat -- *************************************************************************** -function LLChat.sendNearby(msg) - leap.send('LLChatBar', {op='sendChat', message=msg}) +-- 0 is public nearby channel, other channels are used to communicate with LSL scripts +function LLChat.sendNearby(msg, channel) + leap.send('LLChatBar', {op='sendChat', message=msg, channel=channel}) end function LLChat.sendWhisper(msg) @@ -18,11 +19,6 @@ function LLChat.sendShout(msg) leap.send('LLChatBar', {op='sendChat', type='shout', message=msg}) end --- 0 is public nearby channel, other channels are used to communicate with LSL scripts -function LLChat.sendChannel(msg, channel) - leap.send('LLChatBar', {op='sendChat', channel=channel, message=msg}) -end - -- *************************************************************************** -- Group chat -- *************************************************************************** diff --git a/indra/newview/scripts/lua/test_group_chat.lua b/indra/newview/scripts/lua/test_group_chat.lua index 6299ba535f..eaff07ed14 100644 --- a/indra/newview/scripts/lua/test_group_chat.lua +++ b/indra/newview/scripts/lua/test_group_chat.lua @@ -1,5 +1,4 @@ LLChat = require 'LLChat' -inspect = require 'inspect' LLAgent = require 'LLAgent' popup = require 'popup' -- cgit v1.2.3 From 493b8fbe7e435c1a831db2aecf33fd9aac13baf2 Mon Sep 17 00:00:00 2001 From: Nat Goodspeed Date: Tue, 13 Aug 2024 10:47:07 -0400 Subject: Update Luau to v0.638-r2 (2024-08-12 build) --- autobuild.xml | 42 +++++++++++++++++++++--------------------- 1 file changed, 21 insertions(+), 21 deletions(-) diff --git a/autobuild.xml b/autobuild.xml index bc5c979c95..00f9fb2470 100644 --- a/autobuild.xml +++ b/autobuild.xml @@ -1709,11 +1709,11 @@ archive hash - 59bf3d96f9df4b6981c406abac5c46ae276f9b15 + e48f291e2eeb1dbab39f26db5ac9b4c2b2d1c57d hash_algorithm sha1 url - https://github.com/secondlife/3p-luau/releases/download/v0.609-9567db9/luau-0.609-9567db9-darwin64-9567db9.tar.zst + https://github.com/secondlife/3p-luau/releases/download/v0.638-r2/luau-0.638.0-r2-darwin64-10356321602.tar.zst name darwin64 @@ -1723,11 +1723,11 @@ archive hash - 549516ada483ddf276183f66b0a7c3d01e4ef1aa + 3a797d8dae685b25ca787111a370b99d9e57d77c hash_algorithm sha1 url - https://github.com/secondlife/3p-luau/releases/download/v0.609-9567db9/luau-0.609-9567db9-linux64-9567db9.tar.zst + https://github.com/secondlife/3p-luau/releases/download/v0.638-r2/luau-0.638.0-r2-linux64-10356321602.tar.zst name linux64 @@ -1737,11 +1737,11 @@ archive hash - 43e2cc2e6e94299f89655435002864925b640e16 + a8857313496622134b00899141bbe6542f754164 hash_algorithm sha1 url - https://github.com/secondlife/3p-luau/releases/download/v0.609-9567db9/luau-0.609-9567db9-windows64-9567db9.tar.zst + https://github.com/secondlife/3p-luau/releases/download/v0.638-r2/luau-0.638.0-r2-windows64-10356321602.tar.zst name windows64 @@ -1754,7 +1754,7 @@ copyright Copyright (c) 2019-2023 Roblox Corporation, Copyright (c) 1994–2019 Lua.org, PUC-Rio. version - 0.609-9567db9 + 0.638.0-r2 name luau description @@ -1836,18 +1836,6 @@ mikktspace - canonical_repo - https://bitbucket.org/lindenlab/3p-mikktspace - copyright - Copyright (C) 2011 by Morten S. Mikkelsen, Copyright (C) 2022 Blender Authors - description - Mikktspace Tangent Generator - license - Apache 2.0 - license_file - mikktspace.txt - name - mikktspace platforms darwin64 @@ -1893,8 +1881,20 @@ windows64 + license + Apache 2.0 + license_file + mikktspace.txt + copyright + Copyright (C) 2011 by Morten S. Mikkelsen, Copyright (C) 2022 Blender Authors version 1 + name + mikktspace + canonical_repo + https://bitbucket.org/lindenlab/3p-mikktspace + description + Mikktspace Tangent Generator minizip-ng @@ -2811,6 +2811,8 @@ Copyright (c) 2012, 2014, 2015, 2016 nghttp2 contributors LICENSE copyright Copyright (c) 2000-2012, Linden Research, Inc. + version + 3.0-f14b5ec name viewer-manager description @@ -2819,8 +2821,6 @@ Copyright (c) 2012, 2014, 2015, 2016 nghttp2 contributors https://bitbucket.org/lindenlab/vmp-standalone source_type hg - version - 3.0-f14b5ec vlc-bin -- cgit v1.2.3 From b3fb23ee0c6d33f5eba3502328ffb0011b5f25fb Mon Sep 17 00:00:00 2001 From: Nat Goodspeed Date: Thu, 15 Aug 2024 09:49:34 -0400 Subject: Introduce lluau_checkstack(L, n); use instead of luaL_checkstack(). luaL_checkstack() accepts a third parameter which is included in the stack overflow error message. We've been passing nullptr, leading to messages of the form "stack overflow ((null))". lluau_checkstack() implicitly passes __FUNCTION__, so we can distinguish which underlying luaL_checkstack() call encountered the stack overflow condition. Also, when calling each atexit() function, pass Luau's debug.traceback() function as the lua_pcall() error handler. This should help diagnose errors in atexit() functions. --- indra/llcommon/lua_function.cpp | 57 ++++++++++++++++++++++++++--------------- indra/llcommon/lua_function.h | 13 ++++++---- indra/newview/llluamanager.cpp | 6 ++--- 3 files changed, 47 insertions(+), 29 deletions(-) diff --git a/indra/llcommon/lua_function.cpp b/indra/llcommon/lua_function.cpp index b173d17ede..defadea358 100644 --- a/indra/llcommon/lua_function.cpp +++ b/indra/llcommon/lua_function.cpp @@ -127,7 +127,7 @@ std::string lua_tostdstring(lua_State* L, int index) void lua_pushstdstring(lua_State* L, const std::string& str) { - luaL_checkstack(L, 1, nullptr); + lluau_checkstack(L, 1); lua_pushlstring(L, str.c_str(), str.length()); } @@ -240,10 +240,10 @@ LLSD lua_tollsd(lua_State* L, int index) // undefined LLSD. Naturally, though, those won't survive a second // round trip. - // This is the most important of the luaL_checkstack() calls because a + // This is the most important of the lluau_checkstack() calls because a // deeply nested Lua structure will enter this case at each level, and // we'll need another 2 stack slots to traverse each nested table. - luaL_checkstack(L, 2, nullptr); + lluau_checkstack(L, 2); // BEFORE we push nil to initialize the lua_next() traversal, convert // 'index' to absolute! Our caller might have passed a relative index; // we do, below: lua_tollsd(L, -1). If 'index' is -1, then when we @@ -400,7 +400,7 @@ LLSD lua_tollsd(lua_State* L, int index) void lua_pushllsd(lua_State* L, const LLSD& data) { // might need 2 slots for array or map - luaL_checkstack(L, 2, nullptr); + lluau_checkstack(L, 2); switch (data.type()) { case LLSD::TypeUndefined: @@ -487,39 +487,54 @@ LuaState::LuaState(script_finished_fn cb): LuaState::~LuaState() { - // We're just about to destroy this lua_State mState. lua_close() doesn't - // implicitly garbage-collect everything, so (for instance) any lingering - // objects with __gc metadata methods aren't cleaned up. This is why we - // provide atexit(). - luaL_checkstack(mState, 2, nullptr); + // We're just about to destroy this lua_State mState. Did this Lua chunk + // register any atexit() functions? + lluau_checkstack(mState, 3); // look up Registry.atexit lua_getfield(mState, LUA_REGISTRYINDEX, "atexit"); // stack contains Registry.atexit if (lua_istable(mState, -1)) { + // Push debug.traceback() onto the stack as lua_pcall()'s error + // handler function. On error, lua_pcall() calls the specified error + // handler function with the original error message; the message + // returned by the error handler is then returned by lua_pcall(). + // Luau's debug.traceback() is called with a message to prepend to the + // returned traceback string. Almost as if they'd been designed to + // work together... + lua_getglobal(mState, "debug"); + lua_getfield(mState, -1, "traceback"); + // ditch "debug" + lua_remove(mState, -2); + // stack now contains atexit, debug.traceback() + // We happen to know that Registry.atexit is built by appending array // entries using table.insert(). That's important because it means // there are no holes, and therefore lua_objlen() should be correct. // That's important because we walk the atexit table backwards, to // destroy last the things we created (passed to LL.atexit()) first. - for (int i(lua_objlen(mState, -1)); i >= 1; --i) + for (int i(lua_objlen(mState, -2)); i >= 1; --i) { lua_pushinteger(mState, i); - // stack contains Registry.atexit, i - lua_gettable(mState, -2); - // stack contains Registry.atexit, atexit[i] + // stack contains Registry.atexit, debug.traceback(), i + lua_gettable(mState, -3); + // stack contains Registry.atexit, debug.traceback(), atexit[i] // Call atexit[i](), no args, no return values. // Use lua_pcall() because errors in any one atexit() function - // shouldn't cancel the rest of them. - if (lua_pcall(mState, 0, 0, 0) != LUA_OK) + // shouldn't cancel the rest of them. Pass debug.traceback() as + // the error handler function. + if (lua_pcall(mState, 0, 0, -2) != LUA_OK) { auto error{ lua_tostdstring(mState, -1) }; LL_WARNS("Lua") << "atexit() function error: " << error << LL_ENDL; // pop error message lua_pop(mState, 1); } - // lua_pcall() has already popped atexit[i]: stack contains atexit + // lua_pcall() has already popped atexit[i]: + // stack contains atexit, debug.traceback() } + // pop debug.traceback() + lua_pop(mState, 1); } // pop Registry.atexit (either table or nil) lua_pop(mState, 1); @@ -625,7 +640,7 @@ std::pair LuaState::expr(const std::string& desc, const std::string& LuaListener& LuaState::obtainListener(lua_State* L) { - luaL_checkstack(L, 2, nullptr); + lluau_checkstack(L, 2); lua_getfield(L, LUA_REGISTRYINDEX, "LuaListener"); // compare lua_type() because lua_isuserdata() also accepts light userdata if (lua_type(L, -1) != LUA_TUSERDATA) @@ -655,7 +670,7 @@ LuaListener& LuaState::obtainListener(lua_State* L) lua_function(atexit, "atexit(function): " "register Lua function to be called at script termination") { - luaL_checkstack(L, 4, nullptr); + lluau_checkstack(L, 4); // look up the global name "table" lua_getglobal(L, "table"); // stack contains function, table @@ -705,7 +720,7 @@ LuaFunction::LuaFunction(const std::string_view& name, lua_CFunction function, void LuaFunction::init(lua_State* L) { const auto& [registry, lookup] = getRState(); - luaL_checkstack(L, 2, nullptr); + lluau_checkstack(L, 2); // create LL table -- // it happens that we know exactly how many non-array members we want lua_createtable(L, 0, int(narrow(lookup.size()))); @@ -743,7 +758,7 @@ std::pair LuaFunction::getState() *****************************************************************************/ lua_function(source_path, "source_path(): return the source path of the running Lua script") { - luaL_checkstack(L, 1, nullptr); + lluau_checkstack(L, 1); lua_pushstdstring(L, lluau::source_path(L).u8string()); return 1; } @@ -753,7 +768,7 @@ lua_function(source_path, "source_path(): return the source path of the running *****************************************************************************/ lua_function(source_dir, "source_dir(): return the source directory of the running Lua script") { - luaL_checkstack(L, 1, nullptr); + lluau_checkstack(L, 1); lua_pushstdstring(L, lluau::source_path(L).parent_path().u8string()); return 1; } diff --git a/indra/llcommon/lua_function.h b/indra/llcommon/lua_function.h index 7b59af30f5..c1a4d736a0 100644 --- a/indra/llcommon/lua_function.h +++ b/indra/llcommon/lua_function.h @@ -65,6 +65,9 @@ namespace lluau void check_interrupts_counter(lua_State* L); } // namespace lluau +// must be a macro because __FUNCTION__ is context-sensitive +#define lluau_checkstack(L, n) luaL_checkstack((L), (n), __FUNCTION__) + std::string lua_tostdstring(lua_State* L, int index); void lua_pushstdstring(lua_State* L, const std::string& str); LLSD lua_tollsd(lua_State* L, int index); @@ -294,7 +297,7 @@ auto lua_to(lua_State* L, int index) template auto lua_getfieldv(lua_State* L, int index, const char* k) { - luaL_checkstack(L, 1, nullptr); + lluau_checkstack(L, 1); lua_getfield(L, index, k); LuaPopper pop(L, 1); return lua_to(L, -1); @@ -304,7 +307,7 @@ auto lua_getfieldv(lua_State* L, int index, const char* k) template auto lua_setfieldv(lua_State* L, int index, const char* k, const T& value) { - luaL_checkstack(L, 1, nullptr); + lluau_checkstack(L, 1); lua_push(L, value); lua_setfield(L, index, k); } @@ -313,7 +316,7 @@ auto lua_setfieldv(lua_State* L, int index, const char* k, const T& value) template auto lua_rawgetfield(lua_State* L, int index, const std::string_view& k) { - luaL_checkstack(L, 1, nullptr); + lluau_checkstack(L, 1); lua_pushlstring(L, k.data(), k.length()); lua_rawget(L, index); LuaPopper pop(L, 1); @@ -324,7 +327,7 @@ auto lua_rawgetfield(lua_State* L, int index, const std::string_view& k) template void lua_rawsetfield(lua_State* L, int index, const std::string_view& k, const T& value) { - luaL_checkstack(L, 2, nullptr); + lluau_checkstack(L, 2); lua_pushlstring(L, k.data(), k.length()); lua_push(L, value); lua_rawset(L, index); @@ -430,7 +433,7 @@ DistinctInt TypeTag::value; template void lua_emplace(lua_State* L, ARGS&&... args) { - luaL_checkstack(L, 1, nullptr); + lluau_checkstack(L, 1); int tag{ TypeTag::value }; if (! lua_getuserdatadtor(L, tag)) { diff --git a/indra/newview/llluamanager.cpp b/indra/newview/llluamanager.cpp index 7014c59e4e..0b1a73f4e9 100644 --- a/indra/newview/llluamanager.cpp +++ b/indra/newview/llluamanager.cpp @@ -66,7 +66,7 @@ std::string lua_print_msg(lua_State* L, const std::string_view& level) { // On top of existing Lua arguments, we're going to push tostring() and // duplicate each existing stack entry so we can stringize each one. - luaL_checkstack(L, 2, nullptr); + lluau_checkstack(L, 2); luaL_where(L, 1); // start with the 'where' info at the top of the stack std::ostringstream out; @@ -139,7 +139,7 @@ lua_function(get_event_pumps, "Events posted to replypump are queued for get_event_next().\n" "post_on(commandpump, ...) to engage LLEventAPI operations (see helpleap()).") { - luaL_checkstack(L, 2, nullptr); + lluau_checkstack(L, 2); auto& listener{ LuaState::obtainListener(L) }; // return the reply pump name and the command pump name on caller's lua_State lua_pushstdstring(L, listener.getReplyName()); @@ -153,7 +153,7 @@ lua_function(get_event_next, "is returned by get_event_pumps(). Blocks the calling chunk until an\n" "event becomes available.") { - luaL_checkstack(L, 2, nullptr); + lluau_checkstack(L, 2); auto& listener{ LuaState::obtainListener(L) }; const auto& [pump, data]{ listener.getNext() }; lua_pushstdstring(L, pump); -- cgit v1.2.3 From ab0f7ff14cd80b89524ba95eb5a39e2d6df55b26 Mon Sep 17 00:00:00 2001 From: Mnikolenko Productengine Date: Thu, 15 Aug 2024 23:29:54 +0300 Subject: First batch of Inventory api; raise interrupts limit --- indra/llcommon/lua_function.cpp | 2 +- indra/llinventory/llfoldertype.cpp | 17 +++ indra/llinventory/llfoldertype.h | 2 + indra/newview/CMakeLists.txt | 2 + indra/newview/llinventorylistener.cpp | 130 ++++++++++++++++++++++ indra/newview/llinventorylistener.h | 45 ++++++++ indra/newview/llviewerinventory.cpp | 3 + indra/newview/scripts/lua/require/LLInventory.lua | 28 +++++ 8 files changed, 228 insertions(+), 1 deletion(-) create mode 100644 indra/newview/llinventorylistener.cpp create mode 100644 indra/newview/llinventorylistener.h create mode 100644 indra/newview/scripts/lua/require/LLInventory.lua diff --git a/indra/llcommon/lua_function.cpp b/indra/llcommon/lua_function.cpp index b173d17ede..59aa2a0ba7 100644 --- a/indra/llcommon/lua_function.cpp +++ b/indra/llcommon/lua_function.cpp @@ -34,7 +34,7 @@ using namespace std::literals; // e.g. std::string_view literals: "this"sv -const S32 INTERRUPTS_MAX_LIMIT = 20000; +const S32 INTERRUPTS_MAX_LIMIT = 100000; const S32 INTERRUPTS_SUSPEND_LIMIT = 100; #define lua_register(L, n, f) (lua_pushcfunction(L, (f), n), lua_setglobal(L, (n))) diff --git a/indra/llinventory/llfoldertype.cpp b/indra/llinventory/llfoldertype.cpp index 8f968ae2fd..158759c957 100644 --- a/indra/llinventory/llfoldertype.cpp +++ b/indra/llinventory/llfoldertype.cpp @@ -29,6 +29,7 @@ #include "llfoldertype.h" #include "lldictionary.h" #include "llmemory.h" +#include "llsd.h" #include "llsingleton.h" ///---------------------------------------------------------------------------- @@ -220,3 +221,19 @@ const std::string &LLFolderType::badLookup() static const std::string sBadLookup = "llfoldertype_bad_lookup"; return sBadLookup; } + +LLSD LLFolderType::getTypeNames() +{ + LLSD type_names; + for (S32 type = FT_TEXTURE; type < FT_COUNT; ++type) + { + if (lookupIsEnsembleType((LLFolderType::EType)type)) continue; + + const FolderEntry *entry = LLFolderDictionary::getInstance()->lookup((LLFolderType::EType)type); + if (entry) + { + type_names.append(entry->mName); + } + } + return type_names; +} diff --git a/indra/llinventory/llfoldertype.h b/indra/llinventory/llfoldertype.h index 46a1b92a96..dd12693f66 100644 --- a/indra/llinventory/llfoldertype.h +++ b/indra/llinventory/llfoldertype.h @@ -115,6 +115,8 @@ public: static const std::string& badLookup(); // error string when a lookup fails + static LLSD getTypeNames(); + protected: LLFolderType() {} ~LLFolderType() {} diff --git a/indra/newview/CMakeLists.txt b/indra/newview/CMakeLists.txt index 4e7e072289..04eb2c1d6d 100644 --- a/indra/newview/CMakeLists.txt +++ b/indra/newview/CMakeLists.txt @@ -360,6 +360,7 @@ set(viewer_SOURCE_FILES llinventorygallerymenu.cpp llinventoryicon.cpp llinventoryitemslist.cpp + llinventorylistener.cpp llinventorylistitem.cpp llinventorymodel.cpp llinventorymodelbackgroundfetch.cpp @@ -1027,6 +1028,7 @@ set(viewer_HEADER_FILES llinventorygallerymenu.h llinventoryicon.h llinventoryitemslist.h + llinventorylistener.h llinventorylistitem.h llinventorymodel.h llinventorymodelbackgroundfetch.h diff --git a/indra/newview/llinventorylistener.cpp b/indra/newview/llinventorylistener.cpp new file mode 100644 index 0000000000..a8269bb80d --- /dev/null +++ b/indra/newview/llinventorylistener.cpp @@ -0,0 +1,130 @@ +/** + * @file llinventorylistener.cpp + * + * $LicenseInfo:firstyear=2024&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2024, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#include "llviewerprecompiledheaders.h" + +#include "llinventorylistener.h" + +#include "llappearancemgr.h" +#include "llinventoryfunctions.h" +#include "lltransutil.h" +#include "llwearableitemslist.h" +#include "stringize.h" + +LLInventoryListener::LLInventoryListener() + : LLEventAPI("LLInventory", + "API for interactions with viewer Inventory items") +{ + add("getItemsInfo", + "Return information about items or folders defined in [\"items_id\"]:\n" + "reply will contain [\"items\"] and [\"categories\"] tables accordingly", + &LLInventoryListener::getItemsInfo, + llsd::map("items_id", LLSD(), "reply", LLSD())); + + add("getFolderTypeNames", + "Return the table of folder type names, contained in [\"type_names\"]\n", + &LLInventoryListener::getFolderTypeNames, + llsd::map("reply", LLSD())); + + add("getBasicFolderID", + "Return the UUID of the folder by specified folder type name, for example:\n" + "\"Textures\", \"My outfits\", \"Sounds\" and other basic folders which have associated type", + &LLInventoryListener::getBasicFolderID, + llsd::map("ft_name", LLSD(), "reply", LLSD())); + + add("getDirectDescendents", + "Return the direct descendents(both items and folders) of the [\"folder_id\"]", + &LLInventoryListener::getDirectDescendents, + llsd::map("folder_id", LLSD(), "reply", LLSD())); + } + + +void add_item_info(LLEventAPI::Response& response, LLViewerInventoryItem* item) +{ + response["items"].insert(item->getUUID().asString(), + llsd::map("name", item->getName(), "parent_id", item->getParentUUID(), "desc", item->getDescription(), + "inv_type", LLInventoryType::lookup(item->getInventoryType()), "creation_date", + (S32) item->getCreationDate(), "asset_id", item->getAssetUUID(), "is_link", item->getIsLinkType(), + "linked_id", item->getLinkedUUID())); +} + +void add_cat_info(LLEventAPI::Response &response, LLViewerInventoryCategory *cat) +{ + response["categories"].insert(cat->getUUID().asString(), + llsd::map("name", cat->getName(), "parent_id", cat->getParentUUID(), "type", + LLFolderType::lookup(cat->getPreferredType()), "creation_date", (S32) cat->getCreationDate(), + "is_link", cat->getIsLinkType(), "linked_id", cat->getLinkedUUID())); +} + +void LLInventoryListener::getItemsInfo(LLSD const &data) +{ + Response response(LLSD(), data); + + uuid_vec_t ids = LLSDParam(data["items_id"]); + for (auto &it : ids) + { + LLViewerInventoryItem* item = gInventory.getItem(it); + if (item) + { + add_item_info(response, item); + } + LLViewerInventoryCategory* cat = gInventory.getCategory(it); + if (cat) + { + add_cat_info(response, cat); + } + } +} + +void LLInventoryListener::getFolderTypeNames(LLSD const &data) +{ + Response response(llsd::map("type_names", LLFolderType::getTypeNames()), data); +} + +void LLInventoryListener::getBasicFolderID(LLSD const &data) +{ + Response response(llsd::map("id", gInventory.findCategoryUUIDForType(LLFolderType::lookup(data["ft_name"].asString()))), data); +} + + +void LLInventoryListener::getDirectDescendents(LLSD const &data) +{ + Response response(LLSD(), data); + LLInventoryModel::cat_array_t* cats; + LLInventoryModel::item_array_t* items; + gInventory.getDirectDescendentsOf(data["folder_id"], cats, items); + + LLInventoryModel::item_array_t items_copy = *items; + for (LLInventoryModel::item_array_t::iterator iter = items_copy.begin(); iter != items_copy.end(); iter++) + { + add_item_info(response, *iter); + } + LLInventoryModel::cat_array_t cats_copy = *cats; + for (LLInventoryModel::cat_array_t::iterator iter = cats_copy.begin(); iter != cats_copy.end(); iter++) + { + add_cat_info(response, *iter); + } +} + diff --git a/indra/newview/llinventorylistener.h b/indra/newview/llinventorylistener.h new file mode 100644 index 0000000000..e148df48fe --- /dev/null +++ b/indra/newview/llinventorylistener.h @@ -0,0 +1,45 @@ +/** + * @file llinventorylistener.h + * + * $LicenseInfo:firstyear=2024&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2024, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + + +#ifndef LL_LLINVENTORYLISTENER_H +#define LL_LLINVENTORYLISTENER_H + +#include "lleventapi.h" + +class LLInventoryListener : public LLEventAPI +{ +public: + LLInventoryListener(); + +private: + void getItemsInfo(LLSD const &data); + void getFolderTypeNames(LLSD const &data); + void getBasicFolderID(LLSD const &data); + void getDirectDescendents(LLSD const &data); +}; + +#endif // LL_LLINVENTORYLISTENER_H + diff --git a/indra/newview/llviewerinventory.cpp b/indra/newview/llviewerinventory.cpp index 16810efa01..7030426e21 100644 --- a/indra/newview/llviewerinventory.cpp +++ b/indra/newview/llviewerinventory.cpp @@ -71,6 +71,9 @@ #include "llclipboard.h" #include "llhttpretrypolicy.h" #include "llsettingsvo.h" +#include "llinventorylistener.h" + +LLInventoryListener sInventoryListener; // do-nothing ops for use in callbacks. void no_op_inventory_func(const LLUUID&) {} diff --git a/indra/newview/scripts/lua/require/LLInventory.lua b/indra/newview/scripts/lua/require/LLInventory.lua new file mode 100644 index 0000000000..880a2516f1 --- /dev/null +++ b/indra/newview/scripts/lua/require/LLInventory.lua @@ -0,0 +1,28 @@ +local leap = require 'leap' +local mapargs = require 'mapargs' + +local LLInventory = {} + +-- Get the items/folders info by provided IDs, +-- reply will contain "items" and "categories" tables accordingly +function LLInventory.getItemsInfo(items_id) + return leap.request('LLInventory', {op = 'getItemsInfo', items_id=items_id}) +end + +-- Get the table of folder type names, which can be later used to get the ID of the basic folders +function LLInventory.getFolderTypeNames() + return leap.request('LLInventory', {op = 'getFolderTypeNames'}).type_names +end + +-- Get the UUID of the basic folder("Textures", "My outfits", "Sounds" etc.) by specified folder type name +function LLInventory.getBasicFolderID(ft_name) + return leap.request('LLInventory', {op = 'getBasicFolderID', ft_name=ft_name}).id +end + +-- Get the direct descendents of the 'folder_id' provided, +-- reply will contain "items" and "categories" tables accordingly +function LLInventory.getDirectDescendents(folder_id) + return leap.request('LLInventory', {op = 'getDirectDescendents', folder_id=folder_id}) +end + +return LLInventory -- cgit v1.2.3 From c4e1b801b639c5408ec6e6aae615a4bf1e673efe Mon Sep 17 00:00:00 2001 From: Andrey Lihatskiy Date: Tue, 20 Aug 2024 06:30:12 +0300 Subject: Post-merge build fix (#2059) --- indra/llcommon/llexception.cpp | 3 --- indra/llcommon/llexception.h | 7 +++++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/indra/llcommon/llexception.cpp b/indra/llcommon/llexception.cpp index 107fdc2b2d..0a2f2ab607 100644 --- a/indra/llcommon/llexception.cpp +++ b/indra/llcommon/llexception.cpp @@ -18,9 +18,6 @@ #include #include #include -#if LL_WINDOWS -#include -#endif // LL_WINDOWS // external library headers #include #include diff --git a/indra/llcommon/llexception.h b/indra/llcommon/llexception.h index f58a553eb3..2a8c18507a 100644 --- a/indra/llcommon/llexception.h +++ b/indra/llcommon/llexception.h @@ -17,6 +17,9 @@ #include #include #include +#if LL_WINDOWS +#include +#endif // LL_WINDOWS // "Found someone who can comfort me // But there are always exceptions..." @@ -180,6 +183,8 @@ auto catcher(TRYCODE&& trycode, HANDLER&& handler) std::forward(handler)); } +[[noreturn]] void rethrow(U32 code, const std::string& stacktrace); + // monadic variant specifies try(), assumes default filter and handler template auto catcher(TRYCODE&& trycode) @@ -187,8 +192,6 @@ auto catcher(TRYCODE&& trycode) return catcher(std::forward(trycode), rethrow); } -[[noreturn]] void rethrow(U32 code, const std::string& stacktrace); - #else // not LL_WINDOWS ----------------------------------------------------- template -- cgit v1.2.3 From b4ed0089d7d1e01403a45af8e12889debf29b0f0 Mon Sep 17 00:00:00 2001 From: Andrey Lihatskiy Date: Tue, 20 Aug 2024 07:11:17 +0300 Subject: Temporarily disable linux builds (#2059) --- .github/workflows/build.yaml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index 8988453b0b..6fe5a75f48 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -48,9 +48,9 @@ jobs: matrix: runner: [windows-large, macos-12-xl] configuration: ${{ fromJSON(needs.setup.outputs.configurations) }} - include: - - runner: linux-large - configuration: ReleaseOS + # include: + # - runner: linux-large + # configuration: ReleaseOS runs-on: ${{ matrix.runner }} outputs: viewer_channel: ${{ steps.build.outputs.viewer_channel }} -- cgit v1.2.3 From 0fd5efbb2bd5f50dcb7228676bc4ce5c288da8e5 Mon Sep 17 00:00:00 2001 From: Nicky Date: Tue, 20 Aug 2024 20:11:49 +0200 Subject: include for dstd::unique_ptr template --- indra/llcommon/llapr.h | 1 + 1 file changed, 1 insertion(+) diff --git a/indra/llcommon/llapr.h b/indra/llcommon/llapr.h index 00ff4d60b7..faf2d6fc92 100644 --- a/indra/llcommon/llapr.h +++ b/indra/llcommon/llapr.h @@ -33,6 +33,7 @@ #include // Need PATH_MAX in APR headers... #endif +#include #include #include "llwin32headerslean.h" #include "apr_thread_proc.h" -- cgit v1.2.3 From 3cef79d979f2d21c29d17d123d6c166b9f7e7e0e Mon Sep 17 00:00:00 2001 From: Mnikolenko Productengine Date: Tue, 20 Aug 2024 23:09:14 +0300 Subject: Add collectDescendentsIf api for Lua --- indra/llcommon/llassettype.cpp | 17 +++ indra/llcommon/llassettype.h | 2 + indra/llinventory/llfoldertype.cpp | 1 + indra/newview/llinventorylistener.cpp | 140 +++++++++++++++++++++- indra/newview/llinventorylistener.h | 27 +++++ indra/newview/scripts/lua/require/LLInventory.lua | 19 +++ 6 files changed, 200 insertions(+), 6 deletions(-) diff --git a/indra/llcommon/llassettype.cpp b/indra/llcommon/llassettype.cpp index 3e46bde954..1c0893b1bf 100644 --- a/indra/llcommon/llassettype.cpp +++ b/indra/llcommon/llassettype.cpp @@ -30,6 +30,7 @@ #include "lldictionary.h" #include "llmemory.h" #include "llsingleton.h" +#include "llsd.h" ///---------------------------------------------------------------------------- /// Class LLAssetType @@ -244,3 +245,19 @@ bool LLAssetType::lookupIsAssetIDKnowable(EType asset_type) } return false; } + +LLSD LLAssetType::getTypeNames() +{ + LLSD type_names; + const LLAssetDictionary *dict = LLAssetDictionary::getInstance(); + for (S32 type = AT_TEXTURE; type < AT_COUNT; ++type) + { + const AssetEntry *entry = dict->lookup((LLAssetType::EType) type); + // skip llassettype_bad_lookup + if (entry) + { + type_names.append(entry->mTypeName); + } + } + return type_names; +} diff --git a/indra/llcommon/llassettype.h b/indra/llcommon/llassettype.h index 1989155550..4185063ae5 100644 --- a/indra/llcommon/llassettype.h +++ b/indra/llcommon/llassettype.h @@ -163,6 +163,8 @@ public: static bool lookupIsAssetFetchByIDAllowed(EType asset_type); // the asset allows direct download static bool lookupIsAssetIDKnowable(EType asset_type); // asset data can be known by the viewer + static LLSD getTypeNames(); + static const std::string BADLOOKUP; protected: diff --git a/indra/llinventory/llfoldertype.cpp b/indra/llinventory/llfoldertype.cpp index 158759c957..c8ef1ccc1b 100644 --- a/indra/llinventory/llfoldertype.cpp +++ b/indra/llinventory/llfoldertype.cpp @@ -230,6 +230,7 @@ LLSD LLFolderType::getTypeNames() if (lookupIsEnsembleType((LLFolderType::EType)type)) continue; const FolderEntry *entry = LLFolderDictionary::getInstance()->lookup((LLFolderType::EType)type); + //skip llfoldertype_bad_lookup if (entry) { type_names.append(entry->mName); diff --git a/indra/newview/llinventorylistener.cpp b/indra/newview/llinventorylistener.cpp index a8269bb80d..4beb59c58e 100644 --- a/indra/newview/llinventorylistener.cpp +++ b/indra/newview/llinventorylistener.cpp @@ -48,6 +48,11 @@ LLInventoryListener::LLInventoryListener() &LLInventoryListener::getFolderTypeNames, llsd::map("reply", LLSD())); + add("getAssetTypeNames", + "Return the table of asset type names, contained in [\"type_names\"]\n", + &LLInventoryListener::getAssetTypeNames, + llsd::map("reply", LLSD())); + add("getBasicFolderID", "Return the UUID of the folder by specified folder type name, for example:\n" "\"Textures\", \"My outfits\", \"Sounds\" and other basic folders which have associated type", @@ -58,6 +63,15 @@ LLInventoryListener::LLInventoryListener() "Return the direct descendents(both items and folders) of the [\"folder_id\"]", &LLInventoryListener::getDirectDescendents, llsd::map("folder_id", LLSD(), "reply", LLSD())); + + add("collectDescendentsIf", + "Return the descendents(both items and folders) of the [\"folder_id\"], if it passes specified filters:\n" + "[\"name\"] is a substring of object's name,\n" + "[\"desc\"] is a substring of object's description,\n" + "asset [\"type\"] corresponds to the object's asset type\n" + "[\"filter_links\"]: EXCLUDE_LINKS - don't show links, ONLY_LINKS - only show links, INCLUDE_LINKS - show links too (default)", + &LLInventoryListener::collectDescendentsIf, + llsd::map("folder_id", LLSD(), "reply", LLSD())); } @@ -65,17 +79,15 @@ void add_item_info(LLEventAPI::Response& response, LLViewerInventoryItem* item) { response["items"].insert(item->getUUID().asString(), llsd::map("name", item->getName(), "parent_id", item->getParentUUID(), "desc", item->getDescription(), - "inv_type", LLInventoryType::lookup(item->getInventoryType()), "creation_date", - (S32) item->getCreationDate(), "asset_id", item->getAssetUUID(), "is_link", item->getIsLinkType(), - "linked_id", item->getLinkedUUID())); + "inv_type", LLInventoryType::lookup(item->getInventoryType()), "asset_type", LLAssetType::lookup(item->getType()), + "creation_date", (S32) item->getCreationDate(), "asset_id", item->getAssetUUID(), + "is_link", item->getIsLinkType(), "linked_id", item->getLinkedUUID())); } void add_cat_info(LLEventAPI::Response &response, LLViewerInventoryCategory *cat) { response["categories"].insert(cat->getUUID().asString(), - llsd::map("name", cat->getName(), "parent_id", cat->getParentUUID(), "type", - LLFolderType::lookup(cat->getPreferredType()), "creation_date", (S32) cat->getCreationDate(), - "is_link", cat->getIsLinkType(), "linked_id", cat->getLinkedUUID())); + llsd::map("name", cat->getName(), "parent_id", cat->getParentUUID(), "type", LLFolderType::lookup(cat->getPreferredType()))); } void LLInventoryListener::getItemsInfo(LLSD const &data) @@ -103,6 +115,11 @@ void LLInventoryListener::getFolderTypeNames(LLSD const &data) Response response(llsd::map("type_names", LLFolderType::getTypeNames()), data); } +void LLInventoryListener::getAssetTypeNames(LLSD const &data) +{ + Response response(llsd::map("type_names", LLAssetType::getTypeNames()), data); +} + void LLInventoryListener::getBasicFolderID(LLSD const &data) { Response response(llsd::map("id", gInventory.findCategoryUUIDForType(LLFolderType::lookup(data["ft_name"].asString()))), data); @@ -128,3 +145,114 @@ void LLInventoryListener::getDirectDescendents(LLSD const &data) } } +void LLInventoryListener::collectDescendentsIf(LLSD const &data) +{ + Response response(LLSD(), data); + LLUUID folder_id(data["folder_id"].asUUID()); + LLViewerInventoryCategory *cat = gInventory.getCategory(folder_id); + if (!cat) + { + return response.error(stringize("Folder ", std::quoted(data["folder_id"].asString()), " was not found")); + } + LLInventoryModel::cat_array_t cat_array; + LLInventoryModel::item_array_t item_array; + + LLFilteredCollector collector = LLFilteredCollector(data); + + gInventory.collectDescendentsIf(folder_id, cat_array, item_array, LLInventoryModel::EXCLUDE_TRASH, collector); + + for (LLInventoryModel::item_array_t::iterator iter = item_array.begin(); iter != item_array.end(); iter++) + { + add_item_info(response, *iter); + } + for (LLInventoryModel::cat_array_t::iterator iter = cat_array.begin(); iter != cat_array.end(); iter++) + { + add_cat_info(response, *iter); + } +} + +LLFilteredCollector::LLFilteredCollector(LLSD const &data) + : mType(LLAssetType::EType::AT_UNKNOWN), + mLinkFilter(INCLUDE_LINKS) +{ + if (data.has("name")) + { + mName = data["name"]; + } + if (data.has("desc")) + { + mDesc = data["desc"]; + } + if (data.has("type")) + { + mType = LLAssetType::lookup(data["type"]); + } + if (data.has("filter_links")) + { + if (data["filter_links"] == "EXCLUDE_LINKS") + { + mLinkFilter = EXCLUDE_LINKS; + } + else if (data["filter_links"] == "ONLY_LINKS") + { + mLinkFilter = ONLY_LINKS; + } + } +} + +bool LLFilteredCollector::operator()(LLInventoryCategory *cat, LLInventoryItem *item) +{ + bool passed = checkagainstType(cat, item); + passed = passed && checkagainstNameDesc(cat, item); + passed = passed && checkagainstLinks(cat, item); + + return passed; +} + +bool LLFilteredCollector::checkagainstNameDesc(LLInventoryCategory *cat, LLInventoryItem *item) +{ + std::string name, desc; + bool passed(true); + if (cat) + { + if (!mDesc.empty()) return false; + name = cat->getName(); + } + if (item) + { + name = item->getName(); + passed = (mDesc.size() ? item->getDescription().find(mDesc) != std::string::npos : true); + } + + return passed && (mName.size() ? name.find(mName) != std::string::npos : true); +} + +bool LLFilteredCollector::checkagainstType(LLInventoryCategory *cat, LLInventoryItem *item) +{ + if (mType == LLAssetType::AT_UNKNOWN) + { + return true; + } + if (mType == LLAssetType::AT_CATEGORY) + { + if (cat) + { + return true; + } + } + if (item && item->getType() == mType) + { + return true; + } + return false; +} + +bool LLFilteredCollector::checkagainstLinks(LLInventoryCategory *cat, LLInventoryItem *item) +{ + bool is_link = cat ? cat->getIsLinkType() : item->getIsLinkType(); + if (is_link && (mLinkFilter == EXCLUDE_LINKS)) + return false; + if (!is_link && (mLinkFilter == ONLY_LINKS)) + return false; + return true; +} diff --git a/indra/newview/llinventorylistener.h b/indra/newview/llinventorylistener.h index e148df48fe..83e1751e1b 100644 --- a/indra/newview/llinventorylistener.h +++ b/indra/newview/llinventorylistener.h @@ -28,6 +28,7 @@ #define LL_LLINVENTORYLISTENER_H #include "lleventapi.h" +#include "llinventoryfunctions.h" class LLInventoryListener : public LLEventAPI { @@ -37,8 +38,34 @@ public: private: void getItemsInfo(LLSD const &data); void getFolderTypeNames(LLSD const &data); + void getAssetTypeNames(LLSD const &data); void getBasicFolderID(LLSD const &data); void getDirectDescendents(LLSD const &data); + void collectDescendentsIf(LLSD const &data); +}; + +struct LLFilteredCollector : public LLInventoryCollectFunctor +{ + enum EFilterLink + { + INCLUDE_LINKS, // show links too + EXCLUDE_LINKS, // don't show links + ONLY_LINKS // only show links + }; + + LLFilteredCollector(LLSD const &data); + virtual ~LLFilteredCollector() {} + virtual bool operator()(LLInventoryCategory *cat, LLInventoryItem *item); + + protected: + bool checkagainstType(LLInventoryCategory *cat, LLInventoryItem *item); + bool checkagainstNameDesc(LLInventoryCategory *cat, LLInventoryItem *item); + bool checkagainstLinks(LLInventoryCategory *cat, LLInventoryItem *item); + + LLAssetType::EType mType; + std::string mName; + std::string mDesc; + EFilterLink mLinkFilter; }; #endif // LL_LLINVENTORYLISTENER_H diff --git a/indra/newview/scripts/lua/require/LLInventory.lua b/indra/newview/scripts/lua/require/LLInventory.lua index 880a2516f1..e6a347532b 100644 --- a/indra/newview/scripts/lua/require/LLInventory.lua +++ b/indra/newview/scripts/lua/require/LLInventory.lua @@ -19,10 +19,29 @@ function LLInventory.getBasicFolderID(ft_name) return leap.request('LLInventory', {op = 'getBasicFolderID', ft_name=ft_name}).id end +-- Get the table of asset type names, which can be later used to get the specific items via LLInventory.collectDescendentsIf(...) +function LLInventory.getAssetTypeNames() + return leap.request('LLInventory', {op = 'getAssetTypeNames'}).type_names +end + -- Get the direct descendents of the 'folder_id' provided, -- reply will contain "items" and "categories" tables accordingly function LLInventory.getDirectDescendents(folder_id) return leap.request('LLInventory', {op = 'getDirectDescendents', folder_id=folder_id}) end +-- Get the descendents of the 'folder_id' provided, which pass specified filters +-- reply will contain "items" and "categories" tables accordingly +-- LLInventory.collectDescendentsIf{ folder_id -- parent folder ID +-- [, name] -- name (substring) +-- [, desc] -- description (substring) +-- [, type] -- asset type +-- [, filter_links]} -- EXCLUDE_LINKS - don't show links, ONLY_LINKS - only show links, INCLUDE_LINKS - show links too (default) +function LLInventory.collectDescendentsIf(...) + local args = mapargs('folder_id,name,desc,type,filter_links', ...) + args.op = 'collectDescendentsIf' + return leap.request('LLInventory', args) +end + + return LLInventory -- cgit v1.2.3 From a5337adf79b6a49166a15836ca2822adbb50d8c3 Mon Sep 17 00:00:00 2001 From: Nat Goodspeed Date: Tue, 20 Aug 2024 19:38:10 -0400 Subject: Add LL::scope_exit --- indra/llcommon/scope_exit.h | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) create mode 100644 indra/llcommon/scope_exit.h diff --git a/indra/llcommon/scope_exit.h b/indra/llcommon/scope_exit.h new file mode 100644 index 0000000000..00fab069c4 --- /dev/null +++ b/indra/llcommon/scope_exit.h @@ -0,0 +1,34 @@ +/** + * @file scope_exit.h + * @author Nat Goodspeed + * @date 2024-08-15 + * @brief Cheap imitation of std::experimental::scope_exit + * + * $LicenseInfo:firstyear=2024&license=viewerlgpl$ + * Copyright (c) 2024, Linden Research, Inc. + * $/LicenseInfo$ + */ + +#if ! defined(LL_SCOPE_EXIT_H) +#define LL_SCOPE_EXIT_H + +#include + +namespace LL +{ + +class scope_exit +{ +public: + scope_exit(const std::function& func): mFunc(func) {} + scope_exit(const scope_exit&) = delete; + scope_exit& operator=(const scope_exit&) = delete; + ~scope_exit() { mFunc(); } + +private: + std::function mFunc; +}; + +} // namespace LL + +#endif /* ! defined(LL_SCOPE_EXIT_H) */ -- cgit v1.2.3 From 68313aa2defcc7d6e5ebbd96344d78f3a31fdb9a Mon Sep 17 00:00:00 2001 From: Nat Goodspeed Date: Tue, 20 Aug 2024 20:55:48 -0400 Subject: Fix TempSet to use type VAR to store mOldValue. In fact we set mOldValue from mVar, and restore mVar from mOldValue, so the VAR type makes the most sense. The previous way, you'd get actual errors if you tried to use TempSet(pointervar, nullptr): that declared mOldValue to be nullptr_t, which you can't initialize from mVar. --- indra/llcommon/tempset.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/indra/llcommon/tempset.h b/indra/llcommon/tempset.h index e1496bd5fc..07e607a576 100755 --- a/indra/llcommon/tempset.h +++ b/indra/llcommon/tempset.h @@ -35,7 +35,7 @@ public: private: VAR& mVar; - VALUE mOldValue; + VAR mOldValue; }; #endif /* ! defined(LL_TEMPSET_H) */ -- cgit v1.2.3 From 376c890c095fbc59b83402255cc1036c411150b9 Mon Sep 17 00:00:00 2001 From: Nat Goodspeed Date: Tue, 20 Aug 2024 21:12:25 -0400 Subject: Fix for #2237: intermittent Lua data stack overflow. Use a static unordered_map to allow a function receiving (lua_State* L) to look up the LuaState instance managing that lua_State. We've thought about this from time to time already. LuaState's constructor creates the map entry; its destructor removes it; the new static getParent(lua_State* L) method performs the lookup. Migrate lluau::set_interrupts_counter() and check_interrupts_counter() into LuaState member functions. Add a new mInterrupts counter for them. Importantly, LuaState::check_interrupts_counter(), which is indirectly called by a lua_callbacks().interrupt function, no longer performs any Lua stack operations. Empirically, it seems the Lua engine is capable of interrupting itself at a moment when re-entry confuses it. Change previous lluau::set_interrupts_counter(L, 0) calls to LuaState::getParent(L).set_interrupts_counter(0). Also add LuaStackDelta class, and a lua_checkdelta() helper macro, to verify that the Lua data stack depth on exit from a block differs from the depth on entry by exactly the expected amount. Sprinkle lua_checkdelta() macros in likely places. --- indra/llcommon/lua_function.cpp | 126 +++++++++++++++++++++++++++++++--------- indra/llcommon/lua_function.h | 47 +++++++++++++-- indra/newview/llluamanager.cpp | 5 ++ 3 files changed, 146 insertions(+), 32 deletions(-) diff --git a/indra/llcommon/lua_function.cpp b/indra/llcommon/lua_function.cpp index defadea358..ad77a1e040 100644 --- a/indra/llcommon/lua_function.cpp +++ b/indra/llcommon/lua_function.cpp @@ -21,6 +21,7 @@ #include #include // std::unique_ptr #include +#include // external library headers // other Linden headers #include "fsyspath.h" @@ -54,7 +55,10 @@ namespace }; } // anonymous namespace -int lluau::dostring(lua_State* L, const std::string& desc, const std::string& text) +namespace lluau +{ + +int dostring(lua_State* L, const std::string& desc, const std::string& text) { auto r = loadstring(L, desc, text); if (r != LUA_OK) @@ -66,7 +70,7 @@ int lluau::dostring(lua_State* L, const std::string& desc, const std::string& te return lua_pcall(L, 0, LUA_MULTRET, 0); } -int lluau::loadstring(lua_State *L, const std::string &desc, const std::string &text) +int loadstring(lua_State *L, const std::string &desc, const std::string &text) { size_t bytecodeSize = 0; // The char* returned by luau_compile() must be freed by calling free(). @@ -76,7 +80,7 @@ int lluau::loadstring(lua_State *L, const std::string &desc, const std::string & return luau_load(L, desc.data(), bytecode.get(), bytecodeSize, 0); } -fsyspath lluau::source_path(lua_State* L) +fsyspath source_path(lua_State* L) { //Luau lua_Debug and lua_getinfo() are different compared to default Lua: //see https://github.com/luau-lang/luau/blob/80928acb92d1e4b6db16bada6d21b1fb6fa66265/VM/include/lua.h @@ -93,33 +97,14 @@ fsyspath lluau::source_path(lua_State* L) return ar.source; } -void lluau::set_interrupts_counter(lua_State *L, S32 counter) -{ - lua_rawsetfield(L, LUA_REGISTRYINDEX, "_INTERRUPTS"sv, lua_Integer(counter)); -} - -void lluau::check_interrupts_counter(lua_State* L) -{ - auto counter = lua_rawgetfield(L, LUA_REGISTRYINDEX, "_INTERRUPTS"sv); - - set_interrupts_counter(L, ++counter); - if (counter > INTERRUPTS_MAX_LIMIT) - { - lluau::error(L, "Possible infinite loop, terminated."); - } - else if (counter % INTERRUPTS_SUSPEND_LIMIT == 0) - { - LL_DEBUGS("Lua") << LLCoros::getName() << " suspending at " << counter << " interrupts" - << LL_ENDL; - llcoro::suspend(); - } -} +} // namespace lluau /***************************************************************************** * Lua <=> C++ conversions *****************************************************************************/ std::string lua_tostdstring(lua_State* L, int index) { + lua_checkdelta(L); size_t len; const char* strval{ lua_tolstring(L, index, &len) }; return { strval, len }; @@ -127,6 +112,7 @@ std::string lua_tostdstring(lua_State* L, int index) void lua_pushstdstring(lua_State* L, const std::string& str) { + lua_checkdelta(L, 1); lluau_checkstack(L, 1); lua_pushlstring(L, str.c_str(), str.length()); } @@ -148,6 +134,7 @@ void lua_pushstdstring(lua_State* L, const std::string& str) // reached by that block raises a Lua error. LLSD lua_tollsd(lua_State* L, int index) { + lua_checkdelta(L); switch (lua_type(L, index)) { case LUA_TNONE: @@ -399,6 +386,7 @@ LLSD lua_tollsd(lua_State* L, int index) // stack a Lua object corresponding to the passed LLSD object. void lua_pushllsd(lua_State* L, const LLSD& data) { + lua_checkdelta(L, 1); // might need 2 slots for array or map lluau_checkstack(L, 2); switch (data.type()) @@ -471,10 +459,23 @@ void lua_pushllsd(lua_State* L, const LLSD& data) /***************************************************************************** * LuaState class *****************************************************************************/ +namespace +{ + +// If we find we're running Lua scripts from more than one thread, sLuaStateMap +// should be thread_local. Until then, avoid the overhead. +using LuaStateMap = std::unordered_map; +static LuaStateMap sLuaStateMap; + +} // anonymous namespace + LuaState::LuaState(script_finished_fn cb): mCallback(cb), mState(luaL_newstate()) { + // Ensure that we can always find this LuaState instance, given the + // lua_State we just created or any of its coroutines. + sLuaStateMap.emplace(mState, this); luaL_openlibs(mState); // publish to this new lua_State all the LL entry points we defined using // the lua_function() macro @@ -545,7 +546,9 @@ LuaState::~LuaState() { // mError potentially set by previous checkLua() call(s) mCallback(mError); - } + } + // with the demise of this LuaState, remove sLuaStateMap entry + sLuaStateMap.erase(mState); } bool LuaState::checkLua(const std::string& desc, int r) @@ -563,7 +566,7 @@ bool LuaState::checkLua(const std::string& desc, int r) std::pair LuaState::expr(const std::string& desc, const std::string& text) { - lluau::set_interrupts_counter(mState, 0); + set_interrupts_counter(0); lua_callbacks(mState)->interrupt = [](lua_State *L, int gc) { @@ -572,7 +575,7 @@ std::pair LuaState::expr(const std::string& desc, const std::string& return; LLCoros::checkStop(); - lluau::check_interrupts_counter(L); + LuaState::getParent(L).check_interrupts_counter(); }; LL_INFOS("Lua") << desc << " run" << LL_ENDL; @@ -664,12 +667,53 @@ LuaListener& LuaState::obtainListener(lua_State* L) return *listener; } +LuaState& LuaState::getParent(lua_State* L) +{ + // Look up the LuaState instance associated with the *script*, not the + // specific Lua *coroutine*. In other words, first find this lua_State's + // main thread. + auto found{ sLuaStateMap.find(lua_mainthread(L)) }; + // Our constructor creates the map entry, our destructor deletes it. As + // long as the LuaState exists, we should be able to find it. And we + // SHOULD only be talking to a lua_State managed by a LuaState instance. + llassert(found != sLuaStateMap.end()); + return *found->second; +} + +void LuaState::set_interrupts_counter(S32 counter) +{ + mInterrupts = counter; +} + +void LuaState::check_interrupts_counter() +{ + // The official way to manage data associated with a lua_State is to store + // it *as* Lua data within the lua_State. But this method is called by the + // Lua engine via lua_callbacks(L)->interrupt, and empirically we've hit + // mysterious Lua data stack overflows trying to use stack-based Lua data + // access functions in that situation. It seems the Lua engine is capable + // of interrupting itself at a moment when re-entry is not valid. So only + // touch data in this LuaState. + ++mInterrupts; + if (mInterrupts > INTERRUPTS_MAX_LIMIT) + { + lluau::error(mState, "Possible infinite loop, terminated."); + } + else if (mInterrupts % INTERRUPTS_SUSPEND_LIMIT == 0) + { + LL_DEBUGS("Lua") << LLCoros::getName() << " suspending at " << mInterrupts + << " interrupts" << LL_ENDL; + llcoro::suspend(); + } +} + /***************************************************************************** * atexit() *****************************************************************************/ lua_function(atexit, "atexit(function): " "register Lua function to be called at script termination") { + lua_checkdelta(L, -1); lluau_checkstack(L, 4); // look up the global name "table" lua_getglobal(L, "table"); @@ -758,6 +802,7 @@ std::pair LuaFunction::getState() *****************************************************************************/ lua_function(source_path, "source_path(): return the source path of the running Lua script") { + lua_checkdelta(L, 1); lluau_checkstack(L, 1); lua_pushstdstring(L, lluau::source_path(L).u8string()); return 1; @@ -768,6 +813,7 @@ lua_function(source_path, "source_path(): return the source path of the running *****************************************************************************/ lua_function(source_dir, "source_dir(): return the source directory of the running Lua script") { + lua_checkdelta(L, 1); lluau_checkstack(L, 1); lua_pushstdstring(L, lluau::source_path(L).parent_path().u8string()); return 1; @@ -779,6 +825,7 @@ lua_function(source_dir, "source_dir(): return the source directory of the runni lua_function(abspath, "abspath(path): " "for given filesystem path relative to running script, return absolute path") { + lua_checkdelta(L); auto path{ lua_tostdstring(L, 1) }; lua_pop(L, 1); lua_pushstdstring(L, (lluau::source_path(L).parent_path() / path).u8string()); @@ -790,6 +837,7 @@ lua_function(abspath, "abspath(path): " *****************************************************************************/ lua_function(check_stop, "check_stop(): ensure that a Lua script responds to viewer shutdown") { + lua_checkdelta(L); LLCoros::checkStop(); return 0; } @@ -994,3 +1042,27 @@ std::ostream& operator<<(std::ostream& out, const lua_stack& self) out << ']'; return out; } + +/***************************************************************************** +* LuaStackDelta +*****************************************************************************/ +LuaStackDelta::LuaStackDelta(lua_State* L, const std::string& where, int delta): + L(L), + mWhere(where), + mDepth(lua_gettop(L)), + mDelta(delta) +{} + +LuaStackDelta::~LuaStackDelta() +{ + auto depth{ lua_gettop(L) }; + if (mDepth + mDelta != depth) + { + LL_ERRS("Lua") << mWhere << ": Lua stack went from " << mDepth << " to " << depth; + if (mDelta) + { + LL_CONT << ", rather than expected " << (mDepth + mDelta) << " (" << mDelta << ")"; + } + LL_ENDL; + } +} diff --git a/indra/llcommon/lua_function.h b/indra/llcommon/lua_function.h index c1a4d736a0..6965e206ab 100644 --- a/indra/llcommon/lua_function.h +++ b/indra/llcommon/lua_function.h @@ -60,13 +60,10 @@ namespace lluau int loadstring(lua_State* L, const std::string& desc, const std::string& text); fsyspath source_path(lua_State* L); - - void set_interrupts_counter(lua_State *L, S32 counter); - void check_interrupts_counter(lua_State* L); } // namespace lluau -// must be a macro because __FUNCTION__ is context-sensitive -#define lluau_checkstack(L, n) luaL_checkstack((L), (n), __FUNCTION__) +// must be a macro because LL_PRETTY_FUNCTION is context-sensitive +#define lluau_checkstack(L, n) luaL_checkstack((L), (n), LL_PRETTY_FUNCTION) std::string lua_tostdstring(lua_State* L, int index); void lua_pushstdstring(lua_State* L, const std::string& str); @@ -110,10 +107,18 @@ public: // Find or create LuaListener for passed lua_State. static LuaListener& obtainListener(lua_State* L); + // Given lua_State* L, return the LuaState object managing (the main Lua + // thread for) L. + static LuaState& getParent(lua_State* L); + + void set_interrupts_counter(S32 counter); + void check_interrupts_counter(); + private: script_finished_fn mCallback; lua_State* mState; std::string mError; + S32 mInterrupts{ 0 }; }; /***************************************************************************** @@ -172,6 +177,32 @@ private: int mIndex; }; +/***************************************************************************** +* LuaStackDelta +*****************************************************************************/ +/** + * Instantiate LuaStackDelta in a block to compare the Lua data stack depth on + * entry (LuaStackDelta construction) and exit. Optionally, pass the expected + * depth increment. (But be aware that LuaStackDelta cannot observe the effect + * of a LuaPopper or LuaRemover declared previously in the same block.) + */ +class LuaStackDelta +{ +public: + LuaStackDelta(lua_State* L, const std::string& where, int delta=0); + LuaStackDelta(const LuaStackDelta&) = delete; + LuaStackDelta& operator=(const LuaStackDelta&) = delete; + + ~LuaStackDelta(); + +private: + lua_State* L; + std::string mWhere; + int mDepth, mDelta; +}; + +#define lua_checkdelta(L, ...) LuaStackDelta delta(L, LL_PRETTY_FUNCTION, ##__VA_ARGS__) + /***************************************************************************** * lua_push() wrappers for generic code *****************************************************************************/ @@ -297,6 +328,7 @@ auto lua_to(lua_State* L, int index) template auto lua_getfieldv(lua_State* L, int index, const char* k) { + lua_checkdelta(L); lluau_checkstack(L, 1); lua_getfield(L, index, k); LuaPopper pop(L, 1); @@ -307,6 +339,7 @@ auto lua_getfieldv(lua_State* L, int index, const char* k) template auto lua_setfieldv(lua_State* L, int index, const char* k, const T& value) { + lua_checkdelta(L); lluau_checkstack(L, 1); lua_push(L, value); lua_setfield(L, index, k); @@ -316,6 +349,7 @@ auto lua_setfieldv(lua_State* L, int index, const char* k, const T& value) template auto lua_rawgetfield(lua_State* L, int index, const std::string_view& k) { + lua_checkdelta(L); lluau_checkstack(L, 1); lua_pushlstring(L, k.data(), k.length()); lua_rawget(L, index); @@ -327,6 +361,7 @@ auto lua_rawgetfield(lua_State* L, int index, const std::string_view& k) template void lua_rawsetfield(lua_State* L, int index, const std::string_view& k, const T& value) { + lua_checkdelta(L); lluau_checkstack(L, 2); lua_pushlstring(L, k.data(), k.length()); lua_push(L, value); @@ -433,6 +468,7 @@ DistinctInt TypeTag::value; template void lua_emplace(lua_State* L, ARGS&&... args) { + lua_checkdelta(L, 1); lluau_checkstack(L, 1); int tag{ TypeTag::value }; if (! lua_getuserdatadtor(L, tag)) @@ -467,6 +503,7 @@ void lua_emplace(lua_State* L, ARGS&&... args) template T* lua_toclass(lua_State* L, int index) { + lua_checkdelta(L); // get void* pointer to userdata (if that's what it is) void* ptr{ lua_touserdatatagged(L, index, TypeTag::value) }; // Derive the T* from ptr. If in future lua_emplace() must manually diff --git a/indra/newview/llluamanager.cpp b/indra/newview/llluamanager.cpp index 0b1a73f4e9..6de8829308 100644 --- a/indra/newview/llluamanager.cpp +++ b/indra/newview/llluamanager.cpp @@ -53,6 +53,7 @@ std::map LLLUAmanager::sScriptNames; lua_function(sleep, "sleep(seconds): pause the running coroutine") { + lua_checkdelta(L, -1); F32 seconds = lua_tonumber(L, -1); lua_pop(L, 1); llcoro::suspendUntilTimeout(seconds); @@ -125,6 +126,7 @@ lua_function(print_warning, "print_warning(args...): WARNING level logging") lua_function(post_on, "post_on(pumpname, data): post specified data to specified LLEventPump") { + lua_checkdelta(L, -2); std::string pumpname{ lua_tostdstring(L, 1) }; LLSD data{ lua_tollsd(L, 2) }; lua_pop(L, 2); @@ -139,6 +141,7 @@ lua_function(get_event_pumps, "Events posted to replypump are queued for get_event_next().\n" "post_on(commandpump, ...) to engage LLEventAPI operations (see helpleap()).") { + lua_checkdelta(L, 2); lluau_checkstack(L, 2); auto& listener{ LuaState::obtainListener(L) }; // return the reply pump name and the command pump name on caller's lua_State @@ -153,6 +156,7 @@ lua_function(get_event_next, "is returned by get_event_pumps(). Blocks the calling chunk until an\n" "event becomes available.") { + lua_checkdelta(L, 2); lluau_checkstack(L, 2); auto& listener{ LuaState::obtainListener(L) }; const auto& [pump, data]{ listener.getNext() }; @@ -271,6 +275,7 @@ std::string read_file(const std::string &name) lua_function(require, "require(module_name) : load module_name.lua from known places") { + lua_checkdelta(L); std::string name = lua_tostdstring(L, 1); lua_pop(L, 1); -- cgit v1.2.3 From d37aa5c1823fcb202e87b1457842e49655c72b95 Mon Sep 17 00:00:00 2001 From: Nat Goodspeed Date: Tue, 20 Aug 2024 21:22:46 -0400 Subject: Defend timers.Timer(iterate=True) against long callbacks. Specifically, defend against a callback that runs so long it suspends at a point after the next timer tick. --- indra/newview/scripts/lua/require/timers.lua | 34 +++++++++++++++++++++------- 1 file changed, 26 insertions(+), 8 deletions(-) diff --git a/indra/newview/scripts/lua/require/timers.lua b/indra/newview/scripts/lua/require/timers.lua index e4938078dc..ab1615ffbf 100644 --- a/indra/newview/scripts/lua/require/timers.lua +++ b/indra/newview/scripts/lua/require/timers.lua @@ -34,35 +34,53 @@ function timers.Timer:new(delay, callback, iterate) callback = callback or function() obj:tick() end - local first = true + local calls = 0 if iterate then + -- With iterative timers, beware of running a timer callback which + -- performs async actions lasting longer than the timer interval. The + -- lengthy callback suspends, allowing leap to retrieve the next + -- event, which is a timer tick. leap calls a new instance of the + -- callback, even though the previous callback call is still + -- suspended... etc. 'in_callback' defends against that recursive + -- case. Rather than re-enter the suspended callback, drop the + -- too-soon timer event. (We could count the too-soon timer events and + -- iterate calling the callback, but it's a bathtub problem: the + -- callback could end up getting farther and farther behind.) + local in_callback = false obj.id = leap.eventstream( 'Timers', {op='scheduleEvery', every=delay}, function (event) local reqid = event.reqid - if first then - first = false + calls += 1 + if calls == 1 then dbg('timer(%s) first callback', reqid) -- discard the first (immediate) response: don't call callback return nil else - dbg('timer(%s) nth callback', reqid) - return callback(event) + if in_callback then + dbg('dropping timer(%s) callback %d', reqid, calls) + else + dbg('timer(%s) callback %d', reqid, calls) + in_callback = true + local ret = callback(event) + in_callback = false + return ret + end end end ).reqid - else + else -- (not iterate) obj.id = leap.eventstream( 'Timers', {op='scheduleAfter', after=delay}, function (event) + calls += 1 -- Arrange to return nil the first time, true the second. This -- callback is called immediately with the response to -- 'scheduleAfter', and if we immediately returned true, we'd -- be done, and the subsequent timer event would be discarded. - if first then - first = false + if calls == 1 then -- Caller doesn't expect an immediate callback. return nil else -- cgit v1.2.3 From 409745eb370d7609df70da81584142dffbfe9d3f Mon Sep 17 00:00:00 2001 From: Nat Goodspeed Date: Tue, 20 Aug 2024 21:41:26 -0400 Subject: Fix a couple more set_interrupts_counter() calls. --- indra/newview/llluamanager.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/indra/newview/llluamanager.cpp b/indra/newview/llluamanager.cpp index 6de8829308..22b51d7b72 100644 --- a/indra/newview/llluamanager.cpp +++ b/indra/newview/llluamanager.cpp @@ -57,7 +57,7 @@ lua_function(sleep, "sleep(seconds): pause the running coroutine") F32 seconds = lua_tonumber(L, -1); lua_pop(L, 1); llcoro::suspendUntilTimeout(seconds); - lluau::set_interrupts_counter(L, 0); + LuaState::getParent(L).set_interrupts_counter(0); return 0; }; @@ -162,7 +162,7 @@ lua_function(get_event_next, const auto& [pump, data]{ listener.getNext() }; lua_pushstdstring(L, pump); lua_pushllsd(L, data); - lluau::set_interrupts_counter(L, 0); + LuaState::getParent(L).set_interrupts_counter(0); return 2; } -- cgit v1.2.3 From 2b354497c8f35b7832d1a5e310dd2096ab8c95f8 Mon Sep 17 00:00:00 2001 From: Nicky Date: Wed, 21 Aug 2024 12:26:49 +0200 Subject: Add zlib-ng as a dependency (for libpng). --- indra/llwindow/CMakeLists.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/indra/llwindow/CMakeLists.txt b/indra/llwindow/CMakeLists.txt index 9ebd6ef0b0..55f97b33c5 100644 --- a/indra/llwindow/CMakeLists.txt +++ b/indra/llwindow/CMakeLists.txt @@ -58,6 +58,7 @@ set(llwindow_LINK_LIBRARIES ll::glext ll::uilibraries ll::SDL + ll::zlib-ng ) # Libraries on which this library depends, needed for Linux builds -- cgit v1.2.3 From 3ab6f187737de7d459efb33d887dc42b1263f087 Mon Sep 17 00:00:00 2001 From: Nicky Date: Wed, 21 Aug 2024 12:27:43 +0200 Subject: Remove packaging of libexpat.so*, this library is no statically linked. --- indra/newview/viewer_manifest.py | 1 - 1 file changed, 1 deletion(-) diff --git a/indra/newview/viewer_manifest.py b/indra/newview/viewer_manifest.py index 15cec11330..0428c6e3d5 100755 --- a/indra/newview/viewer_manifest.py +++ b/indra/newview/viewer_manifest.py @@ -1409,7 +1409,6 @@ class Linux_x86_64_Manifest(LinuxManifest): with self.prefix(src=relpkgdir, dst="lib"): self.path("libapr-1.so*") self.path("libaprutil-1.so*") - self.path("libexpat.so.*") self.path_optional("libSDL*.so.*") self.path_optional("libjemalloc*.so") -- cgit v1.2.3 From 1f1ac55f5a69ab15844dab3cc33d6a60f9f47444 Mon Sep 17 00:00:00 2001 From: Nicky Date: Wed, 21 Aug 2024 12:40:47 +0200 Subject: Renable Linux builds --- .github/workflows/build.yaml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index 6fe5a75f48..1421b2bd30 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -48,9 +48,9 @@ jobs: matrix: runner: [windows-large, macos-12-xl] configuration: ${{ fromJSON(needs.setup.outputs.configurations) }} - # include: - # - runner: linux-large - # configuration: ReleaseOS + include: + - runner: linux-large + configuration: ReleaseOS runs-on: ${{ matrix.runner }} outputs: viewer_channel: ${{ steps.build.outputs.viewer_channel }} -- cgit v1.2.3 From f4b650b100c120ed99208545864b0a7f36ce058d Mon Sep 17 00:00:00 2001 From: Nat Goodspeed Date: Wed, 21 Aug 2024 09:12:52 -0400 Subject: Suppress ~LuaStackDelta() verification during stack unwinding. Otherwise, an exception raised in the block containing a LuaStackDelta instance -- that might be caught -- would result in an LL_ERRS() crash. We can't expect a block exited via exception to keep its contract wrt the Lua data stack. --- indra/llcommon/lua_function.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/indra/llcommon/lua_function.cpp b/indra/llcommon/lua_function.cpp index ad77a1e040..1e9bbdd651 100644 --- a/indra/llcommon/lua_function.cpp +++ b/indra/llcommon/lua_function.cpp @@ -1056,7 +1056,10 @@ LuaStackDelta::LuaStackDelta(lua_State* L, const std::string& where, int delta): LuaStackDelta::~LuaStackDelta() { auto depth{ lua_gettop(L) }; - if (mDepth + mDelta != depth) + // If we're unwinding the stack due to an exception, then of course we + // can't expect the logic in the block containing this LuaStackDelta + // instance to keep its contract wrt the Lua data stack. + if (std::uncaught_exceptions() == 0 && mDepth + mDelta != depth) { LL_ERRS("Lua") << mWhere << ": Lua stack went from " << mDepth << " to " << depth; if (mDelta) -- cgit v1.2.3 From f2abd050bce3c4132f12d962fc6436d1f06666bd Mon Sep 17 00:00:00 2001 From: Nat Goodspeed Date: Wed, 21 Aug 2024 09:29:26 -0400 Subject: Improve diagnostic output for Lua atexit() functions. --- indra/llcommon/lua_function.cpp | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/indra/llcommon/lua_function.cpp b/indra/llcommon/lua_function.cpp index 1e9bbdd651..452bc24b16 100644 --- a/indra/llcommon/lua_function.cpp +++ b/indra/llcommon/lua_function.cpp @@ -496,6 +496,14 @@ LuaState::~LuaState() // stack contains Registry.atexit if (lua_istable(mState, -1)) { + // We happen to know that Registry.atexit is built by appending array + // entries using table.insert(). That's important because it means + // there are no holes, and therefore lua_objlen() should be correct. + // That's important because we walk the atexit table backwards, to + // destroy last the things we created (passed to LL.atexit()) first. + int len(lua_objlen(mState, -1)); + LL_DEBUGS("Lua") << "Registry.atexit is a table with " << len << " entries" << LL_ENDL; + // Push debug.traceback() onto the stack as lua_pcall()'s error // handler function. On error, lua_pcall() calls the specified error // handler function with the original error message; the message @@ -509,12 +517,7 @@ LuaState::~LuaState() lua_remove(mState, -2); // stack now contains atexit, debug.traceback() - // We happen to know that Registry.atexit is built by appending array - // entries using table.insert(). That's important because it means - // there are no holes, and therefore lua_objlen() should be correct. - // That's important because we walk the atexit table backwards, to - // destroy last the things we created (passed to LL.atexit()) first. - for (int i(lua_objlen(mState, -2)); i >= 1; --i) + for (int i(len); i >= 1; --i) { lua_pushinteger(mState, i); // stack contains Registry.atexit, debug.traceback(), i @@ -524,13 +527,15 @@ LuaState::~LuaState() // Use lua_pcall() because errors in any one atexit() function // shouldn't cancel the rest of them. Pass debug.traceback() as // the error handler function. + LL_DEBUGS("Lua") << "Calling atexit(" << i << ")" << LL_ENDL; if (lua_pcall(mState, 0, 0, -2) != LUA_OK) { auto error{ lua_tostdstring(mState, -1) }; - LL_WARNS("Lua") << "atexit() function error: " << error << LL_ENDL; + LL_WARNS("Lua") << "atexit(" << i << ") error: " << error << LL_ENDL; // pop error message lua_pop(mState, 1); } + LL_DEBUGS("Lua") << "atexit(" << i << ") done" << LL_ENDL; // lua_pcall() has already popped atexit[i]: // stack contains atexit, debug.traceback() } -- cgit v1.2.3 From 605f8b46cfa51e54d4f2013b1a2c721f7acdfb0a Mon Sep 17 00:00:00 2001 From: Andrey Lihatskiy Date: Wed, 21 Aug 2024 19:59:34 +0300 Subject: Fix syntax error in build.yaml --- .github/workflows/build.yaml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index 1421b2bd30..8988453b0b 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -48,9 +48,9 @@ jobs: matrix: runner: [windows-large, macos-12-xl] configuration: ${{ fromJSON(needs.setup.outputs.configurations) }} - include: - - runner: linux-large - configuration: ReleaseOS + include: + - runner: linux-large + configuration: ReleaseOS runs-on: ${{ matrix.runner }} outputs: viewer_channel: ${{ steps.build.outputs.viewer_channel }} -- cgit v1.2.3 From 0e0e1accb62ccd3c703ab031ae9cfe0260fcfa01 Mon Sep 17 00:00:00 2001 From: Andrey Lihatskiy Date: Wed, 21 Aug 2024 20:23:27 +0300 Subject: Fix implicit conversion error in llvoicewebrtc.cpp --- indra/newview/llvoicewebrtc.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/indra/newview/llvoicewebrtc.cpp b/indra/newview/llvoicewebrtc.cpp index 4d4f5dc9e9..af93bdcaf0 100644 --- a/indra/newview/llvoicewebrtc.cpp +++ b/indra/newview/llvoicewebrtc.cpp @@ -2750,7 +2750,7 @@ bool LLVoiceWebRTCConnection::connectionStateMachine() case VOICE_STATE_SESSION_UP: { mRetryWaitPeriod = 0; - mRetryWaitSecs = (F32)((F32) rand() / (RAND_MAX)) + 0.5f; + mRetryWaitSecs = (F32)((F32)rand() / (F32)RAND_MAX) + 0.5f; LLUUID agentRegionID; if (isSpatial() && gAgent.getRegion()) { -- cgit v1.2.3 From 86eec90d97c03ac0f6be8897041185c6b5601968 Mon Sep 17 00:00:00 2001 From: Mnikolenko Productengine Date: Wed, 21 Aug 2024 20:43:38 +0300 Subject: Add item limit for collectDescendentsIf func; add demo script --- indra/newview/llinventoryfunctions.h | 2 ++ indra/newview/llinventorylistener.cpp | 26 +++++++++++++++++++---- indra/newview/llinventorylistener.h | 4 ++++ indra/newview/llinventorymodel.cpp | 8 +++++++ indra/newview/scripts/lua/require/LLInventory.lua | 3 ++- indra/newview/scripts/lua/test_LLInventory.lua | 21 ++++++++++++++++++ 6 files changed, 59 insertions(+), 5 deletions(-) create mode 100644 indra/newview/scripts/lua/test_LLInventory.lua diff --git a/indra/newview/llinventoryfunctions.h b/indra/newview/llinventoryfunctions.h index 78077d2007..f6d910820d 100644 --- a/indra/newview/llinventoryfunctions.h +++ b/indra/newview/llinventoryfunctions.h @@ -189,6 +189,8 @@ public: virtual ~LLInventoryCollectFunctor(){}; virtual bool operator()(LLInventoryCategory* cat, LLInventoryItem* item) = 0; + virtual bool exceedsLimit() { return false; } + static bool itemTransferCommonlyAllowed(const LLInventoryItem* item); }; diff --git a/indra/newview/llinventorylistener.cpp b/indra/newview/llinventorylistener.cpp index 4beb59c58e..4ff25f7f5c 100644 --- a/indra/newview/llinventorylistener.cpp +++ b/indra/newview/llinventorylistener.cpp @@ -33,6 +33,8 @@ #include "llwearableitemslist.h" #include "stringize.h" +static const F32 MAX_ITEM_LIMIT = 100; + LLInventoryListener::LLInventoryListener() : LLEventAPI("LLInventory", "API for interactions with viewer Inventory items") @@ -68,7 +70,8 @@ LLInventoryListener::LLInventoryListener() "Return the descendents(both items and folders) of the [\"folder_id\"], if it passes specified filters:\n" "[\"name\"] is a substring of object's name,\n" "[\"desc\"] is a substring of object's description,\n" - "asset [\"type\"] corresponds to the object's asset type\n" + "asset [\"type\"] corresponds to the object's asset type\n" + "[\"item_limit\"] sets item count limit in reply, maximum and default is 100\n" "[\"filter_links\"]: EXCLUDE_LINKS - don't show links, ONLY_LINKS - only show links, INCLUDE_LINKS - show links too (default)", &LLInventoryListener::collectDescendentsIf, llsd::map("folder_id", LLSD(), "reply", LLSD())); @@ -171,9 +174,11 @@ void LLInventoryListener::collectDescendentsIf(LLSD const &data) } } -LLFilteredCollector::LLFilteredCollector(LLSD const &data) - : mType(LLAssetType::EType::AT_UNKNOWN), - mLinkFilter(INCLUDE_LINKS) +LLFilteredCollector::LLFilteredCollector(LLSD const &data) : + mType(LLAssetType::EType::AT_UNKNOWN), + mLinkFilter(INCLUDE_LINKS), + mItemLimit(MAX_ITEM_LIMIT), + mItemCount(0) { if (data.has("name")) { @@ -198,6 +203,10 @@ LLFilteredCollector::LLFilteredCollector(LLSD const &data) mLinkFilter = ONLY_LINKS; } } + if (data.has("item_limit")) + { + mItemLimit = llclamp(data["item_limit"].asInteger(), 1, MAX_ITEM_LIMIT); + } } bool LLFilteredCollector::operator()(LLInventoryCategory *cat, LLInventoryItem *item) @@ -206,9 +215,18 @@ bool LLFilteredCollector::operator()(LLInventoryCategory *cat, LLInventoryItem * passed = passed && checkagainstNameDesc(cat, item); passed = passed && checkagainstLinks(cat, item); + if (passed) + { + ++mItemCount; + } return passed; } +bool LLFilteredCollector::exceedsLimit() +{ + return (mItemLimit <= mItemCount); +} + bool LLFilteredCollector::checkagainstNameDesc(LLInventoryCategory *cat, LLInventoryItem *item) { std::string name, desc; diff --git a/indra/newview/llinventorylistener.h b/indra/newview/llinventorylistener.h index 83e1751e1b..2c1eb2cb6d 100644 --- a/indra/newview/llinventorylistener.h +++ b/indra/newview/llinventorylistener.h @@ -56,6 +56,7 @@ struct LLFilteredCollector : public LLInventoryCollectFunctor LLFilteredCollector(LLSD const &data); virtual ~LLFilteredCollector() {} virtual bool operator()(LLInventoryCategory *cat, LLInventoryItem *item); + virtual bool exceedsLimit(); protected: bool checkagainstType(LLInventoryCategory *cat, LLInventoryItem *item); @@ -66,6 +67,9 @@ struct LLFilteredCollector : public LLInventoryCollectFunctor std::string mName; std::string mDesc; EFilterLink mLinkFilter; + + S32 mItemLimit; + S32 mItemCount; }; #endif // LL_LLINVENTORYLISTENER_H diff --git a/indra/newview/llinventorymodel.cpp b/indra/newview/llinventorymodel.cpp index 5325c28abf..c98c3482d0 100644 --- a/indra/newview/llinventorymodel.cpp +++ b/indra/newview/llinventorymodel.cpp @@ -1289,6 +1289,10 @@ void LLInventoryModel::collectDescendentsIf(const LLUUID& id, S32 count = cat_array->size(); for(S32 i = 0; i < count; ++i) { + if (add.exceedsLimit()) + { + break; + } LLViewerInventoryCategory* cat = cat_array->at(i); if(add(cat,NULL)) { @@ -1307,6 +1311,10 @@ void LLInventoryModel::collectDescendentsIf(const LLUUID& id, S32 count = item_array->size(); for(S32 i = 0; i < count; ++i) { + if (add.exceedsLimit()) + { + break; + } item = item_array->at(i); if(add(NULL, item)) { diff --git a/indra/newview/scripts/lua/require/LLInventory.lua b/indra/newview/scripts/lua/require/LLInventory.lua index e6a347532b..b3f817da94 100644 --- a/indra/newview/scripts/lua/require/LLInventory.lua +++ b/indra/newview/scripts/lua/require/LLInventory.lua @@ -36,9 +36,10 @@ end -- [, name] -- name (substring) -- [, desc] -- description (substring) -- [, type] -- asset type +-- [, item_limit] -- item count limit in reply, maximum and default is 100 -- [, filter_links]} -- EXCLUDE_LINKS - don't show links, ONLY_LINKS - only show links, INCLUDE_LINKS - show links too (default) function LLInventory.collectDescendentsIf(...) - local args = mapargs('folder_id,name,desc,type,filter_links', ...) + local args = mapargs('folder_id,name,desc,type,filter_links,item_limit', ...) args.op = 'collectDescendentsIf' return leap.request('LLInventory', args) end diff --git a/indra/newview/scripts/lua/test_LLInventory.lua b/indra/newview/scripts/lua/test_LLInventory.lua new file mode 100644 index 0000000000..dc6eb243ad --- /dev/null +++ b/indra/newview/scripts/lua/test_LLInventory.lua @@ -0,0 +1,21 @@ +inspect = require 'inspect' +LLInventory = require 'LLInventory' + +-- Get 'My Landmarks' folder id (you can see all folder types via LLInventory.getFolderTypeNames()) +my_landmarks_id = LLInventory.getBasicFolderID('landmark') +-- Get 3 landmarks from the 'My Landmarks' folder (you can see all folder types via LLInventory.getAssetTypeNames()) +landmarks = LLInventory.collectDescendentsIf{folder_id=my_landmarks_id, type="landmark", item_limit=3} +print(inspect(landmarks)) + +-- Get 'Calling Cards' folder id +calling_cards_id = LLInventory.getBasicFolderID('callcard') +-- Get all items located directly in 'Calling Cards' folder +calling_cards = LLInventory.getDirectDescendents(calling_cards_id).items + +-- Print a random calling card name from 'Calling Cards' folder +local card_names = {} +for _, value in pairs(calling_cards) do + table.insert(card_names, value.name) +end +math.randomseed(os.time()) +print("Random calling card: " .. inspect(card_names[math.random(#card_names)])) -- cgit v1.2.3 From 8887711019f3b37bc830a51609a4940fe10d7cec Mon Sep 17 00:00:00 2001 From: Mnikolenko Productengine Date: Wed, 21 Aug 2024 23:12:21 +0300 Subject: mac build fix --- indra/newview/llinventorylistener.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/indra/newview/llinventorylistener.cpp b/indra/newview/llinventorylistener.cpp index 4ff25f7f5c..0aa1208d83 100644 --- a/indra/newview/llinventorylistener.cpp +++ b/indra/newview/llinventorylistener.cpp @@ -182,11 +182,11 @@ LLFilteredCollector::LLFilteredCollector(LLSD const &data) : { if (data.has("name")) { - mName = data["name"]; + mName = data["name"].asString(); } if (data.has("desc")) { - mDesc = data["desc"]; + mDesc = data["desc"].asString(); } if (data.has("type")) { -- cgit v1.2.3 From 9e24b300d02e5627ea0d304d412cb683ec2de3a4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kyler=20=22F=C3=A9lix=22=20Eastridge?= Date: Thu, 22 Aug 2024 00:42:58 -0400 Subject: Fix local resetting (#2383) --- indra/newview/llhudeffectresetskeleton.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/indra/newview/llhudeffectresetskeleton.cpp b/indra/newview/llhudeffectresetskeleton.cpp index aa5532f0fc..31065a3e76 100644 --- a/indra/newview/llhudeffectresetskeleton.cpp +++ b/indra/newview/llhudeffectresetskeleton.cpp @@ -194,7 +194,8 @@ void LLHUDEffectResetSkeleton::update() if (mTargetObject->isAvatar()) { // Only the owner of a avatar can reset their skeleton like this - if (mSourceObject->getID() == mTargetObject->getID()) + // Also allow reset if we created the effect (Local resetting) + if (mSourceObject->getID() == mTargetObject->getID() || getOriginatedHere()) { LLVOAvatar* avatar = mTargetObject->asAvatar(); avatar->resetSkeleton(mResetAnimations); -- cgit v1.2.3 From 073d3d0e04a2ebe35a201eae9c6dad4b7ae5dc88 Mon Sep 17 00:00:00 2001 From: Mnikolenko Productengine Date: Thu, 22 Aug 2024 23:29:28 +0300 Subject: Fix for #2385: say, shout and whisper messages from the script should be displayed consistently --- indra/newview/llviewermessage.cpp | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/indra/newview/llviewermessage.cpp b/indra/newview/llviewermessage.cpp index 26b088e390..4922e3c9f9 100644 --- a/indra/newview/llviewermessage.cpp +++ b/indra/newview/llviewermessage.cpp @@ -2646,19 +2646,21 @@ void process_chat_from_simulator(LLMessageSystem *msg, void **user_data) else { chat.mText = ""; + std::string chat_text(mesg); + auto [msg_without_prefix, is_lua] = LLStringUtil::withoutPrefix(mesg, LUA_PREFIX); switch(chat.mChatType) { case CHAT_TYPE_WHISPER: - chat.mText = LLTrans::getString("whisper") + " "; + case CHAT_TYPE_SHOUT: + chat_text = LLTrans::getString(chat.mChatType == CHAT_TYPE_WHISPER ? "whisper" : "shout") + " " + msg_without_prefix; + if(is_lua) + chat_text = LUA_PREFIX + chat_text; break; case CHAT_TYPE_DEBUG_MSG: case CHAT_TYPE_OWNER: case CHAT_TYPE_NORMAL: case CHAT_TYPE_DIRECT: break; - case CHAT_TYPE_SHOUT: - chat.mText = LLTrans::getString("shout") + " "; - break; case CHAT_TYPE_START: case CHAT_TYPE_STOP: LL_WARNS("Messaging") << "Got chat type start/stop in main chat processing." << LL_ENDL; @@ -2668,7 +2670,7 @@ void process_chat_from_simulator(LLMessageSystem *msg, void **user_data) break; } - chat.mText += mesg; + chat.mText += chat_text; } // We have a real utterance now, so can stop showing "..." and proceed. -- cgit v1.2.3 From a80b9487dc7c893f5e96f48f15140a5f82b99e30 Mon Sep 17 00:00:00 2001 From: Nat Goodspeed Date: Fri, 23 Aug 2024 17:18:14 -0400 Subject: Allow UI to have lazily-loaded submodules. Equip UI with an __index metamethod. When someone references an unknown key/field in UI, require() that module and cache it for future reference. Add util.setmetamethods() as a way to find or create a metatable on a specified table containing specified metamethods. Exercise the new functionality by referencing UI.popup in test_popup.lua. --- indra/newview/scripts/lua/require/UI.lua | 22 ++++++++++++-- indra/newview/scripts/lua/require/util.lua | 46 +++++++++++++++++++++++------- indra/newview/scripts/lua/test_popup.lua | 3 +- 3 files changed, 57 insertions(+), 14 deletions(-) diff --git a/indra/newview/scripts/lua/require/UI.lua b/indra/newview/scripts/lua/require/UI.lua index 464e6547ea..969a2cbded 100644 --- a/indra/newview/scripts/lua/require/UI.lua +++ b/indra/newview/scripts/lua/require/UI.lua @@ -1,10 +1,26 @@ -- Engage the viewer's UI local leap = require 'leap' -local Timer = (require 'timers').Timer local mapargs = require 'mapargs' - -local UI = {} +local Timer = (require 'timers').Timer +local util = require 'util' + +-- Allow lazily accessing certain other modules on demand, e.g. a reference to +-- UI.Floater lazily loads the Floater module. Use of UI's __index metamethod +-- theoretically permits any other module you can require() to appear as a +-- submodule of UI, but it doesn't make sense to support (e.g.) UI.Queue. +local submods = { 'Floater', 'popup' } +local UI = util.setmetamethods{ + __index=function(t, key) + if not table.find(submods, key) then + error(`Invalid UI submodule {key}`, 2) + end + local mod = require(key) + -- cache the submodule + t[key] = mod + return mod + end +} -- *************************************************************************** -- registered menu actions diff --git a/indra/newview/scripts/lua/require/util.lua b/indra/newview/scripts/lua/require/util.lua index bfbfc8637c..a000359226 100644 --- a/indra/newview/scripts/lua/require/util.lua +++ b/indra/newview/scripts/lua/require/util.lua @@ -15,16 +15,9 @@ local util = {} -- util.classctor(MyClass, MyClass.construct) -- return MyClass function util.classctor(class, ctor) - -- get the metatable for the passed class - local mt = getmetatable(class) - if mt == nil then - -- if it doesn't already have a metatable, then create one - mt = {} - setmetatable(class, mt) - end - -- now that class has a metatable, set its __call method to the specified - -- constructor method (class.new if not specified) - mt.__call = ctor or class.new + -- set class's __call metamethod to the specified constructor function + -- (class.new if not specified) + util.setmetamethods{class, __call=(ctor or class.new)} end -- check if array-like table contains certain value @@ -66,4 +59,37 @@ function util.equal(t1, t2) return util.empty(temp) end +-- Find or create the metatable for a specified table (a new empty table if +-- omitted), and to that metatable assign the specified keys. +-- Setting multiple keys at once is more efficient than a function to set only +-- one at a time, e.g. setametamethod(). +-- t = util.setmetamethods{__index=readfunc, __len=lenfunc} +-- returns a new table with specified metamethods __index, __len +-- util.setmetamethods{t, __call=action} +-- finds or creates the metatable for existing table t and sets __call +-- util.setmetamethods{table=t, __call=action} +-- same as util.setmetamethods{t, __call=action} +function util.setmetamethods(specs) + -- first determine the target table + assert(not (specs.table and specs[1]), + "Pass setmetamethods table either as positional or table=, not both") + local t = specs.table or specs[1] or {} + -- remove both ways of specifying table, leaving only the metamethods + specs.table = nil + specs[1] = nil + local mt = getmetatable(t) + if not mt then + -- t doesn't already have a metatable: just set specs + setmetatable(t, specs) + else + -- t already has a metatable: copy specs into it + local key, value + for key, value in pairs(specs) do + mt[key] = value + end + end + -- having set or enriched t's metatable, return t + return t +end + return util diff --git a/indra/newview/scripts/lua/test_popup.lua b/indra/newview/scripts/lua/test_popup.lua index e48f89c3a7..156c09c78f 100644 --- a/indra/newview/scripts/lua/test_popup.lua +++ b/indra/newview/scripts/lua/test_popup.lua @@ -1,4 +1,5 @@ -popup = require 'popup' +UI = require 'UI' +popup = UI.popup response = popup:alert('This just has a Close button') response = popup:alertOK(string.format('You said "%s", is that OK?', next(response))) -- cgit v1.2.3 From e4a710296943674573be800f5233b24214440929 Mon Sep 17 00:00:00 2001 From: Nat Goodspeed Date: Fri, 23 Aug 2024 20:55:22 -0400 Subject: Look for lazy UI submodules in a require/UI subdirectory. This way encourages "UI = require 'UI'; UI.Floater" instead of just "Floater = require 'Floater'". Moreover, now we don't need UI to maintain a list of allowed submodules; that's effected by membership in the subdirectory. --- indra/newview/scripts/lua/require/Floater.lua | 146 ----------------------- indra/newview/scripts/lua/require/UI.lua | 12 +- indra/newview/scripts/lua/require/UI/Floater.lua | 146 +++++++++++++++++++++++ indra/newview/scripts/lua/require/UI/popup.lua | 53 ++++++++ indra/newview/scripts/lua/require/popup.lua | 53 -------- 5 files changed, 202 insertions(+), 208 deletions(-) delete mode 100644 indra/newview/scripts/lua/require/Floater.lua create mode 100644 indra/newview/scripts/lua/require/UI/Floater.lua create mode 100644 indra/newview/scripts/lua/require/UI/popup.lua delete mode 100644 indra/newview/scripts/lua/require/popup.lua diff --git a/indra/newview/scripts/lua/require/Floater.lua b/indra/newview/scripts/lua/require/Floater.lua deleted file mode 100644 index d057a74386..0000000000 --- a/indra/newview/scripts/lua/require/Floater.lua +++ /dev/null @@ -1,146 +0,0 @@ --- Floater base class - -local leap = require 'leap' -local fiber = require 'fiber' -local util = require 'util' - --- list of all the events that a LLLuaFloater might send -local event_list = leap.request("LLFloaterReg", {op="getFloaterEvents"}).events -local event_set = {} -for _, event in pairs(event_list) do - event_set[event] = true -end - -local function _event(event_name) - if not event_set[event_name] then - error("Incorrect event name: " .. event_name, 3) - end - return event_name -end - --- --------------------------------------------------------------------------- -local Floater = {} - --- Pass: --- relative file path to floater's XUI definition file --- optional: sign up for additional events for defined control --- {={action1, action2, ...}} -function Floater:new(path, extra) - local obj = setmetatable({}, self) - self.__index = self - - local path_parts = string.split(path, '/') - obj.name = 'Floater ' .. path_parts[#path_parts] - - obj._command = {op="showLuaFloater", xml_path=LL.abspath(path)} - if extra then - -- validate each of the actions for each specified control - for control, actions in pairs(extra) do - for _, action in pairs(actions) do - _event(action) - end - end - obj._command.extra_events = extra - end - - return obj -end - -util.classctor(Floater) - -function Floater:show() - -- leap.eventstream() returns the first response, and launches a - -- background fiber to call the passed callback with all subsequent - -- responses. - local event = leap.eventstream( - 'LLFloaterReg', - self._command, - -- handleEvents() returns false when done. - -- eventstream() expects a true return when done. - function(event) return not self:handleEvents(event) end) - self._pump = event.command_name - -- we might need the returned reqid to cancel the eventstream() fiber - self.reqid = event.reqid - - -- The response to 'showLuaFloater' *is* the 'post_build' event. Check if - -- subclass has a post_build() method. Honor the convention that if - -- handleEvents() returns false, we're done. - if not self:handleEvents(event) then - return - end -end - -function Floater:post(action) - leap.send(self._pump, action) -end - -function Floater:request(action) - return leap.request(self._pump, action) -end - --- local inspect = require 'inspect' - -function Floater:handleEvents(event_data) - local event = event_data.event - if event_set[event] == nil then - LL.print_warning(string.format('%s received unknown event %q', self.name, event)) - end - - -- Before checking for a general (e.g.) commit() method, first look for - -- commit_ctrl_name(): in other words, concatenate the event name with the - -- ctrl_name, with an underscore between. If there exists such a specific - -- method, call that. - local handler, ret - if event_data.ctrl_name then - local specific = event .. '_' .. event_data.ctrl_name - handler = self[specific] - if handler then - ret = handler(self, event_data) - -- Avoid 'return ret or true' because we explicitly want to allow - -- the handler to return false. - if ret ~= nil then - return ret - else - return true - end - end - end - - -- No specific "event_on_ctrl()" method found; try just "event()" - handler = self[event] - if handler then - ret = handler(self, event_data) - if ret ~= nil then - return ret - end --- else --- print(string.format('%s ignoring event %s', self.name, inspect(event_data))) - end - - -- We check for event() method before recognizing floater_close in case - -- the consumer needs to react specially to closing the floater. Now that - -- we've checked, recognize it ourselves. Returning false terminates the - -- anonymous fiber function launched by leap.eventstream(). - if event == _event('floater_close') then - LL.print_warning(self.name .. ' closed') - return false - end - return true -end - --- onCtrl() permits a different dispatch style in which the general event() --- method explicitly calls (e.g.) --- self:onCtrl(event_data, { --- ctrl_name=function() --- self:post(...) --- end, --- ... --- }) -function Floater:onCtrl(event_data, ctrl_map) - local handler = ctrl_map[event_data.ctrl_name] - if handler then - handler() - end -end - -return Floater diff --git a/indra/newview/scripts/lua/require/UI.lua b/indra/newview/scripts/lua/require/UI.lua index 969a2cbded..2df70fd453 100644 --- a/indra/newview/scripts/lua/require/UI.lua +++ b/indra/newview/scripts/lua/require/UI.lua @@ -5,17 +5,11 @@ local mapargs = require 'mapargs' local Timer = (require 'timers').Timer local util = require 'util' --- Allow lazily accessing certain other modules on demand, e.g. a reference to --- UI.Floater lazily loads the Floater module. Use of UI's __index metamethod --- theoretically permits any other module you can require() to appear as a --- submodule of UI, but it doesn't make sense to support (e.g.) UI.Queue. -local submods = { 'Floater', 'popup' } +-- Allow lazily accessing UI submodules on demand, e.g. a reference to +-- UI.Floater lazily loads the UI/Floater module. local UI = util.setmetamethods{ __index=function(t, key) - if not table.find(submods, key) then - error(`Invalid UI submodule {key}`, 2) - end - local mod = require(key) + local mod = require('UI/' .. key) -- cache the submodule t[key] = mod return mod diff --git a/indra/newview/scripts/lua/require/UI/Floater.lua b/indra/newview/scripts/lua/require/UI/Floater.lua new file mode 100644 index 0000000000..d057a74386 --- /dev/null +++ b/indra/newview/scripts/lua/require/UI/Floater.lua @@ -0,0 +1,146 @@ +-- Floater base class + +local leap = require 'leap' +local fiber = require 'fiber' +local util = require 'util' + +-- list of all the events that a LLLuaFloater might send +local event_list = leap.request("LLFloaterReg", {op="getFloaterEvents"}).events +local event_set = {} +for _, event in pairs(event_list) do + event_set[event] = true +end + +local function _event(event_name) + if not event_set[event_name] then + error("Incorrect event name: " .. event_name, 3) + end + return event_name +end + +-- --------------------------------------------------------------------------- +local Floater = {} + +-- Pass: +-- relative file path to floater's XUI definition file +-- optional: sign up for additional events for defined control +-- {={action1, action2, ...}} +function Floater:new(path, extra) + local obj = setmetatable({}, self) + self.__index = self + + local path_parts = string.split(path, '/') + obj.name = 'Floater ' .. path_parts[#path_parts] + + obj._command = {op="showLuaFloater", xml_path=LL.abspath(path)} + if extra then + -- validate each of the actions for each specified control + for control, actions in pairs(extra) do + for _, action in pairs(actions) do + _event(action) + end + end + obj._command.extra_events = extra + end + + return obj +end + +util.classctor(Floater) + +function Floater:show() + -- leap.eventstream() returns the first response, and launches a + -- background fiber to call the passed callback with all subsequent + -- responses. + local event = leap.eventstream( + 'LLFloaterReg', + self._command, + -- handleEvents() returns false when done. + -- eventstream() expects a true return when done. + function(event) return not self:handleEvents(event) end) + self._pump = event.command_name + -- we might need the returned reqid to cancel the eventstream() fiber + self.reqid = event.reqid + + -- The response to 'showLuaFloater' *is* the 'post_build' event. Check if + -- subclass has a post_build() method. Honor the convention that if + -- handleEvents() returns false, we're done. + if not self:handleEvents(event) then + return + end +end + +function Floater:post(action) + leap.send(self._pump, action) +end + +function Floater:request(action) + return leap.request(self._pump, action) +end + +-- local inspect = require 'inspect' + +function Floater:handleEvents(event_data) + local event = event_data.event + if event_set[event] == nil then + LL.print_warning(string.format('%s received unknown event %q', self.name, event)) + end + + -- Before checking for a general (e.g.) commit() method, first look for + -- commit_ctrl_name(): in other words, concatenate the event name with the + -- ctrl_name, with an underscore between. If there exists such a specific + -- method, call that. + local handler, ret + if event_data.ctrl_name then + local specific = event .. '_' .. event_data.ctrl_name + handler = self[specific] + if handler then + ret = handler(self, event_data) + -- Avoid 'return ret or true' because we explicitly want to allow + -- the handler to return false. + if ret ~= nil then + return ret + else + return true + end + end + end + + -- No specific "event_on_ctrl()" method found; try just "event()" + handler = self[event] + if handler then + ret = handler(self, event_data) + if ret ~= nil then + return ret + end +-- else +-- print(string.format('%s ignoring event %s', self.name, inspect(event_data))) + end + + -- We check for event() method before recognizing floater_close in case + -- the consumer needs to react specially to closing the floater. Now that + -- we've checked, recognize it ourselves. Returning false terminates the + -- anonymous fiber function launched by leap.eventstream(). + if event == _event('floater_close') then + LL.print_warning(self.name .. ' closed') + return false + end + return true +end + +-- onCtrl() permits a different dispatch style in which the general event() +-- method explicitly calls (e.g.) +-- self:onCtrl(event_data, { +-- ctrl_name=function() +-- self:post(...) +-- end, +-- ... +-- }) +function Floater:onCtrl(event_data, ctrl_map) + local handler = ctrl_map[event_data.ctrl_name] + if handler then + handler() + end +end + +return Floater diff --git a/indra/newview/scripts/lua/require/UI/popup.lua b/indra/newview/scripts/lua/require/UI/popup.lua new file mode 100644 index 0000000000..3aaadf85ba --- /dev/null +++ b/indra/newview/scripts/lua/require/UI/popup.lua @@ -0,0 +1,53 @@ +local leap = require 'leap' +local mapargs = require 'mapargs' + +-- notification is any name defined in notifications.xml as +-- +-- vars is a table providing values for [VAR] substitution keys in the +-- notification body +-- payload prepopulates the response table +-- wait=false means fire and forget, otherwise wait for user response +local popup_meta = { + -- setting this function as getmetatable(popup).__call() means this gets + -- called when a consumer calls popup(notification, vars, payload) + __call = function(self, ...) + local args = mapargs('notification,vars,payload,wait', ...) + -- we use convenience argument names different from 'LLNotifications' + -- listener + args.name = args.notification + args.notification = nil + args.substitutions = args.vars + args.vars = nil + local wait = args.wait + args.wait = nil + args.op = 'requestAdd' + -- Specifically test (wait == false), NOT (not wait), because we treat + -- nil (omitted, default true) differently than false (explicitly + -- DON'T wait). + if wait == false then + leap.send('LLNotifications', args) + else + return leap.request('LLNotifications', args).response + end + end +} + +local popup = setmetatable({}, popup_meta) + +function popup:alert(message) + return self('GenericAlert', {MESSAGE=message}) +end + +function popup:alertOK(message) + return self('GenericAlertOK', {MESSAGE=message}) +end + +function popup:alertYesCancel(message) + return self('GenericAlertYesCancel', {MESSAGE=message}) +end + +function popup:tip(message) + self{'SystemMessageTip', {MESSAGE=message}, wait=false} +end + +return popup diff --git a/indra/newview/scripts/lua/require/popup.lua b/indra/newview/scripts/lua/require/popup.lua deleted file mode 100644 index 3aaadf85ba..0000000000 --- a/indra/newview/scripts/lua/require/popup.lua +++ /dev/null @@ -1,53 +0,0 @@ -local leap = require 'leap' -local mapargs = require 'mapargs' - --- notification is any name defined in notifications.xml as --- --- vars is a table providing values for [VAR] substitution keys in the --- notification body --- payload prepopulates the response table --- wait=false means fire and forget, otherwise wait for user response -local popup_meta = { - -- setting this function as getmetatable(popup).__call() means this gets - -- called when a consumer calls popup(notification, vars, payload) - __call = function(self, ...) - local args = mapargs('notification,vars,payload,wait', ...) - -- we use convenience argument names different from 'LLNotifications' - -- listener - args.name = args.notification - args.notification = nil - args.substitutions = args.vars - args.vars = nil - local wait = args.wait - args.wait = nil - args.op = 'requestAdd' - -- Specifically test (wait == false), NOT (not wait), because we treat - -- nil (omitted, default true) differently than false (explicitly - -- DON'T wait). - if wait == false then - leap.send('LLNotifications', args) - else - return leap.request('LLNotifications', args).response - end - end -} - -local popup = setmetatable({}, popup_meta) - -function popup:alert(message) - return self('GenericAlert', {MESSAGE=message}) -end - -function popup:alertOK(message) - return self('GenericAlertOK', {MESSAGE=message}) -end - -function popup:alertYesCancel(message) - return self('GenericAlertYesCancel', {MESSAGE=message}) -end - -function popup:tip(message) - self{'SystemMessageTip', {MESSAGE=message}, wait=false} -end - -return popup -- cgit v1.2.3 From 2e815acb529159f5b6f0a4365a2eaf64d35330cc Mon Sep 17 00:00:00 2001 From: Nat Goodspeed Date: Fri, 23 Aug 2024 21:28:03 -0400 Subject: Encapsulate the lazy submodule idiom as util.submoduledir(). --- indra/newview/scripts/lua/require/UI.lua | 9 +-------- indra/newview/scripts/lua/require/util.lua | 19 +++++++++++++++++++ 2 files changed, 20 insertions(+), 8 deletions(-) diff --git a/indra/newview/scripts/lua/require/UI.lua b/indra/newview/scripts/lua/require/UI.lua index 2df70fd453..bbcae3514a 100644 --- a/indra/newview/scripts/lua/require/UI.lua +++ b/indra/newview/scripts/lua/require/UI.lua @@ -7,14 +7,7 @@ local util = require 'util' -- Allow lazily accessing UI submodules on demand, e.g. a reference to -- UI.Floater lazily loads the UI/Floater module. -local UI = util.setmetamethods{ - __index=function(t, key) - local mod = require('UI/' .. key) - -- cache the submodule - t[key] = mod - return mod - end -} +local UI = util.submoduledir({}, 'UI') -- *************************************************************************** -- registered menu actions diff --git a/indra/newview/scripts/lua/require/util.lua b/indra/newview/scripts/lua/require/util.lua index a000359226..40737a159a 100644 --- a/indra/newview/scripts/lua/require/util.lua +++ b/indra/newview/scripts/lua/require/util.lua @@ -92,4 +92,23 @@ function util.setmetamethods(specs) return t end +-- On the passed module (i.e. table), set an __index metamethod such that +-- referencing module.submodule lazily requires(path/submodule). +-- The loaded submodule is cached in the module table so it need not be passed +-- to require() again. +-- 'path', like any require() string, can be relative to LuaRequirePath. +-- Returns the enriched module, permitting e.g. +-- mymod = util.submoduledir({}, 'mymod') +function util.submoduledir(module, path) + return util.setmetamethods{ + module, + __index=function(t, key) + local mod = require(`{path}/{key}`) + -- cache the submodule + t[key] = mod + return mod + end + } +end + return util -- cgit v1.2.3 From e6d8379744b08f9a52af6734ba1f0e1b50fb5906 Mon Sep 17 00:00:00 2001 From: Nat Goodspeed Date: Fri, 23 Aug 2024 22:39:30 -0400 Subject: Massage results from UI.popup() for ease of use. In particular, where the raw leap.request().response call would return {OK_okcancelbuttons=true}, just return the string 'OK' or 'Cancel'. Update existing consumer scripts. --- indra/newview/scripts/lua/require/UI/popup.lua | 77 +++++++++++++++------- indra/newview/scripts/lua/test_group_chat.lua | 7 +- .../scripts/lua/test_luafloater_speedometer.lua | 5 +- indra/newview/scripts/lua/test_popup.lua | 9 ++- indra/newview/scripts/lua/test_toolbars.lua | 6 +- 5 files changed, 67 insertions(+), 37 deletions(-) diff --git a/indra/newview/scripts/lua/require/UI/popup.lua b/indra/newview/scripts/lua/require/UI/popup.lua index 3aaadf85ba..8ccf3b87f3 100644 --- a/indra/newview/scripts/lua/require/UI/popup.lua +++ b/indra/newview/scripts/lua/require/UI/popup.lua @@ -1,53 +1,82 @@ local leap = require 'leap' local mapargs = require 'mapargs' +local util = require 'util' -- notification is any name defined in notifications.xml as -- -- vars is a table providing values for [VAR] substitution keys in the -- notification body -- payload prepopulates the response table --- wait=false means fire and forget, otherwise wait for user response -local popup_meta = { - -- setting this function as getmetatable(popup).__call() means this gets - -- called when a consumer calls popup(notification, vars, payload) +-- wait=false means fire and forget, returning nil +-- wait=true waits for user response: +-- * If the viewer returns a table containing exactly one key=true pair, +-- popup() returns just that key. If the key is a string containing an +-- underscore, e.g. 'OK_okcancelbuttons', it's truncated at the first +-- underscore, e.g. 'OK'. +-- * Otherwise the viewer's response is returned unchanged. To suppress the +-- above transformations, pass a non-empty payload table; this will cause +-- the viewer to return a table with at least two keys. +local popup = util.setmetamethods{ + -- this gets called when a consumer calls popup(notification, vars, payload) __call = function(self, ...) local args = mapargs('notification,vars,payload,wait', ...) -- we use convenience argument names different from 'LLNotifications' -- listener - args.name = args.notification - args.notification = nil - args.substitutions = args.vars - args.vars = nil - local wait = args.wait - args.wait = nil - args.op = 'requestAdd' + newargs = {op='requestAdd', + name=args.notification, + substitutions=args.vars, + payload=args.payload} -- Specifically test (wait == false), NOT (not wait), because we treat -- nil (omitted, default true) differently than false (explicitly -- DON'T wait). - if wait == false then - leap.send('LLNotifications', args) + if args.wait == false then + leap.send('LLNotifications', newargs) else - return leap.request('LLNotifications', args).response + local response = leap.request('LLNotifications', newargs).response + -- response is typically a table. It might have multiple keys, + -- e.g. if caller passed non-empty payload. In that case, just + -- return the whole thing. + if type(response) ~= 'table' then + return response + end + -- get first key=value pair, if any + local key, value = next(response) + if (not key) or next(response, key) then + -- key == nil means response is empty + -- next(response, non-nil first key) ~= nil means at least two keys + return response + end + -- Here response is a table containing exactly one key. The + -- notifications system typically returns a table of the form + -- {OK_okcancelbuttons=true}, which is tricky to test for because it + -- varies with each set of buttons. + if value == true then + -- change {key=true} to plain key + response = key + if type(response) == 'string' then + -- change 'OK_okcancelbuttons' to plain 'OK' + response = string.split(response, '_')[1] + end + end + return response end end } -local popup = setmetatable({}, popup_meta) - -function popup:alert(message) - return self('GenericAlert', {MESSAGE=message}) +function popup:alert(message, payload) + return self('GenericAlert', {MESSAGE=message, payload=payload}) end -function popup:alertOK(message) - return self('GenericAlertOK', {MESSAGE=message}) +function popup:alertOK(message, payload) + return self('GenericAlertOK', {MESSAGE=message, payload=payload}) end -function popup:alertYesCancel(message) - return self('GenericAlertYesCancel', {MESSAGE=message}) +function popup:alertYesCancel(message, payload) + return self('GenericAlertYesCancel', {MESSAGE=message, payload=payload}) end -function popup:tip(message) - self{'SystemMessageTip', {MESSAGE=message}, wait=false} +function popup:tip(message, payload) + self{'SystemMessageTip', {MESSAGE=message, payload=payload}, wait=false} end return popup diff --git a/indra/newview/scripts/lua/test_group_chat.lua b/indra/newview/scripts/lua/test_group_chat.lua index eaff07ed14..373411c26c 100644 --- a/indra/newview/scripts/lua/test_group_chat.lua +++ b/indra/newview/scripts/lua/test_group_chat.lua @@ -1,15 +1,14 @@ LLChat = require 'LLChat' LLAgent = require 'LLAgent' -popup = require 'popup' +UI = require 'UI' -local OK = 'OK_okcancelbuttons' local GROUPS = LLAgent.getGroups() -- Choose one of the groups randomly and send group message math.randomseed(os.time()) group_info = GROUPS[math.random(#GROUPS)] LLChat.startGroupChat(group_info.id) -response = popup:alertYesCancel('Started group chat with ' .. group_info.name .. ' group. Send greetings?') -if next(response) == OK then +response = UI.popup:alertYesCancel('Started group chat with ' .. group_info.name .. ' group. Send greetings?') +if response == 'OK' then LLChat.sendGroupIM('Greetings', group_info.id) end diff --git a/indra/newview/scripts/lua/test_luafloater_speedometer.lua b/indra/newview/scripts/lua/test_luafloater_speedometer.lua index af7189a2cb..8bc18ad286 100644 --- a/indra/newview/scripts/lua/test_luafloater_speedometer.lua +++ b/indra/newview/scripts/lua/test_luafloater_speedometer.lua @@ -1,8 +1,9 @@ local Floater = require 'Floater' local leap = require 'leap' -local popup = require 'popup' local startup = require 'startup' local Timer = (require 'timers').Timer +local UI = require 'UI' +local popup = UI.popup local max_speed = 0 local flt = Floater("luafloater_speedometer.xml") startup.wait('STATE_STARTED') @@ -25,7 +26,7 @@ end msg = 'Are you sure you want to run this "speedometer" script?' response = popup:alertYesCancel(msg) -if response.OK_okcancelbuttons then +if response == 'OK' then flt:show() timer = Timer(1, idle, true) -- iterate end diff --git a/indra/newview/scripts/lua/test_popup.lua b/indra/newview/scripts/lua/test_popup.lua index 156c09c78f..7a11895669 100644 --- a/indra/newview/scripts/lua/test_popup.lua +++ b/indra/newview/scripts/lua/test_popup.lua @@ -1,7 +1,10 @@ UI = require 'UI' popup = UI.popup +startup = require 'startup' + +startup.wait('STATE_STARTED') response = popup:alert('This just has a Close button') -response = popup:alertOK(string.format('You said "%s", is that OK?', next(response))) -response = popup:alertYesCancel(string.format('You said "%s"', next(response))) -popup:tip(string.format('You said "%s"', next(response))) +response = popup:alertOK(string.format('You said "%s", is that OK?', response)) +response = popup:alertYesCancel(string.format('You said "%s"', response)) +popup:tip(string.format('You said "%s"', response)) diff --git a/indra/newview/scripts/lua/test_toolbars.lua b/indra/newview/scripts/lua/test_toolbars.lua index 9a832c5644..7683fca8a3 100644 --- a/indra/newview/scripts/lua/test_toolbars.lua +++ b/indra/newview/scripts/lua/test_toolbars.lua @@ -1,13 +1,11 @@ -popup = require 'popup' UI = require 'UI' -local OK = 'OK_okcancelbuttons' local BUTTONS = UI.getToolbarBtnNames() local TOOLBARS = {'left','right','bottom'} -- Clear the toolbars and then add the toolbar buttons to the random toolbar -response = popup:alertYesCancel('Toolbars will be randomly reshuffled. Proceed?') -if next(response) == OK then +response = UI.popup:alertYesCancel('Toolbars will be randomly reshuffled. Proceed?') +if response == 'OK' then UI.clearAllToolbars() math.randomseed(os.time()) -- cgit v1.2.3 From 7b21acd39745d265548eeb62d687cde9febb1f7a Mon Sep 17 00:00:00 2001 From: Nat Goodspeed Date: Sat, 24 Aug 2024 10:37:57 -0400 Subject: Update test scripts to reference UI.Floater, not standalone Floater. --- indra/newview/scripts/lua/require/LLChatListener.lua | 2 +- indra/newview/scripts/lua/test_LLAppearance.lua | 4 ++-- indra/newview/scripts/lua/test_camera_control.lua | 4 ++-- indra/newview/scripts/lua/test_luafloater_demo.lua | 6 +++--- indra/newview/scripts/lua/test_luafloater_gesture_list.lua | 4 ++-- indra/newview/scripts/lua/test_luafloater_speedometer.lua | 3 +-- 6 files changed, 11 insertions(+), 12 deletions(-) diff --git a/indra/newview/scripts/lua/require/LLChatListener.lua b/indra/newview/scripts/lua/require/LLChatListener.lua index 428dca881e..82b28966ce 100644 --- a/indra/newview/scripts/lua/require/LLChatListener.lua +++ b/indra/newview/scripts/lua/require/LLChatListener.lua @@ -23,7 +23,7 @@ function LLChatListener:handleMessages(event_data) end function LLChatListener:start() - waitfor = leap.WaitFor:new(-1, self.name) + waitfor = leap.WaitFor(-1, self.name) function waitfor:filter(pump, data) if pump == "LLNearbyChat" then return data diff --git a/indra/newview/scripts/lua/test_LLAppearance.lua b/indra/newview/scripts/lua/test_LLAppearance.lua index 5ddd9f15ff..a97ec4e0ca 100644 --- a/indra/newview/scripts/lua/test_LLAppearance.lua +++ b/indra/newview/scripts/lua/test_LLAppearance.lua @@ -1,7 +1,7 @@ -local Floater = require 'Floater' local LLAppearance = require 'LLAppearance' local startup = require 'startup' local inspect = require 'inspect' +local UI = require 'UI' local SHOW_OUTFITS = true local SELECTED_OUTFIT_ID = {} @@ -15,7 +15,7 @@ local outfits_title = 'Outfits' local wear_lbl = 'Wear item' local detach_lbl = 'Detach item' -local flt = Floater:new( +local flt = UI.Floater( "luafloater_outfits_list.xml", {outfits_list = {"double_click"}}) diff --git a/indra/newview/scripts/lua/test_camera_control.lua b/indra/newview/scripts/lua/test_camera_control.lua index 9b35cdf8cd..7ac0986ee6 100644 --- a/indra/newview/scripts/lua/test_camera_control.lua +++ b/indra/newview/scripts/lua/test_camera_control.lua @@ -1,8 +1,8 @@ -local Floater = require 'Floater' local LLAgent = require 'LLAgent' local startup = require 'startup' +local UI = require 'UI' -local flt = Floater('luafloater_camera_control.xml') +local flt = UI.Floater('luafloater_camera_control.xml') function getValue(ctrl_name) return flt:request({action="get_value", ctrl_name=ctrl_name}).value diff --git a/indra/newview/scripts/lua/test_luafloater_demo.lua b/indra/newview/scripts/lua/test_luafloater_demo.lua index 3903d01e65..2158134511 100644 --- a/indra/newview/scripts/lua/test_luafloater_demo.lua +++ b/indra/newview/scripts/lua/test_luafloater_demo.lua @@ -1,8 +1,8 @@ -local Floater = require 'Floater' local leap = require 'leap' local startup = require 'startup' +local UI = require 'UI' -local flt = Floater( +local flt = UI.Floater( 'luafloater_demo.xml', {show_time_lbl = {"right_mouse_down", "double_click"}}) @@ -10,7 +10,7 @@ local flt = Floater( function flt:handleEvents(event_data) self:post({action="add_text", ctrl_name="events_editor", value = event_data}) -- forward the call to base-class handleEvents() - return Floater.handleEvents(self, event_data) + return UI.Floater.handleEvents(self, event_data) end function flt:commit_disable_ctrl(event_data) diff --git a/indra/newview/scripts/lua/test_luafloater_gesture_list.lua b/indra/newview/scripts/lua/test_luafloater_gesture_list.lua index bd397ef2a6..5f929c0d0c 100644 --- a/indra/newview/scripts/lua/test_luafloater_gesture_list.lua +++ b/indra/newview/scripts/lua/test_luafloater_gesture_list.lua @@ -1,8 +1,8 @@ -local Floater = require 'Floater' local LLGesture = require 'LLGesture' local startup = require 'startup' +local UI = require 'UI' -local flt = Floater( +local flt = UI.Floater( "luafloater_gesture_list.xml", {gesture_list = {"double_click"}}) diff --git a/indra/newview/scripts/lua/test_luafloater_speedometer.lua b/indra/newview/scripts/lua/test_luafloater_speedometer.lua index 8bc18ad286..2cdd41fd7e 100644 --- a/indra/newview/scripts/lua/test_luafloater_speedometer.lua +++ b/indra/newview/scripts/lua/test_luafloater_speedometer.lua @@ -1,11 +1,10 @@ -local Floater = require 'Floater' local leap = require 'leap' local startup = require 'startup' local Timer = (require 'timers').Timer local UI = require 'UI' local popup = UI.popup local max_speed = 0 -local flt = Floater("luafloater_speedometer.xml") +local flt = UI.Floater("luafloater_speedometer.xml") startup.wait('STATE_STARTED') local timer -- cgit v1.2.3 From 3cc48ac9e22fb562560929a5c45a8b6ef1ecc841 Mon Sep 17 00:00:00 2001 From: Mnikolenko Productengine Date: Mon, 26 Aug 2024 13:48:12 +0300 Subject: clean up Lua prefix --- indra/newview/llviewermessage.cpp | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/indra/newview/llviewermessage.cpp b/indra/newview/llviewermessage.cpp index 4922e3c9f9..56f759833c 100644 --- a/indra/newview/llviewermessage.cpp +++ b/indra/newview/llviewermessage.cpp @@ -2646,15 +2646,19 @@ void process_chat_from_simulator(LLMessageSystem *msg, void **user_data) else { chat.mText = ""; - std::string chat_text(mesg); auto [msg_without_prefix, is_lua] = LLStringUtil::withoutPrefix(mesg, LUA_PREFIX); + std::string prefix; + if (is_lua) + { + prefix = LUA_PREFIX; + } switch(chat.mChatType) { case CHAT_TYPE_WHISPER: + prefix += LLTrans::getString("whisper") + " "; + break; case CHAT_TYPE_SHOUT: - chat_text = LLTrans::getString(chat.mChatType == CHAT_TYPE_WHISPER ? "whisper" : "shout") + " " + msg_without_prefix; - if(is_lua) - chat_text = LUA_PREFIX + chat_text; + prefix += LLTrans::getString("shout") + " "; break; case CHAT_TYPE_DEBUG_MSG: case CHAT_TYPE_OWNER: @@ -2670,7 +2674,7 @@ void process_chat_from_simulator(LLMessageSystem *msg, void **user_data) break; } - chat.mText += chat_text; + chat.mText = prefix + msg_without_prefix; } // We have a real utterance now, so can stop showing "..." and proceed. -- cgit v1.2.3 From 27ce2a23a286709c90579db31c2551e0c3f292e7 Mon Sep 17 00:00:00 2001 From: Mnikolenko Productengine Date: Tue, 27 Aug 2024 16:27:34 +0300 Subject: code clean up --- indra/llcommon/llassettype.cpp | 4 +- indra/llinventory/llfoldertype.cpp | 7 +- indra/newview/llinventorylistener.cpp | 136 +++++++++++++--------- indra/newview/llinventorylistener.h | 28 ----- indra/newview/scripts/lua/require/LLInventory.lua | 12 +- indra/newview/scripts/lua/test_LLInventory.lua | 2 +- 6 files changed, 92 insertions(+), 97 deletions(-) diff --git a/indra/llcommon/llassettype.cpp b/indra/llcommon/llassettype.cpp index 1c0893b1bf..00c61c6e0a 100644 --- a/indra/llcommon/llassettype.cpp +++ b/indra/llcommon/llassettype.cpp @@ -250,9 +250,9 @@ LLSD LLAssetType::getTypeNames() { LLSD type_names; const LLAssetDictionary *dict = LLAssetDictionary::getInstance(); - for (S32 type = AT_TEXTURE; type < AT_COUNT; ++type) + for (S32 type = 0; type < AT_COUNT; ++type) { - const AssetEntry *entry = dict->lookup((LLAssetType::EType) type); + const AssetEntry *entry = dict->lookup(LLAssetType::EType(type)); // skip llassettype_bad_lookup if (entry) { diff --git a/indra/llinventory/llfoldertype.cpp b/indra/llinventory/llfoldertype.cpp index c8ef1ccc1b..95deb46431 100644 --- a/indra/llinventory/llfoldertype.cpp +++ b/indra/llinventory/llfoldertype.cpp @@ -225,11 +225,12 @@ const std::string &LLFolderType::badLookup() LLSD LLFolderType::getTypeNames() { LLSD type_names; - for (S32 type = FT_TEXTURE; type < FT_COUNT; ++type) + const LLFolderDictionary *dict = LLFolderDictionary::getInstance(); + for (S32 type = 0; type < FT_COUNT; ++type) { - if (lookupIsEnsembleType((LLFolderType::EType)type)) continue; + if (lookupIsEnsembleType(LLFolderType::EType(type))) continue; - const FolderEntry *entry = LLFolderDictionary::getInstance()->lookup((LLFolderType::EType)type); + const FolderEntry *entry = dict->lookup(LLFolderType::EType(type)); //skip llfoldertype_bad_lookup if (entry) { diff --git a/indra/newview/llinventorylistener.cpp b/indra/newview/llinventorylistener.cpp index 0aa1208d83..157e04dce3 100644 --- a/indra/newview/llinventorylistener.cpp +++ b/indra/newview/llinventorylistener.cpp @@ -40,18 +40,18 @@ LLInventoryListener::LLInventoryListener() "API for interactions with viewer Inventory items") { add("getItemsInfo", - "Return information about items or folders defined in [\"items_id\"]:\n" + "Return information about items or folders defined in [\"item_ids\"]:\n" "reply will contain [\"items\"] and [\"categories\"] tables accordingly", &LLInventoryListener::getItemsInfo, - llsd::map("items_id", LLSD(), "reply", LLSD())); + llsd::map("item_ids", LLSD(), "reply", LLSD())); add("getFolderTypeNames", - "Return the table of folder type names, contained in [\"type_names\"]\n", + "Return the table of folder type names, contained in [\"names\"]\n", &LLInventoryListener::getFolderTypeNames, llsd::map("reply", LLSD())); add("getAssetTypeNames", - "Return the table of asset type names, contained in [\"type_names\"]\n", + "Return the table of asset type names, contained in [\"names\"]\n", &LLInventoryListener::getAssetTypeNames, llsd::map("reply", LLSD())); @@ -70,8 +70,8 @@ LLInventoryListener::LLInventoryListener() "Return the descendents(both items and folders) of the [\"folder_id\"], if it passes specified filters:\n" "[\"name\"] is a substring of object's name,\n" "[\"desc\"] is a substring of object's description,\n" - "asset [\"type\"] corresponds to the object's asset type\n" - "[\"item_limit\"] sets item count limit in reply, maximum and default is 100\n" + "asset [\"type\"] corresponds to the string name of the object's asset type\n" + "[\"limit\"] sets item count limit in reply, maximum and default is 100\n" "[\"filter_links\"]: EXCLUDE_LINKS - don't show links, ONLY_LINKS - only show links, INCLUDE_LINKS - show links too (default)", &LLInventoryListener::collectDescendentsIf, llsd::map("folder_id", LLSD(), "reply", LLSD())); @@ -81,23 +81,42 @@ LLInventoryListener::LLInventoryListener() void add_item_info(LLEventAPI::Response& response, LLViewerInventoryItem* item) { response["items"].insert(item->getUUID().asString(), - llsd::map("name", item->getName(), "parent_id", item->getParentUUID(), "desc", item->getDescription(), - "inv_type", LLInventoryType::lookup(item->getInventoryType()), "asset_type", LLAssetType::lookup(item->getType()), - "creation_date", (S32) item->getCreationDate(), "asset_id", item->getAssetUUID(), - "is_link", item->getIsLinkType(), "linked_id", item->getLinkedUUID())); + llsd::map("name", item->getName(), + "parent_id", item->getParentUUID(), + "desc", item->getDescription(), + "inv_type", LLInventoryType::lookup(item->getInventoryType()), + "asset_type", LLAssetType::lookup(item->getType()), + "creation_date", (S32) item->getCreationDate(), + "asset_id", item->getAssetUUID(), + "is_link", item->getIsLinkType(), + "linked_id", item->getLinkedUUID())); } void add_cat_info(LLEventAPI::Response &response, LLViewerInventoryCategory *cat) { response["categories"].insert(cat->getUUID().asString(), - llsd::map("name", cat->getName(), "parent_id", cat->getParentUUID(), "type", LLFolderType::lookup(cat->getPreferredType()))); + llsd::map("name", cat->getName(), + "parent_id", cat->getParentUUID(), + "type", LLFolderType::lookup(cat->getPreferredType()))); +} + +void add_objects_info(LLEventAPI::Response& response, LLInventoryModel::cat_array_t cat_array, LLInventoryModel::item_array_t item_array) +{ + for (auto &p : item_array) + { + add_item_info(response, p); + } + for (auto &p : cat_array) + { + add_cat_info(response, p); + } } void LLInventoryListener::getItemsInfo(LLSD const &data) { Response response(LLSD(), data); - uuid_vec_t ids = LLSDParam(data["items_id"]); + uuid_vec_t ids = LLSDParam(data["item_ids"]); for (auto &it : ids) { LLViewerInventoryItem* item = gInventory.getItem(it); @@ -105,22 +124,25 @@ void LLInventoryListener::getItemsInfo(LLSD const &data) { add_item_info(response, item); } - LLViewerInventoryCategory* cat = gInventory.getCategory(it); - if (cat) + else { - add_cat_info(response, cat); + LLViewerInventoryCategory *cat = gInventory.getCategory(it); + if (cat) + { + add_cat_info(response, cat); + } } } } void LLInventoryListener::getFolderTypeNames(LLSD const &data) { - Response response(llsd::map("type_names", LLFolderType::getTypeNames()), data); + Response response(llsd::map("names", LLFolderType::getTypeNames()), data); } void LLInventoryListener::getAssetTypeNames(LLSD const &data) { - Response response(llsd::map("type_names", LLAssetType::getTypeNames()), data); + Response response(llsd::map("names", LLAssetType::getTypeNames()), data); } void LLInventoryListener::getBasicFolderID(LLSD const &data) @@ -136,18 +158,37 @@ void LLInventoryListener::getDirectDescendents(LLSD const &data) LLInventoryModel::item_array_t* items; gInventory.getDirectDescendentsOf(data["folder_id"], cats, items); - LLInventoryModel::item_array_t items_copy = *items; - for (LLInventoryModel::item_array_t::iterator iter = items_copy.begin(); iter != items_copy.end(); iter++) - { - add_item_info(response, *iter); - } - LLInventoryModel::cat_array_t cats_copy = *cats; - for (LLInventoryModel::cat_array_t::iterator iter = cats_copy.begin(); iter != cats_copy.end(); iter++) - { - add_cat_info(response, *iter); - } + add_objects_info(response, *cats, *items); } +struct LLFilteredCollector : public LLInventoryCollectFunctor +{ + enum EFilterLink + { + INCLUDE_LINKS, // show links too + EXCLUDE_LINKS, // don't show links + ONLY_LINKS // only show links + }; + + LLFilteredCollector(LLSD const &data); + virtual ~LLFilteredCollector() {} + virtual bool operator()(LLInventoryCategory *cat, LLInventoryItem *item) override; + virtual bool exceedsLimit() override { return (mItemLimit <= mItemCount); }; + + protected: + bool checkagainstType(LLInventoryCategory *cat, LLInventoryItem *item); + bool checkagainstNameDesc(LLInventoryCategory *cat, LLInventoryItem *item); + bool checkagainstLinks(LLInventoryCategory *cat, LLInventoryItem *item); + + LLAssetType::EType mType; + std::string mName; + std::string mDesc; + EFilterLink mLinkFilter; + + S32 mItemLimit; + S32 mItemCount; +}; + void LLInventoryListener::collectDescendentsIf(LLSD const &data) { Response response(LLSD(), data); @@ -164,14 +205,7 @@ void LLInventoryListener::collectDescendentsIf(LLSD const &data) gInventory.collectDescendentsIf(folder_id, cat_array, item_array, LLInventoryModel::EXCLUDE_TRASH, collector); - for (LLInventoryModel::item_array_t::iterator iter = item_array.begin(); iter != item_array.end(); iter++) - { - add_item_info(response, *iter); - } - for (LLInventoryModel::cat_array_t::iterator iter = cat_array.begin(); iter != cat_array.end(); iter++) - { - add_cat_info(response, *iter); - } + add_objects_info(response, cat_array, item_array); } LLFilteredCollector::LLFilteredCollector(LLSD const &data) : @@ -180,14 +214,10 @@ LLFilteredCollector::LLFilteredCollector(LLSD const &data) : mItemLimit(MAX_ITEM_LIMIT), mItemCount(0) { - if (data.has("name")) - { - mName = data["name"].asString(); - } - if (data.has("desc")) - { - mDesc = data["desc"].asString(); - } + + mName = data["name"].asString(); + mDesc = data["desc"].asString(); + if (data.has("type")) { mType = LLAssetType::lookup(data["type"]); @@ -203,9 +233,9 @@ LLFilteredCollector::LLFilteredCollector(LLSD const &data) : mLinkFilter = ONLY_LINKS; } } - if (data.has("item_limit")) + if (data["limit"].isInteger()) { - mItemLimit = llclamp(data["item_limit"].asInteger(), 1, MAX_ITEM_LIMIT); + mItemLimit = llclamp(data["limit"].asInteger(), 1, MAX_ITEM_LIMIT); } } @@ -222,11 +252,6 @@ bool LLFilteredCollector::operator()(LLInventoryCategory *cat, LLInventoryItem * return passed; } -bool LLFilteredCollector::exceedsLimit() -{ - return (mItemLimit <= mItemCount); -} - bool LLFilteredCollector::checkagainstNameDesc(LLInventoryCategory *cat, LLInventoryItem *item) { std::string name, desc; @@ -239,10 +264,10 @@ bool LLFilteredCollector::checkagainstNameDesc(LLInventoryCategory *cat, LLInven if (item) { name = item->getName(); - passed = (mDesc.size() ? item->getDescription().find(mDesc) != std::string::npos : true); + passed = (mDesc.empty() || (item->getDescription().find(mDesc) != std::string::npos)); } - return passed && (mName.size() ? name.find(mName) != std::string::npos : true); + return passed && (mName.empty() || name.find(mName) != std::string::npos); } bool LLFilteredCollector::checkagainstType(LLInventoryCategory *cat, LLInventoryItem *item) @@ -251,12 +276,9 @@ bool LLFilteredCollector::checkagainstType(LLInventoryCategory *cat, LLInventory { return true; } - if (mType == LLAssetType::AT_CATEGORY) + if (cat && (mType == LLAssetType::AT_CATEGORY)) { - if (cat) - { - return true; - } + return true; } if (item && item->getType() == mType) { diff --git a/indra/newview/llinventorylistener.h b/indra/newview/llinventorylistener.h index 2c1eb2cb6d..5cbac2ca32 100644 --- a/indra/newview/llinventorylistener.h +++ b/indra/newview/llinventorylistener.h @@ -44,33 +44,5 @@ private: void collectDescendentsIf(LLSD const &data); }; -struct LLFilteredCollector : public LLInventoryCollectFunctor -{ - enum EFilterLink - { - INCLUDE_LINKS, // show links too - EXCLUDE_LINKS, // don't show links - ONLY_LINKS // only show links - }; - - LLFilteredCollector(LLSD const &data); - virtual ~LLFilteredCollector() {} - virtual bool operator()(LLInventoryCategory *cat, LLInventoryItem *item); - virtual bool exceedsLimit(); - - protected: - bool checkagainstType(LLInventoryCategory *cat, LLInventoryItem *item); - bool checkagainstNameDesc(LLInventoryCategory *cat, LLInventoryItem *item); - bool checkagainstLinks(LLInventoryCategory *cat, LLInventoryItem *item); - - LLAssetType::EType mType; - std::string mName; - std::string mDesc; - EFilterLink mLinkFilter; - - S32 mItemLimit; - S32 mItemCount; -}; - #endif // LL_LLINVENTORYLISTENER_H diff --git a/indra/newview/scripts/lua/require/LLInventory.lua b/indra/newview/scripts/lua/require/LLInventory.lua index b3f817da94..dd1b910250 100644 --- a/indra/newview/scripts/lua/require/LLInventory.lua +++ b/indra/newview/scripts/lua/require/LLInventory.lua @@ -5,13 +5,13 @@ local LLInventory = {} -- Get the items/folders info by provided IDs, -- reply will contain "items" and "categories" tables accordingly -function LLInventory.getItemsInfo(items_id) - return leap.request('LLInventory', {op = 'getItemsInfo', items_id=items_id}) +function LLInventory.getItemsInfo(item_ids) + return leap.request('LLInventory', {op = 'getItemsInfo', item_ids=item_ids}) end -- Get the table of folder type names, which can be later used to get the ID of the basic folders function LLInventory.getFolderTypeNames() - return leap.request('LLInventory', {op = 'getFolderTypeNames'}).type_names + return leap.request('LLInventory', {op = 'getFolderTypeNames'}).names end -- Get the UUID of the basic folder("Textures", "My outfits", "Sounds" etc.) by specified folder type name @@ -21,7 +21,7 @@ end -- Get the table of asset type names, which can be later used to get the specific items via LLInventory.collectDescendentsIf(...) function LLInventory.getAssetTypeNames() - return leap.request('LLInventory', {op = 'getAssetTypeNames'}).type_names + return leap.request('LLInventory', {op = 'getAssetTypeNames'}).names end -- Get the direct descendents of the 'folder_id' provided, @@ -36,10 +36,10 @@ end -- [, name] -- name (substring) -- [, desc] -- description (substring) -- [, type] -- asset type --- [, item_limit] -- item count limit in reply, maximum and default is 100 +-- [, limit] -- item count limit in reply, maximum and default is 100 -- [, filter_links]} -- EXCLUDE_LINKS - don't show links, ONLY_LINKS - only show links, INCLUDE_LINKS - show links too (default) function LLInventory.collectDescendentsIf(...) - local args = mapargs('folder_id,name,desc,type,filter_links,item_limit', ...) + local args = mapargs('folder_id,name,desc,type,filter_links,limit', ...) args.op = 'collectDescendentsIf' return leap.request('LLInventory', args) end diff --git a/indra/newview/scripts/lua/test_LLInventory.lua b/indra/newview/scripts/lua/test_LLInventory.lua index dc6eb243ad..107b0791d4 100644 --- a/indra/newview/scripts/lua/test_LLInventory.lua +++ b/indra/newview/scripts/lua/test_LLInventory.lua @@ -4,7 +4,7 @@ LLInventory = require 'LLInventory' -- Get 'My Landmarks' folder id (you can see all folder types via LLInventory.getFolderTypeNames()) my_landmarks_id = LLInventory.getBasicFolderID('landmark') -- Get 3 landmarks from the 'My Landmarks' folder (you can see all folder types via LLInventory.getAssetTypeNames()) -landmarks = LLInventory.collectDescendentsIf{folder_id=my_landmarks_id, type="landmark", item_limit=3} +landmarks = LLInventory.collectDescendentsIf{folder_id=my_landmarks_id, type="landmark", limit=3} print(inspect(landmarks)) -- Get 'Calling Cards' folder id -- cgit v1.2.3 From 841e19c1cb62341c10254e6f4bf992c0c19d27b8 Mon Sep 17 00:00:00 2001 From: Nat Goodspeed Date: Wed, 28 Aug 2024 15:07:14 -0400 Subject: Remove obsolete, unreferenced DESTRINGIZE(), DEWSTRINGIZE() macros. --- indra/llcommon/stringize.h | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/indra/llcommon/stringize.h b/indra/llcommon/stringize.h index 63d44a7272..9604d912b5 100644 --- a/indra/llcommon/stringize.h +++ b/indra/llcommon/stringize.h @@ -190,16 +190,4 @@ void destringize_f(std::basic_string const & str, Functor const & f) f(in); } -/** - * DESTRINGIZE(str, item1 >> item2 >> item3 ...) effectively expands to the - * following: - * @code - * std::istringstream in(str); - * in >> item1 >> item2 >> item3 ... ; - * @endcode - */ -#define DESTRINGIZE(STR, EXPRESSION) (destringize_f((STR), [&](auto& in){in >> EXPRESSION;})) -// legacy name, just use DESTRINGIZE() going forward -#define DEWSTRINGIZE(STR, EXPRESSION) DESTRINGIZE(STR, EXPRESSION) - #endif /* ! defined(LL_STRINGIZE_H) */ -- cgit v1.2.3 From 14c8fc3768d978205bf17ffc1905c2772afbd434 Mon Sep 17 00:00:00 2001 From: Nat Goodspeed Date: Wed, 28 Aug 2024 16:47:38 -0400 Subject: Add `LL.setdtor()` function to add a "destructor" to any Lua object. `setdtor('description', object, function)` returns a proxy userdata object referencing object and function. When the proxy is garbage-collected, or at the end of the script, its destructor calls `function(object)`. The original object may be retrieved as `proxy._target`, e.g. to pass it to the `table` library. The proxy also has a metatable with metamethods supporting arithmetic operations, string concatenation, length and table indexing. For other operations, retrieve `proxy._target`. (But don't assign to `proxy._target`. It will appear to work, in that subsequent references to `proxy._target` will retrieve the replacement object -- however, the destructor will still call `function(original object)`.) Fix bugs in `lua_setfieldv()`, `lua_rawgetfield()` and `lua_rawsetfield()`. Add C++ functions `lua_destroyuserdata()` to explicitly destroy a `lua_emplace()` userdata object, plus `lua_destroybounduserdata()`. The latter can bind such a userdata object as an upvalue to pass to `LL.atexit()`. Make `LL.help()` and `LL.leaphelp()` help text include the `LL.` prefix. --- indra/llcommon/lua_function.cpp | 325 ++++++++++++++++++++++++++++- indra/llcommon/lua_function.h | 18 +- indra/newview/scripts/lua/test_setdtor.lua | 62 ++++++ 3 files changed, 396 insertions(+), 9 deletions(-) create mode 100644 indra/newview/scripts/lua/test_setdtor.lua diff --git a/indra/llcommon/lua_function.cpp b/indra/llcommon/lua_function.cpp index 880bc209f6..12cff89fbd 100644 --- a/indra/llcommon/lua_function.cpp +++ b/indra/llcommon/lua_function.cpp @@ -99,6 +99,34 @@ fsyspath source_path(lua_State* L) } // namespace lluau +/***************************************************************************** +* lua_destroyuserdata(), lua_destroybounduserdata() (see lua_emplace()) +*****************************************************************************/ +int lua_destroyuserdata(lua_State* L) +{ + // stack: lua_emplace() userdata to be destroyed + if (int tag; + lua_isuserdata(L, -1) && + (tag = lua_userdatatag(L, -1)) != 0) + { + auto dtor = lua_getuserdatadtor(L, tag); + // detach this userdata from the destructor with tag 'tag' + lua_setuserdatatag(L, -1, 0); + // now run the real destructor + dtor(L, lua_touserdata(L, -1)); + } + lua_settop(L, 0); + return 0; +} + +int lua_destroybounduserdata(lua_State *L) +{ + // called with no arguments -- push bound upvalue + lluau_checkstack(L, 1); + lua_pushvalue(L, lua_upvalueindex(1)); + return lua_destroyuserdata(L); +} + /***************************************************************************** * Lua <=> C++ conversions *****************************************************************************/ @@ -664,10 +692,10 @@ LuaListener& LuaState::obtainListener(lua_State* L) // At this point, one way or the other, the stack top should be (a Lua // userdata containing) our LuaListener. LuaListener* listener{ lua_toclass(L, -1) }; - // userdata objects created by lua_emplace() are bound on the atexit() - // queue, and are thus never garbage collected: they're destroyed only - // when ~LuaState() walks that queue. That's why we dare pop the userdata - // value off the stack while still depending on a pointer into its data. + // Since our LuaListener instance is stored in the Registry, it won't be + // garbage collected: it will be destroyed only when lua_close() clears + // out the Registry. That's why we dare pop the userdata value off the + // stack while still depending on a pointer into its data. lua_pop(L, 1); return *listener; } @@ -851,8 +879,8 @@ lua_function(check_stop, "check_stop(): ensure that a Lua script responds to vie * help() *****************************************************************************/ lua_function(help, - "help(): list viewer's Lua functions\n" - "help(function): show help string for specific function") + "LL.help(): list viewer's Lua functions\n" + "LL.help(function): show help string for specific function") { auto& luapump{ LLEventPumps::instance().obtain("lua output") }; const auto& [registry, lookup]{ LuaFunction::getRState() }; @@ -911,8 +939,8 @@ lua_function(help, *****************************************************************************/ lua_function( leaphelp, - "leaphelp(): list viewer's LEAP APIs\n" - "leaphelp(api): show help for specific api string name") + "LL.leaphelp(): list viewer's LEAP APIs\n" + "LL.leaphelp(api): show help for specific api string name") { LLSD request; int top{ lua_gettop(L) }; @@ -974,6 +1002,287 @@ lua_function( return 0; // void return } +/***************************************************************************** +* setdtor +*****************************************************************************/ +namespace { + +// proxy userdata object returned by setdtor() +struct setdtor_refs +{ + lua_State* L; + std::string desc; + // You can't directly store a Lua object in a C++ object, but you can + // create a Lua "reference" by storing the object in the Lua Registry and + // capturing its Registry index. + int objref; + int dtorref; + + setdtor_refs(lua_State* L, const std::string& desc, int objref, int dtorref): + L(L), + desc(desc), + objref(objref), + dtorref(dtorref) + {} + setdtor_refs(const setdtor_refs&) = delete; + setdtor_refs& operator=(const setdtor_refs&) = delete; + ~setdtor_refs(); + + static void push_metatable(lua_State* L); + static std::string binop(const std::string& name, const std::string& op); + static int meta__index(lua_State* L); +}; + +} // anonymous namespace + +lua_function( + setdtor, + "setdtor(desc, obj, dtorfunc) => proxy object referencing obj and dtorfunc.\n" + "When the returned proxy object is garbage-collected, or when the script\n" + "ends, call dtorfunc(obj). String desc is logged in the error message, if any.\n" + "Use the returned proxy object (or proxy._target) like obj.\n" + "obj won't be destroyed as long as the proxy exists; it's the proxy object's\n" + "lifespan that determines when dtorfunc(obj) will be called.") +{ + if (lua_gettop(L) != 3) + { + return lluau::error(L, "setdtor(desc, obj, dtor) requires exactly 3 arguments"); + } + // called with (desc, obj, dtor), returns proxy object + lua_checkdelta(L, -2); + lluau_checkstack(L, 3); // might get up to 6 stack entries + auto desc{ lua_tostdstring(L, 1) }; + // Get Lua "references" for each of the object and the dtor function. + int objref = lua_ref(L, 2); + int dtorref = lua_ref(L, 3); + // Having captured each of our parameters, discard them. + lua_settop(L, 0); + // Push our setdtor_refs userdata. Not only do we want to push it on L's + // stack, but setdtor_refs's constructor itself requires L. + lua_emplace(L, L, desc, objref, dtorref); + // stack: proxy (i.e. setdtor_refs userdata) + // have to set its metatable + lua_getfield(L, LUA_REGISTRYINDEX, "setdtor_meta"); + // stack: proxy, setdtor_meta (which might be nil) + if (lua_isnil(L, -1)) + { + // discard nil + lua_pop(L, 1); + // compile and push our forwarding metatable + setdtor_refs::push_metatable(L); + // stack: proxy, metatable + // duplicate metatable to save it + lua_pushvalue(L, -1); + // stack: proxy, metatable, metable + // save metatable for future calls + lua_setfield(L, LUA_REGISTRYINDEX, "setdtor_meta"); + // stack: proxy, metatable + } + // stack: proxy, metatable + lua_setmetatable(L, -2); + // stack: proxy + // Because ~setdtor_refs() necessarily uses the Lua stack, the Registry et + // al., we can't let a setdtor_refs instance be destroyed by lua_close(): + // the Lua environment will already be partially shut down. To destroy + // this new setdtor_refs instance BEFORE lua_close(), bind it with + // lua_destroybounduserdata() and register it with LL.atexit(). + // push (the entry point for) LL.atexit() + lua_pushcfunction(L, atexit_luasub::call, "LL.atexit()"); + // stack: proxy, atexit() + lua_pushvalue(L, -2); + // stack: proxy, atexit(), proxy + int tag = lua_userdatatag(L, -1); + // We don't have a lookup table to get from an int Lua userdata tag to the + // corresponding C++ typeinfo name string. We'll introduce one if we need + // it for debugging. But for this particular call, we happen to know it's + // always a setdtor_refs object. + lua_pushcclosure(L, lua_destroybounduserdata, + stringize("lua_destroybounduserdata<", tag, ">()").c_str(), + 1); + // stack: proxy, atexit(), lua_destroybounduserdata + // call atexit(): one argument, no results, let error propagate + lua_call(L, 1, 0); + // stack: proxy + return 1; +} + +namespace { + +void setdtor_refs::push_metatable(lua_State* L) +{ + lua_checkdelta(L, 1); + lluau_checkstack(L, 1); + // Ideally we want a metatable that forwards every operation on our + // setdtor_refs userdata proxy object to the original object. But the + // published C API doesn't include (e.g.) arithmetic operations on Lua + // objects, so in fact it's easier to express the desired metatable in Lua + // than in C++. We could make setdtor() depend on an external Lua module, + // but it seems less fragile to embed the Lua source code right here. + static const std::string setdtor_meta = stringize(R"-( + -- This metatable literal doesn't define __index() because that's + -- implemented in C++. We cannot, in Lua, peek into the setdtor_refs + -- userdata object to obtain objref, nor can we fetch Registry[objref]. + -- So our C++ __index() metamethod recognizes access to '_target' as a + -- reference to Registry[objref]. + -- The rest are defined per https://www.lua.org/manual/5.1/manual.html#2.8. + -- Luau supports destructors instead of __gc metamethod -- we rely on that! + -- We don't set __mode because our proxy is not a table. Real references + -- are stored in the wrapped table, so ITS __mode is what counts. + -- Initial definition of meta omits binary metamethods so they can bind the + -- metatable itself, as explained for binop() below. + local meta = { + __unm = function(arg) + return -arg._target + end, + __len = function(arg) + return #arg._target + end, + -- Comparison metamethods __eq(), __lt() and __le() are only called + -- when both operands have the same metamethod. For our purposes, that + -- means both operands are setdtor_refs userdata objects. + __eq = function(lhs, rhs) + return (lhs._target == rhs._target) + end, + __lt = function(lhs, rhs) + return (lhs._target < rhs._target) + end, + __le = function(lhs, rhs) + return (lhs._target <= rhs._target) + end, + __newindex = function(t, key, value) + t._target[key] = value + end, + __call = function(func, ...) + return func._target(...) + end, + __tostring = function(arg) + -- don't fret about arg._target's __tostring metamethod, + -- if any, because built-in tostring() deals with that + return tostring(arg._target) + end + } +)-", + binop("add", "+"), + binop("sub", "-"), + binop("mul", "*"), + binop("div", "/"), + binop("mod", "%"), + binop("pow", "^"), + binop("concat", ".."), +R"-( + return meta +)-"); + // only needed for debugging binop() +// LL_DEBUGS("Lua") << setdtor_meta << LL_ENDL; + + if (lluau::dostring(L, LL_PRETTY_FUNCTION, setdtor_meta) != LUA_OK) + { + // stack: error message string + lua_error(L); + } + llassert(lua_gettop(L) > 0); + llassert(lua_type(L, -1) == LUA_TTABLE); + // stack: Lua metatable compiled from setdtor_meta source + // Inject our C++ __index metamethod. + lua_rawsetfield(L, -1, "__index"sv, &setdtor_refs::meta__index); +} + +// In the definition of setdtor_meta above, binary arithmethic and +// concatenation metamethods are a little funny in that we don't know a +// priori which operand is the userdata with our metatable: the metamethod +// can be invoked either way. So every such metamethod must check, which +// leads to lots of redundancy. Hence this helper function. Call it a Lua +// macro. +std::string setdtor_refs::binop(const std::string& name, const std::string& op) +{ + return stringize( + " meta.__", name, " = function(lhs, rhs)\n" + " if getmetatable(lhs) == meta then\n" + " return lhs._target ", op, " rhs\n" + " else\n" + " return lhs ", op, " rhs._target\n" + " end\n" + " end\n"); +} + +// setdtor_refs __index() metamethod +int setdtor_refs::meta__index(lua_State* L) +{ + // called with (setdtor_refs userdata, key), returns retrieved object + lua_checkdelta(L, -1); + lluau_checkstack(L, 2); + // stack: proxy, key + // get ptr to the C++ struct data + auto ptr = lua_toclass(L, -2); + // meta__index() should NEVER be called with anything but setdtor_refs! + llassert(ptr); + // push the wrapped object + lua_getref(L, ptr->objref); + // stack: proxy, key, _target + // replace userdata with _target + lua_replace(L, -3); + // stack: _target, key + // Duplicate key because lua_tostring() converts number to string: + // if the key is (e.g.) 1, don't try to retrieve _target["1"]! + lua_pushvalue(L, -1); + // stack: _target, key, key + // recognize the special _target field + if (lua_tostdstring(L, -1) == "_target") + { + // okay, ditch both copies of "_target" string key + lua_pop(L, 2); + // stack: _target + } + else // any key but _target + { + // ditch stringized key + lua_pop(L, 1); + // stack: _target, key + // replace key with _target[key], invoking metamethod if any + lua_gettable(L, -2); + // stack: _target, _target[key] + // discard _target + lua_remove(L, -2); + // stack: _target[key] + } + return 1; +} + +// When Lua destroys a setdtor_refs userdata object, either from garbage +// collection or from LL.atexit(lua_destroybounduserdata), it's time to keep +// its promise to call the specified Lua destructor function with the +// specified Lua object. Of course we must also delete the captured +// "references" to both objects. +setdtor_refs::~setdtor_refs() +{ + lua_checkdelta(L); + lluau_checkstack(L, 2); + // push Registry[dtorref] + lua_getref(L, dtorref); + // push Registry[objref] + lua_getref(L, objref); + // free Registry[dtorref] + lua_unref(L, dtorref); + // free Registry[objref] + lua_unref(L, objref); + // call dtor(obj): one arg, no result, no error function + int rc = lua_pcall(L, 1, 0, 0); + if (rc != LUA_OK) + { + // TODO: we don't really want to propagate the error here. + // If this setdtor_refs instance is being destroyed by + // LL.atexit(), we want to continue cleanup. If it's being + // garbage-collected, the call is completely unpredictable from + // the consuming script's point of view. But what to do about this + // error?? For now, just log it. + LL_WARNS("Lua") << "setdtor(" << std::quoted(desc) << ") error: " + << lua_tostring(L, -1) << LL_ENDL; + lua_pop(L, 1); + } +} + +} // anonymous namespace + /***************************************************************************** * lua_what *****************************************************************************/ diff --git a/indra/llcommon/lua_function.h b/indra/llcommon/lua_function.h index 6965e206ab..83abe8d71e 100644 --- a/indra/llcommon/lua_function.h +++ b/indra/llcommon/lua_function.h @@ -339,6 +339,7 @@ auto lua_getfieldv(lua_State* L, int index, const char* k) template auto lua_setfieldv(lua_State* L, int index, const char* k, const T& value) { + index = lua_absindex(L, index); lua_checkdelta(L); lluau_checkstack(L, 1); lua_push(L, value); @@ -349,6 +350,7 @@ auto lua_setfieldv(lua_State* L, int index, const char* k, const T& value) template auto lua_rawgetfield(lua_State* L, int index, const std::string_view& k) { + index = lua_absindex(L, index); lua_checkdelta(L); lluau_checkstack(L, 1); lua_pushlstring(L, k.data(), k.length()); @@ -361,6 +363,7 @@ auto lua_rawgetfield(lua_State* L, int index, const std::string_view& k) template void lua_rawsetfield(lua_State* L, int index, const std::string_view& k, const T& value) { + index = lua_absindex(L, index); lua_checkdelta(L); lluau_checkstack(L, 2); lua_pushlstring(L, k.data(), k.length()); @@ -459,7 +462,7 @@ DistinctInt TypeTag::value; * containing a newly-constructed C++ object T(args...). The userdata has a * Luau destructor guaranteeing that the new T instance is destroyed when the * userdata is garbage-collected, no later than when the LuaState is - * destroyed. + * destroyed. It may be destroyed explicitly by calling lua_destroyuserdata(). * * Usage: * lua_emplace(L, T constructor args...); @@ -511,6 +514,19 @@ T* lua_toclass(lua_State* L, int index) return static_cast(ptr); } +/** + * Call lua_destroyuserdata() with the doomed userdata on the stack top. + * It must have been created by lua_emplace(). + */ +int lua_destroyuserdata(lua_State* L); + +/** + * Call lua_pushcclosure(L, lua_destroybounduserdata, 1) with the target + * userdata on the stack top. When the resulting C closure is called with no + * arguments, the bound userdata is destroyed by lua_destroyuserdata(). + */ +int lua_destroybounduserdata(lua_State *L); + /***************************************************************************** * lua_what() *****************************************************************************/ diff --git a/indra/newview/scripts/lua/test_setdtor.lua b/indra/newview/scripts/lua/test_setdtor.lua new file mode 100644 index 0000000000..743c5168d0 --- /dev/null +++ b/indra/newview/scripts/lua/test_setdtor.lua @@ -0,0 +1,62 @@ +inspect = require 'inspect' + +print('initial setdtor') +bye = LL.setdtor('initial setdtor', 'Goodbye world!', print) + +print('arithmetic') +n = LL.setdtor('arithmetic', 11, print) +print("n =", n) +print("n._target =", n._target) +print("getmetatable(n) =", inspect(getmetatable(n))) +print("-n =", -n) +for i = 10, 12 do + -- Comparison metamethods are only called if both operands have the same + -- metamethod. + tempi = LL.setdtor('tempi', i, function(n) print('temp', i) end) + print(`n < {i}`, n < tempi) + print(`n <= {i}`, n <= tempi) + print(`n == {i}`, n == tempi) + print(`n ~= {i}`, n ~= tempi) + print(`n >= {i}`, n >= tempi) + print(`n > {i}`, n > tempi) +end +for i = 2, 3 do + print(`n + {i} =`, n + i) + print(`{i} + n =`, i + n) + print(`n - {i} =`, n - i) + print(`{i} - n =`, i - n) + print(`n * {i} =`, n * i) + print(`{i} * n =`, i * n) + print(`n / {i} =`, n / i) + print(`{i} / n =`, i / n) + print(`n % {i} =`, n % i) + print(`{i} % n =`, i % n) + print(`n ^ {i} =`, n ^ i) + print(`{i} ^ n =`, i ^ n) +end + +print('string') +s = LL.setdtor('string', 'hello', print) +print('s =', s) +print('#s =', #s) +print('s .. " world" =', s .. " world") +print('"world " .. s =', "world " .. s) + +print('table') +t = LL.setdtor('table', {'[1]', '[2]', abc='.abc', def='.def'}, + function(t) print(inspect(t)) end) +print('t =', inspect(t)) +print('t._target =', inspect(t._target)) +print('#t =', #t) +print('t[2] =', t[2]) +print('t.def =', t.def) +t[1] = 'new [1]' +print('t[1] =', t[1]) + +print('function') +f = LL.setdtor('function', function(a, b) return (a .. b) end, print) +print('f =', f) +print('f._target =', f._target) +print('f("Hello", " world") =', f("Hello", " world")) + +print('cleanup') -- cgit v1.2.3 From 364ea79ab3a4d48e0d10fbeabb9b8e88f226baac Mon Sep 17 00:00:00 2001 From: Nat Goodspeed Date: Wed, 28 Aug 2024 19:34:05 -0400 Subject: Prevent erroneous assignment to LL.setdtor() proxy._target field. Trim redundant output from test_setdtor.lua. --- indra/llcommon/lua_function.cpp | 4 +++- indra/newview/scripts/lua/test_setdtor.lua | 28 ++++++++++++++-------------- 2 files changed, 17 insertions(+), 15 deletions(-) diff --git a/indra/llcommon/lua_function.cpp b/indra/llcommon/lua_function.cpp index 12cff89fbd..f61cf3fe10 100644 --- a/indra/llcommon/lua_function.cpp +++ b/indra/llcommon/lua_function.cpp @@ -1050,7 +1050,7 @@ lua_function( } // called with (desc, obj, dtor), returns proxy object lua_checkdelta(L, -2); - lluau_checkstack(L, 3); // might get up to 6 stack entries +// lluau_checkstack(L, 0); // might get up to 3 stack entries auto desc{ lua_tostdstring(L, 1) }; // Get Lua "references" for each of the object and the dtor function. int objref = lua_ref(L, 2); @@ -1150,6 +1150,8 @@ void setdtor_refs::push_metatable(lua_State* L) return (lhs._target <= rhs._target) end, __newindex = function(t, key, value) + assert(key ~= '_target', + "Don't try to replace a setdtor() proxy's _target") t._target[key] = value end, __call = function(func, ...) diff --git a/indra/newview/scripts/lua/test_setdtor.lua b/indra/newview/scripts/lua/test_setdtor.lua index 743c5168d0..61ed86dcc8 100644 --- a/indra/newview/scripts/lua/test_setdtor.lua +++ b/indra/newview/scripts/lua/test_setdtor.lua @@ -7,6 +7,7 @@ print('arithmetic') n = LL.setdtor('arithmetic', 11, print) print("n =", n) print("n._target =", n._target) +print(pcall(function() n._target = 12 end)) print("getmetatable(n) =", inspect(getmetatable(n))) print("-n =", -n) for i = 10, 12 do @@ -20,20 +21,19 @@ for i = 10, 12 do print(`n >= {i}`, n >= tempi) print(`n > {i}`, n > tempi) end -for i = 2, 3 do - print(`n + {i} =`, n + i) - print(`{i} + n =`, i + n) - print(`n - {i} =`, n - i) - print(`{i} - n =`, i - n) - print(`n * {i} =`, n * i) - print(`{i} * n =`, i * n) - print(`n / {i} =`, n / i) - print(`{i} / n =`, i / n) - print(`n % {i} =`, n % i) - print(`{i} % n =`, i % n) - print(`n ^ {i} =`, n ^ i) - print(`{i} ^ n =`, i ^ n) -end +i = 2 +print(`n + {i} =`, n + i) +print(`{i} + n =`, i + n) +print(`n - {i} =`, n - i) +print(`{i} - n =`, i - n) +print(`n * {i} =`, n * i) +print(`{i} * n =`, i * n) +print(`n / {i} =`, n / i) +print(`{i} / n =`, i / n) +print(`n % {i} =`, n % i) +print(`{i} % n =`, i % n) +print(`n ^ {i} =`, n ^ i) +print(`{i} ^ n =`, i ^ n) print('string') s = LL.setdtor('string', 'hello', print) -- cgit v1.2.3 From baa7255687f265b2996754490083e7c26c646d72 Mon Sep 17 00:00:00 2001 From: Nat Goodspeed Date: Wed, 28 Aug 2024 20:50:33 -0400 Subject: Add script control to "Inventory.DoCreate" registered menu action. --- indra/newview/llinventorygallerymenu.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/indra/newview/llinventorygallerymenu.cpp b/indra/newview/llinventorygallerymenu.cpp index 37e2228b2e..2845c5b614 100644 --- a/indra/newview/llinventorygallerymenu.cpp +++ b/indra/newview/llinventorygallerymenu.cpp @@ -106,7 +106,8 @@ LLContextMenu* LLInventoryGalleryContextMenu::createMenu() { mGallery->doCreate(mUUIDs.front(), data); } - }); + }, + LLUICtrl::cb_info::UNTRUSTED_BLOCK); std::set uuids(mUUIDs.begin(), mUUIDs.end()); registrar.add("Inventory.Share", boost::bind(&LLAvatarActions::shareWithAvatars, uuids, gFloaterView->getParentFloater(mGallery)), LLUICtrl::cb_info::UNTRUSTED_BLOCK); -- cgit v1.2.3 From 03d7f2b84daf9ab991de6cad7d6149abda1ef716 Mon Sep 17 00:00:00 2001 From: Nat Goodspeed Date: Wed, 28 Aug 2024 21:16:56 -0400 Subject: Ditch trailing spaces. --- indra/cmake/run_build_test.py | 2 +- indra/llcommon/coro_scheduler.cpp | 2 +- indra/llcommon/coro_scheduler.h | 2 +- indra/llcommon/fsyspath.h | 4 +- indra/llcommon/hexdump.h | 2 +- indra/llcommon/llcoros.h | 12 ++-- indra/llcommon/lleventcoro.cpp | 10 ++-- indra/llcommon/lleventdispatcher.h | 28 ++++----- indra/llcommon/llevents.cpp | 26 ++++---- indra/llcommon/llexception.h | 2 +- indra/llcommon/llformat.h | 10 ++-- indra/llcommon/llinstancetracker.h | 28 ++++----- indra/llcommon/llleap.cpp | 2 +- indra/llcommon/llleaplistener.cpp | 2 +- indra/llcommon/llleaplistener.h | 2 +- indra/llcommon/lockstatic.cpp | 2 +- indra/llcommon/lua_function.cpp | 6 +- indra/llcommon/lua_function.h | 2 +- indra/llcommon/lualistener.cpp | 2 +- indra/llcommon/lualistener.h | 2 +- indra/llcommon/scope_exit.h | 2 +- indra/llcommon/stringize.h | 10 ++-- indra/llcommon/tempset.h | 2 +- indra/llcommon/tests/StringVec.h | 2 +- indra/llcommon/tests/lleventcoro_test.cpp | 10 ++-- indra/llcommon/tests/lleventdispatcher_test.cpp | 4 +- indra/llcommon/tests/lleventfilter_test.cpp | 10 ++-- indra/llcommon/tests/llleap_test.cpp | 2 +- indra/llcommon/threadpool.cpp | 2 +- indra/llcommon/threadpool.h | 2 +- indra/llcommon/throttle.cpp | 2 +- indra/llcommon/throttle.h | 2 +- indra/llcommon/workqueue.cpp | 2 +- indra/llcommon/workqueue.h | 2 +- indra/llui/llfloaterreglistener.cpp | 10 ++-- indra/llui/llfloaterreglistener.h | 10 ++-- indra/llui/llluafloater.cpp | 80 ++++++++++++------------- indra/llui/llluafloater.h | 10 ++-- indra/llui/llmenugl.h | 2 +- indra/llui/lluictrl.cpp | 2 +- indra/newview/llappearancemgr.cpp | 2 +- indra/newview/llappearancemgr.h | 2 +- indra/newview/llfloatergodtools.cpp | 2 +- indra/newview/llfloaterluadebug.h | 12 ++-- indra/newview/llfloaterluascripts.cpp | 22 +++---- indra/newview/llfloaterluascripts.h | 12 ++-- indra/newview/llinventorygallerymenu.cpp | 4 +- indra/newview/llinventorylistener.cpp | 2 +- indra/newview/llluamanager.cpp | 14 ++--- indra/newview/llluamanager.h | 16 ++--- indra/newview/llpanelmaininventory.cpp | 2 +- indra/newview/llpanelobjectinventory.cpp | 4 +- indra/newview/lluilistener.cpp | 16 ++--- indra/newview/lluilistener.h | 10 ++-- indra/newview/llviewerchat.cpp | 2 +- indra/newview/llvoavatar.cpp | 2 +- indra/newview/tests/llluamanager_test.cpp | 2 +- indra/test/debug.h | 10 ++-- indra/test/print.h | 2 +- indra/test/writestr.h | 2 +- 60 files changed, 228 insertions(+), 228 deletions(-) diff --git a/indra/cmake/run_build_test.py b/indra/cmake/run_build_test.py index ef4d712254..312d791d67 100755 --- a/indra/cmake/run_build_test.py +++ b/indra/cmake/run_build_test.py @@ -150,7 +150,7 @@ def translate_rc(rc): """ if rc is None: return "still running" - + if rc >= 0: return "terminated with rc %s" % rc diff --git a/indra/llcommon/coro_scheduler.cpp b/indra/llcommon/coro_scheduler.cpp index 337162cbd5..393356a39b 100644 --- a/indra/llcommon/coro_scheduler.cpp +++ b/indra/llcommon/coro_scheduler.cpp @@ -3,7 +3,7 @@ * @author Nat Goodspeed * @date 2024-08-05 * @brief Implementation for llcoro::scheduler. - * + * * $LicenseInfo:firstyear=2024&license=viewerlgpl$ * Copyright (c) 2024, Linden Research, Inc. * $/LicenseInfo$ diff --git a/indra/llcommon/coro_scheduler.h b/indra/llcommon/coro_scheduler.h index a7572ccf4d..cc7e75d798 100644 --- a/indra/llcommon/coro_scheduler.h +++ b/indra/llcommon/coro_scheduler.h @@ -3,7 +3,7 @@ * @author Nat Goodspeed * @date 2024-08-05 * @brief Custom scheduler for viewer's Boost.Fibers (aka coroutines) - * + * * $LicenseInfo:firstyear=2024&license=viewerlgpl$ * Copyright (c) 2024, Linden Research, Inc. * $/LicenseInfo$ diff --git a/indra/llcommon/fsyspath.h b/indra/llcommon/fsyspath.h index 3c749d84de..1b4aec09b4 100644 --- a/indra/llcommon/fsyspath.h +++ b/indra/llcommon/fsyspath.h @@ -3,7 +3,7 @@ * @author Nat Goodspeed * @date 2024-04-03 * @brief Adapt our UTF-8 std::strings for std::filesystem::path - * + * * $LicenseInfo:firstyear=2024&license=viewerlgpl$ * Copyright (c) 2024, Linden Research, Inc. * $/LicenseInfo$ @@ -20,7 +20,7 @@ // ... the method of conversion to the native character set depends on the // character type used by source. -// +// // * If the source character type is char, the encoding of the source is // assumed to be the native narrow encoding (so no conversion takes place on // POSIX systems). diff --git a/indra/llcommon/hexdump.h b/indra/llcommon/hexdump.h index 234168cd61..ab5ba2b16d 100755 --- a/indra/llcommon/hexdump.h +++ b/indra/llcommon/hexdump.h @@ -3,7 +3,7 @@ * @author Nat Goodspeed * @date 2023-10-03 * @brief iostream manipulators to stream hex, or string with nonprinting chars - * + * * $LicenseInfo:firstyear=2023&license=viewerlgpl$ * Copyright (c) 2023, Linden Research, Inc. * $/LicenseInfo$ diff --git a/indra/llcommon/llcoros.h b/indra/llcommon/llcoros.h index 7491a4123a..f26ed882e8 100644 --- a/indra/llcommon/llcoros.h +++ b/indra/llcommon/llcoros.h @@ -3,25 +3,25 @@ * @author Nat Goodspeed * @date 2009-06-02 * @brief Manage running boost::coroutine instances - * + * * $LicenseInfo:firstyear=2009&license=viewerlgpl$ * Second Life Viewer Source Code * Copyright (C) 2010, Linden Research, Inc. - * + * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; * version 2.1 of the License only. - * + * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. - * + * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * + * * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA * $/LicenseInfo$ */ @@ -169,7 +169,7 @@ public: * LLCoros::launch()). */ static std::string getName(); - + /** * rethrow() is called by the thread's main fiber to propagate an * exception from any coroutine into the main fiber, where it can engage diff --git a/indra/llcommon/lleventcoro.cpp b/indra/llcommon/lleventcoro.cpp index d651aae39c..33db27a116 100644 --- a/indra/llcommon/lleventcoro.cpp +++ b/indra/llcommon/lleventcoro.cpp @@ -3,25 +3,25 @@ * @author Nat Goodspeed * @date 2009-04-29 * @brief Implementation for lleventcoro. - * + * * $LicenseInfo:firstyear=2009&license=viewerlgpl$ * Second Life Viewer Source Code * Copyright (C) 2010, Linden Research, Inc. - * + * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; * version 2.1 of the License only. - * + * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. - * + * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * + * * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA * $/LicenseInfo$ */ diff --git a/indra/llcommon/lleventdispatcher.h b/indra/llcommon/lleventdispatcher.h index 698412fdb4..6b5524e1eb 100644 --- a/indra/llcommon/lleventdispatcher.h +++ b/indra/llcommon/lleventdispatcher.h @@ -6,25 +6,25 @@ * useful when you have a single LLEventPump listener on which you can * request different operations, vs. instantiating a different * LLEventPump for each such operation. - * + * * $LicenseInfo:firstyear=2009&license=viewerlgpl$ * Second Life Viewer Source Code * Copyright (C) 2010, Linden Research, Inc. - * + * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; * version 2.1 of the License only. - * + * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. - * + * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * + * * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA * $/LicenseInfo$ */ @@ -201,7 +201,7 @@ public: template ::type, LLSD>::value - >::type> + >::type> void add(const std::string& name, const std::string& desc, R (CLASS::*method)(ARG)) @@ -213,7 +213,7 @@ public: template ::type, LLSD>::value - >::type> + >::type> void add(const std::string& name, const std::string& desc, R (CLASS::*method)(ARG) const) @@ -226,7 +226,7 @@ public: template ::type, LLSD>::value - >::type> + >::type> void add(const std::string& name, const std::string& desc, void (CLASS::*method)(ARG)) @@ -238,7 +238,7 @@ public: template ::type, LLSD>::value - >::type> + >::type> void add(const std::string& name, const std::string& desc, void (CLASS::*method)(ARG) const) @@ -247,7 +247,7 @@ public: } // non-const binary (or more) method - template + template void add(const std::string& name, const std::string& desc, R (CLASS::*method)(ARG0, ARG1, ARGS...)) @@ -256,7 +256,7 @@ public: } // const binary (or more) method - template + template void add(const std::string& name, const std::string& desc, R (CLASS::*method)(ARG0, ARG1, ARGS...) const) @@ -265,7 +265,7 @@ public: } // non-const binary (or more) method returning void - template + template void add(const std::string& name, const std::string& desc, void (CLASS::*method)(ARG0, ARG1, ARGS...)) @@ -274,7 +274,7 @@ public: } // const binary (or more) method returning void - template + template void add(const std::string& name, const std::string& desc, void (CLASS::*method)(ARG0, ARG1, ARGS...) const) @@ -371,7 +371,7 @@ public: const InstanceGetter& getter, const LLSD& params, const LLSD& defaults=LLSD()); - //@} + //@} /// Unregister a callable bool remove(const std::string& name); diff --git a/indra/llcommon/llevents.cpp b/indra/llcommon/llevents.cpp index 0338fd53e5..a694f0dc7f 100644 --- a/indra/llcommon/llevents.cpp +++ b/indra/llcommon/llevents.cpp @@ -3,25 +3,25 @@ * @author Nat Goodspeed * @date 2008-09-12 * @brief Implementation for llevents. - * + * * $LicenseInfo:firstyear=2008&license=viewerlgpl$ * Second Life Viewer Source Code * Copyright (C) 2010, Linden Research, Inc. - * + * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; * version 2.1 of the License only. - * + * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. - * + * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * + * * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA * $/LicenseInfo$ */ @@ -430,8 +430,8 @@ LLBoundListener LLEventPump::listen_impl(const std::string& name, const LLAwareL float nodePosition = 1.0; - // if the supplied name is empty we are not interested in the ordering mechanism - // and can bypass attempting to find the optimal location to insert the new + // if the supplied name is empty we are not interested in the ordering mechanism + // and can bypass attempting to find the optimal location to insert the new // listener. We'll just tack it on to the end. if (!name.empty()) // should be the same as testing against ANONYMOUS { @@ -576,12 +576,12 @@ LLBoundListener LLEventPump::listen_impl(const std::string& name, const LLAwareL // Now that newNode has a value that places it appropriately in mSignal, // connect it. LLBoundListener bound = mSignal->connect_extended(nodePosition, listener); - + if (!name.empty()) { // note that we are not tracking anonymous listeners here either. - // This means that it is the caller's responsibility to either assign - // to a TempBoundListerer (scoped_connection) or manually disconnect - // when done. + // This means that it is the caller's responsibility to either assign + // to a TempBoundListerer (scoped_connection) or manually disconnect + // when done. mConnections[name] = bound; } return bound; @@ -648,9 +648,9 @@ bool LLEventMailDrop::post(const LLSD& event) { // forward the call to our base class bool posted = LLEventStream::post(event); - + if (!posted) - { // if the event was not handled we will save it for later so that it can + { // if the event was not handled we will save it for later so that it can // be posted to any future listeners when they attach. mEventHistory.push_back(event); } diff --git a/indra/llcommon/llexception.h b/indra/llcommon/llexception.h index 9e322db86d..15dacd76fc 100644 --- a/indra/llcommon/llexception.h +++ b/indra/llcommon/llexception.h @@ -3,7 +3,7 @@ * @author Nat Goodspeed * @date 2016-06-29 * @brief Types needed for generic exception handling - * + * * $LicenseInfo:firstyear=2016&license=viewerlgpl$ * Copyright (c) 2016, Linden Research, Inc. * $/LicenseInfo$ diff --git a/indra/llcommon/llformat.h b/indra/llcommon/llformat.h index 97ea3b7b78..0c840d6e8e 100644 --- a/indra/llcommon/llformat.h +++ b/indra/llcommon/llformat.h @@ -1,4 +1,4 @@ -/** +/** * @file llformat.h * @date January 2007 * @brief string formatting utility @@ -6,21 +6,21 @@ * $LicenseInfo:firstyear=2007&license=viewerlgpl$ * Second Life Viewer Source Code * Copyright (C) 2010, Linden Research, Inc. - * + * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; * version 2.1 of the License only. - * + * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. - * + * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * + * * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA * $/LicenseInfo$ */ diff --git a/indra/llcommon/llinstancetracker.h b/indra/llcommon/llinstancetracker.h index aba9f1187b..78582eac43 100644 --- a/indra/llcommon/llinstancetracker.h +++ b/indra/llcommon/llinstancetracker.h @@ -1,4 +1,4 @@ -/** +/** * @file llinstancetracker.h * @brief LLInstanceTracker is a mixin class that automatically tracks object * instances with or without an associated key @@ -6,21 +6,21 @@ * $LicenseInfo:firstyear=2000&license=viewerlgpl$ * Second Life Viewer Source Code * Copyright (C) 2010, Linden Research, Inc. - * + * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; * version 2.1 of the License only. - * + * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. - * + * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * + * * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA * $/LicenseInfo$ */ @@ -101,11 +101,11 @@ public: return mSelf; } - static size_t instanceCount() - { - return LockStatic()->mMap.size(); + static size_t instanceCount() + { + return LockStatic()->mMap.size(); } - + // snapshot of std::pair> pairs, for // some SUBCLASS derived from T template @@ -229,7 +229,7 @@ public: } protected: - LLInstanceTracker(const KEY& key) + LLInstanceTracker(const KEY& key) { // We do not intend to manage the lifespan of this object with // shared_ptr, so give it a no-op deleter. We store shared_ptrs in our @@ -301,9 +301,9 @@ private: static std::string report(const char* key) { return report(std::string(key)); } // caller must instantiate LockStatic - void add_(LockStatic& lock, const KEY& key, const ptr_t& ptr) - { - mInstanceKey = key; + void add_(LockStatic& lock, const KEY& key, const ptr_t& ptr) + { + mInstanceKey = key; InstanceMap& map = lock->mMap; switch(KEY_COLLISION_BEHAVIOR) { @@ -389,7 +389,7 @@ public: { return mSelf; } - + static size_t instanceCount() { return LockStatic()->mSet.size(); diff --git a/indra/llcommon/llleap.cpp b/indra/llcommon/llleap.cpp index ccd27613ae..306d4e48d0 100644 --- a/indra/llcommon/llleap.cpp +++ b/indra/llcommon/llleap.cpp @@ -3,7 +3,7 @@ * @author Nat Goodspeed * @date 2012-02-20 * @brief Implementation for llleap. - * + * * $LicenseInfo:firstyear=2012&license=viewerlgpl$ * Copyright (c) 2012, Linden Research, Inc. * $/LicenseInfo$ diff --git a/indra/llcommon/llleaplistener.cpp b/indra/llcommon/llleaplistener.cpp index 9b9b0f5121..b81ee66ba9 100644 --- a/indra/llcommon/llleaplistener.cpp +++ b/indra/llcommon/llleaplistener.cpp @@ -3,7 +3,7 @@ * @author Nat Goodspeed * @date 2012-03-16 * @brief Implementation for llleaplistener. - * + * * $LicenseInfo:firstyear=2012&license=viewerlgpl$ * Copyright (c) 2012, Linden Research, Inc. * $/LicenseInfo$ diff --git a/indra/llcommon/llleaplistener.h b/indra/llcommon/llleaplistener.h index 040fb737b7..d36d2ff8db 100644 --- a/indra/llcommon/llleaplistener.h +++ b/indra/llcommon/llleaplistener.h @@ -3,7 +3,7 @@ * @author Nat Goodspeed * @date 2012-03-16 * @brief LLEventAPI supporting LEAP plugins - * + * * $LicenseInfo:firstyear=2012&license=viewerlgpl$ * Copyright (c) 2012, Linden Research, Inc. * $/LicenseInfo$ diff --git a/indra/llcommon/lockstatic.cpp b/indra/llcommon/lockstatic.cpp index b647208724..f531ef331e 100755 --- a/indra/llcommon/lockstatic.cpp +++ b/indra/llcommon/lockstatic.cpp @@ -3,7 +3,7 @@ * @author Nat Goodspeed * @date 2024-05-23 * @brief Implementation for lockstatic. - * + * * $LicenseInfo:firstyear=2024&license=viewerlgpl$ * Copyright (c) 2024, Linden Research, Inc. * $/LicenseInfo$ diff --git a/indra/llcommon/lua_function.cpp b/indra/llcommon/lua_function.cpp index 880bc209f6..ffb90032d2 100644 --- a/indra/llcommon/lua_function.cpp +++ b/indra/llcommon/lua_function.cpp @@ -3,7 +3,7 @@ * @author Nat Goodspeed * @date 2024-02-05 * @brief Implementation for lua_function. - * + * * $LicenseInfo:firstyear=2024&license=viewerlgpl$ * Copyright (c) 2024, Linden Research, Inc. * $/LicenseInfo$ @@ -700,11 +700,11 @@ void LuaState::check_interrupts_counter() // of interrupting itself at a moment when re-entry is not valid. So only // touch data in this LuaState. ++mInterrupts; - if (mInterrupts > INTERRUPTS_MAX_LIMIT) + if (mInterrupts > INTERRUPTS_MAX_LIMIT) { lluau::error(mState, "Possible infinite loop, terminated."); } - else if (mInterrupts % INTERRUPTS_SUSPEND_LIMIT == 0) + else if (mInterrupts % INTERRUPTS_SUSPEND_LIMIT == 0) { LL_DEBUGS("Lua") << LLCoros::getName() << " suspending at " << mInterrupts << " interrupts" << LL_ENDL; diff --git a/indra/llcommon/lua_function.h b/indra/llcommon/lua_function.h index 6965e206ab..b9d640f84f 100644 --- a/indra/llcommon/lua_function.h +++ b/indra/llcommon/lua_function.h @@ -3,7 +3,7 @@ * @author Nat Goodspeed * @date 2024-02-05 * @brief Definitions useful for coding a new Luau entry point into C++ - * + * * $LicenseInfo:firstyear=2024&license=viewerlgpl$ * Copyright (c) 2024, Linden Research, Inc. * $/LicenseInfo$ diff --git a/indra/llcommon/lualistener.cpp b/indra/llcommon/lualistener.cpp index 6cb87e8af2..94085c6798 100644 --- a/indra/llcommon/lualistener.cpp +++ b/indra/llcommon/lualistener.cpp @@ -3,7 +3,7 @@ * @author Nat Goodspeed * @date 2024-02-06 * @brief Implementation for lualistener. - * + * * $LicenseInfo:firstyear=2024&license=viewerlgpl$ * Copyright (c) 2024, Linden Research, Inc. * $/LicenseInfo$ diff --git a/indra/llcommon/lualistener.h b/indra/llcommon/lualistener.h index 68131dfa27..4c0a2a5c87 100644 --- a/indra/llcommon/lualistener.h +++ b/indra/llcommon/lualistener.h @@ -3,7 +3,7 @@ * @author Nat Goodspeed * @date 2024-02-06 * @brief Define LuaListener class - * + * * $LicenseInfo:firstyear=2024&license=viewerlgpl$ * Copyright (c) 2024, Linden Research, Inc. * $/LicenseInfo$ diff --git a/indra/llcommon/scope_exit.h b/indra/llcommon/scope_exit.h index 00fab069c4..eb4df4f74d 100644 --- a/indra/llcommon/scope_exit.h +++ b/indra/llcommon/scope_exit.h @@ -3,7 +3,7 @@ * @author Nat Goodspeed * @date 2024-08-15 * @brief Cheap imitation of std::experimental::scope_exit - * + * * $LicenseInfo:firstyear=2024&license=viewerlgpl$ * Copyright (c) 2024, Linden Research, Inc. * $/LicenseInfo$ diff --git a/indra/llcommon/stringize.h b/indra/llcommon/stringize.h index 63d44a7272..e712b6cc28 100644 --- a/indra/llcommon/stringize.h +++ b/indra/llcommon/stringize.h @@ -3,25 +3,25 @@ * @author Nat Goodspeed * @date 2008-12-17 * @brief stringize(item) template function and STRINGIZE(expression) macro - * + * * $LicenseInfo:firstyear=2008&license=viewerlgpl$ * Second Life Viewer Source Code * Copyright (C) 2010, Linden Research, Inc. - * + * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; * version 2.1 of the License only. - * + * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. - * + * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * + * * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA * $/LicenseInfo$ */ diff --git a/indra/llcommon/tempset.h b/indra/llcommon/tempset.h index 07e607a576..773f19f756 100755 --- a/indra/llcommon/tempset.h +++ b/indra/llcommon/tempset.h @@ -3,7 +3,7 @@ * @author Nat Goodspeed * @date 2024-06-12 * @brief Temporarily override a variable for scope duration, then restore - * + * * $LicenseInfo:firstyear=2024&license=viewerlgpl$ * Copyright (c) 2024, Linden Research, Inc. * $/LicenseInfo$ diff --git a/indra/llcommon/tests/StringVec.h b/indra/llcommon/tests/StringVec.h index 761956a012..3f8665affb 100644 --- a/indra/llcommon/tests/StringVec.h +++ b/indra/llcommon/tests/StringVec.h @@ -3,7 +3,7 @@ * @author Nat Goodspeed * @date 2012-02-24 * @brief Extend TUT ensure_equals() to handle std::vector - * + * * $LicenseInfo:firstyear=2012&license=viewerlgpl$ * Copyright (c) 2012, Linden Research, Inc. * $/LicenseInfo$ diff --git a/indra/llcommon/tests/lleventcoro_test.cpp b/indra/llcommon/tests/lleventcoro_test.cpp index e7674fde37..cd3bbb21df 100644 --- a/indra/llcommon/tests/lleventcoro_test.cpp +++ b/indra/llcommon/tests/lleventcoro_test.cpp @@ -3,25 +3,25 @@ * @author Nat Goodspeed * @date 2009-04-22 * @brief Test for coroutine. - * + * * $LicenseInfo:firstyear=2009&license=viewerlgpl$ * Second Life Viewer Source Code * Copyright (C) 2010, Linden Research, Inc. - * + * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; * version 2.1 of the License only. - * + * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. - * + * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * + * * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA * $/LicenseInfo$ */ diff --git a/indra/llcommon/tests/lleventdispatcher_test.cpp b/indra/llcommon/tests/lleventdispatcher_test.cpp index 42d752514b..be05ca4e85 100644 --- a/indra/llcommon/tests/lleventdispatcher_test.cpp +++ b/indra/llcommon/tests/lleventdispatcher_test.cpp @@ -3,7 +3,7 @@ * @author Nat Goodspeed * @date 2011-01-20 * @brief Test for lleventdispatcher. - * + * * $LicenseInfo:firstyear=2011&license=viewerlgpl$ * Copyright (c) 2011, Linden Research, Inc. * $/LicenseInfo$ @@ -470,7 +470,7 @@ namespace tut params["a"], "\n" "params[\"b\"]:\n", params["b"]); - // default LLSD::Binary value + // default LLSD::Binary value std::vector binary; for (size_t ix = 0, h = 0xaa; ix < 6; ++ix, h += 0x11) { diff --git a/indra/llcommon/tests/lleventfilter_test.cpp b/indra/llcommon/tests/lleventfilter_test.cpp index a3d55d0cc6..e1f41faa44 100644 --- a/indra/llcommon/tests/lleventfilter_test.cpp +++ b/indra/llcommon/tests/lleventfilter_test.cpp @@ -3,25 +3,25 @@ * @author Nat Goodspeed * @date 2009-03-06 * @brief Test for lleventfilter. - * + * * $LicenseInfo:firstyear=2009&license=viewerlgpl$ * Second Life Viewer Source Code * Copyright (C) 2010, Linden Research, Inc. - * + * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; * version 2.1 of the License only. - * + * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. - * + * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * + * * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA * $/LicenseInfo$ */ diff --git a/indra/llcommon/tests/llleap_test.cpp b/indra/llcommon/tests/llleap_test.cpp index a7661cc7d8..ca1939c81e 100644 --- a/indra/llcommon/tests/llleap_test.cpp +++ b/indra/llcommon/tests/llleap_test.cpp @@ -3,7 +3,7 @@ * @author Nat Goodspeed * @date 2012-02-21 * @brief Test for llleap. - * + * * $LicenseInfo:firstyear=2012&license=viewerlgpl$ * Copyright (c) 2012, Linden Research, Inc. * $/LicenseInfo$ diff --git a/indra/llcommon/threadpool.cpp b/indra/llcommon/threadpool.cpp index 8c0217de04..c94b5eecce 100644 --- a/indra/llcommon/threadpool.cpp +++ b/indra/llcommon/threadpool.cpp @@ -3,7 +3,7 @@ * @author Nat Goodspeed * @date 2021-10-21 * @brief Implementation for threadpool. - * + * * $LicenseInfo:firstyear=2021&license=viewerlgpl$ * Copyright (c) 2021, Linden Research, Inc. * $/LicenseInfo$ diff --git a/indra/llcommon/threadpool.h b/indra/llcommon/threadpool.h index 7ced7fbf9f..2748d7b073 100644 --- a/indra/llcommon/threadpool.h +++ b/indra/llcommon/threadpool.h @@ -4,7 +4,7 @@ * @date 2021-10-21 * @brief ThreadPool configures a WorkQueue along with a pool of threads to * service it. - * + * * $LicenseInfo:firstyear=2021&license=viewerlgpl$ * Copyright (c) 2021, Linden Research, Inc. * $/LicenseInfo$ diff --git a/indra/llcommon/throttle.cpp b/indra/llcommon/throttle.cpp index d5f7d5dcab..deb0a26fb0 100644 --- a/indra/llcommon/throttle.cpp +++ b/indra/llcommon/throttle.cpp @@ -3,7 +3,7 @@ * @author Nat Goodspeed * @date 2024-08-12 * @brief Implementation for ThrottleBase. - * + * * $LicenseInfo:firstyear=2024&license=viewerlgpl$ * Copyright (c) 2024, Linden Research, Inc. * $/LicenseInfo$ diff --git a/indra/llcommon/throttle.h b/indra/llcommon/throttle.h index 481d1d8f72..7f5389f464 100644 --- a/indra/llcommon/throttle.h +++ b/indra/llcommon/throttle.h @@ -3,7 +3,7 @@ * @author Nat Goodspeed * @date 2024-08-12 * @brief Throttle class - * + * * $LicenseInfo:firstyear=2024&license=viewerlgpl$ * Copyright (c) 2024, Linden Research, Inc. * $/LicenseInfo$ diff --git a/indra/llcommon/workqueue.cpp b/indra/llcommon/workqueue.cpp index 800547084a..9138c862f9 100644 --- a/indra/llcommon/workqueue.cpp +++ b/indra/llcommon/workqueue.cpp @@ -3,7 +3,7 @@ * @author Nat Goodspeed * @date 2021-10-06 * @brief Implementation for WorkQueue. - * + * * $LicenseInfo:firstyear=2021&license=viewerlgpl$ * Copyright (c) 2021, Linden Research, Inc. * $/LicenseInfo$ diff --git a/indra/llcommon/workqueue.h b/indra/llcommon/workqueue.h index 0581f85bfa..eb6923df0b 100644 --- a/indra/llcommon/workqueue.h +++ b/indra/llcommon/workqueue.h @@ -3,7 +3,7 @@ * @author Nat Goodspeed * @date 2021-09-30 * @brief Queue used for inter-thread work passing. - * + * * $LicenseInfo:firstyear=2021&license=viewerlgpl$ * Copyright (c) 2021, Linden Research, Inc. * $/LicenseInfo$ diff --git a/indra/llui/llfloaterreglistener.cpp b/indra/llui/llfloaterreglistener.cpp index e17f9f4dd6..b0dceb55c8 100644 --- a/indra/llui/llfloaterreglistener.cpp +++ b/indra/llui/llfloaterreglistener.cpp @@ -3,25 +3,25 @@ * @author Nat Goodspeed * @date 2009-08-12 * @brief Implementation for llfloaterreglistener. - * + * * $LicenseInfo:firstyear=2009&license=viewerlgpl$ * Second Life Viewer Source Code * Copyright (C) 2010, Linden Research, Inc. - * + * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; * version 2.1 of the License only. - * + * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. - * + * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * + * * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA * $/LicenseInfo$ */ diff --git a/indra/llui/llfloaterreglistener.h b/indra/llui/llfloaterreglistener.h index 2165b1b62f..42e7178cbc 100644 --- a/indra/llui/llfloaterreglistener.h +++ b/indra/llui/llfloaterreglistener.h @@ -3,25 +3,25 @@ * @author Nat Goodspeed * @date 2009-08-12 * @brief Wrap (subset of) LLFloaterReg API with an event API - * + * * $LicenseInfo:firstyear=2009&license=viewerlgpl$ * Second Life Viewer Source Code * Copyright (C) 2010, Linden Research, Inc. - * + * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; * version 2.1 of the License only. - * + * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. - * + * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * + * * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA * $/LicenseInfo$ */ diff --git a/indra/llui/llluafloater.cpp b/indra/llui/llluafloater.cpp index b08508bf9b..ca80a2c451 100644 --- a/indra/llui/llluafloater.cpp +++ b/indra/llui/llluafloater.cpp @@ -1,24 +1,24 @@ -/** +/** * @file llluafloater.cpp * * $LicenseInfo:firstyear=2024&license=viewerlgpl$ * Second Life Viewer Source Code * Copyright (C) 2024, Linden Research, Inc. - * + * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; * version 2.1 of the License only. - * + * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. - * + * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * + * * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA * $/LicenseInfo$ * @@ -68,25 +68,25 @@ LLLuaFloater::LLLuaFloater(const LLSD &key) : }; LLSD requiredParams = llsd::map("ctrl_name", LLSD(), "value", LLSD()); - mDispatchListener.add("set_enabled", "", [ctrl_lookup](const LLSD &event) - { + mDispatchListener.add("set_enabled", "", [ctrl_lookup](const LLSD &event) + { return ctrl_lookup(event, [](LLUICtrl *ctrl, const LLSD &event) { ctrl->setEnabled(event["value"].asBoolean()); return LLSD(); }); }, requiredParams); - mDispatchListener.add("set_visible", "", [ctrl_lookup](const LLSD &event) - { + mDispatchListener.add("set_visible", "", [ctrl_lookup](const LLSD &event) + { return ctrl_lookup(event, [](LLUICtrl *ctrl, const LLSD &event) { ctrl->setVisible(event["value"].asBoolean()); return LLSD(); }); }, requiredParams); - mDispatchListener.add("set_value", "", [ctrl_lookup](const LLSD &event) - { + mDispatchListener.add("set_value", "", [ctrl_lookup](const LLSD &event) + { return ctrl_lookup(event, [](LLUICtrl *ctrl, const LLSD &event) { ctrl->setValue(event["value"]); return LLSD(); }); }, requiredParams); - mDispatchListener.add("add_list_element", "", [this](const LLSD &event) - { + mDispatchListener.add("add_list_element", "", [this](const LLSD &event) + { LLScrollListCtrl *ctrl = getChild(event["ctrl_name"].asString()); - if(ctrl) + if(ctrl) { - LLSD element_data = event["value"]; + LLSD element_data = event["value"]; if (element_data.isArray()) { for (const auto &row : llsd::inArray(element_data)) @@ -94,34 +94,34 @@ LLLuaFloater::LLLuaFloater(const LLSD &key) : ctrl->addElement(row); } } - else + else { ctrl->addElement(element_data); } } }, requiredParams); - mDispatchListener.add("clear_list", "", [this](const LLSD &event) - { + mDispatchListener.add("clear_list", "", [this](const LLSD &event) + { LLScrollListCtrl *ctrl = getChild(event["ctrl_name"].asString()); - if(ctrl) + if(ctrl) { ctrl->deleteAllItems(); } }, llsd::map("ctrl_name", LLSD())); - mDispatchListener.add("add_text", "", [this](const LLSD &event) - { + mDispatchListener.add("add_text", "", [this](const LLSD &event) + { LLTextEditor *editor = getChild(event["ctrl_name"].asString()); - if (editor) + if (editor) { editor->pasteTextWithLinebreaks(stringize(event["value"])); editor->addLineBreakChar(true); } }, requiredParams); - mDispatchListener.add("set_label", "", [this](const LLSD &event) - { + mDispatchListener.add("set_label", "", [this](const LLSD &event) + { LLButton *btn = getChild(event["ctrl_name"].asString()); if (btn) { @@ -129,17 +129,17 @@ LLLuaFloater::LLLuaFloater(const LLSD &key) : } }, requiredParams); - mDispatchListener.add("set_title", "", [this](const LLSD &event) - { + mDispatchListener.add("set_title", "", [this](const LLSD &event) + { setTitle(event["value"].asString()); }, llsd::map("value", LLSD())); - - mDispatchListener.add("get_value", "", [ctrl_lookup](const LLSD &event) - { + + mDispatchListener.add("get_value", "", [ctrl_lookup](const LLSD &event) + { return ctrl_lookup(event, [](LLUICtrl *ctrl, const LLSD &event) { return llsd::map("value", ctrl->getValue()); }); }, llsd::map("ctrl_name", LLSD(), "reqid", LLSD())); - mDispatchListener.add("get_selected_id", "", [this](const LLSD &event) + mDispatchListener.add("get_selected_id", "", [this](const LLSD &event) { LLScrollListCtrl *ctrl = getChild(event["ctrl_name"].asString()); if (!ctrl) @@ -157,7 +157,7 @@ LLLuaFloater::~LLLuaFloater() post(LLSD()); } -BOOL LLLuaFloater::postBuild() +BOOL LLLuaFloater::postBuild() { for (LLView *view : *getChildList()) { @@ -217,10 +217,10 @@ void LLLuaFloater::registerCallback(const std::string &ctrl_name, const std::str auto mouse_event_cb = [this, data](LLUICtrl *ctrl, const LLSD ¶m) { post(data); }; - auto mouse_event_coords_cb = [this, data](LLUICtrl *ctrl, S32 x, S32 y, MASK mask) - { + auto mouse_event_coords_cb = [this, data](LLUICtrl *ctrl, S32 x, S32 y, MASK mask) + { LLSD event(data); - post(event.with("x", x).with("y", y)); + post(event.with("x", x).with("y", y)); }; auto post_with_value = [this, data](LLSD value) @@ -260,12 +260,12 @@ void LLLuaFloater::registerCallback(const std::string &ctrl_name, const std::str { list->setDoubleClickCallback( [post_with_value, list](){ post_with_value(LLSD(list->getCurrentID())); }); } - else + else { ctrl->setDoubleClickCallback(mouse_event_coords_cb); } } - else if (event_is(event, "keystroke")) + else if (event_is(event, "keystroke")) { LLTextEditor* text_editor = dynamic_cast(ctrl); if (text_editor) @@ -278,7 +278,7 @@ void LLLuaFloater::registerCallback(const std::string &ctrl_name, const std::str line_editor->setKeystrokeCallback([post_with_value](LLLineEditor *editor, void* userdata) { post_with_value(editor->getValue()); }, NULL); } } - else + else { LL_WARNS("LuaFloater") << "Can't register callback for unknown event: " << event << " , control: " << ctrl_name << LL_ENDL; } @@ -292,7 +292,7 @@ void LLLuaFloater::post(const LLSD &data) LLEventPumps::instance().obtain(mReplyPumpName).post(stamped_data); } -void LLLuaFloater::postEvent(LLSD data, const std::string &event_name) +void LLLuaFloater::postEvent(LLSD data, const std::string &event_name) { llassert(EVENT_LIST.find(event_name) != EVENT_LIST.end()); post(data.with("event", event_name)); @@ -302,12 +302,12 @@ void LLLuaFloater::showLuaFloater(const LLSD &data) { fsyspath fs_path(data["xml_path"].asString()); std::string path = fs_path.lexically_normal().u8string(); - if (!fs_path.is_absolute()) + if (!fs_path.is_absolute()) { std::string lib_path = gDirUtilp->getExpandedFilename(LL_PATH_SCRIPTS, "lua"); path = (fsyspath(lib_path) / path).u8string(); } - + LLLuaFloater *floater = new LLLuaFloater(data); floater->buildFromFile(path); floater->openFloater(floater->getKey()); diff --git a/indra/llui/llluafloater.h b/indra/llui/llluafloater.h index ccc3ccb39b..d0641d41b0 100644 --- a/indra/llui/llluafloater.h +++ b/indra/llui/llluafloater.h @@ -1,24 +1,24 @@ -/** +/** * @file llluafloater.h * * $LicenseInfo:firstyear=2024&license=viewerlgpl$ * Second Life Viewer Source Code * Copyright (C) 2024, Linden Research, Inc. - * + * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; * version 2.1 of the License only. - * + * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. - * + * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * + * * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA * $/LicenseInfo$ */ diff --git a/indra/llui/llmenugl.h b/indra/llui/llmenugl.h index b6b83c4716..65211bf122 100644 --- a/indra/llui/llmenugl.h +++ b/indra/llui/llmenugl.h @@ -953,7 +953,7 @@ public: typedef LLUICtrl::CommitCallbackInfo cb_info; static void addCommit(view_listener_t *listener, const std::string &name, cb_info::EUntrustedCall handle_untrusted = cb_info::UNTRUSTED_ALLOW) { - LLUICtrl::CommitCallbackRegistry::currentRegistrar().add(name, + LLUICtrl::CommitCallbackRegistry::currentRegistrar().add(name, cb_info([listener](LLUICtrl*, const LLSD& param){ return listener->handleEvent(param); }, handle_untrusted)); } diff --git a/indra/llui/lluictrl.cpp b/indra/llui/lluictrl.cpp index d6631a9cd9..f1dc3d7c08 100644 --- a/indra/llui/lluictrl.cpp +++ b/indra/llui/lluictrl.cpp @@ -284,7 +284,7 @@ LLUICtrl::commit_signal_t::slot_type LLUICtrl::initCommitCallback(const CommitCa std::string function_name = cb.function_name; setFunctionName(function_name); LLUICtrl::CommitCallbackInfo *info = (CommitCallbackRegistry::getValue(function_name)); - if (info && info->callback_func) + if (info && info->callback_func) { if (cb.parameter.isProvided()) return boost::bind((info->callback_func), _1, cb.parameter); diff --git a/indra/newview/llappearancemgr.cpp b/indra/newview/llappearancemgr.cpp index 72c20d377b..632366e801 100644 --- a/indra/newview/llappearancemgr.cpp +++ b/indra/newview/llappearancemgr.cpp @@ -2957,7 +2957,7 @@ bool LLAppearanceMgr::wearOutfit(const LLUUID &cat_id, std::string &error_msg, b return wearOutfit(stringize(cat_id), cat, error_msg, false, append); } -bool LLAppearanceMgr::wearOutfit(const std::string &desc, LLInventoryCategory* cat, +bool LLAppearanceMgr::wearOutfit(const std::string &desc, LLInventoryCategory* cat, std::string &error_msg, bool copy_items, bool append) { if (!cat) diff --git a/indra/newview/llappearancemgr.h b/indra/newview/llappearancemgr.h index d15394df15..9e624f593f 100644 --- a/indra/newview/llappearancemgr.h +++ b/indra/newview/llappearancemgr.h @@ -264,7 +264,7 @@ private: static void onOutfitRename(const LLSD& notification, const LLSD& response); // used by both wearOutfit(LLUUID) and wearOutfitByName(std::string) - bool wearOutfit(const std::string &desc, LLInventoryCategory* cat, + bool wearOutfit(const std::string &desc, LLInventoryCategory* cat, std::string &error_msg, bool copy_items, bool append); bool mAttachmentInvLinkEnabled; diff --git a/indra/newview/llfloatergodtools.cpp b/indra/newview/llfloatergodtools.cpp index 4217b3ad49..f2884c3a6e 100644 --- a/indra/newview/llfloatergodtools.cpp +++ b/indra/newview/llfloatergodtools.cpp @@ -854,7 +854,7 @@ void LLPanelRegionTools::onSelectRegion() LLPanelGridTools::LLPanelGridTools() : LLPanel() { - mCommitCallbackRegistrar.add("GridTools.FlushMapVisibilityCaches", + mCommitCallbackRegistrar.add("GridTools.FlushMapVisibilityCaches", { boost::bind(&LLPanelGridTools::onClickFlushMapVisibilityCaches, this), cb_info::UNTRUSTED_BLOCK }); } diff --git a/indra/newview/llfloaterluadebug.h b/indra/newview/llfloaterluadebug.h index e513d9a095..9f044df9d9 100644 --- a/indra/newview/llfloaterluadebug.h +++ b/indra/newview/llfloaterluadebug.h @@ -1,24 +1,24 @@ -/** +/** * @file llfloaterluadebug.h * * $LicenseInfo:firstyear=2023&license=viewerlgpl$ * Second Life Viewer Source Code * Copyright (C) 2023, Linden Research, Inc. - * + * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; * version 2.1 of the License only. - * + * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. - * + * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * + * * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA * $/LicenseInfo$ */ @@ -41,7 +41,7 @@ extern "C" class LLLineEditor; class LLTextEditor; -class LLFloaterLUADebug : +class LLFloaterLUADebug : public LLFloater { public: diff --git a/indra/newview/llfloaterluascripts.cpp b/indra/newview/llfloaterluascripts.cpp index 189ee0c9d1..60cdadfbee 100644 --- a/indra/newview/llfloaterluascripts.cpp +++ b/indra/newview/llfloaterluascripts.cpp @@ -1,24 +1,24 @@ -/** +/** * @file llfloaterluascriptsinfo.cpp * * $LicenseInfo:firstyear=2024&license=viewerlgpl$ * Second Life Viewer Source Code * Copyright (C) 2024, Linden Research, Inc. - * + * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; * version 2.1 of the License only. - * + * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. - * + * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * + * * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA * $/LicenseInfo$ */ @@ -41,16 +41,16 @@ LLFloaterLUAScripts::LLFloaterLUAScripts(const LLSD &key) mUpdateTimer(new LLTimer()), mContextMenuHandle() { - mCommitCallbackRegistrar.add("Script.OpenFolder", {[this](LLUICtrl*, const LLSD &userdata) - { + mCommitCallbackRegistrar.add("Script.OpenFolder", {[this](LLUICtrl*, const LLSD &userdata) + { if (mScriptList->hasSelectedItem()) { std::string target_folder_path = std::filesystem::path((mScriptList->getFirstSelected()->getColumn(1)->getValue().asString())).parent_path().string(); gViewerWindow->getWindow()->openFolder(target_folder_path); } }, cb_info::UNTRUSTED_BLOCK }); - mCommitCallbackRegistrar.add("Script.Terminate", {[this](LLUICtrl*, const LLSD &userdata) - { + mCommitCallbackRegistrar.add("Script.Terminate", {[this](LLUICtrl*, const LLSD &userdata) + { if (mScriptList->hasSelectedItem()) { std::string coro_name = mScriptList->getSelectedValue(); @@ -75,7 +75,7 @@ BOOL LLFloaterLUAScripts::postBuild() return TRUE; } -LLFloaterLUAScripts::~LLFloaterLUAScripts() +LLFloaterLUAScripts::~LLFloaterLUAScripts() { auto menu = mContextMenuHandle.get(); if (menu) @@ -94,7 +94,7 @@ void LLFloaterLUAScripts::draw() LLFloater::draw(); } -void LLFloaterLUAScripts::populateScriptList() +void LLFloaterLUAScripts::populateScriptList() { S32 prev_pos = mScriptList->getScrollPos(); LLSD prev_selected = mScriptList->getSelectedValue(); diff --git a/indra/newview/llfloaterluascripts.h b/indra/newview/llfloaterluascripts.h index 932c5c78dd..24d3f00a80 100644 --- a/indra/newview/llfloaterluascripts.h +++ b/indra/newview/llfloaterluascripts.h @@ -1,24 +1,24 @@ -/** +/** * @file llfloaterluascriptsinfo.h * * $LicenseInfo:firstyear=2024&license=viewerlgpl$ * Second Life Viewer Source Code * Copyright (C) 2024, Linden Research, Inc. - * + * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; * version 2.1 of the License only. - * + * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. - * + * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * + * * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA * $/LicenseInfo$ */ @@ -30,7 +30,7 @@ class LLScrollListCtrl; -class LLFloaterLUAScripts : +class LLFloaterLUAScripts : public LLFloater { public: diff --git a/indra/newview/llinventorygallerymenu.cpp b/indra/newview/llinventorygallerymenu.cpp index 2845c5b614..133ef51a21 100644 --- a/indra/newview/llinventorygallerymenu.cpp +++ b/indra/newview/llinventorygallerymenu.cpp @@ -92,9 +92,9 @@ LLContextMenu* LLInventoryGalleryContextMenu::createMenu() registrar.add("Inventory.DoToSelected", boost::bind(&LLInventoryGalleryContextMenu::doToSelected, this, _2), LLUICtrl::cb_info::UNTRUSTED_BLOCK); registrar.add("Inventory.FileUploadLocation", boost::bind(&LLInventoryGalleryContextMenu::fileUploadLocation, this, _2), LLUICtrl::cb_info::UNTRUSTED_BLOCK); - registrar.add("Inventory.EmptyTrash", + registrar.add("Inventory.EmptyTrash", boost::bind(&LLInventoryModel::emptyFolderType, &gInventory, "ConfirmEmptyTrash", LLFolderType::FT_TRASH), LLUICtrl::cb_info::UNTRUSTED_BLOCK); - registrar.add("Inventory.EmptyLostAndFound", + registrar.add("Inventory.EmptyLostAndFound", boost::bind(&LLInventoryModel::emptyFolderType, &gInventory, "ConfirmEmptyLostAndFound", LLFolderType::FT_LOST_AND_FOUND), LLUICtrl::cb_info::UNTRUSTED_BLOCK); registrar.add("Inventory.DoCreate", [this](LLUICtrl*, const LLSD& data) { diff --git a/indra/newview/llinventorylistener.cpp b/indra/newview/llinventorylistener.cpp index 157e04dce3..9263663997 100644 --- a/indra/newview/llinventorylistener.cpp +++ b/indra/newview/llinventorylistener.cpp @@ -154,7 +154,7 @@ void LLInventoryListener::getBasicFolderID(LLSD const &data) void LLInventoryListener::getDirectDescendents(LLSD const &data) { Response response(LLSD(), data); - LLInventoryModel::cat_array_t* cats; + LLInventoryModel::cat_array_t* cats; LLInventoryModel::item_array_t* items; gInventory.getDirectDescendentsOf(data["folder_id"], cats, items); diff --git a/indra/newview/llluamanager.cpp b/indra/newview/llluamanager.cpp index 22b51d7b72..41b1cd55b7 100644 --- a/indra/newview/llluamanager.cpp +++ b/indra/newview/llluamanager.cpp @@ -1,25 +1,25 @@ -/** +/** * @file llluamanager.cpp - * @brief classes and functions for interfacing with LUA. + * @brief classes and functions for interfacing with LUA. * * $LicenseInfo:firstyear=2023&license=viewerlgpl$ * Second Life Viewer Source Code * Copyright (C) 2023, Linden Research, Inc. - * + * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; * version 2.1 of the License only. - * + * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. - * + * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * + * * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA * $/LicenseInfo$ * @@ -193,7 +193,7 @@ void LLLUAmanager::runScriptFile(const std::string &filename, script_result_fn r llifstream in_file; in_file.open(filename.c_str()); - if (in_file.is_open()) + if (in_file.is_open()) { // A script_finished_fn is used to initialize the LuaState. // It will be called when the LuaState is destroyed. diff --git a/indra/newview/llluamanager.h b/indra/newview/llluamanager.h index 50f922a80f..08997c29a5 100644 --- a/indra/newview/llluamanager.h +++ b/indra/newview/llluamanager.h @@ -1,25 +1,25 @@ -/** +/** * @file llluamanager.h - * @brief classes and functions for interfacing with LUA. + * @brief classes and functions for interfacing with LUA. * * $LicenseInfo:firstyear=2023&license=viewerlgpl$ * Second Life Viewer Source Code * Copyright (C) 2023, Linden Research, Inc. - * + * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; * version 2.1 of the License only. - * + * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. - * + * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * + * * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA * $/LicenseInfo$ */ @@ -110,9 +110,9 @@ class LLRequireResolver class ScriptObserver { public: - ScriptObserver(const std::string &coro_name, const std::string &filename) : mCoroName(coro_name) + ScriptObserver(const std::string &coro_name, const std::string &filename) : mCoroName(coro_name) { - LLLUAmanager::sScriptNames[mCoroName] = filename; + LLLUAmanager::sScriptNames[mCoroName] = filename; } ~ScriptObserver() { LLLUAmanager::sScriptNames.erase(mCoroName); } diff --git a/indra/newview/llpanelmaininventory.cpp b/indra/newview/llpanelmaininventory.cpp index fc5390e0dc..622698c5fa 100644 --- a/indra/newview/llpanelmaininventory.cpp +++ b/indra/newview/llpanelmaininventory.cpp @@ -127,7 +127,7 @@ LLPanelMainInventory::LLPanelMainInventory(const LLPanel::Params& p) // Menu Callbacks (non contex menus) mCommitCallbackRegistrar.add("Inventory.DoToSelected", { boost::bind(&LLPanelMainInventory::doToSelected, this, _2), LLUICtrl::cb_info::UNTRUSTED_BLOCK }); mCommitCallbackRegistrar.add("Inventory.CloseAllFolders", { boost::bind(&LLPanelMainInventory::closeAllFolders, this) }); - mCommitCallbackRegistrar.add("Inventory.EmptyTrash", { boost::bind(&LLInventoryModel::emptyFolderType, &gInventory, + mCommitCallbackRegistrar.add("Inventory.EmptyTrash", { boost::bind(&LLInventoryModel::emptyFolderType, &gInventory, "ConfirmEmptyTrash", LLFolderType::FT_TRASH), LLUICtrl::cb_info::UNTRUSTED_BLOCK }); mCommitCallbackRegistrar.add("Inventory.EmptyLostAndFound", { boost::bind(&LLInventoryModel::emptyFolderType, &gInventory, "ConfirmEmptyLostAndFound", LLFolderType::FT_LOST_AND_FOUND), LLUICtrl::cb_info::UNTRUSTED_BLOCK }); diff --git a/indra/newview/llpanelobjectinventory.cpp b/indra/newview/llpanelobjectinventory.cpp index 64a9518e21..395e9eca82 100644 --- a/indra/newview/llpanelobjectinventory.cpp +++ b/indra/newview/llpanelobjectinventory.cpp @@ -1347,9 +1347,9 @@ LLPanelObjectInventory::LLPanelObjectInventory(const LLPanelObjectInventory::Par { // Setup context menu callbacks mCommitCallbackRegistrar.add("Inventory.DoToSelected", { boost::bind(&LLPanelObjectInventory::doToSelected, this, _2), cb_info::UNTRUSTED_BLOCK }); - mCommitCallbackRegistrar.add("Inventory.EmptyTrash", + mCommitCallbackRegistrar.add("Inventory.EmptyTrash", { boost::bind(&LLInventoryModel::emptyFolderType, &gInventory, "ConfirmEmptyTrash", LLFolderType::FT_TRASH), cb_info::UNTRUSTED_BLOCK }); - mCommitCallbackRegistrar.add("Inventory.EmptyLostAndFound", + mCommitCallbackRegistrar.add("Inventory.EmptyLostAndFound", { boost::bind(&LLInventoryModel::emptyFolderType, &gInventory, "ConfirmEmptyLostAndFound", LLFolderType::FT_LOST_AND_FOUND), cb_info::UNTRUSTED_BLOCK }); mCommitCallbackRegistrar.add("Inventory.DoCreate", { boost::bind(&do_nothing) }); mCommitCallbackRegistrar.add("Inventory.AttachObject", { boost::bind(&do_nothing) }); diff --git a/indra/newview/lluilistener.cpp b/indra/newview/lluilistener.cpp index 0280836075..6f567e67f8 100644 --- a/indra/newview/lluilistener.cpp +++ b/indra/newview/lluilistener.cpp @@ -3,25 +3,25 @@ * @author Nat Goodspeed * @date 2009-08-18 * @brief Implementation for lluilistener. - * + * * $LicenseInfo:firstyear=2009&license=viewerlgpl$ * Second Life Viewer Source Code * Copyright (C) 2010, Linden Research, Inc. - * + * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; * version 2.1 of the License only. - * + * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. - * + * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * + * * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA * $/LicenseInfo$ */ @@ -93,7 +93,7 @@ LLUIListener::LLUIListener(): "to the [\"parent_menu\"] within the Top menu.", &LLUIListener::addMenuItem, required_args.with("func", LLSD())); - + add("addMenuSeparator", "Add menu separator to the [\"parent_menu\"] within the Top menu.", &LLUIListener::addMenuSeparator, @@ -147,7 +147,7 @@ void LLUIListener::call(const LLSD& event) { return response.error(stringize("Function ", std::quoted(event["function"].asString()), " was not found")); } - if (info->handle_untrusted == cb_info::UNTRUSTED_BLOCK) + if (info->handle_untrusted == cb_info::UNTRUSTED_BLOCK) { return response.error(stringize("Function ", std::quoted(event["function"].asString()), " may not be called from the script")); } @@ -226,7 +226,7 @@ void LLUIListener::getValue(const LLSD& event) const const LLView* view = LLUI::getInstance()->resolvePath(root, event["path"].asString()); const LLUICtrl* ctrl(dynamic_cast(view)); - if (ctrl) + if (ctrl) { response["value"] = ctrl->getValue(); } diff --git a/indra/newview/lluilistener.h b/indra/newview/lluilistener.h index c839c03a57..8fc8e6ebb4 100644 --- a/indra/newview/lluilistener.h +++ b/indra/newview/lluilistener.h @@ -3,25 +3,25 @@ * @author Nat Goodspeed * @date 2009-08-18 * @brief Engage named functions as specified by XUI - * + * * $LicenseInfo:firstyear=2009&license=viewerlgpl$ * Second Life Viewer Source Code * Copyright (C) 2010, Linden Research, Inc. - * + * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; * version 2.1 of the License only. - * + * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. - * + * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * + * * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA * $/LicenseInfo$ */ diff --git a/indra/newview/llviewerchat.cpp b/indra/newview/llviewerchat.cpp index 00520f100e..440c0a2f69 100644 --- a/indra/newview/llviewerchat.cpp +++ b/indra/newview/llviewerchat.cpp @@ -227,7 +227,7 @@ void LLViewerChat::formatChatMsg(const LLChat& chat, std::string& formated_msg) formated_msg = tmpmsg; } - if (chat.mIsScript) + if (chat.mIsScript) { formated_msg = LLTrans::getString("ScriptStr") + formated_msg; } diff --git a/indra/newview/llvoavatar.cpp b/indra/newview/llvoavatar.cpp index ae6500da70..7f8f3961d3 100644 --- a/indra/newview/llvoavatar.cpp +++ b/indra/newview/llvoavatar.cpp @@ -11784,7 +11784,7 @@ class LLVOAvatarListener : public LLEventAPI } void isInAir(const LLSD &request) const { - Response response(llsd::map("value", gAgentAvatarp->mInAir, + Response response(llsd::map("value", gAgentAvatarp->mInAir, "duration", gAgentAvatarp->mInAir ? gAgentAvatarp->mTimeInAir.getElapsedTimeF32() : 0), request); } }; diff --git a/indra/newview/tests/llluamanager_test.cpp b/indra/newview/tests/llluamanager_test.cpp index 3209d93d39..4b143b52db 100644 --- a/indra/newview/tests/llluamanager_test.cpp +++ b/indra/newview/tests/llluamanager_test.cpp @@ -3,7 +3,7 @@ * @author Nat Goodspeed * @date 2023-09-28 * @brief Test for llluamanager. - * + * * $LicenseInfo:firstyear=2023&license=viewerlgpl$ * Copyright (c) 2023, Linden Research, Inc. * $/LicenseInfo$ diff --git a/indra/test/debug.h b/indra/test/debug.h index 2f6a114761..ea9c634cc7 100644 --- a/indra/test/debug.h +++ b/indra/test/debug.h @@ -3,25 +3,25 @@ * @author Nat Goodspeed * @date 2009-05-28 * @brief Debug output for unit test code - * + * * $LicenseInfo:firstyear=2009&license=viewerlgpl$ * Second Life Viewer Source Code * Copyright (C) 2010, Linden Research, Inc. - * + * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; * version 2.1 of the License only. - * + * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. - * + * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * + * * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA * $/LicenseInfo$ */ diff --git a/indra/test/print.h b/indra/test/print.h index 749603907e..6906eae581 100644 --- a/indra/test/print.h +++ b/indra/test/print.h @@ -3,7 +3,7 @@ * @author Nat Goodspeed * @date 2020-01-02 * @brief print() function for debugging - * + * * $LicenseInfo:firstyear=2020&license=viewerlgpl$ * Copyright (c) 2020, Linden Research, Inc. * $/LicenseInfo$ diff --git a/indra/test/writestr.h b/indra/test/writestr.h index df1dab2f10..af8be5a3aa 100755 --- a/indra/test/writestr.h +++ b/indra/test/writestr.h @@ -3,7 +3,7 @@ * @author Nat Goodspeed * @date 2024-05-21 * @brief writestr() function for when iostream isn't set up - * + * * $LicenseInfo:firstyear=2024&license=viewerlgpl$ * Copyright (c) 2024, Linden Research, Inc. * $/LicenseInfo$ -- cgit v1.2.3 From e25d23aaac77f3793207aa0baf59ae64ea5eba41 Mon Sep 17 00:00:00 2001 From: Andrey Lihatskiy Date: Thu, 29 Aug 2024 08:54:40 +0300 Subject: #2059 Linux build fix --- indra/newview/pipeline.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/indra/newview/pipeline.cpp b/indra/newview/pipeline.cpp index eaf54356a2..cd93df71c7 100644 --- a/indra/newview/pipeline.cpp +++ b/indra/newview/pipeline.cpp @@ -121,7 +121,7 @@ #define A_GCC 1 #pragma GCC diagnostic ignored "-Wunused-function" #pragma GCC diagnostic ignored "-Wunused-variable" -#if LL_LINUX +#if LL_LINUX && defined(__GNUC__) && !defined(__clang__) #pragma GCC diagnostic ignored "-Wrestrict" #endif #endif -- cgit v1.2.3 From a098a3d42bf862e0e3789e21f6b8f3f0e71d60d0 Mon Sep 17 00:00:00 2001 From: Nat Goodspeed Date: Thu, 29 Aug 2024 08:27:43 -0400 Subject: Add Lua script name to log messages. --- indra/llcommon/lua_function.cpp | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/indra/llcommon/lua_function.cpp b/indra/llcommon/lua_function.cpp index f61cf3fe10..be39a7d095 100644 --- a/indra/llcommon/lua_function.cpp +++ b/indra/llcommon/lua_function.cpp @@ -26,6 +26,7 @@ // other Linden headers #include "fsyspath.h" #include "hexdump.h" +#include "llcoros.h" #include "lleventcoro.h" #include "llsd.h" #include "llsdutil.h" @@ -530,7 +531,8 @@ LuaState::~LuaState() // That's important because we walk the atexit table backwards, to // destroy last the things we created (passed to LL.atexit()) first. int len(lua_objlen(mState, -1)); - LL_DEBUGS("Lua") << "Registry.atexit is a table with " << len << " entries" << LL_ENDL; + LL_DEBUGS("Lua") << LLCoros::getName() << ": Registry.atexit is a table with " + << len << " entries" << LL_ENDL; // Push debug.traceback() onto the stack as lua_pcall()'s error // handler function. On error, lua_pcall() calls the specified error @@ -555,15 +557,17 @@ LuaState::~LuaState() // Use lua_pcall() because errors in any one atexit() function // shouldn't cancel the rest of them. Pass debug.traceback() as // the error handler function. - LL_DEBUGS("Lua") << "Calling atexit(" << i << ")" << LL_ENDL; + LL_DEBUGS("Lua") << LLCoros::getName() + << ": calling atexit(" << i << ")" << LL_ENDL; if (lua_pcall(mState, 0, 0, -2) != LUA_OK) { auto error{ lua_tostdstring(mState, -1) }; - LL_WARNS("Lua") << "atexit(" << i << ") error: " << error << LL_ENDL; + LL_WARNS("Lua") << LLCoros::getName() + << ": atexit(" << i << ") error: " << error << LL_ENDL; // pop error message lua_pop(mState, 1); } - LL_DEBUGS("Lua") << "atexit(" << i << ") done" << LL_ENDL; + LL_DEBUGS("Lua") << LLCoros::getName() << ": atexit(" << i << ") done" << LL_ENDL; // lua_pcall() has already popped atexit[i]: // stack contains atexit, debug.traceback() } @@ -1277,7 +1281,8 @@ setdtor_refs::~setdtor_refs() // garbage-collected, the call is completely unpredictable from // the consuming script's point of view. But what to do about this // error?? For now, just log it. - LL_WARNS("Lua") << "setdtor(" << std::quoted(desc) << ") error: " + LL_WARNS("Lua") << LLCoros::getName() + << ": setdtor(" << std::quoted(desc) << ") error: " << lua_tostring(L, -1) << LL_ENDL; lua_pop(L, 1); } @@ -1377,7 +1382,8 @@ LuaStackDelta::~LuaStackDelta() // instance to keep its contract wrt the Lua data stack. if (std::uncaught_exceptions() == 0 && mDepth + mDelta != depth) { - LL_ERRS("Lua") << mWhere << ": Lua stack went from " << mDepth << " to " << depth; + LL_ERRS("Lua") << LLCoros::getName() << ": " << mWhere + << ": Lua stack went from " << mDepth << " to " << depth; if (mDelta) { LL_CONT << ", rather than expected " << (mDepth + mDelta) << " (" << mDelta << ")"; -- cgit v1.2.3 From 11e6c77129ae27756df627ccc1ea0ffa279976e6 Mon Sep 17 00:00:00 2001 From: Maxim Nikolenko Date: Thu, 29 Aug 2024 20:04:34 +0300 Subject: Add simple metrics of Lua usage --- indra/newview/llappviewer.cpp | 2 +- indra/newview/llfloaterluadebug.cpp | 2 +- indra/newview/llluamanager.cpp | 16 ++++++++++++---- indra/newview/llluamanager.h | 5 ++++- indra/newview/llviewerstats.cpp | 5 +++++ 5 files changed, 23 insertions(+), 7 deletions(-) diff --git a/indra/newview/llappviewer.cpp b/indra/newview/llappviewer.cpp index c259275d8f..686b97390a 100644 --- a/indra/newview/llappviewer.cpp +++ b/indra/newview/llappviewer.cpp @@ -1252,7 +1252,7 @@ bool LLAppViewer::init() while (scripts.next(script)) { LL_DEBUGS("InitInfo") << "LuaAutorunPath: " << absdir << ": " << script << LL_ENDL; - LLLUAmanager::runScriptFile((abspath / script).string()); + LLLUAmanager::runScriptFile((abspath / script).string(), true); } }); diff --git a/indra/newview/llfloaterluadebug.cpp b/indra/newview/llfloaterluadebug.cpp index ef24481464..1831edb452 100644 --- a/indra/newview/llfloaterluadebug.cpp +++ b/indra/newview/llfloaterluadebug.cpp @@ -127,7 +127,7 @@ void LLFloaterLUADebug::runSelectedScript(const std::vector &filena if (!filepath.empty()) { mScriptPath->setText(filepath); - LLLUAmanager::runScriptFile(filepath, [this](int count, const LLSD &result) + LLLUAmanager::runScriptFile(filepath, false, [this](int count, const LLSD &result) { completion(count, result); }); diff --git a/indra/newview/llluamanager.cpp b/indra/newview/llluamanager.cpp index 22b51d7b72..befd231e92 100644 --- a/indra/newview/llluamanager.cpp +++ b/indra/newview/llluamanager.cpp @@ -49,6 +49,8 @@ #include #include +S32 LLLUAmanager::sAutorunScriptCount = 0; +S32 LLLUAmanager::sScriptCount = 0; std::map LLLUAmanager::sScriptNames; lua_function(sleep, "sleep(seconds): pause the running coroutine") @@ -172,7 +174,7 @@ LLLUAmanager::startScriptFile(const std::string& filename) // Despite returning from startScriptFile(), we need this Promise to // remain alive until the callback has fired. auto promise{ std::make_shared>() }; - runScriptFile(filename, + runScriptFile(filename, false, [promise](int count, LLSD result) { promise->set_value({ count, result }); }); return LLCoros::getFuture(*promise); @@ -183,11 +185,11 @@ LLLUAmanager::script_result LLLUAmanager::waitScriptFile(const std::string& file return startScriptFile(filename).get(); } -void LLLUAmanager::runScriptFile(const std::string &filename, script_result_fn result_cb, - script_finished_fn finished_cb) +void LLLUAmanager::runScriptFile(const std::string &filename, bool autorun, + script_result_fn result_cb, script_finished_fn finished_cb) { // A script_result_fn will be called when LuaState::expr() completes. - LLCoros::instance().launch(filename, [filename, result_cb, finished_cb]() + LLCoros::instance().launch(filename, [filename, autorun, result_cb, finished_cb]() { ScriptObserver observer(LLCoros::getName(), filename); llifstream in_file; @@ -195,6 +197,12 @@ void LLLUAmanager::runScriptFile(const std::string &filename, script_result_fn r if (in_file.is_open()) { + if (autorun) + { + sAutorunScriptCount++; + } + sScriptCount++; + // A script_finished_fn is used to initialize the LuaState. // It will be called when the LuaState is destroyed. LuaState L(finished_cb); diff --git a/indra/newview/llluamanager.h b/indra/newview/llluamanager.h index 50f922a80f..309cb87cac 100644 --- a/indra/newview/llluamanager.h +++ b/indra/newview/llluamanager.h @@ -58,7 +58,7 @@ public: // same semantics as script_result_fn parameters typedef std::pair script_result; - static void runScriptFile(const std::string &filename, script_result_fn result_cb = {}, + static void runScriptFile(const std::string &filename, bool autorun = false, script_result_fn result_cb = {}, script_finished_fn finished_cb = {}); // Start running a Lua script file, returning an LLCoros::Future whose // get() method will pause the calling coroutine until it can deliver the @@ -84,6 +84,9 @@ public: static const std::map getScriptNames() { return sScriptNames; } + static S32 sAutorunScriptCount; + static S32 sScriptCount; + private: static std::map sScriptNames; }; diff --git a/indra/newview/llviewerstats.cpp b/indra/newview/llviewerstats.cpp index 62b4c390d0..60e8cf2e52 100644 --- a/indra/newview/llviewerstats.cpp +++ b/indra/newview/llviewerstats.cpp @@ -66,6 +66,7 @@ #include "llinventorymodel.h" #include "lluiusage.h" #include "lltranslate.h" +#include "llluamanager.h" // "Minimal Vulkan" to get max API Version @@ -673,6 +674,10 @@ void send_viewer_stats(bool include_preferences) system["shader_level"] = shader_level; + LLSD &scripts = body["scripts"]; + scripts["lua_scripts"] = LLLUAmanager::sScriptCount; + scripts["lua_auto_scripts"] = LLLUAmanager::sAutorunScriptCount; + LLSD &download = body["downloads"]; download["world_kbytes"] = F64Kilobytes(gTotalWorldData).value(); -- cgit v1.2.3 From 2d5cf36be6e0e367efec2bfa01378146269f33db Mon Sep 17 00:00:00 2001 From: Nat Goodspeed Date: Thu, 29 Aug 2024 21:36:39 -0400 Subject: Support next(), pairs(), ipairs() for LL.setdtor() table proxies. Replace the global next(), pairs() and ipairs() functions with a C++ function that drills down through layers of setdtor() proxy objects and then forwards the updated arguments to the original global function. Add a Luau __iter() metamethod to setdtor() proxy objects that, like other proxy metamethods, drills down to the underlying _target object. __iter() recognizes the case of a _target table which itself has a __iter() metamethod. Also add __idiv() metamethod to support integer division. Add tests for proxy // division, next(proxy), next(proxy, key), pairs(proxy), ipairs(proxy) and 'for k, v in proxy'. Also test the case where the table wrapped in the proxy has an __iter() metamethod of its own. --- indra/llcommon/lua_function.cpp | 62 ++++++++++++++++++++++++++++++ indra/newview/scripts/lua/test_setdtor.lua | 29 ++++++++++++++ 2 files changed, 91 insertions(+) diff --git a/indra/llcommon/lua_function.cpp b/indra/llcommon/lua_function.cpp index be39a7d095..850dff1a33 100644 --- a/indra/llcommon/lua_function.cpp +++ b/indra/llcommon/lua_function.cpp @@ -496,6 +496,9 @@ namespace using LuaStateMap = std::unordered_map; static LuaStateMap sLuaStateMap; +// replacement next(), pairs(), ipairs() that understand setdtor() proxy args +int lua_proxydrill(lua_State* L); + } // anonymous namespace LuaState::LuaState(script_finished_fn cb): @@ -513,6 +516,28 @@ LuaState::LuaState(script_finished_fn cb): lua_register(mState, "print", LuaFunction::get("print_info")); // We don't want to have to prefix require(). lua_register(mState, "require", LuaFunction::get("require")); + + // Replace certain key global functions so they understand our + // LL.setdtor() proxy objects. + // (We could also do this for selected library functions as well, + // e.g. the table, string, math libraries... let's see if needed.) + for (const auto& func : { "next", "pairs", "ipairs" }) + { + // push the function's name string twice + lua_pushstring(mState, func); + lua_pushvalue(mState, -1); + // stack: name, name + // look up the existing global + lua_rawget(mState, LUA_GLOBALSINDEX); + // stack: name, global function + // bind original function as the upvalue for lua_proxydrill() + lua_pushcclosure(mState, lua_proxydrill, + stringize("lua_proxydrill(", func, ')').c_str(), + 1); + // stack: name, lua_proxydrill(func) + // global name = lua_proxydrill(func) + lua_rawset(mState, LUA_GLOBALSINDEX); + } } LuaState::~LuaState() @@ -1165,6 +1190,14 @@ void setdtor_refs::push_metatable(lua_State* L) -- don't fret about arg._target's __tostring metamethod, -- if any, because built-in tostring() deals with that return tostring(arg._target) + end, + __iter = function(arg) + local iter = (getmetatable(arg._target) or {}).__iter + if iter then + return iter(arg._target) + else + return next, arg._target + end end } )-", @@ -1172,6 +1205,7 @@ void setdtor_refs::push_metatable(lua_State* L) binop("sub", "-"), binop("mul", "*"), binop("div", "/"), + binop("idiv", "//"), binop("mod", "%"), binop("pow", "^"), binop("concat", ".."), @@ -1254,6 +1288,34 @@ int setdtor_refs::meta__index(lua_State* L) return 1; } +// replacement for global next(), pairs(), ipairs(): +// its lua_upvalueindex(1) is the original function it's replacing +int lua_proxydrill(lua_State* L) +{ + // Accept however many arguments the original function normally accepts. + // If our first arg is a userdata, check if it's a setdtor_refs proxy. + // Drill through as many levels of proxy wrapper as needed. + while (const setdtor_refs* ptr = lua_toclass(L, 1)) + { + // push original object + lua_getref(L, ptr->objref); + // replace first argument with that + lua_replace(L, 1); + } + // We've reached a first argument that's not a setdtor() proxy. + // How many arguments were we passed, anyway? + int args = lua_gettop(L); + // Push the original function, captured as our upvalue. + lua_pushvalue(L, lua_upvalueindex(1)); + // Shift the stack so the original function is first. + lua_insert(L, 1); + // Call the original function with all original args, no error checking. + // Don't truncate however many values that function returns. + lua_call(L, args, LUA_MULTRET); + // Return as many values as the original function returned. + return lua_gettop(L); +} + // When Lua destroys a setdtor_refs userdata object, either from garbage // collection or from LL.atexit(lua_destroybounduserdata), it's time to keep // its promise to call the specified Lua destructor function with the diff --git a/indra/newview/scripts/lua/test_setdtor.lua b/indra/newview/scripts/lua/test_setdtor.lua index 61ed86dcc8..ec5cd47e93 100644 --- a/indra/newview/scripts/lua/test_setdtor.lua +++ b/indra/newview/scripts/lua/test_setdtor.lua @@ -30,6 +30,8 @@ print(`n * {i} =`, n * i) print(`{i} * n =`, i * n) print(`n / {i} =`, n / i) print(`{i} / n =`, i / n) +print(`n // {i} =`, n // i) +print(`{i} // n =`, i // n) print(`n % {i} =`, n % i) print(`{i} % n =`, i % n) print(`n ^ {i} =`, n ^ i) @@ -48,10 +50,37 @@ t = LL.setdtor('table', {'[1]', '[2]', abc='.abc', def='.def'}, print('t =', inspect(t)) print('t._target =', inspect(t._target)) print('#t =', #t) +print('next(t) =', next(t)) +print('next(t, 1) =', next(t, 1)) print('t[2] =', t[2]) print('t.def =', t.def) t[1] = 'new [1]' print('t[1] =', t[1]) +print('for k, v in pairs(t) do') +for k, v in pairs(t) do + print(`{k}: {v}`) +end +print('for k, v in ipairs(t) do') +for k, v in ipairs(t) do + print(`{k}: {v}`) +end +print('for k, v in t do') +for k, v in t do + print(`{k}: {v}`) +end +-- and now for something completely different +setmetatable( + t._target, + { + __iter = function(arg) + return next, {'alternate', '__iter'} + end + } +) +print('for k, v in t with __iter() metamethod do') +for k, v in t do + print(`{k}: {v}`) +end print('function') f = LL.setdtor('function', function(a, b) return (a .. b) end, print) -- cgit v1.2.3 From 6e47dc1af90c242c792c90e93d013355c9a43b97 Mon Sep 17 00:00:00 2001 From: Mnikolenko Productengine Date: Fri, 30 Aug 2024 12:18:20 +0300 Subject: Add Lua api to start/stop playing animation --- indra/newview/llagentlistener.cpp | 66 +++++++++++++++++++++++++++ indra/newview/llagentlistener.h | 4 ++ indra/newview/scripts/lua/require/LLAgent.lua | 14 ++++++ 3 files changed, 84 insertions(+) diff --git a/indra/newview/llagentlistener.cpp b/indra/newview/llagentlistener.cpp index 80460666a5..95b7ebd5db 100644 --- a/indra/newview/llagentlistener.cpp +++ b/indra/newview/llagentlistener.cpp @@ -34,12 +34,14 @@ #include "llagentcamera.h" #include "llvoavatar.h" #include "llcommandhandler.h" +#include "llinventorymodel.h" #include "llslurl.h" #include "llurldispatcher.h" #include "llviewernetwork.h" #include "llviewerobject.h" #include "llviewerobjectlist.h" #include "llviewerregion.h" +#include "llvoavatarself.h" #include "llsdutil.h" #include "llsdutil_math.h" #include "lltoolgrab.h" @@ -157,6 +159,19 @@ LLAgentListener::LLAgentListener(LLAgent &agent) add("removeCameraParams", "Reset Follow camera params", &LLAgentListener::removeFollowCamParams); + + add("playAnimation", + "Play [\"item_id\"] animation locally (by default) or [\"inworld\"] (when set to true)", + &LLAgentListener::playAnimation, + llsd::map("item_id", LLSD(), "reply", LLSD())); + add("stopAnimation", + "Stop playing [\"item_id\"] animation", + &LLAgentListener::stopAnimation, + llsd::map("item_id", LLSD(), "reply", LLSD())); + add("getAnimationInfo", + "Return information about [\"item_id\"] animation", + &LLAgentListener::getAnimationInfo, + llsd::map("item_id", LLSD(), "reply", LLSD())); } void LLAgentListener::requestTeleport(LLSD const & event_data) const @@ -591,3 +606,54 @@ void LLAgentListener::removeFollowCamParams(LLSD const & event) const { LLFollowCamMgr::getInstance()->removeFollowCamParams(gAgentID); } + +void LLAgentListener::playAnimation(LLSD const &event_data) +{ + Response response(LLSD(), event_data); + LLViewerInventoryItem* item = gInventory.getItem(event_data["item_id"].asUUID()); + if (!item || (item->getInventoryType() != LLInventoryType::IT_ANIMATION)) + { + return response.error(stringize("Item ", std::quoted(event_data["item_id"].asString()), " was not found")); + } + LLUUID assset_id = item->getAssetUUID(); + if(event_data["inworld"].asBoolean()) + { + gAgent.sendAnimationRequest(assset_id, ANIM_REQUEST_START); + } + else + { + gAgentAvatarp->startMotion(assset_id); + } +} + +void LLAgentListener::stopAnimation(LLSD const &event_data) +{ + Response response(LLSD(), event_data); + LLViewerInventoryItem* item = gInventory.getItem(event_data["item_id"].asUUID()); + if (!item || (item->getInventoryType() != LLInventoryType::IT_ANIMATION)) + { + return response.error(stringize("Item ", std::quoted(event_data["item_id"].asString()), " was not found")); + } + LLUUID assset_id = item->getAssetUUID(); + gAgentAvatarp->stopMotion(assset_id); + gAgent.sendAnimationRequest(assset_id, ANIM_REQUEST_STOP); +} + +void LLAgentListener::getAnimationInfo(LLSD const &event_data) +{ + Response response(LLSD(), event_data); + LLUUID item_id(event_data["item_id"].asUUID()); + LLViewerInventoryItem* item = gInventory.getItem(item_id); + if (!item || (item->getInventoryType() != LLInventoryType::IT_ANIMATION)) + { + return response.error(stringize("Item ", std::quoted(item_id.asString()), " was not found")); + } + // if motion exists, will return existing one + LLMotion* motion = gAgentAvatarp->createMotion(item->getAssetUUID()); + response["anim_info"].insert(item_id.asString(), + llsd::map("duration", motion->getDuration(), + "is_loop", motion->getLoop(), + "num_joints", motion->getNumJointMotions(), + "asset_id", item->getAssetUUID(), + "priority", motion->getPriority())); +} diff --git a/indra/newview/llagentlistener.h b/indra/newview/llagentlistener.h index 2a24de3f52..cf914a17d0 100644 --- a/indra/newview/llagentlistener.h +++ b/indra/newview/llagentlistener.h @@ -60,6 +60,10 @@ private: void setFollowCamParams(LLSD const & event_data) const; void setFollowCamActive(LLSD const & event_data) const; void removeFollowCamParams(LLSD const & event_data) const; + + void playAnimation(LLSD const &event_data); + void stopAnimation(LLSD const &event_data); + void getAnimationInfo(LLSD const &event_data); LLViewerObject * findObjectClosestTo( const LLVector3 & position ) const; diff --git a/indra/newview/scripts/lua/require/LLAgent.lua b/indra/newview/scripts/lua/require/LLAgent.lua index 5ee092f2f6..56907f53cd 100644 --- a/indra/newview/scripts/lua/require/LLAgent.lua +++ b/indra/newview/scripts/lua/require/LLAgent.lua @@ -53,4 +53,18 @@ function LLAgent.removeCamParams() leap.send('LLAgent', {op = 'removeCameraParams'}) end +function LLAgent.playAnimation(...) + local args = mapargs('item_id,inworld', ...) + args.op = 'playAnimation' + return leap.request('LLAgent', args) +end + +function LLAgent.stopAnimation(item_id) + return leap.request('LLAgent', {op = 'stopAnimation', item_id=item_id}) +end + +function LLAgent.getAnimationInfo(item_id) + return leap.request('LLAgent', {op = 'getAnimationInfo', item_id=item_id}).anim_info +end + return LLAgent -- cgit v1.2.3 From 4312a2b7703f6ba35a1cdcd1edc48dddfe084270 Mon Sep 17 00:00:00 2001 From: Mnikolenko Productengine Date: Fri, 30 Aug 2024 14:48:36 +0300 Subject: Add throttle for playing an animation; add demo script --- indra/newview/llagentlistener.cpp | 59 +++++++++++++++------------ indra/newview/llagentlistener.h | 4 ++ indra/newview/scripts/lua/require/LLAgent.lua | 4 ++ indra/newview/scripts/lua/test_animation.lua | 28 +++++++++++++ 4 files changed, 70 insertions(+), 25 deletions(-) create mode 100644 indra/newview/scripts/lua/test_animation.lua diff --git a/indra/newview/llagentlistener.cpp b/indra/newview/llagentlistener.cpp index 95b7ebd5db..4980c7075f 100644 --- a/indra/newview/llagentlistener.cpp +++ b/indra/newview/llagentlistener.cpp @@ -49,9 +49,12 @@ #include "llagentcamera.h" #include +static const F64 PLAY_ANIM_THROTTLE_PERIOD = 1.f; + LLAgentListener::LLAgentListener(LLAgent &agent) : LLEventAPI("LLAgent", "LLAgent listener to (e.g.) teleport, sit, stand, etc."), + mPlayAnimThrottle("playAnimation", &LLAgentListener::playAnimation_, this, PLAY_ANIM_THROTTLE_PERIOD), mAgent(agent) { add("requestTeleport", @@ -607,53 +610,59 @@ void LLAgentListener::removeFollowCamParams(LLSD const & event) const LLFollowCamMgr::getInstance()->removeFollowCamParams(gAgentID); } -void LLAgentListener::playAnimation(LLSD const &event_data) +LLViewerInventoryItem* get_anim_item(LLEventAPI::Response &response, const LLSD &event_data) { - Response response(LLSD(), event_data); LLViewerInventoryItem* item = gInventory.getItem(event_data["item_id"].asUUID()); if (!item || (item->getInventoryType() != LLInventoryType::IT_ANIMATION)) { - return response.error(stringize("Item ", std::quoted(event_data["item_id"].asString()), " was not found")); + response.error(stringize("Animation item ", std::quoted(event_data["item_id"].asString()), " was not found")); + return NULL; + } + return item; +} + +void LLAgentListener::playAnimation(LLSD const &event_data) +{ + Response response(LLSD(), event_data); + if (LLViewerInventoryItem* item = get_anim_item(response, event_data)) + { + mPlayAnimThrottle(item->getAssetUUID(), event_data["inworld"].asBoolean()); } - LLUUID assset_id = item->getAssetUUID(); - if(event_data["inworld"].asBoolean()) +} + +void LLAgentListener::playAnimation_(const LLUUID& asset_id, const bool inworld) +{ + if (inworld) { - gAgent.sendAnimationRequest(assset_id, ANIM_REQUEST_START); + mAgent.sendAnimationRequest(asset_id, ANIM_REQUEST_START); } else { - gAgentAvatarp->startMotion(assset_id); + gAgentAvatarp->startMotion(asset_id); } } void LLAgentListener::stopAnimation(LLSD const &event_data) { Response response(LLSD(), event_data); - LLViewerInventoryItem* item = gInventory.getItem(event_data["item_id"].asUUID()); - if (!item || (item->getInventoryType() != LLInventoryType::IT_ANIMATION)) + if (LLViewerInventoryItem* item = get_anim_item(response, event_data)) { - return response.error(stringize("Item ", std::quoted(event_data["item_id"].asString()), " was not found")); + gAgentAvatarp->stopMotion(item->getAssetUUID()); + mAgent.sendAnimationRequest(item->getAssetUUID(), ANIM_REQUEST_STOP); } - LLUUID assset_id = item->getAssetUUID(); - gAgentAvatarp->stopMotion(assset_id); - gAgent.sendAnimationRequest(assset_id, ANIM_REQUEST_STOP); } void LLAgentListener::getAnimationInfo(LLSD const &event_data) { Response response(LLSD(), event_data); - LLUUID item_id(event_data["item_id"].asUUID()); - LLViewerInventoryItem* item = gInventory.getItem(item_id); - if (!item || (item->getInventoryType() != LLInventoryType::IT_ANIMATION)) + if (LLViewerInventoryItem* item = get_anim_item(response, event_data)) { - return response.error(stringize("Item ", std::quoted(item_id.asString()), " was not found")); + // if motion exists, will return existing one + LLMotion* motion = gAgentAvatarp->createMotion(item->getAssetUUID()); + response["anim_info"] = llsd::map("duration", motion->getDuration(), + "is_loop", motion->getLoop(), + "num_joints", motion->getNumJointMotions(), + "asset_id", item->getAssetUUID(), + "priority", motion->getPriority()); } - // if motion exists, will return existing one - LLMotion* motion = gAgentAvatarp->createMotion(item->getAssetUUID()); - response["anim_info"].insert(item_id.asString(), - llsd::map("duration", motion->getDuration(), - "is_loop", motion->getLoop(), - "num_joints", motion->getNumJointMotions(), - "asset_id", item->getAssetUUID(), - "priority", motion->getPriority())); } diff --git a/indra/newview/llagentlistener.h b/indra/newview/llagentlistener.h index cf914a17d0..c77d1b3fc9 100644 --- a/indra/newview/llagentlistener.h +++ b/indra/newview/llagentlistener.h @@ -31,6 +31,7 @@ #define LL_LLAGENTLISTENER_H #include "lleventapi.h" +#include "throttle.h" class LLAgent; class LLSD; @@ -62,6 +63,7 @@ private: void removeFollowCamParams(LLSD const & event_data) const; void playAnimation(LLSD const &event_data); + void playAnimation_(const LLUUID& asset_id, const bool inworld); void stopAnimation(LLSD const &event_data); void getAnimationInfo(LLSD const &event_data); @@ -70,6 +72,8 @@ private: private: LLAgent & mAgent; LLUUID mFollowTarget; + + LogThrottle mPlayAnimThrottle; }; #endif // LL_LLAGENTLISTENER_H diff --git a/indra/newview/scripts/lua/require/LLAgent.lua b/indra/newview/scripts/lua/require/LLAgent.lua index 56907f53cd..07ef1e0b0b 100644 --- a/indra/newview/scripts/lua/require/LLAgent.lua +++ b/indra/newview/scripts/lua/require/LLAgent.lua @@ -53,6 +53,8 @@ function LLAgent.removeCamParams() leap.send('LLAgent', {op = 'removeCameraParams'}) end +-- Play specified animation by "item_id" locally +-- if "inworld" is specified as true, animation will be played inworld instead function LLAgent.playAnimation(...) local args = mapargs('item_id,inworld', ...) args.op = 'playAnimation' @@ -63,6 +65,8 @@ function LLAgent.stopAnimation(item_id) return leap.request('LLAgent', {op = 'stopAnimation', item_id=item_id}) end +-- Get animation info by "item_id" +-- reply contains "duration", "is_loop", "num_joints", "asset_id", "priority" function LLAgent.getAnimationInfo(item_id) return leap.request('LLAgent', {op = 'getAnimationInfo', item_id=item_id}).anim_info end diff --git a/indra/newview/scripts/lua/test_animation.lua b/indra/newview/scripts/lua/test_animation.lua new file mode 100644 index 0000000000..c16fef4918 --- /dev/null +++ b/indra/newview/scripts/lua/test_animation.lua @@ -0,0 +1,28 @@ +LLInventory = require 'LLInventory' +LLAgent = require 'LLAgent' + +-- Get 'Animations' folder id (you can see all folder types via LLInventory.getFolderTypeNames()) +animations_id = LLInventory.getBasicFolderID('animatn') +-- Get animations from the 'Animation' folder (you can see all folder types via LLInventory.getAssetTypeNames()) +anims = LLInventory.collectDescendentsIf{folder_id=animations_id, type="animatn"}.items + +local anim_ids = {} +for key in pairs(anims) do + table.insert(anim_ids, key) +end + +-- Start playing a random animation +math.randomseed(os.time()) +local random_id = anim_ids[math.random(#anim_ids)] +local anim_info = LLAgent.getAnimationInfo(random_id) + +print("Starting animation locally: " .. anims[random_id].name) +print("Loop: " .. anim_info.is_loop .. " Joints: " .. anim_info.num_joints .. " Duration " .. tonumber(string.format("%.2f", anim_info.duration))) +LLAgent.playAnimation{item_id=random_id} + +-- Stop animation after 3 sec if it's looped or longer than 3 sec +if anim_info.is_loop == 1 or anim_info.duration > 3 then + LL.sleep(3) + print("Stop animation.") + LLAgent.stopAnimation(random_id) +end -- cgit v1.2.3 From a662fea2840bd6e8b7856b5f3c8e2f43e706178e Mon Sep 17 00:00:00 2001 From: Nat Goodspeed Date: Fri, 30 Aug 2024 17:07:58 -0400 Subject: Change LLInstanceTracker::destruct() to erase(). One could argue that LLInstanceTracker is a container of sorts, and erase() is more conventional. This affects no other code, as destruct() is not currently referenced. --- indra/llcommon/llinstancetracker.h | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/indra/llcommon/llinstancetracker.h b/indra/llcommon/llinstancetracker.h index aba9f1187b..722cb483fe 100644 --- a/indra/llcommon/llinstancetracker.h +++ b/indra/llcommon/llinstancetracker.h @@ -262,19 +262,19 @@ public: virtual const KEY& getKey() const { return mInstanceKey; } /// for use ONLY for an object we're sure resides on the heap! - static bool destruct(const KEY& key) + static bool erase(const KEY& key) { - return destruct(getInstance(key)); + return erase(getInstance(key)); } /// for use ONLY for an object we're sure resides on the heap! - static bool destruct(const weak_t& ptr) + static bool erase(const weak_t& ptr) { - return destruct(ptr.lock()); + return erase(ptr.lock()); } /// for use ONLY for an object we're sure resides on the heap! - static bool destruct(const ptr_t& ptr) + static bool erase(const ptr_t& ptr) { if (! ptr) { @@ -480,13 +480,13 @@ public: using key_snapshot_of = instance_snapshot_of; /// for use ONLY for an object we're sure resides on the heap! - static bool destruct(const weak_t& ptr) + static bool erase(const weak_t& ptr) { - return destruct(ptr.lock()); + return erase(ptr.lock()); } /// for use ONLY for an object we're sure resides on the heap! - static bool destruct(const ptr_t& ptr) + static bool erase(const ptr_t& ptr) { if (! ptr) { -- cgit v1.2.3 From 2eb0d173c3d24997a70b5ca9e78562ba48fc2f51 Mon Sep 17 00:00:00 2001 From: Nat Goodspeed Date: Fri, 30 Aug 2024 17:13:26 -0400 Subject: Add LLIntTracker, an LLInstanceTracker with generated keys. The point of LLIntTracker is to generate its keys implicitly, so that its int getKey() can be treated more or less like an instance pointer, with the added bonus that the key can be passed around via LLSD. LLIntTracker generates random int keys to try to make it a little harder for one script to mess with an LLIntTracker instance belonging to another. --- indra/llcommon/CMakeLists.txt | 1 + indra/llcommon/llinttracker.h | 43 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 44 insertions(+) create mode 100644 indra/llcommon/llinttracker.h diff --git a/indra/llcommon/CMakeLists.txt b/indra/llcommon/CMakeLists.txt index 8472eac9f6..8577d94be1 100644 --- a/indra/llcommon/CMakeLists.txt +++ b/indra/llcommon/CMakeLists.txt @@ -189,6 +189,7 @@ set(llcommon_HEADER_FILES llinitparam.h llinstancetracker.h llinstancetrackersubclass.h + llinttracker.h llkeybind.h llkeythrottle.h llleap.h diff --git a/indra/llcommon/llinttracker.h b/indra/llcommon/llinttracker.h new file mode 100644 index 0000000000..86c30bc7aa --- /dev/null +++ b/indra/llcommon/llinttracker.h @@ -0,0 +1,43 @@ +/** + * @file llinttracker.h + * @author Nat Goodspeed + * @date 2024-08-30 + * @brief LLIntTracker isa LLInstanceTracker with generated int keys. + * + * $LicenseInfo:firstyear=2024&license=viewerlgpl$ + * Copyright (c) 2024, Linden Research, Inc. + * $/LicenseInfo$ + */ + +#if ! defined(LL_LLINTTRACKER_H) +#define LL_LLINTTRACKER_H + +#include "llinstancetracker.h" + +template +class LLIntTracker: public LLInstanceTracker +{ + using super = LLInstanceTracker; +public: + LLIntTracker(): + super(getUniqueKey()) + {} + +private: + static int getUniqueKey() + { + // Find a random key that does NOT already correspond to an instance. + // Passing a duplicate key to LLInstanceTracker would do Bad Things. + int key; + do + { + key = std::rand(); + } while (super::getInstance(key)); + // This could be racy, if we were instantiating new LLIntTrackers + // on multiple threads. If we need that, have to lock between checking + // getInstance() and constructing the new super. + return key; + } +}; + +#endif /* ! defined(LL_LLINTTRACKER_H) */ -- cgit v1.2.3 From 23f0aafb74551a741de8c87d62d74e7c6cee8b01 Mon Sep 17 00:00:00 2001 From: Nat Goodspeed Date: Sat, 31 Aug 2024 09:42:52 -0400 Subject: Give certain LLInventory queries an API based on result sets. Introduce abstract base class InvResultSet, derived from LLIntTracker so each instance has a unique int key. InvResultSet supports virtual getLength() and getSlice() operations. getSlice() returns an LLSD array limited to MAX_ITEM_LIMIT result set entries. It permits retrieving a "slice" of the contained result set starting at an arbitrary index. A sequence of getSlice() calls can eventually retrieve a whole result set. InvResultSet has subclasses CatResultSet containing cat_array_t, and ItemResultSet containing item_array_t. Each implements a virtual method that produces an LLSD map from a single array item. Make LLInventoryListener::getItemsInfo(), getDirectDescendants() and collectDescendantsIf() instantiate heap CatResultSet and ItemResultSet objects containing the resultant LLPointer arrays, and return their int keys for categories and items. Add LLInventoryListener::getSlice() and closeResult() methods that accept the int keys of result sets. getSlice() returns the requested LLSD array to its caller, while closeResult() is fire-and-forget. Because bulk data transfer is now performed by getSlice() rather than by collectDescendantsIf(), change the latter's "limit" default to unlimited. Allow the C++ code to collect an arbitrary number of LLPointer array entries, as long as getSlice() limits retrieval overhead. Spell "descendants" correctly, unlike the "descendents" spelling embedded in the rest of the viewer... sigh. Make the Lua module provide both spellings. Make MAX_ITEM_LIMIT a U32 instead of F32. In LLInventory.lua, store int result set keys from 'getItemsInfo', 'getDirectDescendants' and 'collectDescendantsIf' in a table with a close() function. The close() function invokes 'closeResult' with the bound int keys. Give that table an __index() metamethod that recognizes only 'categories' and 'items' keys: anything else returns nil. For either of the recognized keys, call 'getSlice' with the corresponding result set key to retrieve (the initial slice of) the actual result set. Cache that result. Lazy retrieval means that if the caller only cares about categories, or only about items, the other result set need never be retrieved at all. This is a first step: like the previous code, it still retrieves only up to the first 100 result set entries. But the C++ code now supports retrieval of additional slices, so extending result set retrieval is mostly Lua work. Finally, wrap the table-with-metamethod in an LL.setdtor() proxy whose destructor calls its close() method to tell LLInventoryListener to destroy the CatResultSet and ItemResultSet with the bound keys. --- indra/newview/llinventorylistener.cpp | 261 ++++++++++++++++++---- indra/newview/llinventorylistener.h | 9 +- indra/newview/scripts/lua/require/LLInventory.lua | 79 ++++++- 3 files changed, 289 insertions(+), 60 deletions(-) diff --git a/indra/newview/llinventorylistener.cpp b/indra/newview/llinventorylistener.cpp index 157e04dce3..c330ef42a0 100644 --- a/indra/newview/llinventorylistener.cpp +++ b/indra/newview/llinventorylistener.cpp @@ -28,12 +28,13 @@ #include "llinventorylistener.h" #include "llappearancemgr.h" +#include "llinttracker.h" #include "llinventoryfunctions.h" #include "lltransutil.h" #include "llwearableitemslist.h" #include "stringize.h" -static const F32 MAX_ITEM_LIMIT = 100; +constexpr U32 MAX_ITEM_LIMIT = 100; LLInventoryListener::LLInventoryListener() : LLEventAPI("LLInventory", @@ -41,7 +42,7 @@ LLInventoryListener::LLInventoryListener() { add("getItemsInfo", "Return information about items or folders defined in [\"item_ids\"]:\n" - "reply will contain [\"items\"] and [\"categories\"] tables accordingly", + "reply will contain [\"items\"] and [\"categories\"] result set keys", &LLInventoryListener::getItemsInfo, llsd::map("item_ids", LLSD(), "reply", LLSD())); @@ -61,78 +62,190 @@ LLInventoryListener::LLInventoryListener() &LLInventoryListener::getBasicFolderID, llsd::map("ft_name", LLSD(), "reply", LLSD())); - add("getDirectDescendents", - "Return the direct descendents(both items and folders) of the [\"folder_id\"]", - &LLInventoryListener::getDirectDescendents, + add("getDirectDescendants", + "Return result set keys [\"categories\"] and [\"items\"] for the direct\n" + "descendants of the [\"folder_id\"]", + &LLInventoryListener::getDirectDescendants, llsd::map("folder_id", LLSD(), "reply", LLSD())); - add("collectDescendentsIf", - "Return the descendents(both items and folders) of the [\"folder_id\"], if it passes specified filters:\n" + add("collectDescendantsIf", + "Return result set keys [\"categories\"] and [\"items\"] for the descendants\n" + "of the [\"folder_id\"], if it passes specified filters:\n" "[\"name\"] is a substring of object's name,\n" "[\"desc\"] is a substring of object's description,\n" "asset [\"type\"] corresponds to the string name of the object's asset type\n" - "[\"limit\"] sets item count limit in reply, maximum and default is 100\n" + "[\"limit\"] sets item count limit in result set (default unlimited)\n" "[\"filter_links\"]: EXCLUDE_LINKS - don't show links, ONLY_LINKS - only show links, INCLUDE_LINKS - show links too (default)", - &LLInventoryListener::collectDescendentsIf, + &LLInventoryListener::collectDescendantsIf, llsd::map("folder_id", LLSD(), "reply", LLSD())); - } - -void add_item_info(LLEventAPI::Response& response, LLViewerInventoryItem* item) -{ - response["items"].insert(item->getUUID().asString(), - llsd::map("name", item->getName(), - "parent_id", item->getParentUUID(), - "desc", item->getDescription(), - "inv_type", LLInventoryType::lookup(item->getInventoryType()), - "asset_type", LLAssetType::lookup(item->getType()), - "creation_date", (S32) item->getCreationDate(), - "asset_id", item->getAssetUUID(), - "is_link", item->getIsLinkType(), - "linked_id", item->getLinkedUUID())); +/*==========================================================================*| + add("getSingle", + "Return LLSD [\"single\"] for a single folder or item from the specified\n" + "[\"result\"] key at the specified 0-relative [\"index\"].", + &LLInventoryListener::getSingle, + llsd::map("result", LLSD::Integer(), "index", LLSD::Integer(), + "reply", LLSD::String())); +|*==========================================================================*/ + + add("getSlice", + stringize( + "Return an LLSD array [\"slice\"] from the specified [\"result\"] key\n" + "starting at 0-relative [\"index\"] with (up to) [\"count\"] entries.\n" + "count is limited to ", MAX_ITEM_LIMIT, " (default and max)."), + &LLInventoryListener::getSlice, + llsd::map("result", LLSD::Integer(), "index", LLSD::Integer(), + "reply", LLSD::String())); + + add("closeResult", + "Release resources associated with specified [\"result\"] key,\n" + "or keys if [\"result\"] is an array.", + &LLInventoryListener::closeResult, + llsd::map("result", LLSD())); } -void add_cat_info(LLEventAPI::Response &response, LLViewerInventoryCategory *cat) +// This abstract base class defines the interface for CatResultSet and +// ItemResultSet. It isa LLIntTracker so we can pass its unique int key to a +// consuming script via LLSD. +struct InvResultSet: public LLIntTracker { - response["categories"].insert(cat->getUUID().asString(), - llsd::map("name", cat->getName(), - "parent_id", cat->getParentUUID(), - "type", LLFolderType::lookup(cat->getPreferredType()))); -} + // Get the length of the result set. Indexes are 0-relative. + virtual int getLength() const = 0; +/*==========================================================================*| + // Retrieve LLSD corresponding to a single entry from the result set, + // with index validation. + LLSD getSingle(int index) const + { + if (0 <= index && index < getLength()) + { + return getSingle_(index); + } + else + { + return {}; + } + } +|*==========================================================================*/ + // Retrieve LLSD corresponding to a single entry from the result set, + // once we're sure the index is valid. + virtual LLSD getSingle(int index) const = 0; + // Retrieve LLSD corresponding to a "slice" of the result set: a + // contiguous sub-array starting at index. The returned LLSD array might + // be shorter than count entries if count > MAX_ITEM_LIMIT, or if the + // specified slice contains the end of the result set. + LLSD getSlice(int index, int count) const + { + // only call getLength() once + auto length = getLength(); + // Adjust bounds [start, end) to overlap the actual result set from + // [0, getLength()). Permit negative index; e.g. with a result set + // containing 5 entries, getSlice(-2, 5) will adjust start to 0 and + // end to 3. + int start = llclamp(index, 0, length); + // Constrain count to MAX_ITEM_LIMIT even before clamping end. + int end = llclamp(index + llclamp(count, 0, MAX_ITEM_LIMIT), 0, length); + LLSD result{ LLSD::emptyArray() }; + // beware of count == 0, or an [index, count) range that doesn't even + // overlap [0, length) at all + if (end > start) + { + // right away expand the result array to the size we'll need + result[end - 1] = LLSD(); + for (int i = start; i < end; ++i) + { + result[i] = getSingle(i); + } + } + return result; + } + + /*---------------- the rest is solely for debug logging ----------------*/ + std::string mName; + + friend std::ostream& operator<<(std::ostream& out, const InvResultSet& self) + { + return out << "InvResultSet(" << self.mName << ", " << self.getKey() << ")"; + } + + InvResultSet(const std::string& name): + mName(name) + { + LL_DEBUGS("Lua") << *this << LL_ENDL; + } + virtual ~InvResultSet() + { + // We want to be able to observe that the consuming script uses + // LL.setdtor() to eventually destroy each of these InvResultSets. + LL_DEBUGS("Lua") << "~" << *this << LL_ENDL; + } +}; -void add_objects_info(LLEventAPI::Response& response, LLInventoryModel::cat_array_t cat_array, LLInventoryModel::item_array_t item_array) +// This struct captures (possibly large) category results from +// getDirectDescendants() and collectDescendantsIf(). +struct CatResultSet: public InvResultSet { - for (auto &p : item_array) + CatResultSet(): InvResultSet("categories") {} + LLInventoryModel::cat_array_t mCategories; + + int getLength() const override { return narrow(mCategories.size()); } + LLSD getSingle(int index) const override { - add_item_info(response, p); + auto cat = mCategories[index]; + return llsd::map("name", cat->getName(), + "parent_id", cat->getParentUUID(), + "type", LLFolderType::lookup(cat->getPreferredType())); } - for (auto &p : cat_array) +}; + +// This struct captures (possibly large) item results from +// getDirectDescendants() and collectDescendantsIf(). +struct ItemResultSet: public InvResultSet +{ + ItemResultSet(): InvResultSet("items") {} + LLInventoryModel::item_array_t mItems; + + int getLength() const override { return narrow(mItems.size()); } + LLSD getSingle(int index) const override { - add_cat_info(response, p); + auto item = mItems[index]; + return llsd::map("name", item->getName(), + "parent_id", item->getParentUUID(), + "desc", item->getDescription(), + "inv_type", LLInventoryType::lookup(item->getInventoryType()), + "asset_type", LLAssetType::lookup(item->getType()), + "creation_date", LLSD::Integer(item->getCreationDate()), + "asset_id", item->getAssetUUID(), + "is_link", item->getIsLinkType(), + "linked_id", item->getLinkedUUID()); } -} +}; void LLInventoryListener::getItemsInfo(LLSD const &data) { Response response(LLSD(), data); + auto catresult = new CatResultSet; + auto itemresult = new ItemResultSet; + uuid_vec_t ids = LLSDParam(data["item_ids"]); for (auto &it : ids) { LLViewerInventoryItem* item = gInventory.getItem(it); if (item) { - add_item_info(response, item); + itemresult->mItems.push_back(item); } else { LLViewerInventoryCategory *cat = gInventory.getCategory(it); if (cat) { - add_cat_info(response, cat); + catresult->mCategories.push_back(cat); } } } + response["categories"] = catresult->getKey(); + response["items"] = itemresult->getKey(); } void LLInventoryListener::getFolderTypeNames(LLSD const &data) @@ -151,14 +264,21 @@ void LLInventoryListener::getBasicFolderID(LLSD const &data) } -void LLInventoryListener::getDirectDescendents(LLSD const &data) +void LLInventoryListener::getDirectDescendants(LLSD const &data) { Response response(LLSD(), data); LLInventoryModel::cat_array_t* cats; LLInventoryModel::item_array_t* items; gInventory.getDirectDescendentsOf(data["folder_id"], cats, items); - add_objects_info(response, *cats, *items); + auto catresult = new CatResultSet; + auto itemresult = new ItemResultSet; + + catresult->mCategories = *cats; + itemresult->mItems = *items; + + response["categories"] = catresult->getKey(); + response["items"] = itemresult->getKey(); } struct LLFilteredCollector : public LLInventoryCollectFunctor @@ -173,7 +293,11 @@ struct LLFilteredCollector : public LLInventoryCollectFunctor LLFilteredCollector(LLSD const &data); virtual ~LLFilteredCollector() {} virtual bool operator()(LLInventoryCategory *cat, LLInventoryItem *item) override; - virtual bool exceedsLimit() override { return (mItemLimit <= mItemCount); }; + virtual bool exceedsLimit() override + { + // mItemLimit == 0 means unlimited + return (mItemLimit && mItemLimit <= mItemCount); + } protected: bool checkagainstType(LLInventoryCategory *cat, LLInventoryItem *item); @@ -189,7 +313,7 @@ struct LLFilteredCollector : public LLInventoryCollectFunctor S32 mItemCount; }; -void LLInventoryListener::collectDescendentsIf(LLSD const &data) +void LLInventoryListener::collectDescendantsIf(LLSD const &data) { Response response(LLSD(), data); LLUUID folder_id(data["folder_id"].asUUID()); @@ -198,20 +322,63 @@ void LLInventoryListener::collectDescendentsIf(LLSD const &data) { return response.error(stringize("Folder ", std::quoted(data["folder_id"].asString()), " was not found")); } - LLInventoryModel::cat_array_t cat_array; - LLInventoryModel::item_array_t item_array; + auto catresult = new CatResultSet; + auto itemresult = new ItemResultSet; LLFilteredCollector collector = LLFilteredCollector(data); - gInventory.collectDescendentsIf(folder_id, cat_array, item_array, LLInventoryModel::EXCLUDE_TRASH, collector); + // Populate results directly into the catresult and itemresult arrays. + // TODO: sprinkle count-based coroutine yields into the real + // collectDescendentsIf() method so it doesn't steal too many cycles. + gInventory.collectDescendentsIf( + folder_id, + catresult->mCategories, + itemresult->mItems, + LLInventoryModel::EXCLUDE_TRASH, + collector); + + response["categories"] = catresult->getKey(); + response["items"] = itemresult->getKey(); +} + +/*==========================================================================*| +void LLInventoryListener::getSingle(LLSD const& data) +{ + auto result = InvResultSet::getInstance(data["result"]); + sendReply(llsd::map("single", result->getSingle(data["index"])), data); +} +|*==========================================================================*/ - add_objects_info(response, cat_array, item_array); +void LLInventoryListener::getSlice(LLSD const& data) +{ + auto result = InvResultSet::getInstance(data["result"]); + int count = data.has("count")? data["count"].asInteger() : MAX_ITEM_LIMIT; + LL_DEBUGS("Lua") << *result << ".getSlice(" << data["index"].asInteger() + << ", " << count << ')' << LL_ENDL; + sendReply(llsd::map("slice", result->getSlice(data["index"], count)), data); +} + +void LLInventoryListener::closeResult(LLSD const& data) +{ + LLSD results = data["result"]; + if (results.isInteger()) + { + results = llsd::array(results); + } + for (const auto& result : llsd::inArray(results)) + { + auto ptr = InvResultSet::getInstance(result); + if (ptr) + { + delete ptr.get(); + } + } } LLFilteredCollector::LLFilteredCollector(LLSD const &data) : mType(LLAssetType::EType::AT_UNKNOWN), mLinkFilter(INCLUDE_LINKS), - mItemLimit(MAX_ITEM_LIMIT), + mItemLimit(0), mItemCount(0) { @@ -235,7 +402,7 @@ LLFilteredCollector::LLFilteredCollector(LLSD const &data) : } if (data["limit"].isInteger()) { - mItemLimit = llclamp(data["limit"].asInteger(), 1, MAX_ITEM_LIMIT); + mItemLimit = std::max(data["limit"].asInteger(), 1); } } diff --git a/indra/newview/llinventorylistener.h b/indra/newview/llinventorylistener.h index 5cbac2ca32..a05385f2c8 100644 --- a/indra/newview/llinventorylistener.h +++ b/indra/newview/llinventorylistener.h @@ -40,8 +40,13 @@ private: void getFolderTypeNames(LLSD const &data); void getAssetTypeNames(LLSD const &data); void getBasicFolderID(LLSD const &data); - void getDirectDescendents(LLSD const &data); - void collectDescendentsIf(LLSD const &data); + void getDirectDescendants(LLSD const &data); + void collectDescendantsIf(LLSD const &data); +/*==========================================================================*| + void getSingle(LLSD const& data); +|*==========================================================================*/ + void getSlice(LLSD const& data); + void closeResult(LLSD const& data); }; #endif // LL_LLINVENTORYLISTENER_H diff --git a/indra/newview/scripts/lua/require/LLInventory.lua b/indra/newview/scripts/lua/require/LLInventory.lua index dd1b910250..0ff6b9fb37 100644 --- a/indra/newview/scripts/lua/require/LLInventory.lua +++ b/indra/newview/scripts/lua/require/LLInventory.lua @@ -1,12 +1,66 @@ local leap = require 'leap' local mapargs = require 'mapargs' +local function result(keys) + return LL.setdtor( + 'LLInventory result', + setmetatable( + -- the basic table wrapped by setmetatable just captures the int + -- result-set keys from 'keys', but with underscore prefixes + { + _categories=keys.categories, + _items=keys.items, + -- call result:close() to release result sets before garbage + -- collection or script completion + close = function(self) + leap.send('LLInventory', + {op='closeResult', + result={self._categories, self._items}}) + end + }, + -- The caller of one of our methods that returns a result set + -- isn't necessarily interested in both categories and items, so + -- don't proactively populate both. Instead, when caller references + -- either 'categories' or 'items', the __index() metamethod + -- populates that field. + { + __index = function(t, key) + -- we really don't care about references to any other field + if not table.find({'categories', 'items'}, key) then + return nil + end + -- We cleverly saved the int result set key in a field + -- with the same name but an underscore prefix. + local resultkey = t['_' .. key] + -- TODO: This only ever fetches the FIRST slice. What we + -- really want is to return a table with metamethods that + -- manage indexed access and table iteration. + -- Remember our C++ entry point uses 0-relative indexing. + local slice = leap.request( + 'LLInventory', + {op='getSlice', result=resultkey, index=0}).slice + print(`getSlice({resultkey}, 0) => {slice} ({#slice} entries)`) + -- cache this slice for future reference + t[key] = slice + return slice + end + } + ), + -- When the table-with-metatable above is destroyed, tell LLInventory + -- we're done with its result sets -- whether or not we ever fetched + -- either of them. + function(keys) + keys:close() + end + ) +end + local LLInventory = {} -- Get the items/folders info by provided IDs, -- reply will contain "items" and "categories" tables accordingly function LLInventory.getItemsInfo(item_ids) - return leap.request('LLInventory', {op = 'getItemsInfo', item_ids=item_ids}) + return result(leap.request('LLInventory', {op = 'getItemsInfo', item_ids=item_ids})) end -- Get the table of folder type names, which can be later used to get the ID of the basic folders @@ -19,30 +73,33 @@ function LLInventory.getBasicFolderID(ft_name) return leap.request('LLInventory', {op = 'getBasicFolderID', ft_name=ft_name}).id end --- Get the table of asset type names, which can be later used to get the specific items via LLInventory.collectDescendentsIf(...) +-- Get the table of asset type names, which can be later used to get the specific items via LLInventory.collectDescendantsIf(...) function LLInventory.getAssetTypeNames() return leap.request('LLInventory', {op = 'getAssetTypeNames'}).names end --- Get the direct descendents of the 'folder_id' provided, +-- Get the direct descendants of the 'folder_id' provided, -- reply will contain "items" and "categories" tables accordingly -function LLInventory.getDirectDescendents(folder_id) - return leap.request('LLInventory', {op = 'getDirectDescendents', folder_id=folder_id}) +function LLInventory.getDirectDescendants(folder_id) + return result(leap.request('LLInventory', {op = 'getDirectDescendants', folder_id=folder_id})) end +-- backwards compatibility +LLInventory.getDirectDescendents = LLInventory.getDirectDescendants --- Get the descendents of the 'folder_id' provided, which pass specified filters +-- Get the descendants of the 'folder_id' provided, which pass specified filters -- reply will contain "items" and "categories" tables accordingly --- LLInventory.collectDescendentsIf{ folder_id -- parent folder ID +-- LLInventory.collectDescendantsIf{ folder_id -- parent folder ID -- [, name] -- name (substring) -- [, desc] -- description (substring) -- [, type] -- asset type -- [, limit] -- item count limit in reply, maximum and default is 100 -- [, filter_links]} -- EXCLUDE_LINKS - don't show links, ONLY_LINKS - only show links, INCLUDE_LINKS - show links too (default) -function LLInventory.collectDescendentsIf(...) +function LLInventory.collectDescendantsIf(...) local args = mapargs('folder_id,name,desc,type,filter_links,limit', ...) - args.op = 'collectDescendentsIf' - return leap.request('LLInventory', args) + args.op = 'collectDescendantsIf' + return result(leap.request('LLInventory', args)) end - +-- backwards compatibility +LLInventory.collectDescendentsIf = LLInventory.collectDescendantsIf return LLInventory -- cgit v1.2.3 From 15db5010a0330bcb2ca2d0e4125ac3374f22b9cf Mon Sep 17 00:00:00 2001 From: Nat Goodspeed Date: Sat, 31 Aug 2024 12:37:57 -0400 Subject: Make global pairs(), ipairs() honor metamethods. Specifically, make pairs(obj) honor obj's __iter() metamethod if any. Make ipairs(obj) honor obj's __index() metamethod, if any. Given the semantics of the __index() metamethod, though, this only works for a proxy table if the proxy has no array entries (int keys) of its own. --- indra/llcommon/lua_function.cpp | 132 +++++++++++++++++++++++++++++++++++----- 1 file changed, 116 insertions(+), 16 deletions(-) diff --git a/indra/llcommon/lua_function.cpp b/indra/llcommon/lua_function.cpp index 850dff1a33..e4758d25a4 100644 --- a/indra/llcommon/lua_function.cpp +++ b/indra/llcommon/lua_function.cpp @@ -496,8 +496,19 @@ namespace using LuaStateMap = std::unordered_map; static LuaStateMap sLuaStateMap; -// replacement next(), pairs(), ipairs() that understand setdtor() proxy args +// replace table-at-index[name] with passed func, +// binding the original table-at-index[name] as func's upvalue +void replace_entry(lua_State* L, int index, + const std::string& name, lua_CFunction func); + +// replacement next() function that understands setdtor() proxy args int lua_proxydrill(lua_State* L); +// replacement pairs() function that supports __iter() metamethod +int lua_metapairs(lua_State* L); +// replacement ipairs() function that supports __index() metamethod +int lua_metaipairs(lua_State* L); +// helper for lua_metaipairs() (actual generator function) +int lua_metaipair(lua_State* L); } // anonymous namespace @@ -521,25 +532,114 @@ LuaState::LuaState(script_finished_fn cb): // LL.setdtor() proxy objects. // (We could also do this for selected library functions as well, // e.g. the table, string, math libraries... let's see if needed.) - for (const auto& func : { "next", "pairs", "ipairs" }) + replace_entry(mState, LUA_GLOBALSINDEX, "next", lua_proxydrill); + // Replacing pairs() with lua_metapairs() makes global pairs() honor + // objects with __iter() metamethods. + replace_entry(mState, LUA_GLOBALSINDEX, "pairs", lua_metapairs); + // Replacing ipairs() with lua_metaipairs() makes global ipairs() honor + // objects with __index() metamethods -- as long as the object in question + // has no array entries (int keys) of its own. (If it does, object[i] will + // retrieve table[i] instead of calling __index(table, i).) + replace_entry(mState, LUA_GLOBALSINDEX, "ipairs", lua_metaipairs); +} + +namespace +{ + +void replace_entry(lua_State* L, int index, + const std::string& name, lua_CFunction func) +{ + index = lua_absindex(L, index); + lua_checkdelta(L); + // push the function's name string twice + lua_pushlstring(L, name.data(), name.length()); + lua_pushvalue(L, -1); + // stack: name, name + // look up the existing table entry + lua_rawget(L, index); + // stack: name, original function + // bind original function as the upvalue for func() + lua_pushcclosure(L, func, (name + "()").c_str(), 1); + // stack: name, func-with-bound-original + // table[name] = func-with-bound-original + lua_rawset(L, index); +} + +int lua_metapairs(lua_State* L) +{ + // pairs(obj): object is at index 1 + // discard any erroneous surplus parameters + lua_settop(L, 1); + // stack: obj + if (luaL_getmetafield(L, 1, "__iter")) + { + // stack: obj, getmetatable(obj).__iter + lua_insert(L, 1); + // stack: __iter, obj + // We don't use the even nicer shorthand luaL_callmeta() because + // luaL_callmeta() only permits the metamethod to return a single + // value, and __iter() returns up to 3. Use lua_call() instead. + lua_call(L, 1, LUA_MULTRET); + // return as many values as __iter(obj) returned + return lua_gettop(L); + } + // otherwise, just return (next, obj) + lluau_checkstack(L, 1); + // stack: obj + lua_getglobal(L, "next"); + // stack: obj, next + lua_insert(L, 1); + // stack: next, obj + return 2; +} + +int lua_metaipairs(lua_State* L) +{ + lua_checkdelta(L, 2); + // ipairs(obj): object is at index 1 + // discard any erroneous surplus parameters + lua_settop(L, 1); + // stack: obj + lua_pushcfunction(L, lua_metaipair, "lua_metaipair"); + // stack: obj, lua_metaipair + lua_insert(L, 1); + // stack: lua_metaipair, obj + // push explicit 0 so lua_metaipair need not special-case nil + lua_pushinteger(L, 0); + // stack: lua_metaipair, obj, 0 + return 3; +} + +int lua_metaipair(lua_State* L) +{ + // called with (obj, previous-index) + // increment previous-index for this call + lua_Integer i = luaL_checkinteger(L, 2) + 1; + lua_pop(L, 1); + // stack: obj + lua_pushinteger(L, i); + // stack: obj, i + lua_pushvalue(L, -1); + // stack: obj, i, i + lua_insert(L, 1); + // stack: i, obj, i + lua_gettable(L, -2); + // stack: i, obj, obj[i] + lua_remove(L, -2); + // stack: i, obj[i] + if (! lua_isnil(L, -1)) { - // push the function's name string twice - lua_pushstring(mState, func); - lua_pushvalue(mState, -1); - // stack: name, name - // look up the existing global - lua_rawget(mState, LUA_GLOBALSINDEX); - // stack: name, global function - // bind original function as the upvalue for lua_proxydrill() - lua_pushcclosure(mState, lua_proxydrill, - stringize("lua_proxydrill(", func, ')').c_str(), - 1); - // stack: name, lua_proxydrill(func) - // global name = lua_proxydrill(func) - lua_rawset(mState, LUA_GLOBALSINDEX); + // great, obj[i] isn't nil: return (i, obj[i]) + return 2; } + // obj[i] is nil. ipairs() is documented to stop at the first hole, + // regardless of #obj. Clear the stack, i.e. return nil. + lua_settop(L, 0); + return 0; } +} // anonymous namespace + LuaState::~LuaState() { // We're just about to destroy this lua_State mState. Did this Lua chunk -- cgit v1.2.3 From c820bb2929a37fb222ce0d7368d699e81f5edc00 Mon Sep 17 00:00:00 2001 From: Kitty Barnett Date: Sat, 31 Aug 2024 01:33:27 +0200 Subject: Minimal code needed to add RLVa with an on/off toggle --- indra/newview/CMakeLists.txt | 7 ++++ indra/newview/app_settings/settings.xml | 11 ++++++ indra/newview/llappviewer.cpp | 8 ++++ indra/newview/llstartup.cpp | 3 ++ indra/newview/rlvactions.cpp | 15 ++++++++ indra/newview/rlvactions.h | 19 ++++++++++ indra/newview/rlvcommon.cpp | 40 ++++++++++++++++++++ indra/newview/rlvcommon.h | 16 ++++++++ indra/newview/rlvdefines.h | 51 ++++++++++++++++++++++++++ indra/newview/rlvhandler.cpp | 40 ++++++++++++++++++++ indra/newview/rlvhandler.h | 25 +++++++++++++ indra/newview/skins/default/xui/en/strings.xml | 1 + 12 files changed, 236 insertions(+) create mode 100644 indra/newview/rlvactions.cpp create mode 100644 indra/newview/rlvactions.h create mode 100644 indra/newview/rlvcommon.cpp create mode 100644 indra/newview/rlvcommon.h create mode 100644 indra/newview/rlvdefines.h create mode 100644 indra/newview/rlvhandler.cpp create mode 100644 indra/newview/rlvhandler.h diff --git a/indra/newview/CMakeLists.txt b/indra/newview/CMakeLists.txt index 7a9f3a46b5..70a9f09e1f 100644 --- a/indra/newview/CMakeLists.txt +++ b/indra/newview/CMakeLists.txt @@ -726,6 +726,9 @@ set(viewer_SOURCE_FILES llxmlrpctransaction.cpp noise.cpp pipeline.cpp + rlvactions.cpp + rlvcommon.cpp + rlvhandler.cpp ) set(VIEWER_BINARY_NAME "secondlife-bin" CACHE STRING @@ -1386,6 +1389,10 @@ set(viewer_HEADER_FILES llxmlrpctransaction.h noise.h pipeline.h + rlvdefines.h + rlvactions.h + rlvcommon.h + rlvhandler.h roles_constants.h VertexCache.h VorbisFramework.h diff --git a/indra/newview/app_settings/settings.xml b/indra/newview/app_settings/settings.xml index ec06582d90..48e0878383 100644 --- a/indra/newview/app_settings/settings.xml +++ b/indra/newview/app_settings/settings.xml @@ -9843,6 +9843,17 @@ Value https://feedback.secondlife.com/ + RestrainedLove + + Comment + Toggles RLVa features (requires restart) + Persist + 1 + Type + Boolean + Value + 1 + RevokePermsOnStopAnimation Comment diff --git a/indra/newview/llappviewer.cpp b/indra/newview/llappviewer.cpp index afa701c5f2..6a2498c57a 100644 --- a/indra/newview/llappviewer.cpp +++ b/indra/newview/llappviewer.cpp @@ -250,6 +250,10 @@ using namespace LL; #include "llcoproceduremanager.h" #include "llviewereventrecorder.h" +#include "rlvactions.h" +#include "rlvcommon.h" +#include "rlvhandler.h" + // *FIX: These extern globals should be cleaned up. // The globals either represent state/config/resource-storage of either // this app, or another 'component' of the viewer. App globals should be @@ -1710,6 +1714,9 @@ bool LLAppViewer::cleanup() //ditch LLVOAvatarSelf instance gAgentAvatarp = NULL; + // Sanity check to catch cases where someone forgot to do an RlvActions::isRlvEnabled() check + LL_ERRS_IF(!RlvHandler::isEnabled() && RlvHandler::instanceExists()) << "RLV handler instance exists even though RLVa is disabled" << LL_ENDL; + LLNotifications::instance().clear(); // workaround for DEV-35406 crash on shutdown @@ -3333,6 +3340,7 @@ LLSD LLAppViewer::getViewerInfo() const } #endif + info["RLV_VERSION"] = RlvActions::isRlvEnabled() ? RlvStrings::getVersionAbout() : "(disabled)"; info["OPENGL_VERSION"] = ll_safe_string((const char*)(glGetString(GL_VERSION))); // Settings diff --git a/indra/newview/llstartup.cpp b/indra/newview/llstartup.cpp index b32b80331a..3b027df460 100644 --- a/indra/newview/llstartup.cpp +++ b/indra/newview/llstartup.cpp @@ -205,6 +205,7 @@ #include "threadpool.h" #include "llperfstats.h" +#include "rlvhandler.h" #if LL_WINDOWS #include "lldxhardware.h" @@ -876,6 +877,8 @@ bool idle_startup() return false; } + RlvHandler::setEnabled(gSavedSettings.get(Rlv::Settings::Main)); + // reset the values that could have come in from a slurl // DEV-42215: Make sure they're not empty -- gUserCredential // might already have been set from gSavedSettings, and it's too bad diff --git a/indra/newview/rlvactions.cpp b/indra/newview/rlvactions.cpp new file mode 100644 index 0000000000..1988c99ecf --- /dev/null +++ b/indra/newview/rlvactions.cpp @@ -0,0 +1,15 @@ +#include "llviewerprecompiledheaders.h" + +#include "rlvactions.h" +#include "rlvhandler.h" + +// ============================================================================ +// Helper functions +// + +bool RlvActions::isRlvEnabled() +{ + return RlvHandler::isEnabled(); +} + +// ============================================================================ diff --git a/indra/newview/rlvactions.h b/indra/newview/rlvactions.h new file mode 100644 index 0000000000..ac4fc73339 --- /dev/null +++ b/indra/newview/rlvactions.h @@ -0,0 +1,19 @@ +#pragma once + +// ============================================================================ +// RlvActions - developer-friendly non-RLVa code facing class, use in lieu of RlvHandler whenever possible +// + +class RlvActions +{ + // ================ + // Helper functions + // ================ +public: + /* + * Convenience function to check if RLVa is enabled without having to include rlvhandler.h + */ + static bool isRlvEnabled(); +}; + +// ============================================================================ diff --git a/indra/newview/rlvcommon.cpp b/indra/newview/rlvcommon.cpp new file mode 100644 index 0000000000..f641d56a85 --- /dev/null +++ b/indra/newview/rlvcommon.cpp @@ -0,0 +1,40 @@ +#include "llviewerprecompiledheaders.h" +#include "llversioninfo.h" + +#include "rlvdefines.h" +#include "rlvcommon.h" + +using namespace Rlv; + +// ============================================================================ +// RlvStrings +// + +std::string RlvStrings::getVersion(bool wants_legacy) +{ + return llformat("%s viewer v%d.%d.%d (RLVa %d.%d.%d)", + !wants_legacy ? "RestrainedLove" : "RestrainedLife", + SpecVersion::Major, SpecVersion::Minor, SpecVersion::Patch, + ImplVersion::Major, ImplVersion::Minor, ImplVersion::Patch); +} + +std::string RlvStrings::getVersionAbout() +{ + return llformat("RLV v%d.%d.%d / RLVa v%d.%d.%d.%d", + SpecVersion::Major, SpecVersion::Minor, SpecVersion::Patch, + ImplVersion::Major, ImplVersion::Minor, ImplVersion::Patch, LLVersionInfo::instance().getBuild()); +} + +std::string RlvStrings::getVersionNum() +{ + return llformat("%d%02d%02d%02d", + SpecVersion::Major, SpecVersion::Minor, SpecVersion::Patch, SpecVersion::Build); +} + +std::string RlvStrings::getVersionImplNum() +{ + return llformat("%d%02d%02d%02d", + ImplVersion::Major, ImplVersion::Minor, ImplVersion::Patch, ImplVersion::ImplId); +} + +// ============================================================================ diff --git a/indra/newview/rlvcommon.h b/indra/newview/rlvcommon.h new file mode 100644 index 0000000000..288d48e373 --- /dev/null +++ b/indra/newview/rlvcommon.h @@ -0,0 +1,16 @@ +#pragma once + +// ============================================================================ +// RlvStrings +// + +class RlvStrings +{ +public: + static std::string getVersion(bool wants_legacy); + static std::string getVersionAbout(); + static std::string getVersionImplNum(); + static std::string getVersionNum(); +}; + +// ============================================================================ diff --git a/indra/newview/rlvdefines.h b/indra/newview/rlvdefines.h new file mode 100644 index 0000000000..dc1e58f47c --- /dev/null +++ b/indra/newview/rlvdefines.h @@ -0,0 +1,51 @@ +#pragma once + +// ============================================================================ +// Defines +// + +namespace Rlv +{ + // Version of the specification we report + struct SpecVersion { + static constexpr S32 Major = 4; + static constexpr S32 Minor = 0; + static constexpr S32 Patch = 0; + static constexpr S32 Build = 0; + }; + + // RLVa implementation version + struct ImplVersion { + static constexpr S32 Major = 3; + static constexpr S32 Minor = 0; + static constexpr S32 Patch = 0; + static constexpr S32 ImplId = 2; /* Official viewer */ + }; +} + +// Defining these makes it easier if we ever need to change our tag +#define RLV_WARNS LL_WARNS("RLV") +#define RLV_INFOS LL_INFOS("RLV") +#define RLV_DEBUGS LL_DEBUGS("RLV") +#define RLV_ENDL LL_ENDL + +// ============================================================================ +// Settings +// + +namespace Rlv +{ + namespace Settings + { + constexpr char Main[] = "RestrainedLove"; + constexpr char Debug[] = "RestrainedLoveDebug"; + + constexpr char DebugHideUnsetDup[] = "RLVaDebugHideUnsetDuplicate"; + constexpr char EnableIMQuery[] = "RLVaEnableIMQuery"; + constexpr char EnableTempAttach[] = "RLVaEnableTemporaryAttachments"; + constexpr char TopLevelMenu[] = "RLVaTopLevelMenu"; + }; + +}; + +// ============================================================================ diff --git a/indra/newview/rlvhandler.cpp b/indra/newview/rlvhandler.cpp new file mode 100644 index 0000000000..3315ed1999 --- /dev/null +++ b/indra/newview/rlvhandler.cpp @@ -0,0 +1,40 @@ +#include "llviewerprecompiledheaders.h" +#include "llappviewer.h" +#include "llstartup.h" + +#include "rlvdefines.h" +#include "rlvcommon.h" +#include "rlvhandler.h" + +using namespace Rlv; + +// ============================================================================ +// Static variable initialization +// + +bool RlvHandler::mIsEnabled = false; + +// ============================================================================ +// Initialization helper functions +// + +bool RlvHandler::canEnable() +{ + return LLStartUp::getStartupState() <= STATE_LOGIN_CLEANUP; +} + +bool RlvHandler::setEnabled(bool enable) +{ + if (mIsEnabled == enable) + return enable; + + if (enable && canEnable()) + { + RLV_INFOS << "Enabling Restrained Love API support - " << RlvStrings::getVersionAbout() << RLV_ENDL; + mIsEnabled = true; + } + + return mIsEnabled; +} + +// ============================================================================ diff --git a/indra/newview/rlvhandler.h b/indra/newview/rlvhandler.h new file mode 100644 index 0000000000..0d4cbe98fb --- /dev/null +++ b/indra/newview/rlvhandler.h @@ -0,0 +1,25 @@ +#pragma once + +#include "llsingleton.h" + +#include "rlvdefines.h" + +// ============================================================================ +// RlvHandler class +// + +class RlvHandler : public LLSingleton +{ + LLSINGLETON_EMPTY_CTOR(RlvHandler); + +public: + // Initialization (deliberately static so they can safely be called in tight loops) + static bool canEnable(); + static bool isEnabled() { return mIsEnabled; } + static bool setEnabled(bool enable); + +private: + static bool mIsEnabled; +}; + +// ============================================================================ diff --git a/indra/newview/skins/default/xui/en/strings.xml b/indra/newview/skins/default/xui/en/strings.xml index 2d04b3e0fe..a270bc473d 100644 --- a/indra/newview/skins/default/xui/en/strings.xml +++ b/indra/newview/skins/default/xui/en/strings.xml @@ -60,6 +60,7 @@ Disk cache: [DISK_CACHE_INFO] HiDPI display mode: [HIDPI] +RestrainedLove API: [RLV_VERSION] J2C Decoder Version: [J2C_VERSION] Audio Driver Version: [AUDIO_DRIVER_VERSION] [LIBCEF_VERSION] -- cgit v1.2.3 From b82e9b73d35e41ed51063905dd31ccced9e97266 Mon Sep 17 00:00:00 2001 From: Kitty Barnett Date: Sun, 1 Sep 2024 01:21:00 +0200 Subject: Add owner say chat hook --- indra/newview/CMakeLists.txt | 2 + indra/newview/app_settings/settings.xml | 44 ++++++++ indra/newview/llviewermessage.cpp | 28 ++++- indra/newview/rlvdefines.h | 62 +++++++++++ indra/newview/rlvhandler.cpp | 44 +++++++- indra/newview/rlvhandler.h | 11 ++ indra/newview/rlvhelper.cpp | 136 +++++++++++++++++++++++++ indra/newview/rlvhelper.h | 24 +++++ indra/newview/skins/default/xui/en/strings.xml | 22 ++++ 9 files changed, 368 insertions(+), 5 deletions(-) create mode 100644 indra/newview/rlvhelper.cpp create mode 100644 indra/newview/rlvhelper.h diff --git a/indra/newview/CMakeLists.txt b/indra/newview/CMakeLists.txt index 70a9f09e1f..a7bbadfd86 100644 --- a/indra/newview/CMakeLists.txt +++ b/indra/newview/CMakeLists.txt @@ -728,6 +728,7 @@ set(viewer_SOURCE_FILES pipeline.cpp rlvactions.cpp rlvcommon.cpp + rlvhelper.cpp rlvhandler.cpp ) @@ -1392,6 +1393,7 @@ set(viewer_HEADER_FILES rlvdefines.h rlvactions.h rlvcommon.h + rlvhelper.h rlvhandler.h roles_constants.h VertexCache.h diff --git a/indra/newview/app_settings/settings.xml b/indra/newview/app_settings/settings.xml index 48e0878383..be88ad6e2f 100644 --- a/indra/newview/app_settings/settings.xml +++ b/indra/newview/app_settings/settings.xml @@ -9852,6 +9852,50 @@ Type Boolean Value + 0 + + RestrainedLoveDebug + + Comment + Toggles RLVa debug mode (displays the commands when in debug mode) + Persist + 1 + Type + Boolean + Value + 0 + + RLVaBlockedExperiences + + Comment + List of experiences blocked from interacting with RLVa + Persist + 1 + Type + String + Value + bfe25fb4-222c-11e5-85a2-fa4c4ccaa202 + + RLVaDebugHideUnsetDuplicate + + Comment + Suppresses reporting "unset" or "duplicate" command restrictions when RestrainedLoveDebug is TRUE + Persist + 1 + Type + Boolean + Value + 0 + + RLVaEnableTemporaryAttachments + + Comment + Allows temporary attachments (regardless of origin) to issue RLV commands + Persist + 1 + Type + Boolean + Value 1 RevokePermsOnStopAnimation diff --git a/indra/newview/llviewermessage.cpp b/indra/newview/llviewermessage.cpp index 872a9a1581..4ed16c19a6 100644 --- a/indra/newview/llviewermessage.cpp +++ b/indra/newview/llviewermessage.cpp @@ -118,6 +118,8 @@ #include "llpanelplaceprofile.h" #include "llviewerregion.h" #include "llfloaterregionrestarting.h" +#include "rlvactions.h" +#include "rlvhandler.h" #include "llnotificationmanager.h" // #include "llexperiencecache.h" @@ -2382,15 +2384,16 @@ void process_chat_from_simulator(LLMessageSystem *msg, void **user_data) } bool is_audible = (CHAT_AUDIBLE_FULLY == chat.mAudible); + bool show_script_chat_particles = chat.mSourceType == CHAT_SOURCE_OBJECT + && chat.mChatType != CHAT_TYPE_DEBUG_MSG + && gSavedSettings.getBOOL("EffectScriptChatParticles"); chatter = gObjectList.findObject(from_id); if (chatter) { chat.mPosAgent = chatter->getPositionAgent(); // Make swirly things only for talking objects. (not script debug messages, though) - if (chat.mSourceType == CHAT_SOURCE_OBJECT - && chat.mChatType != CHAT_TYPE_DEBUG_MSG - && gSavedSettings.getBOOL("EffectScriptChatParticles") ) + if (show_script_chat_particles && (!RlvActions::isRlvEnabled() || CHAT_TYPE_OWNER != chat.mChatType) ) { LLPointer psc = new LLViewerPartSourceChat(chatter->getPositionAgent()); psc->setSourceObject(chatter); @@ -2470,8 +2473,25 @@ void process_chat_from_simulator(LLMessageSystem *msg, void **user_data) case CHAT_TYPE_WHISPER: chat.mText = LLTrans::getString("whisper") + " "; break; - case CHAT_TYPE_DEBUG_MSG: case CHAT_TYPE_OWNER: + if (RlvActions::isRlvEnabled()) + { + if (RlvHandler::instance().handleSimulatorChat(mesg, chat, chatter)) + { + break; + } + else if (show_script_chat_particles) + { + LLPointer psc = new LLViewerPartSourceChat(chatter->getPositionAgent()); + psc->setSourceObject(chatter); + psc->setColor(color); + //We set the particles to be owned by the object's owner, + //just in case they should be muted by the mute list + psc->setOwnerUUID(owner_id); + LLViewerPartSim::getInstance()->addPartSource(psc); + } + } + case CHAT_TYPE_DEBUG_MSG: case CHAT_TYPE_NORMAL: case CHAT_TYPE_DIRECT: break; diff --git a/indra/newview/rlvdefines.h b/indra/newview/rlvdefines.h index dc1e58f47c..6ba2afbc69 100644 --- a/indra/newview/rlvdefines.h +++ b/indra/newview/rlvdefines.h @@ -29,6 +29,68 @@ namespace Rlv #define RLV_DEBUGS LL_DEBUGS("RLV") #define RLV_ENDL LL_ENDL +#define RLV_RELEASE +#if LL_RELEASE_WITH_DEBUG_INFO || LL_DEBUG + // Make sure we halt execution on errors + #define RLV_ERRS LL_ERRS("RLV") + // Keep our asserts separate from LL's + #define RLV_ASSERT(f) if (!(f)) { RLV_ERRS << "ASSERT (" << #f << ")" << RLV_ENDL; } + #define RLV_ASSERT_DBG(f) RLV_ASSERT(f) +#else + // We don't want to check assertions in release builds + #ifndef RLV_RELEASE + #define RLV_ASSERT(f) RLV_VERIFY(f) + #define RLV_ASSERT_DBG(f) + #else + #define RLV_ASSERT(f) + #define RLV_ASSERT_DBG(f) + #endif // RLV_RELEASE +#endif // LL_RELEASE_WITH_DEBUG_INFO || LL_DEBUG + +namespace Rlv +{ + namespace Constants + { + constexpr char CmdPrefix = '@'; + } +} + +// ============================================================================ +// Enumeration declarations +// + +namespace Rlv +{ + enum class ECmdRet{ + Unknown = 0x0000, // Unknown error (should only be used internally) + Retained, // Command was retained + Success = 0x0100, // Command executed successfully + SuccessUnset, // Command executed successfully (RLV_TYPE_REMOVE for an unrestricted behaviour) + SuccessDuplicate, // Command executed successfully (RLV_TYPE_ADD for an already restricted behaviour) + SuccessDeprecated, // Command executed successfully but has been marked as deprecated + SuccessDelayed, // Command parsed valid but will execute at a later time + Failed = 0x0200, // Command failed (general failure) + FailedSyntax, // Command failed (syntax error) + FailedOption, // Command failed (invalid option) + FailedParam, // Command failed (invalid param) + FailedLock, // Command failed (command is locked by another object) + FailedDisabled, // Command failed (command disabled by user) + FailedUnknown, // Command failed (unknown command) + FailedNoSharedRoot, // Command failed (missing #RLV) + FailedDeprecated, // Command failed (deprecated and no longer supported) + FailedNoBehaviour, // Command failed (force modifier on an object with no active restrictions) + FailedUnheldBehaviour, // Command failed (local modifier on an object that doesn't hold the base behaviour) + FailedBlocked, // Command failed (object is blocked) + FailedThrottled, // Command failed (throttled) + FailedNoProcessor // Command doesn't have a template processor define (legacy code) + }; + + constexpr bool isReturnCodeSuccess(ECmdRet eRet) + { + return (static_cast(eRet) & static_cast(ECmdRet::Success)) == static_cast(ECmdRet::Success); + } +} + // ============================================================================ // Settings // diff --git a/indra/newview/rlvhandler.cpp b/indra/newview/rlvhandler.cpp index 3315ed1999..bf78a0a38a 100644 --- a/indra/newview/rlvhandler.cpp +++ b/indra/newview/rlvhandler.cpp @@ -1,10 +1,12 @@ #include "llviewerprecompiledheaders.h" #include "llappviewer.h" #include "llstartup.h" +#include "llviewercontrol.h" +#include "llviewerobject.h" -#include "rlvdefines.h" #include "rlvcommon.h" #include "rlvhandler.h" +#include "rlvhelper.h" using namespace Rlv; @@ -14,6 +16,46 @@ using namespace Rlv; bool RlvHandler::mIsEnabled = false; +// ============================================================================ +// Command processing functions +// + +bool RlvHandler::handleSimulatorChat(std::string& message, const LLChat& chat, const LLViewerObject* chatObj) +{ + static LLCachedControl enable_temp_attach(gSavedSettings, Settings::EnableTempAttach); + static LLCachedControl show_debug_output(gSavedSettings, Settings::Debug); + static LLCachedControl hide_unset_dupes(gSavedSettings, Settings::DebugHideUnsetDup); + + if ( message.length() <= 3 || Rlv::Constants::CmdPrefix != message[0] || CHAT_TYPE_OWNER != chat.mChatType || + (chatObj && chatObj->isTempAttachment() && !enable_temp_attach()) ) + { + return false; + } + + message.erase(0, 1); + LLStringUtil::toLower(message); + CommandDbgOut cmdDbgOut(message); + + boost_tokenizer tokens(message, boost::char_separator(",", "", boost::drop_empty_tokens)); + for (const std::string& strCmd : tokens) + { + ECmdRet eRet = (ECmdRet)processCommand(chat.mFromID, strCmd, true); + if ( show_debug_output() && + (!hide_unset_dupes() || (ECmdRet::SuccessUnset != eRet && ECmdRet::SuccessDuplicate != eRet)) ) + { + cmdDbgOut.add(strCmd, eRet); + } + } + + message = cmdDbgOut.get(); + return true; +} + +ECmdRet RlvHandler::processCommand(const LLUUID& idObj, const std::string& strCmd, bool fromObj) +{ + return ECmdRet::FailedNoProcessor; +} + // ============================================================================ // Initialization helper functions // diff --git a/indra/newview/rlvhandler.h b/indra/newview/rlvhandler.h index 0d4cbe98fb..8cf054e98e 100644 --- a/indra/newview/rlvhandler.h +++ b/indra/newview/rlvhandler.h @@ -1,9 +1,12 @@ #pragma once +#include "llchat.h" #include "llsingleton.h" #include "rlvdefines.h" +class LLViewerObject; + // ============================================================================ // RlvHandler class // @@ -13,6 +16,14 @@ class RlvHandler : public LLSingleton LLSINGLETON_EMPTY_CTOR(RlvHandler); public: + /* + * Helper functions + */ +public: + // Command processing helper functions + bool handleSimulatorChat(std::string& message, const LLChat& chat, const LLViewerObject* chatObj); + Rlv::ECmdRet processCommand(const LLUUID& idObj, const std::string& stCmd, bool fromObj); + // Initialization (deliberately static so they can safely be called in tight loops) static bool canEnable(); static bool isEnabled() { return mIsEnabled; } diff --git a/indra/newview/rlvhelper.cpp b/indra/newview/rlvhelper.cpp new file mode 100644 index 0000000000..ade2b83dd7 --- /dev/null +++ b/indra/newview/rlvhelper.cpp @@ -0,0 +1,136 @@ +#include "llviewerprecompiledheaders.h" + +#include "lltrans.h" + +#include "rlvhelper.h" + +using namespace Rlv; + +// ========================================================================= +// Various helper classes/timers/functors +// + +namespace Rlv +{ + // =========================================================================== + // CommandDbgOut + // + + void CommandDbgOut::add(std::string strCmd, ECmdRet eRet) + { + ECmdRet resultBucket; + + // Successful and retained commands are added as-is + if (isReturnCodeSuccess(eRet)) + resultBucket = ECmdRet::Success; + else if (ECmdRet::Retained == eRet) + resultBucket = ECmdRet::Retained; + else + { + // Failed commands get the failure reason appended to help troubleshooting + resultBucket = ECmdRet::Failed; + strCmd.append(llformat(" (%s)", getReturnCodeString(eRet).c_str())); + } + + std::string& strResult = mCommandResults[resultBucket]; + if (!strResult.empty()) + strResult.append(", "); + strResult.append(strCmd); + } + + std::string CommandDbgOut::get() const { + std::ostringstream result; + + if (1 == mCommandResults.size()) + { + auto it = mCommandResults.begin(); + result << " " << getDebugVerbFromReturnCode(it->first) << ": @" << it->second;; + } + else if (mCommandResults.size() > 1) + { + auto appendResult = [&](ECmdRet eRet, const std::string& name) + { + auto it = mCommandResults.find(eRet); + if (it == mCommandResults.end()) return; + result << "\n - " << LLTrans::getString(name) << ": @" << it->second; + }; + result << ": @" << mOrigCmd; + appendResult(ECmdRet::Success, "RlvDebugExecuted"); + appendResult(ECmdRet::Failed, "RlvDebugFailed"); + appendResult(ECmdRet::Retained, "RlvDebugRetained"); + } + + return result.str(); + } + + std::string CommandDbgOut::getDebugVerbFromReturnCode(ECmdRet eRet) + { + switch (eRet) + { + case ECmdRet::Success: + return LLTrans::getString("RlvDebugExecuted"); + case ECmdRet::Failed: + return LLTrans::getString("RlvDebugFailed"); + case ECmdRet::Retained: + return LLTrans::getString("RlvDebugRetained"); + default: + RLV_ASSERT(false); + return LLStringUtil::null; + } + } + + std::string CommandDbgOut::getReturnCodeString(ECmdRet eRet) + { + switch (eRet) + { + case ECmdRet::SuccessUnset: + return LLTrans::getString("RlvReturnCodeUnset"); + case ECmdRet::SuccessDuplicate: + return LLTrans::getString("RlvReturnCodeDuplicate"); + case ECmdRet::SuccessDelayed: + return LLTrans::getString("RlvReturnCodeDelayed"); + case ECmdRet::SuccessDeprecated: + return LLTrans::getString("RlvReturnCodeDeprecated"); + case ECmdRet::FailedSyntax: + return LLTrans::getString("RlvReturnCodeSyntax"); + case ECmdRet::FailedOption: + return LLTrans::getString("RlvReturnCodeOption"); + case ECmdRet::FailedParam: + return LLTrans::getString("RlvReturnCodeParam"); + case ECmdRet::FailedLock: + return LLTrans::getString("RlvReturnCodeLock"); + case ECmdRet::FailedDisabled: + return LLTrans::getString("RlvReturnCodeDisabled"); + case ECmdRet::FailedUnknown: + return LLTrans::getString("RlvReturnCodeUnknown"); + case ECmdRet::FailedNoSharedRoot: + return LLTrans::getString("RlvReturnCodeNoSharedRoot"); + case ECmdRet::FailedDeprecated: + return LLTrans::getString("RlvReturnCodeDeprecatedAndDisabled"); + case ECmdRet::FailedNoBehaviour: + return LLTrans::getString("RlvReturnCodeNoBehaviour"); + case ECmdRet::FailedUnheldBehaviour: + return LLTrans::getString("RlvReturnCodeUnheldBehaviour"); + case ECmdRet::FailedBlocked: + return LLTrans::getString("RlvReturnCodeBlocked"); + case ECmdRet::FailedThrottled: + return LLTrans::getString("RlvReturnCodeThrottled"); + case ECmdRet::FailedNoProcessor: + return LLTrans::getString("RlvReturnCodeNoProcessor"); + // The following are identified by the chat verb + case ECmdRet::Retained: + case ECmdRet::Success: + case ECmdRet::Failed: + return LLStringUtil::null; + // The following shouldn't occur + case ECmdRet::Unknown: + default: + RLV_ASSERT(false); + return LLStringUtil::null; + } + } + + // =========================================================================== +} + +// ============================================================================ diff --git a/indra/newview/rlvhelper.h b/indra/newview/rlvhelper.h new file mode 100644 index 0000000000..89bdc709d5 --- /dev/null +++ b/indra/newview/rlvhelper.h @@ -0,0 +1,24 @@ +#pragma once + +#include "rlvdefines.h" + +// ============================================================================ +// Various helper classes/timers/functors +// + +namespace Rlv +{ + struct CommandDbgOut + { + CommandDbgOut(const std::string& orig_cmd) : mOrigCmd(orig_cmd) {} + void add(std::string strCmd, ECmdRet eRet); + std::string get() const; + static std::string getDebugVerbFromReturnCode(ECmdRet eRet); + static std::string getReturnCodeString(ECmdRet eRet); + private: + std::string mOrigCmd; + std::map mCommandResults; + }; +} + +// ============================================================================ diff --git a/indra/newview/skins/default/xui/en/strings.xml b/indra/newview/skins/default/xui/en/strings.xml index a270bc473d..39a663298a 100644 --- a/indra/newview/skins/default/xui/en/strings.xml +++ b/indra/newview/skins/default/xui/en/strings.xml @@ -4376,4 +4376,26 @@ and report the problem. [https://community.secondlife.com/knowledgebase/english/error-messages-r520/#Section__3 Knowledge Base] + + executed + failed + retained + unset + duplicate + delayed + deprecated + thingy error + invalid option + invalid param + locked command + disabled command + unknown command + missing #RLV + deprecated and disabled + no active behaviours + base behaviour not held + blocked object + throttled + no command processor found + -- cgit v1.2.3 From 7402fe6412e98e4b295ee3e04874f379c752f7a0 Mon Sep 17 00:00:00 2001 From: Kitty Barnett Date: Mon, 2 Sep 2024 01:39:17 +0200 Subject: Add basic scaffolding to support reply commands and handle @versionXXX as an illustration --- indra/newview/app_settings/settings.xml | 11 ++ indra/newview/llappviewer.cpp | 2 +- indra/newview/rlvcommon.cpp | 37 ++++- indra/newview/rlvcommon.h | 49 ++++-- indra/newview/rlvdefines.h | 142 +++++++++++------ indra/newview/rlvhandler.cpp | 93 ++++++++++- indra/newview/rlvhandler.h | 8 +- indra/newview/rlvhelper.cpp | 231 ++++++++++++++++++++++++++++ indra/newview/rlvhelper.h | 265 ++++++++++++++++++++++++++++++-- 9 files changed, 757 insertions(+), 81 deletions(-) diff --git a/indra/newview/app_settings/settings.xml b/indra/newview/app_settings/settings.xml index be88ad6e2f..ab577200f5 100644 --- a/indra/newview/app_settings/settings.xml +++ b/indra/newview/app_settings/settings.xml @@ -9897,6 +9897,17 @@ Boolean Value 1 + + RLVaExperimentalCommands + + Comment + Enables the experimental command set + Persist + 1 + Type + Boolean + Value + 1 RevokePermsOnStopAnimation diff --git a/indra/newview/llappviewer.cpp b/indra/newview/llappviewer.cpp index 6a2498c57a..6a2f24a103 100644 --- a/indra/newview/llappviewer.cpp +++ b/indra/newview/llappviewer.cpp @@ -3340,7 +3340,7 @@ LLSD LLAppViewer::getViewerInfo() const } #endif - info["RLV_VERSION"] = RlvActions::isRlvEnabled() ? RlvStrings::getVersionAbout() : "(disabled)"; + info["RLV_VERSION"] = RlvActions::isRlvEnabled() ? Rlv::Strings::getVersionAbout() : "(disabled)"; info["OPENGL_VERSION"] = ll_safe_string((const char*)(glGetString(GL_VERSION))); // Settings diff --git a/indra/newview/rlvcommon.cpp b/indra/newview/rlvcommon.cpp index f641d56a85..898df3af42 100644 --- a/indra/newview/rlvcommon.cpp +++ b/indra/newview/rlvcommon.cpp @@ -1,5 +1,10 @@ #include "llviewerprecompiledheaders.h" +#include "llagent.h" +#include "llchat.h" +#include "lldbstrings.h" #include "llversioninfo.h" +#include "llviewerstats.h" +#include "message.h" #include "rlvdefines.h" #include "rlvcommon.h" @@ -10,7 +15,7 @@ using namespace Rlv; // RlvStrings // -std::string RlvStrings::getVersion(bool wants_legacy) +std::string Strings::getVersion(bool wants_legacy) { return llformat("%s viewer v%d.%d.%d (RLVa %d.%d.%d)", !wants_legacy ? "RestrainedLove" : "RestrainedLife", @@ -18,23 +23,47 @@ std::string RlvStrings::getVersion(bool wants_legacy) ImplVersion::Major, ImplVersion::Minor, ImplVersion::Patch); } -std::string RlvStrings::getVersionAbout() +std::string Strings::getVersionAbout() { return llformat("RLV v%d.%d.%d / RLVa v%d.%d.%d.%d", SpecVersion::Major, SpecVersion::Minor, SpecVersion::Patch, ImplVersion::Major, ImplVersion::Minor, ImplVersion::Patch, LLVersionInfo::instance().getBuild()); } -std::string RlvStrings::getVersionNum() +std::string Strings::getVersionNum() { return llformat("%d%02d%02d%02d", SpecVersion::Major, SpecVersion::Minor, SpecVersion::Patch, SpecVersion::Build); } -std::string RlvStrings::getVersionImplNum() +std::string Strings::getVersionImplNum() { return llformat("%d%02d%02d%02d", ImplVersion::Major, ImplVersion::Minor, ImplVersion::Patch, ImplVersion::ImplId); } // ============================================================================ +// RlvUtil +// + +bool Util::sendChatReply(S32 nChannel, const std::string& strUTF8Text) +{ + if (!isValidReplyChannel(nChannel)) + return false; + + // Copy/paste from send_chat_from_viewer() + gMessageSystem->newMessageFast(_PREHASH_ChatFromViewer); + gMessageSystem->nextBlockFast(_PREHASH_AgentData); + gMessageSystem->addUUIDFast(_PREHASH_AgentID, gAgent.getID()); + gMessageSystem->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID()); + gMessageSystem->nextBlockFast(_PREHASH_ChatData); + gMessageSystem->addStringFast(_PREHASH_Message, utf8str_truncate(strUTF8Text, MAX_MSG_STR_LEN)); + gMessageSystem->addU8Fast(_PREHASH_Type, CHAT_TYPE_SHOUT); + gMessageSystem->addS32("Channel", nChannel); + gAgent.sendReliableMessage(); + add(LLStatViewer::CHAT_COUNT, 1); + + return true; +} + +// ============================================================================ diff --git a/indra/newview/rlvcommon.h b/indra/newview/rlvcommon.h index 288d48e373..79ac6e1704 100644 --- a/indra/newview/rlvcommon.h +++ b/indra/newview/rlvcommon.h @@ -1,16 +1,41 @@ #pragma once -// ============================================================================ -// RlvStrings -// - -class RlvStrings +namespace Rlv { -public: - static std::string getVersion(bool wants_legacy); - static std::string getVersionAbout(); - static std::string getVersionImplNum(); - static std::string getVersionNum(); -}; + // ============================================================================ + // RlvStrings + // + + class Strings + { + public: + static std::string getVersion(bool wants_legacy); + static std::string getVersionAbout(); + static std::string getVersionImplNum(); + static std::string getVersionNum(); + }; + + // ============================================================================ + // RlvUtil + // + + namespace Util + { + bool isValidReplyChannel(S32 nChannel, bool isLoopback = false); + bool sendChatReply(S32 nChannel, const std::string& strUTF8Text); + bool sendChatReply(const std::string& strChannel, const std::string& strUTF8Text); + }; + + inline bool Util::isValidReplyChannel(S32 nChannel, bool isLoopback) + { + return (nChannel > (!isLoopback ? 0 : -1)) && (CHAT_CHANNEL_DEBUG != nChannel); + } + + inline bool Util::sendChatReply(const std::string& strChannel, const std::string& strUTF8Text) + { + S32 nChannel; + return LLStringUtil::convertToS32(strChannel, nChannel) ? sendChatReply(nChannel, strUTF8Text) : false; + } -// ============================================================================ + // ============================================================================ +} diff --git a/indra/newview/rlvdefines.h b/indra/newview/rlvdefines.h index 6ba2afbc69..0459b59483 100644 --- a/indra/newview/rlvdefines.h +++ b/indra/newview/rlvdefines.h @@ -4,32 +4,14 @@ // Defines // -namespace Rlv -{ - // Version of the specification we report - struct SpecVersion { - static constexpr S32 Major = 4; - static constexpr S32 Minor = 0; - static constexpr S32 Patch = 0; - static constexpr S32 Build = 0; - }; - - // RLVa implementation version - struct ImplVersion { - static constexpr S32 Major = 3; - static constexpr S32 Minor = 0; - static constexpr S32 Patch = 0; - static constexpr S32 ImplId = 2; /* Official viewer */ - }; -} - // Defining these makes it easier if we ever need to change our tag #define RLV_WARNS LL_WARNS("RLV") #define RLV_INFOS LL_INFOS("RLV") #define RLV_DEBUGS LL_DEBUGS("RLV") #define RLV_ENDL LL_ENDL +#define RLV_VERIFY(f) (f) -#define RLV_RELEASE +#define RLV_DEBUG #if LL_RELEASE_WITH_DEBUG_INFO || LL_DEBUG // Make sure we halt execution on errors #define RLV_ERRS LL_ERRS("RLV") @@ -37,18 +19,36 @@ namespace Rlv #define RLV_ASSERT(f) if (!(f)) { RLV_ERRS << "ASSERT (" << #f << ")" << RLV_ENDL; } #define RLV_ASSERT_DBG(f) RLV_ASSERT(f) #else + // Don't halt execution on errors in release + #define RLV_ERRS LL_WARNS("RLV") // We don't want to check assertions in release builds - #ifndef RLV_RELEASE + #ifdef RLV_DEBUG #define RLV_ASSERT(f) RLV_VERIFY(f) #define RLV_ASSERT_DBG(f) #else #define RLV_ASSERT(f) #define RLV_ASSERT_DBG(f) - #endif // RLV_RELEASE + #endif // RLV_DEBUG #endif // LL_RELEASE_WITH_DEBUG_INFO || LL_DEBUG namespace Rlv { + // Version of the specification we report + namespace SpecVersion { + constexpr S32 Major = 4; + constexpr S32 Minor = 0; + constexpr S32 Patch = 0; + constexpr S32 Build = 0; + }; + + // RLVa implementation version + namespace ImplVersion { + constexpr S32 Major = 3; + constexpr S32 Minor = 0; + constexpr S32 Patch = 0; + constexpr S32 ImplId = 13; + }; + namespace Constants { constexpr char CmdPrefix = '@'; @@ -61,33 +61,84 @@ namespace Rlv namespace Rlv { - enum class ECmdRet{ - Unknown = 0x0000, // Unknown error (should only be used internally) - Retained, // Command was retained - Success = 0x0100, // Command executed successfully - SuccessUnset, // Command executed successfully (RLV_TYPE_REMOVE for an unrestricted behaviour) - SuccessDuplicate, // Command executed successfully (RLV_TYPE_ADD for an already restricted behaviour) - SuccessDeprecated, // Command executed successfully but has been marked as deprecated - SuccessDelayed, // Command parsed valid but will execute at a later time - Failed = 0x0200, // Command failed (general failure) - FailedSyntax, // Command failed (syntax error) - FailedOption, // Command failed (invalid option) - FailedParam, // Command failed (invalid param) - FailedLock, // Command failed (command is locked by another object) - FailedDisabled, // Command failed (command disabled by user) - FailedUnknown, // Command failed (unknown command) - FailedNoSharedRoot, // Command failed (missing #RLV) - FailedDeprecated, // Command failed (deprecated and no longer supported) - FailedNoBehaviour, // Command failed (force modifier on an object with no active restrictions) - FailedUnheldBehaviour, // Command failed (local modifier on an object that doesn't hold the base behaviour) - FailedBlocked, // Command failed (object is blocked) - FailedThrottled, // Command failed (throttled) - FailedNoProcessor // Command doesn't have a template processor define (legacy code) + enum class EBehaviour { + Version = 0, + VersionNew, + VersionNum, + + Count, + Unknown, }; + enum class EBehaviourOptionType + { + None, // Behaviour takes no parameters + Exception, // Behaviour requires an exception as a parameter + NoneOrException, // Behaviour takes either no parameters or an exception + }; + + enum class EParamType { + Unknown = 0x00, + Add = 0x01, // == "n"|"add" + Remove = 0x02, // == "y"|"rem" + Force = 0x04, // == "force" + Reply = 0x08, // == + Clear = 0x10, + AddRem = Add | Remove + }; + + enum class ECmdRet { + Unknown = 0x0000, // Unknown error (should only be used internally) + Retained, // Command was retained + Success = 0x0100, // Command executed successfully + SuccessUnset, // Command executed successfully (RLV_TYPE_REMOVE for an unrestricted behaviour) + SuccessDuplicate, // Command executed successfully (RLV_TYPE_ADD for an already restricted behaviour) + SuccessDeprecated, // Command executed successfully but has been marked as deprecated + SuccessDelayed, // Command parsed valid but will execute at a later time + Failed = 0x0200, // Command failed (general failure) + FailedSyntax, // Command failed (syntax error) + FailedOption, // Command failed (invalid option) + FailedParam, // Command failed (invalid param) + FailedLock, // Command failed (command is locked by another object) + FailedDisabled, // Command failed (command disabled by user) + FailedUnknown, // Command failed (unknown command) + FailedNoSharedRoot, // Command failed (missing #RLV) + FailedDeprecated, // Command failed (deprecated and no longer supported) + FailedNoBehaviour, // Command failed (force modifier on an object with no active restrictions) + FailedUnheldBehaviour, // Command failed (local modifier on an object that doesn't hold the base behaviour) + FailedBlocked, // Command failed (object is blocked) + FailedThrottled, // Command failed (throttled) + FailedNoProcessor // Command doesn't have a template processor define (legacy code) + }; + + enum class EExceptionCheck + { + Permissive, // Exception can be set by any object + Strict, // Exception must be set by all objects holding the restriction + Default, // Permissive or strict will be determined by currently enforced restrictions + }; + + // Replace&remove in c++23 + template + constexpr std::enable_if_t && !std::is_convertible_v, std::underlying_type_t> to_underlying(E e) noexcept + { + return static_cast>(e); + } + + template + constexpr std::enable_if_t && !std::is_convertible_v, bool> has_flag(E value, E flag) noexcept + { + return (to_underlying(value) & to_underlying(flag)) != 0; + } + constexpr bool isReturnCodeSuccess(ECmdRet eRet) { - return (static_cast(eRet) & static_cast(ECmdRet::Success)) == static_cast(ECmdRet::Success); + return (to_underlying(eRet) & to_underlying(ECmdRet::Success)) == to_underlying(ECmdRet::Success); + } + + constexpr bool isReturnCodeFailed(ECmdRet eRet) + { + return (to_underlying(eRet) & to_underlying(ECmdRet::Failed)) == to_underlying(ECmdRet::Failed); } } @@ -103,6 +154,7 @@ namespace Rlv constexpr char Debug[] = "RestrainedLoveDebug"; constexpr char DebugHideUnsetDup[] = "RLVaDebugHideUnsetDuplicate"; + constexpr char EnableExperimentalCommands[] = "RLVaExperimentalCommands"; constexpr char EnableIMQuery[] = "RLVaEnableIMQuery"; constexpr char EnableTempAttach[] = "RLVaEnableTemporaryAttachments"; constexpr char TopLevelMenu[] = "RLVaTopLevelMenu"; diff --git a/indra/newview/rlvhandler.cpp b/indra/newview/rlvhandler.cpp index bf78a0a38a..3d7f73937f 100644 --- a/indra/newview/rlvhandler.cpp +++ b/indra/newview/rlvhandler.cpp @@ -1,4 +1,5 @@ #include "llviewerprecompiledheaders.h" +#include "llagent.h" #include "llappviewer.h" #include "llstartup.h" #include "llviewercontrol.h" @@ -22,11 +23,12 @@ bool RlvHandler::mIsEnabled = false; bool RlvHandler::handleSimulatorChat(std::string& message, const LLChat& chat, const LLViewerObject* chatObj) { + // *TODO: There's an edge case for temporary attachments when going from enabled -> disabled with restrictions already in place static LLCachedControl enable_temp_attach(gSavedSettings, Settings::EnableTempAttach); static LLCachedControl show_debug_output(gSavedSettings, Settings::Debug); static LLCachedControl hide_unset_dupes(gSavedSettings, Settings::DebugHideUnsetDup); - if ( message.length() <= 3 || Rlv::Constants::CmdPrefix != message[0] || CHAT_TYPE_OWNER != chat.mChatType || + if ( message.length() <= 3 || Constants::CmdPrefix != message[0] || CHAT_TYPE_OWNER != chat.mChatType || (chatObj && chatObj->isTempAttachment() && !enable_temp_attach()) ) { return false; @@ -39,7 +41,7 @@ bool RlvHandler::handleSimulatorChat(std::string& message, const LLChat& chat, c boost_tokenizer tokens(message, boost::char_separator(",", "", boost::drop_empty_tokens)); for (const std::string& strCmd : tokens) { - ECmdRet eRet = (ECmdRet)processCommand(chat.mFromID, strCmd, true); + ECmdRet eRet = processCommand(chat.mFromID, strCmd, true); if ( show_debug_output() && (!hide_unset_dupes() || (ECmdRet::SuccessUnset != eRet && ECmdRet::SuccessDuplicate != eRet)) ) { @@ -53,7 +55,44 @@ bool RlvHandler::handleSimulatorChat(std::string& message, const LLChat& chat, c ECmdRet RlvHandler::processCommand(const LLUUID& idObj, const std::string& strCmd, bool fromObj) { - return ECmdRet::FailedNoProcessor; + const RlvCommand rlvCmd(idObj, strCmd); + return processCommand(std::ref(rlvCmd), fromObj); +} + +ECmdRet RlvHandler::processCommand(std::reference_wrapper rlvCmd, bool fromObj) +{ + { + const RlvCommand& rlvCmdTmp = rlvCmd; // Reference to the temporary with limited variable scope since we don't want it to leak below + + RLV_DEBUGS << "[" << rlvCmdTmp.getObjectID() << "]: " << rlvCmdTmp.asString() << RLV_ENDL; + if (!rlvCmdTmp.isValid()) + { + RLV_DEBUGS << "\t-> invalid syntax" << RLV_ENDL; + return ECmdRet::FailedSyntax; + } + if (rlvCmdTmp.isBlocked()) + { + RLV_DEBUGS << "\t-> blocked command" << RLV_ENDL; + return ECmdRet::FailedDisabled; + } + } + + ECmdRet eRet = ECmdRet::Unknown; + switch (rlvCmd.get().getParamType()) + { + case EParamType::Reply: + eRet = rlvCmd.get().processCommand(); + break; + case EParamType::Unknown: + default: + eRet = ECmdRet::FailedParam; + break; + } + RLV_ASSERT(ECmdRet::Unknown != eRet); + + RLV_DEBUGS << "\t--> command " << (isReturnCodeSuccess(eRet) ? "succeeded" : "failed") << RLV_ENDL; + + return eRet; } // ============================================================================ @@ -72,7 +111,7 @@ bool RlvHandler::setEnabled(bool enable) if (enable && canEnable()) { - RLV_INFOS << "Enabling Restrained Love API support - " << RlvStrings::getVersionAbout() << RLV_ENDL; + RLV_INFOS << "Enabling Restrained Love API support - " << Strings::getVersionAbout() << RLV_ENDL; mIsEnabled = true; } @@ -80,3 +119,49 @@ bool RlvHandler::setEnabled(bool enable) } // ============================================================================ +// Command handlers (RLV_TYPE_REPLY) +// + +ECmdRet CommandHandlerBaseImpl::processCommand(const RlvCommand& rlvCmd, ReplyHandlerFunc* pHandler) +{ + // Sanity check - should specify a - valid - reply channel + S32 nChannel; + if (!LLStringUtil::convertToS32(rlvCmd.getParam(), nChannel) || !Util::isValidReplyChannel(nChannel, rlvCmd.getObjectID() == gAgent.getID())) + return ECmdRet::FailedParam; + + std::string strReply; + ECmdRet eRet = (*pHandler)(rlvCmd, strReply); + + // If we made it this far then: + // - the command was handled successfully so we send off the response + // - the command failed but we still send off an - empty - response to keep the issuing script from blocking + if (nChannel != 0) + { + Util::sendChatReply(nChannel, strReply); + } + + return eRet; +} + +// Handles: @version= and @versionnew= +template<> template<> +ECmdRet VersionReplyHandler::onCommand(const RlvCommand& rlvCmd, std::string& strReply) +{ + strReply = Strings::getVersion(EBehaviour::Version == rlvCmd.getBehaviourType()); + return ECmdRet::Success; +} + +// Handles: @versionnum[:impl]= +template<> template<> +ECmdRet ReplyHandler::onCommand(const RlvCommand& rlvCmd, std::string& strReply) +{ + if (!rlvCmd.hasOption()) + strReply = Strings::getVersionNum(); + else if ("impl" == rlvCmd.getOption()) + strReply = Strings::getVersionImplNum(); + else + return ECmdRet::FailedOption; + return ECmdRet::Success; +} + +// ============================================================================ diff --git a/indra/newview/rlvhandler.h b/indra/newview/rlvhandler.h index 8cf054e98e..a5e91548ef 100644 --- a/indra/newview/rlvhandler.h +++ b/indra/newview/rlvhandler.h @@ -3,7 +3,7 @@ #include "llchat.h" #include "llsingleton.h" -#include "rlvdefines.h" +#include "rlvhelper.h" class LLViewerObject; @@ -15,15 +15,17 @@ class RlvHandler : public LLSingleton { LLSINGLETON_EMPTY_CTOR(RlvHandler); -public: /* - * Helper functions + * Command processing */ public: // Command processing helper functions bool handleSimulatorChat(std::string& message, const LLChat& chat, const LLViewerObject* chatObj); Rlv::ECmdRet processCommand(const LLUUID& idObj, const std::string& stCmd, bool fromObj); +protected: + Rlv::ECmdRet processCommand(std::reference_wrapper rlvCmdRef, bool fromObj); +public: // Initialization (deliberately static so they can safely be called in tight loops) static bool canEnable(); static bool isEnabled() { return mIsEnabled; } diff --git a/indra/newview/rlvhelper.cpp b/indra/newview/rlvhelper.cpp index ade2b83dd7..c3f9e6f756 100644 --- a/indra/newview/rlvhelper.cpp +++ b/indra/newview/rlvhelper.cpp @@ -1,11 +1,242 @@ #include "llviewerprecompiledheaders.h" #include "lltrans.h" +#include "llviewercontrol.h" #include "rlvhelper.h" +#include + using namespace Rlv; +// ============================================================================ +// BehaviourDictionary +// + +BehaviourDictionary::BehaviourDictionary() +{ + // + // Restrictions + // + + // + // Reply-only + // + addEntry(new ReplyProcessor("version")); + addEntry(new ReplyProcessor("versionnew")); + addEntry(new ReplyProcessor("versionnum")); + + // Populate mString2InfoMap (the tuple should be unique) + for (const BehaviourInfo* bhvr_info_p : mBhvrInfoList) + { + RLV_VERIFY(mString2InfoMap.insert(std::make_pair(std::make_pair(bhvr_info_p->getBehaviour(), static_cast(bhvr_info_p->getParamTypeMask())), bhvr_info_p)).second); + } + + // Populate m_Bhvr2InfoMap (there can be multiple entries per ERlvBehaviour) + for (const BehaviourInfo* bhvr_info_p : mBhvrInfoList) + { + if ((bhvr_info_p->getParamTypeMask() & to_underlying(EParamType::AddRem)) && !bhvr_info_p->isSynonym()) + { +#ifdef RLV_DEBUG + for (const auto& itBhvr : boost::make_iterator_range(mBhvr2InfoMap.lower_bound(bhvr_info_p->getBehaviourType()), mBhvr2InfoMap.upper_bound(bhvr_info_p->getBehaviourType()))) + { + RLV_ASSERT((itBhvr.first != bhvr_info_p->getBehaviourType()) || (itBhvr.second->getBehaviourFlags() != bhvr_info_p->getBehaviourFlags())); + } +#endif // RLV_DEBUG + mBhvr2InfoMap.insert(std::pair(bhvr_info_p->getBehaviourType(), bhvr_info_p)); + } + } +} + +BehaviourDictionary::~BehaviourDictionary() +{ + for (const BehaviourInfo* bhvr_info_p : mBhvrInfoList) + { + delete bhvr_info_p; + } + mBhvrInfoList.clear(); +} + +void BehaviourDictionary::addEntry(const BehaviourInfo* entry_p) +{ + // Filter experimental commands (if disabled) + static LLCachedControl sEnableExperimental(gSavedSettings, Settings::EnableExperimentalCommands); + if (!entry_p || (!sEnableExperimental && entry_p->isExperimental())) + { + return; + } + + // Sanity check for duplicate entries +#ifndef LL_RELEASE_FOR_DOWNLOAD + std::for_each(mBhvrInfoList.begin(), mBhvrInfoList.end(), + [&entry_p](const BehaviourInfo* bhvr_info_p) { + RLV_ASSERT_DBG((bhvr_info_p->getBehaviour() != entry_p->getBehaviour()) || ((bhvr_info_p->getParamTypeMask() & entry_p->getParamTypeMask()) == 0)); + }); +#endif // LL_RELEASE_FOR_DOWNLOAD + + mBhvrInfoList.push_back(entry_p); +} + +const BehaviourInfo* BehaviourDictionary::getBehaviourInfo(EBehaviour eBhvr, EParamType eParamType) const +{ + const BehaviourInfo* bhvr_info_p = nullptr; + for (auto itBhvrLower = mBhvr2InfoMap.lower_bound(eBhvr), itBhvrUpper = mBhvr2InfoMap.upper_bound(eBhvr); + std::find_if(itBhvrLower, itBhvrUpper, [eParamType](const auto& bhvrEntry) { return bhvrEntry.second->getParamTypeMask() == to_underlying(eParamType); }) != itBhvrUpper; + ++itBhvrLower) + { + if (bhvr_info_p) + return nullptr; + bhvr_info_p = itBhvrLower->second; + } + return bhvr_info_p; +} + +const BehaviourInfo* BehaviourDictionary::getBehaviourInfo(const std::string& strBhvr, EParamType eParamType, bool* is_strict_p) const +{ + size_t idxBhvrLastPart = strBhvr.find_last_of('_'); + std::string strBhvrLastPart((std::string::npos != idxBhvrLastPart) && (idxBhvrLastPart < strBhvr.size()) ? strBhvr.substr(idxBhvrLastPart + 1) : LLStringUtil::null); + + bool isStrict = (strBhvrLastPart.compare("sec") == 0); + if (is_strict_p) + *is_strict_p = isStrict; + + auto itBhvr = mString2InfoMap.find(std::make_pair((!isStrict) ? strBhvr : strBhvr.substr(0, strBhvr.size() - 4), (has_flag(eParamType, EParamType::AddRem)) ? EParamType::AddRem : eParamType)); + if ((mString2InfoMap.end() == itBhvr) && (!isStrict) && (!strBhvrLastPart.empty()) && (EParamType::Force == eParamType)) + { + // No match found but it could still be a local scope modifier + auto itBhvrMod = mString2InfoMap.find(std::make_pair(strBhvr.substr(0, idxBhvrLastPart), EParamType::AddRem)); + } + + return ((itBhvr != mString2InfoMap.end()) && ((!isStrict) || (itBhvr->second->hasStrict()))) ? itBhvr->second : nullptr; +} + +EBehaviour BehaviourDictionary::getBehaviourFromString(const std::string& strBhvr, EParamType eParamType, bool* pisStrict) const +{ + const BehaviourInfo* bhvr_info_p = getBehaviourInfo(strBhvr, eParamType, pisStrict); + // Filter out locally scoped modifier commands since they don't actually have a unique behaviour value of their own + return bhvr_info_p->getBehaviourType(); +} + +bool BehaviourDictionary::getCommands(const std::string& strMatch, EParamType eParamType, std::list& cmdList) const +{ + cmdList.clear(); + for (const BehaviourInfo* bhvr_info_p : mBhvrInfoList) + { + if ((bhvr_info_p->getParamTypeMask() & to_underlying(eParamType)) || (EParamType::Unknown == eParamType)) + { + std::string strCmd = bhvr_info_p->getBehaviour(); + if ((std::string::npos != strCmd.find(strMatch)) || (strMatch.empty())) + cmdList.push_back(strCmd); + if ((bhvr_info_p->hasStrict()) && ((std::string::npos != strCmd.append("_sec").find(strMatch)) || (strMatch.empty()))) + cmdList.push_back(strCmd); + } + } + return !cmdList.empty(); +} + +bool BehaviourDictionary::getHasStrict(EBehaviour eBhvr) const +{ + for (const auto& itBhvr : boost::make_iterator_range(mBhvr2InfoMap.lower_bound(eBhvr), mBhvr2InfoMap.upper_bound(eBhvr))) + { + // Only restrictions can be strict + if (to_underlying(EParamType::AddRem) != itBhvr.second->getParamTypeMask()) + continue; + return itBhvr.second->hasStrict(); + } + RLV_ASSERT(false); + return false; +} + +void BehaviourDictionary::toggleBehaviourFlag(const std::string& strBhvr, EParamType eParamType, BehaviourInfo::EBehaviourFlags eBhvrFlag, bool fEnable) +{ + auto itBhvr = mString2InfoMap.find(std::make_pair(strBhvr, (has_flag(eParamType, EParamType::AddRem)) ? EParamType::AddRem : eParamType)); + if (mString2InfoMap.end() != itBhvr) + { + const_cast(itBhvr->second)->toggleBehaviourFlag(eBhvrFlag, fEnable); + } +} + +// ============================================================================ +// RlvCommmand +// + +RlvCommand::RlvCommand(const LLUUID& idObj, const std::string& strCmd) + : mObjId(idObj) +{ + if (parseCommand(strCmd, mBehaviour, mOption, mParam)) + { + if ("n" == mParam || "add" == mParam) + mParamType = EParamType::Add; + else if ("y" == mParam || "rem" == mParam) + mParamType = EParamType::Remove; + else if ("clear" == mBehaviour) // clear is the odd one out so just make it its own type + mParamType = EParamType::Clear; + else if ("force" == mParam) + mParamType = EParamType::Force; + else if (S32 nTemp; LLStringUtil::convertToS32(mParam, nTemp)) // Assume it's a reply command if we can convert to an S32 + mParamType = EParamType::Reply; + } + + mIsValid = mParamType != EParamType::Unknown; + if (!mIsValid) + { + mOption.clear(); + mParam.clear(); + return; + } + + mBhvrInfo = BehaviourDictionary::instance().getBehaviourInfo(mBehaviour, mParamType, &mIsStrict); +} + +RlvCommand::RlvCommand(const RlvCommand& rlvCmd, EParamType eParamType) + : mIsValid(rlvCmd.mIsValid), mObjId(rlvCmd.mObjId), mBehaviour(rlvCmd.mBehaviour), mBhvrInfo(rlvCmd.mBhvrInfo) + , mParamType( (EParamType::Unknown == eParamType) ? rlvCmd.mParamType : eParamType) + , mIsStrict(rlvCmd.mIsStrict), mOption(rlvCmd.mOption), mParam(rlvCmd.mParam), mIsRefCounted(rlvCmd.mIsRefCounted) +{ +} + +bool RlvCommand::parseCommand(const std::string& strCmd, std::string& strBhvr, std::string& strOption, std::string& strParam) +{ + // Format: [: - linux64 - - archive - - hash - 08491c609b5f77835977fa459e386fddbad00064 - hash_algorithm - sha1 - url - https://github.com/secondlife/dullahan/releases/download/v1.14.0-r2/dullahan-1.14.0.202404051708_118.4.1_g3dd6078_chromium-118.0.5993.54-linux64-8573290624.tar.zst - - name - linux64 - version 1.14.0.202408091639_118.4.1_g3dd6078_chromium-118.0.5993.54 @@ -641,72 +627,6 @@ description Expat is an XML parser library written in C - fmodstudio - - platforms - - darwin64 - - archive - - creds - github - hash - a2074b67de7ad4c04b5ca8f8f161506add9697b2 - hash_algorithm - sha1 - url - https://api.github.com/repos/secondlife/3p-fmodstudio/releases/assets/149207589 - - name - darwin64 - - linux64 - - archive - - creds - github - hash - 8c1b701648c077220dbc576c3d9aefbef47f8324 - hash_algorithm - sha1 - url - https://api.github.com/repos/secondlife/3p-fmodstudio/releases/assets/149207592 - - name - linux64 - - windows64 - - archive - - creds - github - hash - 7e0c3d50e8b99d8735c6c9596a72ded9ee2bc1c8 - hash_algorithm - sha1 - url - https://api.github.com/repos/secondlife/3p-fmodstudio/releases/assets/149207594 - - name - windows64 - - - license - fmod - license_file - LICENSES/fmodstudio.txt - copyright - FMOD Studio by Firelight Technologies Pty Ltd. - version - 2.02.20.c78ef55 - name - fmodstudio - description - FMOD Studio API - fontconfig platforms @@ -1026,6 +946,8 @@ url https://github.com/secondlife/3p-jpeg_encoder_js/releases/download/v1.0-790015a/jpegencoderbasic-1.0-linux64-790015a.tar.zst + name + linux64 windows64 @@ -1167,22 +1089,6 @@ name windows64 - linux - - archive - - creds - github - hash - 711b82f9f588d3a125af7dcd8c81f93d9c343a7d - hash_algorithm - sha1 - url - https://api.github.com/repos/secondlife/3p-kdu/releases/assets/136774121 - - name - linux - license Kakadu @@ -1731,11 +1637,11 @@ archive hash - 6f4509dca9e32e3b4f9c4b13d875ce0e24340efc + c947107c0aca46e94e22f66328a3cbbd01d99b36 hash_algorithm sha1 url - https://github.com/secondlife/3p-meshoptimizer/releases/download/v160-4f905dd/meshoptimizer-160-linux64-4f905dd.tar.zst + https://github.com/secondlife/3p-meshoptimizer/releases/download/v210-r2/meshoptimizer-210.0.0-r2-linux64-10341021290.tar.zst name linux64 @@ -1754,20 +1660,6 @@ name windows64 - linux64 - - archive - - hash - c947107c0aca46e94e22f66328a3cbbd01d99b36 - hash_algorithm - sha1 - url - https://github.com/secondlife/3p-meshoptimizer/releases/download/v210-r2/meshoptimizer-210.0.0-r2-linux64-10341021290.tar.zst - - name - linux64 - license meshoptimizer @@ -2418,6 +2310,8 @@ Copyright (c) 2012, 2014, 2015, 2016 nghttp2 contributors url https://github.com/secondlife/3p-three_js/releases/download/v0.132.2-5da28d9/threejs-0.132.2-common-8454371083.tar.zst + name + linux64 windows64 diff --git a/indra/newview/llappviewerlinux.cpp b/indra/newview/llappviewerlinux.cpp index 2f366f0538..38f2f1ae7f 100644 --- a/indra/newview/llappviewerlinux.cpp +++ b/indra/newview/llappviewerlinux.cpp @@ -68,8 +68,7 @@ extern "C" #include "breakpad/common/linux/http_upload.h" #include "lldir.h" #include "../llcrashlogger/llcrashlogger.h" -#include "jsoncpp/reader.h" // JSON - +#include "boost/json.hpp" #endif #define VIEWERAPI_SERVICE "com.secondlife.ViewerAppAPIService" @@ -181,26 +180,28 @@ void setupBreadpad() return; } - Json::Reader reader; - Json::Value build_data; - if(!reader.parse(inf, build_data, false)) + boost::json::error_code ec; + boost::json::value build_data = boost::json::parse(inf, ec); + if(ec.failed()) { LL_WARNS("BUGSPLAT") << "Can't initialize BugSplat, can't parse '" << build_data_fname << "': " - << reader.getFormatedErrorMessages() << LL_ENDL; + << ec.what() << LL_ENDL; return; } - Json::Value BugSplat_DB = build_data["BugSplat DB"]; - if(!BugSplat_DB) + if (!build_data.is_object() || !build_data.as_object().contains("BugSplat DB")) { LL_WARNS("BUGSPLAT") << "Can't initialize BugSplat, no 'BugSplat DB' entry in '" << build_data_fname << "'" << LL_ENDL; return; } + gVersion = STRINGIZE( LL_VIEWER_VERSION_MAJOR << '.' << LL_VIEWER_VERSION_MINOR << '.' << LL_VIEWER_VERSION_PATCH << '.' << LL_VIEWER_VERSION_BUILD); - gBugsplatDB = BugSplat_DB.asString(); + + boost::json::value BugSplat_DB = build_data.at("BugSplat DB"); + gBugsplatDB = boost::json::value_to(BugSplat_DB); LL_INFOS("BUGSPLAT") << "Initializing with crash logger: " << gCrashLogger << " database: " << gBugsplatDB << " version: " << gVersion << LL_ENDL; diff --git a/indra/newview/llconversationloglist.cpp b/indra/newview/llconversationloglist.cpp index 3e08c6ee60..58c05af4db 100644 --- a/indra/newview/llconversationloglist.cpp +++ b/indra/newview/llconversationloglist.cpp @@ -94,8 +94,7 @@ bool LLConversationLogList::handleRightMouseDown(S32 x, S32 y, MASK mask) if (context_menu && size()) { context_menu->buildDrawLabels(); - if (context_menu && size()) - context_menu->updateParent(LLMenuGL::sMenuContainer); + context_menu->updateParent(LLMenuGL::sMenuContainer); LLMenuGL::showPopup(this, context_menu, x, y); } -- cgit v1.2.3 From 963993efed2e509357ea0fe17c36ba3e0cbeda0f Mon Sep 17 00:00:00 2001 From: Ansariel Hiller Date: Wed, 4 Sep 2024 21:24:14 +0200 Subject: Fix name of vsync param in LLWindowSDL (#2502) --- indra/llwindow/llwindowsdl.cpp | 11 +++++------ indra/llwindow/llwindowsdl.h | 6 +++--- 2 files changed, 8 insertions(+), 9 deletions(-) diff --git a/indra/llwindow/llwindowsdl.cpp b/indra/llwindow/llwindowsdl.cpp index e2d9d752ee..89f0d152c6 100644 --- a/indra/llwindow/llwindowsdl.cpp +++ b/indra/llwindow/llwindowsdl.cpp @@ -359,11 +359,10 @@ LLWindowSDL::LLWindowSDL(LLWindowCallbacks* callbacks, const std::string& title, S32 x, S32 y, S32 width, S32 height, U32 flags, bool fullscreen, bool clearBg, - bool disable_vsync, bool use_gl, + bool enable_vsync, bool use_gl, bool ignore_pixel_depth, U32 fsaa_samples) : LLWindow(callbacks, fullscreen, flags), Lock_Display(NULL), - //Unlock_Display(NULL), mGamma(1.0f) Unlock_Display(NULL), mGamma(1.0f) { // Initialize the keyboard @@ -396,7 +395,7 @@ LLWindowSDL::LLWindowSDL(LLWindowCallbacks* callbacks, mWindowTitle = title; // Create the GL context and set it up for windowed or fullscreen, as appropriate. - if(createContext(x, y, width, height, 32, fullscreen, disable_vsync)) + if(createContext(x, y, width, height, 32, fullscreen, enable_vsync)) { gGLManager.initGL(); @@ -632,7 +631,7 @@ void LLWindowSDL::tryFindFullscreenSize( int &width, int &height ) } } -bool LLWindowSDL::createContext(int x, int y, int width, int height, int bits, bool fullscreen, bool disable_vsync) +bool LLWindowSDL::createContext(int x, int y, int width, int height, int bits, bool fullscreen, bool enable_vsync) { //bool glneedsinit = false; @@ -892,7 +891,7 @@ bool LLWindowSDL::createContext(int x, int y, int width, int height, int bits, b // changing fullscreen resolution, or switching between windowed and fullscreen mode. -bool LLWindowSDL::switchContext(bool fullscreen, const LLCoordScreen &size, bool disable_vsync, const LLCoordScreen * const posp) +bool LLWindowSDL::switchContext(bool fullscreen, const LLCoordScreen &size, bool enable_vsync, const LLCoordScreen * const posp) { const bool needsRebuild = true; // Just nuke the context and start over. bool result = true; @@ -902,7 +901,7 @@ bool LLWindowSDL::switchContext(bool fullscreen, const LLCoordScreen &size, bool if(needsRebuild) { destroyContext(); - result = createContext(0, 0, size.mX, size.mY, 0, fullscreen, disable_vsync); + result = createContext(0, 0, size.mX, size.mY, 0, fullscreen, enable_vsync); if (result) { gGLManager.initGL(); diff --git a/indra/llwindow/llwindowsdl.h b/indra/llwindow/llwindowsdl.h index 4b2306da87..10769bb3ba 100644 --- a/indra/llwindow/llwindowsdl.h +++ b/indra/llwindow/llwindowsdl.h @@ -80,7 +80,7 @@ public: bool setSizeImpl(LLCoordWindow size) override; - bool switchContext(bool fullscreen, const LLCoordScreen &size, bool disable_vsync, + bool switchContext(bool fullscreen, const LLCoordScreen &size, bool enable_vsync, const LLCoordScreen *const posp = NULL) override; bool setCursorPosition(LLCoordWindow position) override; @@ -212,7 +212,7 @@ public: protected: LLWindowSDL(LLWindowCallbacks *callbacks, const std::string &title, int x, int y, int width, int height, U32 flags, - bool fullscreen, bool clearBg, bool disable_vsync, bool use_gl, + bool fullscreen, bool clearBg, bool enable_vsync, bool use_gl, bool ignore_pixel_depth, U32 fsaa_samples); ~LLWindowSDL(); @@ -241,7 +241,7 @@ protected: // // create or re-create the GL context/window. Called from the constructor and switchContext(). - bool createContext(int x, int y, int width, int height, int bits, bool fullscreen, bool disable_vsync); + bool createContext(int x, int y, int width, int height, int bits, bool fullscreen, bool enable_vsync); void destroyContext(); -- cgit v1.2.3 From 5319d314206c7c1c21b2bbd3a661c0c520373dcc Mon Sep 17 00:00:00 2001 From: Nat Goodspeed Date: Wed, 4 Sep 2024 16:24:42 -0400 Subject: Windows build fixes --- indra/llcommon/llcoromutex.h | 2 +- indra/llcommon/lua_function.cpp | 4 ++-- indra/newview/llfloatersettingsdebug.cpp | 2 +- indra/newview/tests/llluamanager_test.cpp | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/indra/llcommon/llcoromutex.h b/indra/llcommon/llcoromutex.h index e740e494c2..3405e79478 100644 --- a/indra/llcommon/llcoromutex.h +++ b/indra/llcommon/llcoromutex.h @@ -3,7 +3,7 @@ * @author Nat Goodspeed * @date 2024-09-04 * @brief Coroutine-aware synchronization primitives - * + * * $LicenseInfo:firstyear=2024&license=viewerlgpl$ * Copyright (c) 2024, Linden Research, Inc. * $/LicenseInfo$ diff --git a/indra/llcommon/lua_function.cpp b/indra/llcommon/lua_function.cpp index ffb90032d2..c0090dd395 100644 --- a/indra/llcommon/lua_function.cpp +++ b/indra/llcommon/lua_function.cpp @@ -418,7 +418,7 @@ void lua_pushllsd(lua_State* L, const LLSD& data) case LLSD::TypeMap: { // push a new table with space for our non-array keys - lua_createtable(L, 0, data.size()); + lua_createtable(L, 0, narrow(data.size())); for (const auto& pair: llsd::inMap(data)) { // push value -- so now table is at -2, value at -1 @@ -432,7 +432,7 @@ void lua_pushllsd(lua_State* L, const LLSD& data) case LLSD::TypeArray: { // push a new table with space for array entries - lua_createtable(L, data.size(), 0); + lua_createtable(L, narrow(data.size()), 0); lua_Integer key{ 0 }; for (const auto& item: llsd::inArray(data)) { diff --git a/indra/newview/llfloatersettingsdebug.cpp b/indra/newview/llfloatersettingsdebug.cpp index 9c87e6fb3b..63ae1e60e8 100644 --- a/indra/newview/llfloatersettingsdebug.cpp +++ b/indra/newview/llfloatersettingsdebug.cpp @@ -660,5 +660,5 @@ void LLFloaterSettingsDebug::hideUIControls() void LLFloaterSettingsDebug::onClickCopy() { std::string setting_name = mSettingName->getText(); - LLClipboard::instance().copyToClipboard(utf8str_to_wstring(setting_name), 0, setting_name.size()); + LLClipboard::instance().copyToClipboard(utf8str_to_wstring(setting_name), 0, narrow(setting_name.size())); } diff --git a/indra/newview/tests/llluamanager_test.cpp b/indra/newview/tests/llluamanager_test.cpp index 4b143b52db..8ce5c357e0 100644 --- a/indra/newview/tests/llluamanager_test.cpp +++ b/indra/newview/tests/llluamanager_test.cpp @@ -287,7 +287,7 @@ namespace tut while (expect_array.size() > 0 && send_array[expect_array.size() - 1].isUndefined()) { - expect_array.erase(expect_array.size() - 1); + expect_array.erase(LLSD::Integer(expect_array.size() - 1)); } round_trip("array", send_array, expect_array); -- cgit v1.2.3 From 6a747e1ce027700a3609f4c377179bfa29c3ce31 Mon Sep 17 00:00:00 2001 From: Nat Goodspeed Date: Wed, 4 Sep 2024 16:29:09 -0400 Subject: Insidious trailing whitespace --- indra/newview/llagentlistener.cpp | 2 +- indra/newview/llagentlistener.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/indra/newview/llagentlistener.cpp b/indra/newview/llagentlistener.cpp index 2ff4adcf5e..10810b569f 100644 --- a/indra/newview/llagentlistener.cpp +++ b/indra/newview/llagentlistener.cpp @@ -162,7 +162,7 @@ LLAgentListener::LLAgentListener(LLAgent &agent) add("removeCameraParams", "Reset Follow camera params", &LLAgentListener::removeFollowCamParams); - + add("playAnimation", "Play [\"item_id\"] animation locally (by default) or [\"inworld\"] (when set to true)", &LLAgentListener::playAnimation, diff --git a/indra/newview/llagentlistener.h b/indra/newview/llagentlistener.h index c77d1b3fc9..2765bb5b66 100644 --- a/indra/newview/llagentlistener.h +++ b/indra/newview/llagentlistener.h @@ -61,7 +61,7 @@ private: void setFollowCamParams(LLSD const & event_data) const; void setFollowCamActive(LLSD const & event_data) const; void removeFollowCamParams(LLSD const & event_data) const; - + void playAnimation(LLSD const &event_data); void playAnimation_(const LLUUID& asset_id, const bool inworld); void stopAnimation(LLSD const &event_data); -- cgit v1.2.3 From 49bf86b52459b183d3988388dbb74d8888a71925 Mon Sep 17 00:00:00 2001 From: Nat Goodspeed Date: Thu, 5 Sep 2024 08:45:24 -0400 Subject: Fix a few trailing whitespaces. --- indra/llcommon/llinttracker.h | 2 +- indra/llcommon/resultset.cpp | 2 +- indra/llcommon/resultset.h | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/indra/llcommon/llinttracker.h b/indra/llcommon/llinttracker.h index 86c30bc7aa..fd6d24d0fd 100644 --- a/indra/llcommon/llinttracker.h +++ b/indra/llcommon/llinttracker.h @@ -3,7 +3,7 @@ * @author Nat Goodspeed * @date 2024-08-30 * @brief LLIntTracker isa LLInstanceTracker with generated int keys. - * + * * $LicenseInfo:firstyear=2024&license=viewerlgpl$ * Copyright (c) 2024, Linden Research, Inc. * $/LicenseInfo$ diff --git a/indra/llcommon/resultset.cpp b/indra/llcommon/resultset.cpp index cb46db04fa..4d7b00eabd 100644 --- a/indra/llcommon/resultset.cpp +++ b/indra/llcommon/resultset.cpp @@ -3,7 +3,7 @@ * @author Nat Goodspeed * @date 2024-09-03 * @brief Implementation for resultset. - * + * * $LicenseInfo:firstyear=2024&license=viewerlgpl$ * Copyright (c) 2024, Linden Research, Inc. * $/LicenseInfo$ diff --git a/indra/llcommon/resultset.h b/indra/llcommon/resultset.h index 9b9ecbb21e..90d52b6fe4 100644 --- a/indra/llcommon/resultset.h +++ b/indra/llcommon/resultset.h @@ -4,7 +4,7 @@ * @date 2024-09-03 * @brief ResultSet is an abstract base class to allow scripted access to * potentially large collections representable as LLSD arrays. - * + * * $LicenseInfo:firstyear=2024&license=viewerlgpl$ * Copyright (c) 2024, Linden Research, Inc. * $/LicenseInfo$ -- cgit v1.2.3 From 866f900a3be8bbbf8fb10e6ccb87bb80882b4a6e Mon Sep 17 00:00:00 2001 From: Nat Goodspeed Date: Thu, 5 Sep 2024 11:08:26 -0400 Subject: Fix a merge glitch in llcoros.h. --- indra/llcommon/llcoros.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/indra/llcommon/llcoros.h b/indra/llcommon/llcoros.h index 2272237fa3..0291d7f1d9 100644 --- a/indra/llcommon/llcoros.h +++ b/indra/llcommon/llcoros.h @@ -324,7 +324,7 @@ public: // LockType is deprecated; see llcoromutex.h using LockType = llcoro::LockType; using cv_status = llcoro::cv_status; - using ConditionVariable = llcoro::condition_variable; + using ConditionVariable = llcoro::ConditionVariable; /// for data local to each running coroutine template -- cgit v1.2.3 From 9830f56b1b9d6afc6adfcc3ee3fbd540da9f1737 Mon Sep 17 00:00:00 2001 From: Nat Goodspeed Date: Thu, 5 Sep 2024 11:10:17 -0400 Subject: In llcoromutex.h, pull in llcoro::RMutex from develop branch. Also add develop branch's comments about llcoro::LockType being deprecated. --- indra/llcommon/llcoromutex.h | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/indra/llcommon/llcoromutex.h b/indra/llcommon/llcoromutex.h index 3405e79478..c0ceac4b22 100644 --- a/indra/llcommon/llcoromutex.h +++ b/indra/llcommon/llcoromutex.h @@ -18,11 +18,13 @@ // e.g. #include LLCOROS_MUTEX_HEADER #define LLCOROS_MUTEX_HEADER +#define LLCOROS_RMUTEX_HEADER #define LLCOROS_CONDVAR_HEADER namespace boost { namespace fibers { class mutex; + class recursive_mutex; enum class cv_status; class condition_variable; } @@ -47,6 +49,12 @@ static Future getFuture(Promise& promise) { return promise.get_future(); } // use mutex, lock, condition_variable suitable for coroutines using Mutex = boost::fibers::mutex; +using RMutex = boost::fibers::recursive_mutex; +// With C++17, LockType is deprecated: at this point we can directly +// declare 'std::unique_lock lk(some_mutex)' without explicitly stating +// the mutex type. Sadly, making LockType an alias template for +// std::unique_lock doesn't work the same way: Class Template Argument +// Deduction only works for class templates, not alias templates. using LockType = std::unique_lock; using cv_status = boost::fibers::cv_status; using ConditionVariable = boost::fibers::condition_variable; -- cgit v1.2.3 From 953f7c9c1da4b83cabbf91f281445c3704a2f229 Mon Sep 17 00:00:00 2001 From: Nat Goodspeed Date: Thu, 5 Sep 2024 12:33:47 -0400 Subject: Fix build errors from merging develop into release/luau-scripting. --- indra/llcommon/llerror.cpp | 2 ++ indra/newview/llfloatergltfasseteditor.cpp | 4 ++-- indra/newview/llfloatersettingscolor.cpp | 4 ++-- indra/newview/llfloatersettingsdebug.cpp | 3 +-- indra/newview/lluilistener.cpp | 5 +++-- 5 files changed, 10 insertions(+), 8 deletions(-) diff --git a/indra/llcommon/llerror.cpp b/indra/llcommon/llerror.cpp index 8705758d3f..e6fe1eca75 100644 --- a/indra/llcommon/llerror.cpp +++ b/indra/llcommon/llerror.cpp @@ -64,6 +64,8 @@ #define BOOST_STACKTRACE_GNU_SOURCE_NOT_REQUIRED #include +#include LLCOROS_RMUTEX_HEADER + namespace { #if LL_WINDOWS void debugger_print(const std::string& s) diff --git a/indra/newview/llfloatergltfasseteditor.cpp b/indra/newview/llfloatergltfasseteditor.cpp index d2cf24f1dd..f28c01d713 100644 --- a/indra/newview/llfloatergltfasseteditor.cpp +++ b/indra/newview/llfloatergltfasseteditor.cpp @@ -46,8 +46,8 @@ LLFloaterGLTFAssetEditor::LLFloaterGLTFAssetEditor(const LLSD& key) { setTitle("GLTF Asset Editor (WIP)"); - mCommitCallbackRegistrar.add("PanelObject.menuDoToSelected", [this](LLUICtrl* ctrl, const LLSD& data) { onMenuDoToSelected(data); }); - mEnableCallbackRegistrar.add("PanelObject.menuEnable", [this](LLUICtrl* ctrl, const LLSD& data) { return onMenuEnableItem(data); }); + mCommitCallbackRegistrar.add("PanelObject.menuDoToSelected", { [this](LLUICtrl* ctrl, const LLSD& data) { onMenuDoToSelected(data); }}); + mEnableCallbackRegistrar.add("PanelObject.menuEnable", { [this](LLUICtrl* ctrl, const LLSD& data) { return onMenuEnableItem(data); }}); } LLFloaterGLTFAssetEditor::~LLFloaterGLTFAssetEditor() diff --git a/indra/newview/llfloatersettingscolor.cpp b/indra/newview/llfloatersettingscolor.cpp index d9c382a1dc..1ca2412907 100644 --- a/indra/newview/llfloatersettingscolor.cpp +++ b/indra/newview/llfloatersettingscolor.cpp @@ -43,8 +43,8 @@ LLFloaterSettingsColor::LLFloaterSettingsColor(const LLSD& key) : LLFloater(key), mSettingList(NULL) { - mCommitCallbackRegistrar.add("CommitSettings", boost::bind(&LLFloaterSettingsColor::onCommitSettings, this)); - mCommitCallbackRegistrar.add("ClickDefault", boost::bind(&LLFloaterSettingsColor::onClickDefault, this)); + mCommitCallbackRegistrar.add("CommitSettings", { boost::bind(&LLFloaterSettingsColor::onCommitSettings, this) }); + mCommitCallbackRegistrar.add("ClickDefault", { boost::bind(&LLFloaterSettingsColor::onClickDefault, this) }); } LLFloaterSettingsColor::~LLFloaterSettingsColor() diff --git a/indra/newview/llfloatersettingsdebug.cpp b/indra/newview/llfloatersettingsdebug.cpp index 4a053a99de..0a01fff36d 100644 --- a/indra/newview/llfloatersettingsdebug.cpp +++ b/indra/newview/llfloatersettingsdebug.cpp @@ -66,7 +66,6 @@ bool LLFloaterSettingsDebug::postBuild() mSettingNameText = getChild("setting_name_txt"); mComment = getChild("comment_text"); - mSettingName = getChild("setting_name_txt"); mLLSDVal = getChild("llsd_text"); mCopyBtn = getChild("copy_btn"); @@ -658,6 +657,6 @@ void LLFloaterSettingsDebug::hideUIControls() void LLFloaterSettingsDebug::onClickCopy() { - std::string setting_name = mSettingName->getText(); + std::string setting_name = mSettingNameText->getText(); LLClipboard::instance().copyToClipboard(utf8str_to_wstring(setting_name), 0, narrow(setting_name.size())); } diff --git a/indra/newview/lluilistener.cpp b/indra/newview/lluilistener.cpp index 6f567e67f8..c389d04443 100644 --- a/indra/newview/lluilistener.cpp +++ b/indra/newview/lluilistener.cpp @@ -255,10 +255,11 @@ LLMenuGL::Params get_params(const LLSD&event) LLMenuGL* get_parent_menu(LLEventAPI::Response& response, const LLSD&event) { - LLMenuGL* parent_menu = gMenuBarView->findChildMenuByName(event["parent_menu"], true); + auto parent_menu_name{ event["parent_menu"].asString() }; + LLMenuGL* parent_menu = gMenuBarView->findChildMenuByName(parent_menu_name, true); if(!parent_menu) { - response.error(stringize("Parent menu ", std::quoted(event["parent_menu"].asString()), " was not found")); + response.error(stringize("Parent menu ", std::quoted(parent_menu_name), " was not found")); } return parent_menu; } -- cgit v1.2.3 From 25a86618002a397d1d8dabf2ec1f093489b2f816 Mon Sep 17 00:00:00 2001 From: Nat Goodspeed Date: Thu, 5 Sep 2024 13:41:41 -0400 Subject: Fix Windows build errors from develop => release/luau-scripting. --- indra/llcommon/coro_scheduler.cpp | 4 ++-- indra/llcommon/coro_scheduler.h | 8 ++++---- indra/llcommon/llcallbacklist.cpp | 6 +++--- indra/newview/llinventorylistener.cpp | 2 +- indra/newview/llluamanager.cpp | 4 ++-- 5 files changed, 12 insertions(+), 12 deletions(-) diff --git a/indra/llcommon/coro_scheduler.cpp b/indra/llcommon/coro_scheduler.cpp index 393356a39b..02b9f11333 100644 --- a/indra/llcommon/coro_scheduler.cpp +++ b/indra/llcommon/coro_scheduler.cpp @@ -26,7 +26,7 @@ namespace llcoro { -const F32 scheduler::DEFAULT_TIMESLICE{ LL::Timers::DEFAULT_TIMESLICE }; +const F64 scheduler::DEFAULT_TIMESLICE{ LL::Timers::DEFAULT_TIMESLICE }; const std::string qname("General"); @@ -72,7 +72,7 @@ boost::fibers::context* scheduler::pick_next() noexcept // When the main fiber is ready, and it's been more than mTimeslice since // the main fiber last ran, it's time to intervene. - F32 elapsed(now - mMainLast); + F64 elapsed(now - mMainLast); if (mMainCtx && elapsed > mTimeslice) { // We claim that the main fiber is not only stored in mMainCtx, but is diff --git a/indra/llcommon/coro_scheduler.h b/indra/llcommon/coro_scheduler.h index cc7e75d798..eee2d746b5 100644 --- a/indra/llcommon/coro_scheduler.h +++ b/indra/llcommon/coro_scheduler.h @@ -38,7 +38,7 @@ class scheduler: public boost::fibers::algo::round_robin public: // If the main fiber is ready, and it's been at least this long since the // main fiber last ran, jump the main fiber to the head of the queue. - static const F32 DEFAULT_TIMESLICE; + static const F64 DEFAULT_TIMESLICE; scheduler(); void awakened( boost::fibers::context*) noexcept override; @@ -57,11 +57,11 @@ private: bool mMainRunning{ false }; // If it's been at least this long since the last time the main fiber got // control, jump it to the head of the queue. - F32 mTimeslice{ DEFAULT_TIMESLICE }; + F64 mTimeslice{ DEFAULT_TIMESLICE }; // Timestamp as of the last time we suspended the main fiber. - F32 mMainLast{ 0 }; + F64 mMainLast{ 0 }; // Timestamp of start time - F32 mStart{ 0 }; + F64 mStart{ 0 }; // count context switches U64 mSwitches{ 0 }; // WorkQueue for deferred logging diff --git a/indra/llcommon/llcallbacklist.cpp b/indra/llcommon/llcallbacklist.cpp index 7cbe7a8c02..7b05c25c21 100644 --- a/indra/llcommon/llcallbacklist.cpp +++ b/indra/llcommon/llcallbacklist.cpp @@ -205,7 +205,7 @@ F32 Timers::timeUntilCall(handle_t timer) const } else { - return found->second.mTime - now(); + return narrow(found->second.mTime - now()); } } @@ -436,7 +436,7 @@ void TimersListener::scheduleAfter(const LLSD& params) // ditch mHandles entry mHandles.erase(key); }, - after)); + narrow(after))); } void TimersListener::scheduleEvery(const LLSD& params) @@ -461,7 +461,7 @@ void TimersListener::scheduleEvery(const LLSD& params) // we can't use a handshake -- always keep the ball rolling return false; }, - every)); + narrow(every))); } LLSD TimersListener::cancel(const LLSD& params) diff --git a/indra/newview/llinventorylistener.cpp b/indra/newview/llinventorylistener.cpp index 9263663997..753ad3ddeb 100644 --- a/indra/newview/llinventorylistener.cpp +++ b/indra/newview/llinventorylistener.cpp @@ -33,7 +33,7 @@ #include "llwearableitemslist.h" #include "stringize.h" -static const F32 MAX_ITEM_LIMIT = 100; +constexpr S32 MAX_ITEM_LIMIT = 100; LLInventoryListener::LLInventoryListener() : LLEventAPI("LLInventory", diff --git a/indra/newview/llluamanager.cpp b/indra/newview/llluamanager.cpp index 91a34345be..6a725e785f 100644 --- a/indra/newview/llluamanager.cpp +++ b/indra/newview/llluamanager.cpp @@ -56,9 +56,9 @@ std::map LLLUAmanager::sScriptNames; lua_function(sleep, "sleep(seconds): pause the running coroutine") { lua_checkdelta(L, -1); - F32 seconds = lua_tonumber(L, -1); + lua_Number seconds = lua_tonumber(L, -1); lua_pop(L, 1); - llcoro::suspendUntilTimeout(seconds); + llcoro::suspendUntilTimeout(narrow(seconds)); LuaState::getParent(L).set_interrupts_counter(0); return 0; }; -- cgit v1.2.3 From 89992713218dba9f1a15973decad897127e90545 Mon Sep 17 00:00:00 2001 From: Nat Goodspeed Date: Thu, 5 Sep 2024 17:31:14 -0400 Subject: Fix typo in cppfeatures_test.cpp --- indra/newview/tests/cppfeatures_test.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/indra/newview/tests/cppfeatures_test.cpp b/indra/newview/tests/cppfeatures_test.cpp index f5ea3a522b..ca94dcfc95 100644 --- a/indra/newview/tests/cppfeatures_test.cpp +++ b/indra/newview/tests/cppfeatures_test.cpp @@ -283,7 +283,7 @@ void cpp_features_test_object_t::test<8>() ensure("init member inline 1", ii.mFoo==10); InitInlineWithConstructor iici; - ensure("init member inline 2", iici.mFoo=10); + ensure("init member inline 2", iici.mFoo==10); ensure("init member inline 3", iici.mBar==25); } -- cgit v1.2.3 From c816fefb3de3b9b5c0421cf446bacfe1284c13a5 Mon Sep 17 00:00:00 2001 From: Nat Goodspeed Date: Thu, 5 Sep 2024 17:33:13 -0400 Subject: Avoid some classic-C style pointer casts. --- indra/llmeshoptimizer/llmeshoptimizer.cpp | 32 +++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/indra/llmeshoptimizer/llmeshoptimizer.cpp b/indra/llmeshoptimizer/llmeshoptimizer.cpp index 7339454367..9d62a72188 100644 --- a/indra/llmeshoptimizer/llmeshoptimizer.cpp +++ b/indra/llmeshoptimizer/llmeshoptimizer.cpp @@ -57,7 +57,7 @@ void LLMeshOptimizer::generateShadowIndexBufferU32(U32 *destination, S32 index = 0; if (vertex_positions) { - streams[index].data = (const float*)vertex_positions; + streams[index].data = vertex_positions->getF32ptr(); // Despite being LLVector4a, only x, y and z are in use streams[index].size = sizeof(F32) * 3; streams[index].stride = sizeof(F32) * 4; @@ -65,14 +65,14 @@ void LLMeshOptimizer::generateShadowIndexBufferU32(U32 *destination, } if (normals) { - streams[index].data = (const float*)normals; + streams[index].data = normals->getF32ptr(); streams[index].size = sizeof(F32) * 3; streams[index].stride = sizeof(F32) * 4; index++; } if (text_coords) { - streams[index].data = (const float*)text_coords; + streams[index].data = text_coords->mV; streams[index].size = sizeof(F32) * 2; streams[index].stride = sizeof(F32) * 2; index++; @@ -108,21 +108,21 @@ void LLMeshOptimizer::generateShadowIndexBufferU16(U16 *destination, S32 index = 0; if (vertex_positions) { - streams[index].data = (const float*)vertex_positions; + streams[index].data = vertex_positions->getF32ptr(); streams[index].size = sizeof(F32) * 3; streams[index].stride = sizeof(F32) * 4; index++; } if (normals) { - streams[index].data = (const float*)normals; + streams[index].data = normals->getF32ptr(); streams[index].size = sizeof(F32) * 3; streams[index].stride = sizeof(F32) * 4; index++; } if (text_coords) { - streams[index].data = (const float*)text_coords; + streams[index].data = text_coords->mV; streams[index].size = sizeof(F32) * 2; streams[index].stride = sizeof(F32) * 2; index++; @@ -162,9 +162,9 @@ size_t LLMeshOptimizer::generateRemapMultiU32( U64 vertex_count) { meshopt_Stream streams[] = { - {(const float*)vertex_positions, sizeof(F32) * 3, sizeof(F32) * 4}, - {(const float*)normals, sizeof(F32) * 3, sizeof(F32) * 4}, - {(const float*)text_coords, sizeof(F32) * 2, sizeof(F32) * 2}, + {vertex_positions->getF32ptr(), sizeof(F32) * 3, sizeof(F32) * 4}, + {normals->getF32ptr(), sizeof(F32) * 3, sizeof(F32) * 4}, + {text_coords->mV, sizeof(F32) * 2, sizeof(F32) * 2}, }; // Remap can function without indices, @@ -236,7 +236,7 @@ void LLMeshOptimizer::remapPositionsBuffer(LLVector4a * destination_vertices, U64 vertex_count, const unsigned int* remap) { - meshopt_remapVertexBuffer((float*)destination_vertices, (const float*)vertex_positions, vertex_count, sizeof(LLVector4a), remap); + meshopt_remapVertexBuffer(destination_vertices->getF32ptr(), vertex_positions->getF32ptr(), vertex_count, sizeof(LLVector4a), remap); } void LLMeshOptimizer::remapNormalsBuffer(LLVector4a * destination_normalss, @@ -244,7 +244,7 @@ void LLMeshOptimizer::remapNormalsBuffer(LLVector4a * destination_normalss, U64 mormals_count, const unsigned int* remap) { - meshopt_remapVertexBuffer((float*)destination_normalss, (const float*)normals, mormals_count, sizeof(LLVector4a), remap); + meshopt_remapVertexBuffer(destination_normalss->getF32ptr(), normals->getF32ptr(), mormals_count, sizeof(LLVector4a), remap); } void LLMeshOptimizer::remapUVBuffer(LLVector2 * destination_uvs, @@ -252,7 +252,7 @@ void LLMeshOptimizer::remapUVBuffer(LLVector2 * destination_uvs, U64 uv_count, const unsigned int* remap) { - meshopt_remapVertexBuffer((float*)destination_uvs, (const float*)uv_positions, uv_count, sizeof(LLVector2), remap); + meshopt_remapVertexBuffer(destination_uvs->mV, uv_positions->mV, uv_count, sizeof(LLVector2), remap); } //static @@ -273,7 +273,7 @@ U64 LLMeshOptimizer::simplifyU32(U32 *destination, return meshopt_simplifySloppy(destination, indices, index_count, - (const float*)vertex_positions, + vertex_positions->getF32ptr(), vertex_count, vertex_positions_stride, target_index_count, @@ -286,7 +286,7 @@ U64 LLMeshOptimizer::simplifyU32(U32 *destination, return meshopt_simplify(destination, indices, index_count, - (const float*)vertex_positions, + vertex_positions->getF32ptr(), vertex_count, vertex_positions_stride, target_index_count, @@ -315,7 +315,7 @@ U64 LLMeshOptimizer::simplify(U16 *destination, return meshopt_simplifySloppy(destination, indices, index_count, - (const float*)vertex_positions, + vertex_positions->getF32ptr(), vertex_count, vertex_positions_stride, target_index_count, @@ -328,7 +328,7 @@ U64 LLMeshOptimizer::simplify(U16 *destination, return meshopt_simplify(destination, indices, index_count, - (const float*)vertex_positions, + vertex_positions->getF32ptr(), vertex_count, vertex_positions_stride, target_index_count, -- cgit v1.2.3 From 2841ca7229e076f9ddd1b2ec2e77099bab7753d9 Mon Sep 17 00:00:00 2001 From: Ansariel Date: Fri, 6 Sep 2024 00:11:01 +0200 Subject: Fix remaining occurrences of fake BOOL --- indra/newview/llfloaterluadebug.cpp | 2 +- indra/newview/llfloaterluascripts.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/indra/newview/llfloaterluadebug.cpp b/indra/newview/llfloaterluadebug.cpp index dc989fe15d..06877df816 100644 --- a/indra/newview/llfloaterluadebug.cpp +++ b/indra/newview/llfloaterluadebug.cpp @@ -69,7 +69,7 @@ bool LLFloaterLUADebug::postBuild() mLineInput->setCommitCallback(boost::bind(&LLFloaterLUADebug::onExecuteClicked, this)); mLineInput->setSelectAllonCommit(false); - return TRUE; + return true; } LLFloaterLUADebug::~LLFloaterLUADebug() diff --git a/indra/newview/llfloaterluascripts.cpp b/indra/newview/llfloaterluascripts.cpp index 79623cdefb..0eba45ec29 100644 --- a/indra/newview/llfloaterluascripts.cpp +++ b/indra/newview/llfloaterluascripts.cpp @@ -72,7 +72,7 @@ bool LLFloaterLUAScripts::postBuild() mContextMenuHandle = menu->getHandle(); } - return TRUE; + return true; } LLFloaterLUAScripts::~LLFloaterLUAScripts() -- cgit v1.2.3 From 8de8daca601dc85e2b73687856f0a321016b4463 Mon Sep 17 00:00:00 2001 From: Nat Goodspeed Date: Fri, 6 Sep 2024 12:31:24 -0400 Subject: Introduce llless(), and use it for llmin(), llmax(). Add tests to verify that llless() correctly handles signed <=> unsigned comparison, which native "<" does not. --- indra/llcommon/lldefs.h | 46 +++++++++++++++++++++- indra/llcommon/tests/llcond_test.cpp | 75 ++++++++++++++++++++++++++++++++++++ 2 files changed, 119 insertions(+), 2 deletions(-) diff --git a/indra/llcommon/lldefs.h b/indra/llcommon/lldefs.h index 2fbb26dc1a..ed075e8d96 100644 --- a/indra/llcommon/lldefs.h +++ b/indra/llcommon/lldefs.h @@ -28,6 +28,7 @@ #define LL_LLDEFS_H #include "stdtypes.h" +#include #include // Often used array indices @@ -169,6 +170,31 @@ constexpr U32 MAXADDRSTR = 17; // 123.567.901.345 = 15 chars + \0 + // llclampb(a) // clamps a to [0 .. 255] // +// llless(d0, d1) safely compares d0 < d1 even if one is signed and the other +// is unsigned. A simple (d0 < d1) expression converts the signed operand to +// unsigned before comparing. If the signed operand is negative, that flips +// the negative value to a huge positive value, producing the wrong answer! +// llless() specifically addresses that case. +template +inline bool llless(T0 d0, T1 d1) +{ + if constexpr (std::is_signed_v && ! std::is_signed_v) + { + // T0 signed, T1 unsigned: negative d0 is less than any unsigned d1 + if (d0 < 0) + return true; + } + else if constexpr (! std::is_signed_v && std::is_signed_v) + { + // T0 unsigned, T1 signed: any unsigned d0 is greater than negative d1 + if (d1 < 0) + return false; + } + // both T0 and T1 are signed, or both are unsigned, or both non-negative: + // straightforward comparison works + return d0 < d1; +} + // recursion tail template inline auto llmax(T data) @@ -180,7 +206,7 @@ template inline auto llmax(T0 d0, T1 d1, Ts... rest) { auto maxrest = llmax(d1, rest...); - return (d0 > maxrest)? d0 : maxrest; + return llless(maxrest, d0)? d0 : maxrest; } // recursion tail @@ -194,12 +220,28 @@ template inline auto llmin(T0 d0, T1 d1, Ts... rest) { auto minrest = llmin(d1, rest...); - return (d0 < minrest) ? d0 : minrest; + return llless(d0, minrest) ? d0 : minrest; } template inline A llclamp(A a, MIN minval, MAX maxval) { + // The only troublesome case is if A is unsigned and either minval or + // maxval is both signed and negative. Casting a negative number to + // unsigned flips it to a huge positive number, making this llclamp() call + // ineffective. + if constexpr (! std::is_signed_v) + { + if constexpr (std::is_signed_v) + { + assert(minval >= 0); + } + if constexpr (std::is_signed_v) + { + assert(maxval >= 0); + } + } + A aminval{ static_cast(minval) }, amaxval{ static_cast(maxval) }; if ( a < aminval ) { diff --git a/indra/llcommon/tests/llcond_test.cpp b/indra/llcommon/tests/llcond_test.cpp index f2a302ed13..7d1ac6edb9 100644 --- a/indra/llcommon/tests/llcond_test.cpp +++ b/indra/llcommon/tests/llcond_test.cpp @@ -19,6 +19,7 @@ // other Linden headers #include "../test/lltut.h" #include "llcoros.h" +#include "lldefs.h" // llless() /***************************************************************************** * TUT @@ -64,4 +65,78 @@ namespace tut cond.set_all(2); cond.wait_equal(3); } + + template + struct compare + { + const char* desc; + T0 lhs; + T1 rhs; + bool expect; + + void test() const + { + // fails +// ensure_equals(desc, (lhs < rhs), expect); + ensure_equals(desc, llless(lhs, rhs), expect); + } + }; + + template<> template<> + void object::test<3>() + { + set_test_name("comparison"); + // Try to construct signed and unsigned variables such that the + // compiler can't optimize away the code to compare at runtime. + std::istringstream input("-1 10 20 10 20"); + int minus1, s10, s20; + input >> minus1 >> s10 >> s20; + unsigned u10, u20; + input >> u10 >> u20; + ensure_equals("minus1 wrong", minus1, -1); + ensure_equals("s10 wrong", s10, 10); + ensure_equals("s20 wrong", s20, 20); + ensure_equals("u10 wrong", u10, 10); + ensure_equals("u20 wrong", u20, 20); + // signed < signed should always work! + compare ss[] = + { {"minus1 < s10", minus1, s10, true}, + {"s10 < s10", s10, s10, false}, + {"s20 < s10", s20, s20, false} + }; + for (const auto& cmp : ss) + { + cmp.test(); + } + // unsigned < unsigned should always work! + compare uu[] = + { {"u10 < u20", u10, u20, true}, + {"u20 < u20", u20, u20, false}, + {"u20 < u10", u20, u10, false} + }; + for (const auto& cmp : uu) + { + cmp.test(); + } + // signed < unsigned ?? + compare su[] = + { {"minus1 < u10", minus1, u10, true}, + {"s10 < u10", s10, u10, false}, + {"s20 < u10", s20, u10, false} + }; + for (const auto& cmp : su) + { + cmp.test(); + } + // unsigned < signed ?? + compare us[] = + { {"u10 < minus1", u10, minus1, false}, + {"u10 < s10", u10, s10, false}, + {"u10 < s20", u10, s20, true} + }; + for (const auto& cmp : us) + { + cmp.test(); + } + } } // namespace tut -- cgit v1.2.3 From 0a3e5074bbacdd9184311bd99a74913d1ce0423c Mon Sep 17 00:00:00 2001 From: Nat Goodspeed Date: Fri, 6 Sep 2024 14:43:05 -0400 Subject: Allow LLMenuGL::insert() to append as well as inserting. Appending is effected by passing position == getItemCount(). Until now, insert() disallowed that value, so you could insert before the last existing entry but not after it. --- indra/llui/llmenugl.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/indra/llui/llmenugl.cpp b/indra/llui/llmenugl.cpp index 69ffa9a94f..cc770ca90a 100644 --- a/indra/llui/llmenugl.cpp +++ b/indra/llui/llmenugl.cpp @@ -2625,7 +2625,9 @@ void LLMenuGL::insert( S32 position, LLView * ctrl, bool arrange /*= true*/ ) { LLMenuItemGL * item = dynamic_cast(ctrl); - if (NULL == item || position < 0 || position >= mItems.size()) + // If position == size(), std::advance() will return end() -- which is + // okay, because insert(end()) is the same as append(). + if (NULL == item || position < 0 || position > mItems.size()) { return; } -- cgit v1.2.3 From 13ec4bccb534ef55a773004bd3c5a5ac9027600b Mon Sep 17 00:00:00 2001 From: Nat Goodspeed Date: Fri, 6 Sep 2024 15:06:54 -0400 Subject: Add pos to 'UI' listener's 'addMenuItem' and 'addMenuSeparator' 'pos' is a 0-relative index at which to insert the desired menu item or separator. If 'pos' is omitted, the item is appended to the menu. --- indra/newview/lluilistener.cpp | 41 ++++++++++++++++++++++++++++++++++++----- 1 file changed, 36 insertions(+), 5 deletions(-) diff --git a/indra/newview/lluilistener.cpp b/indra/newview/lluilistener.cpp index c389d04443..44eb8461f1 100644 --- a/indra/newview/lluilistener.cpp +++ b/indra/newview/lluilistener.cpp @@ -90,12 +90,14 @@ LLUIListener::LLUIListener(): add("addMenuItem", "Add new menu item [\"name\"] with displayed [\"label\"]\n" "and call-on-click UI function [\"func\"] with optional [\"param\"]\n" - "to the [\"parent_menu\"] within the Top menu.", + "to the [\"parent_menu\"] within the Top menu.\n" + "If [\"pos\"] is present, insert at specified 0-relative position.", &LLUIListener::addMenuItem, required_args.with("func", LLSD())); add("addMenuSeparator", - "Add menu separator to the [\"parent_menu\"] within the Top menu.", + "Add menu separator to the [\"parent_menu\"] within the Top menu.\n" + "If [\"pos\"] is present, insert at specified 0-relative position.", &LLUIListener::addMenuSeparator, llsd::map("parent_menu", LLSD(), "reply", LLSD())); @@ -264,6 +266,13 @@ LLMenuGL* get_parent_menu(LLEventAPI::Response& response, const LLSD&event) return parent_menu; } +// Return event["pos"].asInteger() if passed, but clamp (0 <= pos <= size). +// Reserve -1 return to mean event has no "pos" key. +S32 get_pos(const LLSD& event, U32 size) +{ + return event["pos"].isInteger()? llclamp(event["pos"].asInteger(), 0, size) : -1; +} + void LLUIListener::addMenu(const LLSD&event) const { Response response(LLSD(), event); @@ -302,9 +311,19 @@ void LLUIListener::addMenuItem(const LLSD&event) const item_params.on_click = item_func; if(LLMenuGL* parent_menu = get_parent_menu(response, event)) { - if(!parent_menu->append(LLUICtrlFactory::create(item_params))) + auto item = LLUICtrlFactory::create(item_params); + // Clamp pos to getItemCount(), meaning append. If pos exceeds that, + // insert() will silently ignore the request. + auto pos = get_pos(event, parent_menu->getItemCount()); + if (pos >= 0) { - response.error(stringize("Menu item ", std::quoted(event["name"].asString()), " was not added")); + // insert() returns void: we just have to assume it worked. + parent_menu->insert(pos, item); + } + else if (! parent_menu->append(item)) + { + response.error(stringize("Menu item ", std::quoted(event["name"].asString()), + " was not added")); } } } @@ -314,7 +333,19 @@ void LLUIListener::addMenuSeparator(const LLSD&event) const Response response(LLSD(), event); if(LLMenuGL* parent_menu = get_parent_menu(response, event)) { - if(!parent_menu->addSeparator()) + // Clamp pos to getItemCount(), meaning append. If pos exceeds that, + // insert() will silently ignore the request. + auto pos = get_pos(event, parent_menu->getItemCount()); + if (pos >= 0) + { + // Even though addSeparator() does not accept a position, + // LLMenuItemSeparatorGL isa LLMenuItemGL, so we can use insert(). + LLMenuItemSeparatorGL::Params p; + LLMenuItemGL* separator = LLUICtrlFactory::create(p); + // insert() returns void: we just have to assume it worked. + parent_menu->insert(pos, separator); + } + else if (! parent_menu->addSeparator()) { response.error("Separator was not added"); } -- cgit v1.2.3 From 782a898efadadf2747cc3310749f34a8dde8dd60 Mon Sep 17 00:00:00 2001 From: Nat Goodspeed Date: Fri, 6 Sep 2024 15:11:15 -0400 Subject: Introduce LuaFeature debug setting, default off. Make central Lua engine functionality conditional on that flag. --- indra/llcommon/lua_function.cpp | 160 ++++++++++++++++++++------------ indra/llcommon/lua_function.h | 5 +- indra/newview/app_settings/settings.xml | 11 +++ 3 files changed, 117 insertions(+), 59 deletions(-) diff --git a/indra/llcommon/lua_function.cpp b/indra/llcommon/lua_function.cpp index f7876e4aaf..eefb1e62cf 100644 --- a/indra/llcommon/lua_function.cpp +++ b/indra/llcommon/lua_function.cpp @@ -24,6 +24,7 @@ #include // external library headers // other Linden headers +#include "commoncontrol.h" #include "fsyspath.h" #include "hexdump.h" #include "llcoros.h" @@ -529,9 +530,35 @@ int lua_metaipair(lua_State* L); } // anonymous namespace LuaState::LuaState(script_finished_fn cb): - mCallback(cb), - mState(luaL_newstate()) + mCallback(cb) { + /*---------------------------- feature flag ----------------------------*/ + try + { + mFeature = LL::CommonControl::get("Global", "LuaFeature").asBoolean(); + } + catch (const LL::CommonControl::NoListener&) + { + // If this program doesn't have an LLViewerControlListener, + // it's probably a test program; go ahead. + mFeature = true; + } + catch (const LL::CommonControl::ParamError&) + { + // We found LLViewerControlListener, but its settings do not include + // "LuaFeature". Hmm, fishy: that feature flag was introduced at the + // same time as this code. + mFeature = false; + } + // None of the rest of this is necessary if we're not going to run anything. + if (! mFeature) + { + mError = "Lua feature disabled"; + return; + } + /*---------------------------- feature flag ----------------------------*/ + + mState = luaL_newstate(); // Ensure that we can always find this LuaState instance, given the // lua_State we just created or any of its coroutines. sLuaStateMap.emplace(mState, this); @@ -682,75 +709,81 @@ int lua_metaipair(lua_State* L) LuaState::~LuaState() { - // We're just about to destroy this lua_State mState. Did this Lua chunk - // register any atexit() functions? - lluau_checkstack(mState, 3); - // look up Registry.atexit - lua_getfield(mState, LUA_REGISTRYINDEX, "atexit"); - // stack contains Registry.atexit - if (lua_istable(mState, -1)) + /*---------------------------- feature flag ----------------------------*/ + if (mFeature) + /*---------------------------- feature flag ----------------------------*/ { - // We happen to know that Registry.atexit is built by appending array - // entries using table.insert(). That's important because it means - // there are no holes, and therefore lua_objlen() should be correct. - // That's important because we walk the atexit table backwards, to - // destroy last the things we created (passed to LL.atexit()) first. - int len(lua_objlen(mState, -1)); - LL_DEBUGS("Lua") << LLCoros::getName() << ": Registry.atexit is a table with " - << len << " entries" << LL_ENDL; - - // Push debug.traceback() onto the stack as lua_pcall()'s error - // handler function. On error, lua_pcall() calls the specified error - // handler function with the original error message; the message - // returned by the error handler is then returned by lua_pcall(). - // Luau's debug.traceback() is called with a message to prepend to the - // returned traceback string. Almost as if they'd been designed to - // work together... - lua_getglobal(mState, "debug"); - lua_getfield(mState, -1, "traceback"); - // ditch "debug" - lua_remove(mState, -2); - // stack now contains atexit, debug.traceback() - - for (int i(len); i >= 1; --i) + // We're just about to destroy this lua_State mState. Did this Lua chunk + // register any atexit() functions? + lluau_checkstack(mState, 3); + // look up Registry.atexit + lua_getfield(mState, LUA_REGISTRYINDEX, "atexit"); + // stack contains Registry.atexit + if (lua_istable(mState, -1)) { - lua_pushinteger(mState, i); - // stack contains Registry.atexit, debug.traceback(), i - lua_gettable(mState, -3); - // stack contains Registry.atexit, debug.traceback(), atexit[i] - // Call atexit[i](), no args, no return values. - // Use lua_pcall() because errors in any one atexit() function - // shouldn't cancel the rest of them. Pass debug.traceback() as - // the error handler function. - LL_DEBUGS("Lua") << LLCoros::getName() - << ": calling atexit(" << i << ")" << LL_ENDL; - if (lua_pcall(mState, 0, 0, -2) != LUA_OK) + // We happen to know that Registry.atexit is built by appending array + // entries using table.insert(). That's important because it means + // there are no holes, and therefore lua_objlen() should be correct. + // That's important because we walk the atexit table backwards, to + // destroy last the things we created (passed to LL.atexit()) first. + int len(lua_objlen(mState, -1)); + LL_DEBUGS("Lua") << LLCoros::getName() << ": Registry.atexit is a table with " + << len << " entries" << LL_ENDL; + + // Push debug.traceback() onto the stack as lua_pcall()'s error + // handler function. On error, lua_pcall() calls the specified error + // handler function with the original error message; the message + // returned by the error handler is then returned by lua_pcall(). + // Luau's debug.traceback() is called with a message to prepend to the + // returned traceback string. Almost as if they'd been designed to + // work together... + lua_getglobal(mState, "debug"); + lua_getfield(mState, -1, "traceback"); + // ditch "debug" + lua_remove(mState, -2); + // stack now contains atexit, debug.traceback() + + for (int i(len); i >= 1; --i) { - auto error{ lua_tostdstring(mState, -1) }; - LL_WARNS("Lua") << LLCoros::getName() - << ": atexit(" << i << ") error: " << error << LL_ENDL; - // pop error message - lua_pop(mState, 1); + lua_pushinteger(mState, i); + // stack contains Registry.atexit, debug.traceback(), i + lua_gettable(mState, -3); + // stack contains Registry.atexit, debug.traceback(), atexit[i] + // Call atexit[i](), no args, no return values. + // Use lua_pcall() because errors in any one atexit() function + // shouldn't cancel the rest of them. Pass debug.traceback() as + // the error handler function. + LL_DEBUGS("Lua") << LLCoros::getName() + << ": calling atexit(" << i << ")" << LL_ENDL; + if (lua_pcall(mState, 0, 0, -2) != LUA_OK) + { + auto error{ lua_tostdstring(mState, -1) }; + LL_WARNS("Lua") << LLCoros::getName() + << ": atexit(" << i << ") error: " << error << LL_ENDL; + // pop error message + lua_pop(mState, 1); + } + LL_DEBUGS("Lua") << LLCoros::getName() << ": atexit(" << i << ") done" << LL_ENDL; + // lua_pcall() has already popped atexit[i]: + // stack contains atexit, debug.traceback() } - LL_DEBUGS("Lua") << LLCoros::getName() << ": atexit(" << i << ") done" << LL_ENDL; - // lua_pcall() has already popped atexit[i]: - // stack contains atexit, debug.traceback() + // pop debug.traceback() + lua_pop(mState, 1); } - // pop debug.traceback() + // pop Registry.atexit (either table or nil) lua_pop(mState, 1); - } - // pop Registry.atexit (either table or nil) - lua_pop(mState, 1); - lua_close(mState); + // with the demise of this LuaState, remove sLuaStateMap entry + sLuaStateMap.erase(mState); + + lua_close(mState); + } if (mCallback) { // mError potentially set by previous checkLua() call(s) mCallback(mError); } - // with the demise of this LuaState, remove sLuaStateMap entry - sLuaStateMap.erase(mState); } bool LuaState::checkLua(const std::string& desc, int r) @@ -768,6 +801,14 @@ bool LuaState::checkLua(const std::string& desc, int r) std::pair LuaState::expr(const std::string& desc, const std::string& text) { + /*---------------------------- feature flag ----------------------------*/ + if (! mFeature) + { + // fake an error + return { -1, stringize("Not running ", desc) }; + } + /*---------------------------- feature flag ----------------------------*/ + set_interrupts_counter(0); lua_callbacks(mState)->interrupt = [](lua_State *L, int gc) @@ -843,6 +884,9 @@ std::pair LuaState::expr(const std::string& desc, const std::string& return result; } +// We think we don't need mFeature tests in the rest of these LuaState methods +// because, if expr() isn't running code, nobody should be calling any of them. + LuaListener& LuaState::obtainListener(lua_State* L) { lluau_checkstack(L, 2); diff --git a/indra/llcommon/lua_function.h b/indra/llcommon/lua_function.h index e28656c03b..967d8eaba1 100644 --- a/indra/llcommon/lua_function.h +++ b/indra/llcommon/lua_function.h @@ -115,8 +115,11 @@ public: void check_interrupts_counter(); private: + /*---------------------------- feature flag ----------------------------*/ + bool mFeature{ false }; + /*---------------------------- feature flag ----------------------------*/ script_finished_fn mCallback; - lua_State* mState; + lua_State* mState{ nullptr }; std::string mError; S32 mInterrupts{ 0 }; }; diff --git a/indra/newview/app_settings/settings.xml b/indra/newview/app_settings/settings.xml index 248992fd07..c6946d1ec1 100644 --- a/indra/newview/app_settings/settings.xml +++ b/indra/newview/app_settings/settings.xml @@ -3999,6 +3999,17 @@ scripts/lua + LuaFeature + + Comment + Enable viewer's Lua script engine. + Persist + 1 + Type + Boolean + Value + 0 + LuaRequirePath Comment -- cgit v1.2.3 From 8c68abb2f63d54aeb4614c63a109b838ed8d0656 Mon Sep 17 00:00:00 2001 From: Nat Goodspeed Date: Fri, 6 Sep 2024 15:14:59 -0400 Subject: Remove Lua floaters from menu_viewer.xml; re-add if Lua enabled. Add a menus.lua autorun script that waits until login, then adds the Lua floaters back into the Develop->Consoles menu where they were originally. Extend UI.addMenuItem() and addMenuSeparator() to support pos argument. --- indra/newview/scripts/lua/auto/menus.lua | 51 ++++++++++++++++++++++ indra/newview/scripts/lua/require/UI.lua | 4 +- indra/newview/skins/default/xui/en/menu_viewer.xml | 21 --------- 3 files changed, 53 insertions(+), 23 deletions(-) create mode 100644 indra/newview/scripts/lua/auto/menus.lua diff --git a/indra/newview/scripts/lua/auto/menus.lua b/indra/newview/scripts/lua/auto/menus.lua new file mode 100644 index 0000000000..b2f54d83df --- /dev/null +++ b/indra/newview/scripts/lua/auto/menus.lua @@ -0,0 +1,51 @@ +-- Inject Lua-related menus into the top menu structure. Run this as a Lua +-- script so that turning off the Lua feature also disables these menus. + +-- Under Develop -> Consoles, want to present the equivalent of: +-- +-- +-- +-- +-- +-- +-- +-- +-- + +local startup = require 'startup' +local UI = require 'UI' + +-- Don't mess with the viewer's menu structure until we've logged in. +startup.wait('STATE_STARTED') + +-- Add LUA Debug Console to Develop->Consoles +local pos = 9 +UI.addMenuSeparator{ + parent_menu='Consoles', pos=pos, +} +pos += 1 +UI.addMenuItem{ + parent_menu='Consoles', pos=pos, + name='lua_debug', label='LUA Debug Console', + func='Floater.ToggleOrBringToFront', param='lua_debug', +} +pos += 1 + +-- Add LUA Scripts Info to Develop->Consoles +UI.addMenuItem{ + parent_menu='Consoles', pos=pos, + name='lua_scripts', label='LUA Scripts Info', + func='Floater.ToggleOrBringToFront', param='lua_scripts', +} diff --git a/indra/newview/scripts/lua/require/UI.lua b/indra/newview/scripts/lua/require/UI.lua index 73a76fa6b8..cf2695917e 100644 --- a/indra/newview/scripts/lua/require/UI.lua +++ b/indra/newview/scripts/lua/require/UI.lua @@ -170,13 +170,13 @@ end -- see UI.callables() for valid values of 'func' function UI.addMenuItem(...) - local args = mapargs('name,label,parent_menu,func,param', ...) + local args = mapargs('name,label,parent_menu,func,param,pos', ...) args.op = 'addMenuItem' return leap.request('UI', args) end function UI.addMenuSeparator(...) - local args = mapargs('parent_menu', ...) + local args = mapargs('parent_menu,pos', ...) args.op = 'addMenuSeparator' return leap.request('UI', args) end diff --git a/indra/newview/skins/default/xui/en/menu_viewer.xml b/indra/newview/skins/default/xui/en/menu_viewer.xml index 8999797049..7fe9d0efe5 100644 --- a/indra/newview/skins/default/xui/en/menu_viewer.xml +++ b/indra/newview/skins/default/xui/en/menu_viewer.xml @@ -2565,27 +2565,6 @@ function="World.EnvPreset" parameter="scene monitor" /> - - - - - - - - - Date: Fri, 6 Sep 2024 16:57:12 -0400 Subject: Avoid VC fatal warning when trying to fix un/signed comparison. --- indra/llcommon/lldefs.h | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/indra/llcommon/lldefs.h b/indra/llcommon/lldefs.h index ed075e8d96..d4b063f88c 100644 --- a/indra/llcommon/lldefs.h +++ b/indra/llcommon/lldefs.h @@ -183,16 +183,23 @@ inline bool llless(T0 d0, T1 d1) // T0 signed, T1 unsigned: negative d0 is less than any unsigned d1 if (d0 < 0) return true; + // both are non-negative: explicitly cast to avoid C4018 + return std::make_unsigned_t(d0) < d1; } else if constexpr (! std::is_signed_v && std::is_signed_v) { // T0 unsigned, T1 signed: any unsigned d0 is greater than negative d1 if (d1 < 0) return false; + // both are non-negative: explicitly cast to avoid C4018 + return d0 < std::make_unsigned_t(d1); + } + else + { + // both T0 and T1 are signed, or both are unsigned: + // straightforward comparison works + return d0 < d1; } - // both T0 and T1 are signed, or both are unsigned, or both non-negative: - // straightforward comparison works - return d0 < d1; } // recursion tail -- cgit v1.2.3 From ea8d7cb40501af9b31d784da78aaaf7353af58f3 Mon Sep 17 00:00:00 2001 From: Andrey Lihatskiy Date: Mon, 9 Sep 2024 18:27:44 +0300 Subject: Fix viewer channel selection based on a branch (#2528) --- .github/workflows/build.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index 55bef46ff6..1804da749f 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -152,7 +152,7 @@ jobs: id: build shell: bash env: - AUTOBUILD_VCS_BRANCH: ${{ needs.setvar.outputs.branch }} + AUTOBUILD_VCS_BRANCH: ${{ steps.which-branch.outputs.branch }} RUNNER_OS: ${{ runner.os }} run: | # set up things the viewer's build.sh script expects -- cgit v1.2.3 From b8678d8fa3521f4496ddc569de391633711e46cb Mon Sep 17 00:00:00 2001 From: Nat Goodspeed Date: Tue, 10 Sep 2024 09:56:36 -0400 Subject: Fix a couple errors from merging in new code. --- indra/newview/llviewermenu.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/indra/newview/llviewermenu.cpp b/indra/newview/llviewermenu.cpp index a5d199d9d7..3b45dd71a7 100644 --- a/indra/newview/llviewermenu.cpp +++ b/indra/newview/llviewermenu.cpp @@ -9768,8 +9768,8 @@ void initialize_menus() registrar.add("Agent.ToggleMicrophone", boost::bind(&LLAgent::toggleMicrophone, _2), cb_info::UNTRUSTED_BLOCK); enable.add("Agent.IsMicrophoneOn", boost::bind(&LLAgent::isMicrophoneOn, _2)); enable.add("Agent.IsActionAllowed", boost::bind(&LLAgent::isActionAllowed, _2)); - commit.add("Agent.ToggleHearMediaSoundFromAvatar", boost::bind(&LLAgent::toggleHearMediaSoundFromAvatar)); - commit.add("Agent.ToggleHearVoiceFromAvatar", boost::bind(&LLAgent::toggleHearVoiceFromAvatar)); + registrar.add("Agent.ToggleHearMediaSoundFromAvatar", boost::bind(&LLAgent::toggleHearMediaSoundFromAvatar), cb_info::UNTRUSTED_BLOCK); + registrar.add("Agent.ToggleHearVoiceFromAvatar", boost::bind(&LLAgent::toggleHearVoiceFromAvatar), cb_info::UNTRUSTED_BLOCK); // File menu init_menu_file(); -- cgit v1.2.3 From 26efc7e376ef52284a6281f36cf45eb03bc13507 Mon Sep 17 00:00:00 2001 From: Nat Goodspeed Date: Tue, 10 Sep 2024 15:25:07 -0400 Subject: Pass std::string_view by value, not by const reference. Consensus seems to be that (a) string_view is, in effect, already a reference, (b) it's small enough to make pass-by-value reasonable and (c) the optimizer can reason about values way better than it can about references. --- indra/llcommon/hexdump.h | 4 ++-- indra/llcommon/llleaplistener.cpp | 2 +- indra/llcommon/llleaplistener.h | 2 +- indra/llcommon/lua_function.cpp | 4 ++-- indra/llcommon/lua_function.h | 8 ++++---- indra/llcommon/tests/llrand_test.cpp | 2 +- indra/newview/llluamanager.cpp | 2 +- indra/newview/tests/llluamanager_test.cpp | 2 +- indra/test/namedtempfile.h | 24 ++++++++++++------------ 9 files changed, 25 insertions(+), 25 deletions(-) diff --git a/indra/llcommon/hexdump.h b/indra/llcommon/hexdump.h index ab5ba2b16d..4b734426a3 100755 --- a/indra/llcommon/hexdump.h +++ b/indra/llcommon/hexdump.h @@ -25,7 +25,7 @@ namespace LL class hexdump { public: - hexdump(const std::string_view& data): + hexdump(std::string_view data): hexdump(data.data(), data.length()) {} @@ -66,7 +66,7 @@ private: class hexmix { public: - hexmix(const std::string_view& data): + hexmix(std::string_view data): mData(data) {} diff --git a/indra/llcommon/llleaplistener.cpp b/indra/llcommon/llleaplistener.cpp index b81ee66ba9..9742c9e9de 100644 --- a/indra/llcommon/llleaplistener.cpp +++ b/indra/llcommon/llleaplistener.cpp @@ -54,7 +54,7 @@ return features; } -LLLeapListener::LLLeapListener(const std::string_view& caller, const Callback& callback): +LLLeapListener::LLLeapListener(std::string_view caller, const Callback& callback): // Each LEAP plugin has an instance of this listener. Make the command // pump name difficult for other such plugins to guess. LLEventAPI(LLUUID::generateNewID().asString(), diff --git a/indra/llcommon/llleaplistener.h b/indra/llcommon/llleaplistener.h index d36d2ff8db..d38a6f4ace 100644 --- a/indra/llcommon/llleaplistener.h +++ b/indra/llcommon/llleaplistener.h @@ -28,7 +28,7 @@ public: * event is received. */ using Callback = std::function; - LLLeapListener(const std::string_view& caller, const Callback& callback); + LLLeapListener(std::string_view caller, const Callback& callback); ~LLLeapListener(); LLEventPump& getReplyPump() { return mReplyPump; } diff --git a/indra/llcommon/lua_function.cpp b/indra/llcommon/lua_function.cpp index eefb1e62cf..380e650360 100644 --- a/indra/llcommon/lua_function.cpp +++ b/indra/llcommon/lua_function.cpp @@ -999,8 +999,8 @@ LuaPopper::~LuaPopper() /***************************************************************************** * LuaFunction class *****************************************************************************/ -LuaFunction::LuaFunction(const std::string_view& name, lua_CFunction function, - const std::string_view& helptext) +LuaFunction::LuaFunction(std::string_view name, lua_CFunction function, + std::string_view helptext) { const auto& [registry, lookup] = getState(); registry.emplace(name, Registry::mapped_type{ function, helptext }); diff --git a/indra/llcommon/lua_function.h b/indra/llcommon/lua_function.h index 967d8eaba1..12e74b5d04 100644 --- a/indra/llcommon/lua_function.h +++ b/indra/llcommon/lua_function.h @@ -351,7 +351,7 @@ auto lua_setfieldv(lua_State* L, int index, const char* k, const T& value) // return to C++, from table at index, the value of field k (without metamethods) template -auto lua_rawgetfield(lua_State* L, int index, const std::string_view& k) +auto lua_rawgetfield(lua_State* L, int index, std::string_view k) { index = lua_absindex(L, index); lua_checkdelta(L); @@ -364,7 +364,7 @@ auto lua_rawgetfield(lua_State* L, int index, const std::string_view& k) // set in table at index, as field k, the specified C++ value (without metamethods) template -void lua_rawsetfield(lua_State* L, int index, const std::string_view& k, const T& value) +void lua_rawsetfield(lua_State* L, int index, std::string_view k, const T& value) { index = lua_absindex(L, index); lua_checkdelta(L); @@ -389,8 +389,8 @@ void lua_rawsetfield(lua_State* L, int index, const std::string_view& k, const T class LuaFunction { public: - LuaFunction(const std::string_view& name, lua_CFunction function, - const std::string_view& helptext); + LuaFunction(std::string_view name, lua_CFunction function, + std::string_view helptext); static void init(lua_State* L); diff --git a/indra/llcommon/tests/llrand_test.cpp b/indra/llcommon/tests/llrand_test.cpp index a0dd4ef576..85dd53ce96 100644 --- a/indra/llcommon/tests/llrand_test.cpp +++ b/indra/llcommon/tests/llrand_test.cpp @@ -38,7 +38,7 @@ // testing extent < 0, negate the return value and the extent before passing // into ensure_in_range(). template -void ensure_in_range(const std::string_view& name, +void ensure_in_range(std::string_view name, NUMBER value, NUMBER low, NUMBER high) { auto failmsg{ stringize(name, " >= ", low, " (", value, ')') }; diff --git a/indra/newview/llluamanager.cpp b/indra/newview/llluamanager.cpp index 6a725e785f..0bd21516bc 100644 --- a/indra/newview/llluamanager.cpp +++ b/indra/newview/llluamanager.cpp @@ -65,7 +65,7 @@ lua_function(sleep, "sleep(seconds): pause the running coroutine") // This function consumes ALL Lua stack arguments and returns concatenated // message string -std::string lua_print_msg(lua_State* L, const std::string_view& level) +std::string lua_print_msg(lua_State* L, std::string_view level) { // On top of existing Lua arguments, we're going to push tostring() and // duplicate each existing stack entry so we can stringize each one. diff --git a/indra/newview/tests/llluamanager_test.cpp b/indra/newview/tests/llluamanager_test.cpp index 8d1333815b..f0a1b32eed 100644 --- a/indra/newview/tests/llluamanager_test.cpp +++ b/indra/newview/tests/llluamanager_test.cpp @@ -123,7 +123,7 @@ namespace tut } } - void from_lua(const std::string& desc, const std::string_view& construct, const LLSD& expect) + void from_lua(const std::string& desc, std::string_view construct, const LLSD& expect) { LLSD fromlua; LLStreamListener pump("testpump", diff --git a/indra/test/namedtempfile.h b/indra/test/namedtempfile.h index 8027f95728..96b19523ab 100644 --- a/indra/test/namedtempfile.h +++ b/indra/test/namedtempfile.h @@ -32,18 +32,18 @@ class NamedTempFile: public boost::noncopyable { LOG_CLASS(NamedTempFile); public: - NamedTempFile(const std::string_view& pfx, - const std::string_view& content, - const std::string_view& sfx=std::string_view("")) + NamedTempFile(std::string_view pfx, + std::string_view content, + std::string_view sfx=std::string_view("")) { createFile(pfx, [&content](std::ostream& out){ out << content; }, sfx); } // Disambiguate when passing string literal -- unclear why a string // literal should be ambiguous wrt std::string_view and Streamer - NamedTempFile(const std::string_view& pfx, + NamedTempFile(std::string_view pfx, const char* content, - const std::string_view& sfx=std::string_view("")) + std::string_view sfx=std::string_view("")) { createFile(pfx, [&content](std::ostream& out){ out << content; }, sfx); } @@ -53,9 +53,9 @@ public: // (boost::phoenix::placeholders::arg1 << "the value is " << 17 << '\n') typedef std::function Streamer; - NamedTempFile(const std::string_view& pfx, + NamedTempFile(std::string_view pfx, const Streamer& func, - const std::string_view& sfx=std::string_view("")) + std::string_view sfx=std::string_view("")) { createFile(pfx, func, sfx); } @@ -94,8 +94,8 @@ public: return out; } - static boost::filesystem::path temp_path(const std::string_view& pfx="", - const std::string_view& sfx="") + static boost::filesystem::path temp_path(std::string_view pfx="", + std::string_view sfx="") { // This variable is set by GitHub actions and is the recommended place // to put temp files belonging to an actions job. @@ -114,9 +114,9 @@ public: } protected: - void createFile(const std::string_view& pfx, + void createFile(std::string_view pfx, const Streamer& func, - const std::string_view& sfx) + std::string_view sfx) { // Create file in a temporary place. mPath = temp_path(pfx, sfx); @@ -137,7 +137,7 @@ class NamedExtTempFile: public NamedTempFile { LOG_CLASS(NamedExtTempFile); public: - NamedExtTempFile(const std::string& ext, const std::string_view& content): + NamedExtTempFile(const std::string& ext, std::string_view content): NamedTempFile(remove_dot(ext), content, ensure_dot(ext)) {} -- cgit v1.2.3 From 9f38f25b93be2566399fac2d528da9810edd2fa6 Mon Sep 17 00:00:00 2001 From: Nat Goodspeed Date: Tue, 10 Sep 2024 15:28:16 -0400 Subject: llinstancetracker.h was missing an #include from last merge. --- indra/llcommon/llinstancetracker.h | 1 + 1 file changed, 1 insertion(+) diff --git a/indra/llcommon/llinstancetracker.h b/indra/llcommon/llinstancetracker.h index 03418e9bad..b5c681e60a 100644 --- a/indra/llcommon/llinstancetracker.h +++ b/indra/llcommon/llinstancetracker.h @@ -41,6 +41,7 @@ #include #include +#include "llprofiler.h" #include "lockstatic.h" #include "stringize.h" -- cgit v1.2.3 From dca6f0deae49d133f180ef937939b8648649fbc6 Mon Sep 17 00:00:00 2001 From: Nat Goodspeed Date: Tue, 10 Sep 2024 17:16:14 -0400 Subject: Fix risky signature of `wchar_to_utf8chars()`. Add `ll_convert()` alias. `wchar_to_utf8chars()` used to require a `char*` output buffer with no length, assuming that its caller knew enough to provide a buffer of sufficient length. In fact a `char[8]` buffer suffices, but nothing in the header indicated that. Eliminate the output parameter and return `std::string`. Fix the few existing callers. Also set an `ll_convert_alias` so that `ll_convert_to(llwchar)` directly calls `wchar_to_utf8chars()`. Replace instances of the workaround `wstring_to_utf8str(LLWString(1, llwchar))`. --- indra/llcommon/llstring.cpp | 20 ++++++++------------ indra/llcommon/llstring.h | 6 +++++- indra/llui/llviewereventrecorder.cpp | 11 +++++------ indra/newview/llpanelemojicomplete.cpp | 3 +-- indra/newview/llviewermedia.cpp | 3 ++- 5 files changed, 21 insertions(+), 22 deletions(-) diff --git a/indra/llcommon/llstring.cpp b/indra/llcommon/llstring.cpp index 505789f9ea..4d7cf90310 100644 --- a/indra/llcommon/llstring.cpp +++ b/indra/llcommon/llstring.cpp @@ -141,10 +141,10 @@ std::string rawstr_to_utf8(const std::string& raw) return wstring_to_utf8str(wstr); } -std::ptrdiff_t wchar_to_utf8chars(llwchar in_char, char* outchars) +std::string wchar_to_utf8chars(llwchar in_char) { - U32 cur_char = (U32)in_char; - char* base = outchars; + U32 cur_char(in_char); + char buff[8], *outchars = buff; if (cur_char < 0x80) { *outchars++ = (U8)cur_char; @@ -189,7 +189,7 @@ std::ptrdiff_t wchar_to_utf8chars(llwchar in_char, char* outchars) LL_WARNS() << "Invalid Unicode character " << cur_char << "!" << LL_ENDL; *outchars++ = LL_UNKNOWN_CHAR; } - return outchars - base; + return { buff, std::string::size_type(outchars - buff) }; } auto utf16chars_to_wchar(const U16* inchars, llwchar* outchar) @@ -367,13 +367,12 @@ std::string wchar_utf8_preview(const llwchar wc) std::ostringstream oss; oss << std::hex << std::uppercase << (U32)wc; - U8 out_bytes[8]; - U32 size = (U32)wchar_to_utf8chars(wc, (char*)out_bytes); + auto out_bytes = wchar_to_utf8chars(wc); - if (size > 1) + if (out_bytes.length() > 1) { oss << " ["; - for (U32 i = 0; i < size; ++i) + for (U32 i = 0; i < out_bytes.length(); ++i) { if (i) { @@ -492,10 +491,7 @@ std::string wstring_to_utf8str(const llwchar* utf32str, size_t len) S32 i = 0; while (i < len) { - char tchars[8]; /* Flawfinder: ignore */ - auto n = wchar_to_utf8chars(utf32str[i], tchars); - tchars[n] = 0; - out += tchars; + out += wchar_to_utf8chars(utf32str[i]); i++; } return out; diff --git a/indra/llcommon/llstring.h b/indra/llcommon/llstring.h index e4be1efaed..b552aede82 100644 --- a/indra/llcommon/llstring.h +++ b/indra/llcommon/llstring.h @@ -707,7 +707,11 @@ ll_convert_forms(ll_convert_alias, LLWString, std::string, utf8str_to_ // Same function, better name. JC inline LLWString utf8string_to_wstring(const std::string& utf8_string) { return utf8str_to_wstring(utf8_string); } -LL_COMMON_API std::ptrdiff_t wchar_to_utf8chars(llwchar inchar, char* outchars); +// return a UTF-8 string representation of a single llwchar, which we +// occasionally require: +// cheaper than ll_convert_to(LLWString(1, inchar)) +LL_COMMON_API std::string wchar_to_utf8chars(llwchar inchar); +ll_convert_alias(std::string, llwchar, wchar_to_utf8chars(in)); ll_convert_forms(ll_convert_alias, std::string, LLWString, wstring_to_utf8str); ll_convert_forms(ll_convert_u16_alias, std::string, llutf16string, utf16str_to_utf8str); diff --git a/indra/llui/llviewereventrecorder.cpp b/indra/llui/llviewereventrecorder.cpp index 6d907d7e45..0a4fe5234b 100644 --- a/indra/llui/llviewereventrecorder.cpp +++ b/indra/llui/llviewereventrecorder.cpp @@ -24,9 +24,10 @@ */ -#include "llviewereventrecorder.h" -#include "llui.h" #include "llleap.h" +#include "llstring.h" +#include "llui.h" +#include "llviewereventrecorder.h" LLViewerEventRecorder::LLViewerEventRecorder() { @@ -247,11 +248,9 @@ void LLViewerEventRecorder::logKeyUnicodeEvent(llwchar uni_char) { // keycode...or // char - LL_DEBUGS() << "Wrapped in conversion to wstring " << wstring_to_utf8str(LLWString( 1, uni_char)) << "\n" << LL_ENDL; + LL_DEBUGS() << "Wrapped in conversion to wstring " << ll_convert_to(uni_char) << "\n" << LL_ENDL; - event.insert("char", - LLSD( wstring_to_utf8str(LLWString( 1,uni_char)) ) - ); + event.insert("char", LLSD(ll_convert_to(uni_char))); // path (optional) - for now we are not recording path for key events during record - should not be needed for full record and playback of recorded steps // as a vita script - it does become useful if you edit the resulting vita script and wish to remove some steps leading to a key event - that sort of edit might diff --git a/indra/newview/llpanelemojicomplete.cpp b/indra/newview/llpanelemojicomplete.cpp index cb89a5910e..7f72677e34 100644 --- a/indra/newview/llpanelemojicomplete.cpp +++ b/indra/newview/llpanelemojicomplete.cpp @@ -280,8 +280,7 @@ void LLPanelEmojiComplete::onCommit() { if (mCurSelected < mTotalEmojis) { - LLSD value(wstring_to_utf8str(LLWString(1, mEmojis[mCurSelected].Character))); - setValue(value); + setValue(ll_convert_to(mEmojis[mCurSelected].Character)); LLUICtrl::onCommit(); } } diff --git a/indra/newview/llviewermedia.cpp b/indra/newview/llviewermedia.cpp index 9739cac311..1c8f1a32b9 100644 --- a/indra/newview/llviewermedia.cpp +++ b/indra/newview/llviewermedia.cpp @@ -2766,7 +2766,8 @@ bool LLViewerMediaImpl::handleUnicodeCharHere(llwchar uni_char) { LLSD native_key_data = gViewerWindow->getWindow()->getNativeKeyData(); - mMediaSource->textInput(wstring_to_utf8str(LLWString(1, uni_char)), gKeyboard->currentMask(false), native_key_data); + mMediaSource->textInput(ll_convert_to(uni_char), + gKeyboard->currentMask(false), native_key_data); } } -- cgit v1.2.3 From 96e061547d15cb44dbe091e7aec507b2d9c87da2 Mon Sep 17 00:00:00 2001 From: Nat Goodspeed Date: Tue, 10 Sep 2024 17:22:35 -0400 Subject: In llstring.cpp, build result strings using basic_ostringstream. Many of the string conversion functions in llstring.cpp would build their result strings using successive concatenation operations, piece by piece. This can be expensive in allocations. Instead, use a std::basic_ostringstream of char type appropriate to the return string type to aggregate piecewise string building. --- indra/llcommon/llstring.cpp | 100 +++++++++++++++++++++++++------------------- 1 file changed, 58 insertions(+), 42 deletions(-) diff --git a/indra/llcommon/llstring.cpp b/indra/llcommon/llstring.cpp index 4d7cf90310..614075498c 100644 --- a/indra/llcommon/llstring.cpp +++ b/indra/llcommon/llstring.cpp @@ -214,7 +214,8 @@ auto utf16chars_to_wchar(const U16* inchars, llwchar* outchar) llutf16string wstring_to_utf16str(const llwchar* utf32str, size_t len) { - llutf16string out; + // ostringstream for llutf16string + std::basic_ostringstream out; S32 i = 0; while (i < len) @@ -222,16 +223,16 @@ llutf16string wstring_to_utf16str(const llwchar* utf32str, size_t len) U32 cur_char = utf32str[i]; if (cur_char > 0xFFFF) { - out += (0xD7C0 + (cur_char >> 10)); - out += (0xDC00 | (cur_char & 0x3FF)); + out.put(U16(0xD7C0 + (cur_char >> 10))); + out.put(U16(0xDC00 | (cur_char & 0x3FF))); } else { - out += cur_char; + out.put(U16(cur_char)); } i++; } - return out; + return out.str(); } llutf16string utf8str_to_utf16str( const char* utf8str, size_t len ) @@ -242,8 +243,10 @@ llutf16string utf8str_to_utf16str( const char* utf8str, size_t len ) LLWString utf16str_to_wstring(const U16* utf16str, size_t len) { - LLWString wout; - if (len == 0) return wout; + if (len == 0) return {}; + + // ostringstream for LLWString + std::basic_ostringstream wout; S32 i = 0; const U16* chars16 = utf16str; @@ -251,9 +254,9 @@ LLWString utf16str_to_wstring(const U16* utf16str, size_t len) { llwchar cur_char; i += (S32)utf16chars_to_wchar(chars16+i, &cur_char); - wout += cur_char; + wout << cur_char; } - return wout; + return wout.str(); } // Length in llwchar (UTF-32) of the first len units (16 bits) of the given UTF-16 string. @@ -398,7 +401,8 @@ S32 wstring_utf8_length(const LLWString& wstr) LLWString utf8str_to_wstring(const char* utf8str, size_t len) { - LLWString wout; + // ostringstream for LLWString + std::basic_ostringstream wout; S32 i = 0; while (i < len) @@ -441,7 +445,7 @@ LLWString utf8str_to_wstring(const char* utf8str, size_t len) } else { - wout += LL_UNKNOWN_CHAR; + wout << LL_UNKNOWN_CHAR; ++i; continue; } @@ -478,23 +482,21 @@ LLWString utf8str_to_wstring(const char* utf8str, size_t len) } } - wout += unichar; + wout << unichar; ++i; } - return wout; + return wout.str(); } std::string wstring_to_utf8str(const llwchar* utf32str, size_t len) { - std::string out; + std::ostringstream out; - S32 i = 0; - while (i < len) + for (size_t i = 0; i < len; ++i) { - out += wchar_to_utf8chars(utf32str[i]); - i++; + out << wchar_to_utf8chars(utf32str[i]); } - return out; + return out.str(); } std::string utf16str_to_utf8str(const U16* utf16str, size_t len) @@ -682,7 +684,21 @@ llwchar utf8str_to_wchar(const std::string& utf8str, size_t offset, size_t lengt std::string utf8str_showBytesUTF8(const std::string& utf8str) { - std::string result; + std::ostringstream result; + char lastchar = '\0'; + auto append = [&result, &lastchar](char c) + { + lastchar = c; + result << c; + }; + auto appends = [&result, &lastchar](const std::string& s) + { + if (! s.empty()) + { + lastchar = s.back(); + result << s; + } + }; bool in_sequence = false; size_t sequence_size = 0; @@ -691,9 +707,9 @@ std::string utf8str_showBytesUTF8(const std::string& utf8str) auto open_sequence = [&]() { - if (!result.empty() && result.back() != '\n') - result += '\n'; // Use LF as a separator before new UTF-8 sequence - result += '['; + if (lastchar != '\0' && lastchar != '\n') + append('\n'); // Use LF as a separator before new UTF-8 sequence + append('['); in_sequence = true; }; @@ -702,9 +718,9 @@ std::string utf8str_showBytesUTF8(const std::string& utf8str) llwchar unicode = utf8str_to_wchar(utf8str, byte_index - sequence_size, sequence_size); if (unicode != LL_UNKNOWN_CHAR) { - result += llformat("+%04X", unicode); + appends(llformat("+%04X", unicode)); } - result += ']'; + append(']'); in_sequence = false; sequence_size = 0; }; @@ -725,9 +741,9 @@ std::string utf8str_showBytesUTF8(const std::string& utf8str) } else // Continue the same UTF-8 sequence { - result += '.'; + append('.'); } - result += llformat("%02X", byte); // The byte is represented in hexadecimal form + appends(llformat("%02X", byte)); // The byte is represented in hexadecimal form ++sequence_size; } else // ASCII symbol is represented as a character @@ -737,10 +753,10 @@ std::string utf8str_showBytesUTF8(const std::string& utf8str) close_sequence(); if (byte != '\n') { - result += '\n'; // Use LF as a separator between UTF-8 and ASCII + append('\n'); // Use LF as a separator between UTF-8 and ASCII } } - result += byte; + append(byte); } ++byte_index; } @@ -750,7 +766,7 @@ std::string utf8str_showBytesUTF8(const std::string& utf8str) close_sequence(); } - return result; + return result.str(); } // Search for any emoji symbol, return true if found @@ -1583,7 +1599,7 @@ S32 LLStringUtil::format(std::string& s, const format_map_t& substitutions) LL_PROFILE_ZONE_SCOPED_CATEGORY_STRING; S32 res = 0; - std::string output; + std::ostringstream output; std::vector tokens; std::string::size_type start = 0; @@ -1591,7 +1607,7 @@ S32 LLStringUtil::format(std::string& s, const format_map_t& substitutions) std::string::size_type key_start = 0; while ((key_start = getSubstitution(s, start, tokens)) != std::string::npos) { - output += std::string(s, prev_start, key_start-prev_start); + output << std::string(s, prev_start, key_start-prev_start); prev_start = start; bool found_replacement = false; @@ -1632,20 +1648,20 @@ S32 LLStringUtil::format(std::string& s, const format_map_t& substitutions) if (found_replacement) { - output += replacement; + output << replacement; res++; } else { // we had no replacement, use the string as is // e.g. "hello [MISSING_REPLACEMENT]" or "-=[Stylized Name]=-" - output += std::string(s, key_start, start-key_start); + output << std::string(s, key_start, start-key_start); } tokens.clear(); } // send the remainder of the string (with no further matches for bracketed names) - output += std::string(s, start); - s = output; + output << std::string(s, start); + s = output.str(); return res; } @@ -1661,7 +1677,7 @@ S32 LLStringUtil::format(std::string& s, const LLSD& substitutions) return res; } - std::string output; + std::ostringstream output; std::vector tokens; std::string::size_type start = 0; @@ -1669,7 +1685,7 @@ S32 LLStringUtil::format(std::string& s, const LLSD& substitutions) std::string::size_type key_start = 0; while ((key_start = getSubstitution(s, start, tokens)) != std::string::npos) { - output += std::string(s, prev_start, key_start-prev_start); + output << std::string(s, prev_start, key_start-prev_start); prev_start = start; bool found_replacement = false; @@ -1702,20 +1718,20 @@ S32 LLStringUtil::format(std::string& s, const LLSD& substitutions) if (found_replacement) { - output += replacement; + output << replacement; res++; } else { // we had no replacement, use the string as is // e.g. "hello [MISSING_REPLACEMENT]" or "-=[Stylized Name]=-" - output += std::string(s, key_start, start-key_start); + output << std::string(s, key_start, start-key_start); } tokens.clear(); } // send the remainder of the string (with no further matches for bracketed names) - output += std::string(s, start); - s = output; + output << std::string(s, start); + s = output.str(); return res; } -- cgit v1.2.3 From d5712689d36a1ee1af32242706901fde7229b08d Mon Sep 17 00:00:00 2001 From: Nat Goodspeed Date: Wed, 11 Sep 2024 12:29:18 -0400 Subject: Make Develop->Render Tests->Frame Profile dump JSON to a file too. Make `LLGLSLShader::finishProfile()` accept a string pathname instead of a bool and, in addition to logging statistics to the viewer log, output statistics to that file as JSON. The calls that used to pass `emit_report=false` now pass `report_name=std::string()`. Make llviewerdisplay.cpp's `display()` function synthesize a profile filename in the viewer's logs directory, and pass that filename to `LLGLSLShader::finishProfile()`. --- indra/llrender/llglslshader.cpp | 120 +++++++++++++++++++++++-------------- indra/llrender/llglslshader.h | 5 +- indra/newview/llfeaturemanager.cpp | 2 +- indra/newview/llglsandbox.cpp | 2 +- indra/newview/llviewerdisplay.cpp | 102 +++++++++++++++++++++++-------- 5 files changed, 158 insertions(+), 73 deletions(-) diff --git a/indra/llrender/llglslshader.cpp b/indra/llrender/llglslshader.cpp index a157bfee21..bb734971d5 100644 --- a/indra/llrender/llglslshader.cpp +++ b/indra/llrender/llglslshader.cpp @@ -41,6 +41,8 @@ #include "OpenGL/OpenGL.h" #endif +#include + // Print-print list of shader included source files that are linked together via glAttachShader() // i.e. On macOS / OSX the AMD GLSL linker will display an error if a varying is left in an undefined state. #define DEBUG_SHADER_INCLUDES 0 @@ -101,9 +103,9 @@ void LLGLSLShader::initProfile() sTotalSamplesDrawn = 0; sTotalBinds = 0; - for (std::set::iterator iter = sInstances.begin(); iter != sInstances.end(); ++iter) + for (auto ptr : sInstances) { - (*iter)->clearStats(); + ptr->clearStats(); } } @@ -117,48 +119,71 @@ struct LLGLSLShaderCompareTimeElapsed }; //static -void LLGLSLShader::finishProfile(bool emit_report) +void LLGLSLShader::finishProfile(const std::string& report_name) { sProfileEnabled = false; - if (emit_report) + if (! report_name.empty()) { - std::vector sorted; - - for (std::set::iterator iter = sInstances.begin(); iter != sInstances.end(); ++iter) - { - sorted.push_back(*iter); - } - + std::vector sorted(sInstances.begin(), sInstances.end()); std::sort(sorted.begin(), sorted.end(), LLGLSLShaderCompareTimeElapsed()); + boost::json::object stats; + auto shadersit = stats.emplace("shaders", boost::json::array_kind).first; + auto& shaders = shadersit->value().as_array(); bool unbound = false; - for (std::vector::iterator iter = sorted.begin(); iter != sorted.end(); ++iter) + for (auto ptr : sorted) { - (*iter)->dumpStats(); - if ((*iter)->mBinds == 0) + if (ptr->mBinds == 0) { unbound = true; } + else + { + auto& shaderit = shaders.emplace_back(boost::json::object_kind); + ptr->dumpStats(shaderit.as_object()); + } } + constexpr float mega = 1'000'000.f; + float totalTimeMs = sTotalTimeElapsed / mega; LL_INFOS() << "-----------------------------------" << LL_ENDL; - LL_INFOS() << "Total rendering time: " << llformat("%.4f ms", sTotalTimeElapsed / 1000000.f) << LL_ENDL; - LL_INFOS() << "Total samples drawn: " << llformat("%.4f million", sTotalSamplesDrawn / 1000000.f) << LL_ENDL; - LL_INFOS() << "Total triangles drawn: " << llformat("%.3f million", sTotalTrianglesDrawn / 1000000.f) << LL_ENDL; + LL_INFOS() << "Total rendering time: " << llformat("%.4f ms", totalTimeMs) << LL_ENDL; + LL_INFOS() << "Total samples drawn: " << llformat("%.4f million", sTotalSamplesDrawn / mega) << LL_ENDL; + LL_INFOS() << "Total triangles drawn: " << llformat("%.3f million", sTotalTrianglesDrawn / mega) << LL_ENDL; LL_INFOS() << "-----------------------------------" << LL_ENDL; - + auto totalsit = stats.emplace("totals", boost::json::object_kind).first; + auto& totals = totalsit->value().as_object(); + totals.emplace("time", totalTimeMs / 1000.0); + totals.emplace("binds", sTotalBinds); + totals.emplace("samples", sTotalSamplesDrawn); + totals.emplace("triangles", sTotalTrianglesDrawn); + + auto unusedit = stats.emplace("unused", boost::json::array_kind).first; + auto& unused = unusedit->value().as_array(); if (unbound) { LL_INFOS() << "The following shaders were unused: " << LL_ENDL; - for (std::vector::iterator iter = sorted.begin(); iter != sorted.end(); ++iter) + for (auto ptr : sorted) { - if ((*iter)->mBinds == 0) + if (ptr->mBinds == 0) { - LL_INFOS() << (*iter)->mName << LL_ENDL; + LL_INFOS() << ptr->mName << LL_ENDL; + unused.emplace_back(ptr->mName); } } } + + std::ofstream outf(report_name); + if (! outf) + { + LL_WARNS() << "Couldn't write to " << std::quoted(report_name) << LL_ENDL; + } + else + { + outf << stats; + LL_INFOS() << "(also dumped to " << std::quoted(report_name) << ")" << LL_ENDL; + } } } @@ -170,36 +195,43 @@ void LLGLSLShader::clearStats() mBinds = 0; } -void LLGLSLShader::dumpStats() +void LLGLSLShader::dumpStats(boost::json::object& stats) { - if (mBinds > 0) + stats.emplace("name", mName); + auto filesit = stats.emplace("files", boost::json::array_kind).first; + auto& files = filesit->value().as_array(); + LL_INFOS() << "=============================================" << LL_ENDL; + LL_INFOS() << mName << LL_ENDL; + for (U32 i = 0; i < mShaderFiles.size(); ++i) { - LL_INFOS() << "=============================================" << LL_ENDL; - LL_INFOS() << mName << LL_ENDL; - for (U32 i = 0; i < mShaderFiles.size(); ++i) - { - LL_INFOS() << mShaderFiles[i].first << LL_ENDL; - } - LL_INFOS() << "=============================================" << LL_ENDL; + LL_INFOS() << mShaderFiles[i].first << LL_ENDL; + files.emplace_back(mShaderFiles[i].first); + } + LL_INFOS() << "=============================================" << LL_ENDL; - F32 ms = mTimeElapsed / 1000000.f; - F32 seconds = ms / 1000.f; + constexpr float mega = 1'000'000.f; + constexpr double giga = 1'000'000'000.0; + F32 ms = mTimeElapsed / mega; + F32 seconds = ms / 1000.f; - F32 pct_tris = (F32)mTrianglesDrawn / (F32)sTotalTrianglesDrawn * 100.f; - F32 tris_sec = (F32)(mTrianglesDrawn / 1000000.0); - tris_sec /= seconds; + F32 pct_tris = (F32)mTrianglesDrawn / (F32)sTotalTrianglesDrawn * 100.f; + F32 tris_sec = (F32)(mTrianglesDrawn / mega); + tris_sec /= seconds; - F32 pct_samples = (F32)((F64)mSamplesDrawn / (F64)sTotalSamplesDrawn) * 100.f; - F32 samples_sec = (F32)(mSamplesDrawn / 1000000000.0); - samples_sec /= seconds; + F32 pct_samples = (F32)((F64)mSamplesDrawn / (F64)sTotalSamplesDrawn) * 100.f; + F32 samples_sec = (F32)(mSamplesDrawn / giga); + samples_sec /= seconds; - F32 pct_binds = (F32)mBinds / (F32)sTotalBinds * 100.f; + F32 pct_binds = (F32)mBinds / (F32)sTotalBinds * 100.f; - LL_INFOS() << "Triangles Drawn: " << mTrianglesDrawn << " " << llformat("(%.2f pct of total, %.3f million/sec)", pct_tris, tris_sec) << LL_ENDL; - LL_INFOS() << "Binds: " << mBinds << " " << llformat("(%.2f pct of total)", pct_binds) << LL_ENDL; - LL_INFOS() << "SamplesDrawn: " << mSamplesDrawn << " " << llformat("(%.2f pct of total, %.3f billion/sec)", pct_samples, samples_sec) << LL_ENDL; - LL_INFOS() << "Time Elapsed: " << mTimeElapsed << " " << llformat("(%.2f pct of total, %.5f ms)\n", (F32)((F64)mTimeElapsed / (F64)sTotalTimeElapsed) * 100.f, ms) << LL_ENDL; - } + LL_INFOS() << "Triangles Drawn: " << mTrianglesDrawn << " " << llformat("(%.2f pct of total, %.3f million/sec)", pct_tris, tris_sec) << LL_ENDL; + LL_INFOS() << "Binds: " << mBinds << " " << llformat("(%.2f pct of total)", pct_binds) << LL_ENDL; + LL_INFOS() << "SamplesDrawn: " << mSamplesDrawn << " " << llformat("(%.2f pct of total, %.3f billion/sec)", pct_samples, samples_sec) << LL_ENDL; + LL_INFOS() << "Time Elapsed: " << mTimeElapsed << " " << llformat("(%.2f pct of total, %.5f ms)\n", (F32)((F64)mTimeElapsed / (F64)sTotalTimeElapsed) * 100.f, ms) << LL_ENDL; + stats.emplace("time", seconds); + stats.emplace("binds", mBinds); + stats.emplace("samples", mSamplesDrawn); + stats.emplace("triangles", mTrianglesDrawn); } //static diff --git a/indra/llrender/llglslshader.h b/indra/llrender/llglslshader.h index 27c8f0b7d0..efcbaf42e8 100644 --- a/indra/llrender/llglslshader.h +++ b/indra/llrender/llglslshader.h @@ -30,6 +30,7 @@ #include "llgl.h" #include "llrender.h" #include "llstaticstringtable.h" +#include #include class LLShaderFeatures @@ -169,14 +170,14 @@ public: static U32 sMaxGLTFNodes; static void initProfile(); - static void finishProfile(bool emit_report = true); + static void finishProfile(const std::string& report_name={}); static void startProfile(); static void stopProfile(); void unload(); void clearStats(); - void dumpStats(); + void dumpStats(boost::json::object& stats); // place query objects for profiling if profiling is enabled // if for_runtime is true, will place timer query only whether or not profiling is enabled diff --git a/indra/newview/llfeaturemanager.cpp b/indra/newview/llfeaturemanager.cpp index aa04221f4b..dded4d1e92 100644 --- a/indra/newview/llfeaturemanager.cpp +++ b/indra/newview/llfeaturemanager.cpp @@ -393,7 +393,7 @@ F32 logExceptionBenchmark() __except (msc_exception_filter(GetExceptionCode(), GetExceptionInformation())) { // HACK - ensure that profiling is disabled - LLGLSLShader::finishProfile(false); + LLGLSLShader::finishProfile(); // convert to C++ styled exception char integer_string[32]; diff --git a/indra/newview/llglsandbox.cpp b/indra/newview/llglsandbox.cpp index 930a8c28d9..c932312135 100644 --- a/indra/newview/llglsandbox.cpp +++ b/indra/newview/llglsandbox.cpp @@ -923,7 +923,7 @@ struct ShaderProfileHelper } ~ShaderProfileHelper() { - LLGLSLShader::finishProfile(false); + LLGLSLShader::finishProfile(); } }; diff --git a/indra/newview/llviewerdisplay.cpp b/indra/newview/llviewerdisplay.cpp index 9bd0973cc0..c62702e820 100644 --- a/indra/newview/llviewerdisplay.cpp +++ b/indra/newview/llviewerdisplay.cpp @@ -28,58 +28,69 @@ #include "llviewerdisplay.h" -#include "llgl.h" -#include "llrender.h" -#include "llglheaders.h" -#include "llgltfmateriallist.h" +#include "fsyspath.h" +#include "hexdump.h" #include "llagent.h" #include "llagentcamera.h" -#include "llviewercontrol.h" +#include "llappviewer.h" #include "llcoord.h" #include "llcriticaldamp.h" +#include "llcubemap.h" #include "lldir.h" -#include "lldynamictexture.h" #include "lldrawpoolalpha.h" +#include "lldrawpoolbump.h" +#include "lldrawpoolwater.h" +#include "lldynamictexture.h" +#include "llenvironment.h" +#include "llfasttimer.h" #include "llfeaturemanager.h" -//#include "llfirstuse.h" +#include "llfloatertools.h" +#include "llfocusmgr.h" +#include "llgl.h" +#include "llglheaders.h" +#include "llgltfmateriallist.h" #include "llhudmanager.h" #include "llimagepng.h" +#include "llmachineid.h" #include "llmemory.h" +#include "llparcel.h" +#include "llperfstats.h" +#include "llpostprocess.h" +#include "llrender.h" +#include "llscenemonitor.h" #include "llselectmgr.h" #include "llsky.h" +#include "llspatialpartition.h" #include "llstartup.h" +#include "llstartup.h" +#include "lltooldraganddrop.h" #include "lltoolfocus.h" #include "lltoolmgr.h" -#include "lltooldraganddrop.h" #include "lltoolpie.h" #include "lltracker.h" #include "lltrans.h" #include "llui.h" +#include "lluuid.h" +#include "llversioninfo.h" #include "llviewercamera.h" +#include "llviewercontrol.h" +#include "llviewernetwork.h" #include "llviewerobjectlist.h" #include "llviewerparcelmgr.h" +#include "llviewerregion.h" +#include "llviewershadermgr.h" +#include "llviewertexturelist.h" #include "llviewerwindow.h" #include "llvoavatarself.h" #include "llvograss.h" #include "llworld.h" #include "pipeline.h" -#include "llspatialpartition.h" -#include "llappviewer.h" -#include "llstartup.h" -#include "llviewershadermgr.h" -#include "llfasttimer.h" -#include "llfloatertools.h" -#include "llviewertexturelist.h" -#include "llfocusmgr.h" -#include "llcubemap.h" -#include "llviewerregion.h" -#include "lldrawpoolwater.h" -#include "lldrawpoolbump.h" -#include "llpostprocess.h" -#include "llscenemonitor.h" -#include "llenvironment.h" -#include "llperfstats.h" +#include + +#include +#include +#include extern LLPointer gStartTexture; extern bool gShiftFrame; @@ -123,6 +134,8 @@ void render_ui_3d(); void render_ui_2d(); void render_disconnected_background(); +std::string getProfileStatsFilename(); + void display_startup() { if ( !gViewerWindow @@ -1023,10 +1036,49 @@ void display(bool rebuild, F32 zoom_factor, int subfield, bool for_snapshot) if (gShaderProfileFrame) { gShaderProfileFrame = false; - LLGLSLShader::finishProfile(); + LLGLSLShader::finishProfile(getProfileStatsFilename()); } } +std::string getProfileStatsFilename() +{ + std::ostringstream basebuff; + // viewer build + basebuff << "profile.v" << LLVersionInfo::instance().getBuild(); + // machine ID: zero-initialize unique_id in case LLMachineID fails + unsigned char unique_id[MAC_ADDRESS_BYTES]{}; + LLMachineID::getUniqueID(unique_id, sizeof(unique_id)); + basebuff << ".m" << LL::hexdump(unique_id, sizeof(unique_id)); + // region ID + LLViewerRegion *region = gAgent.getRegion(); + basebuff << ".r" << (region? region->getRegionID() : LLUUID()); + // local parcel ID + LLParcel* parcel = LLViewerParcelMgr::instance().getAgentParcel(); + basebuff << ".p" << (parcel? parcel->getLocalID() : 0); + // date/time -- omit seconds for now + auto now = LLDate::now(); + basebuff << ".t" << LLDate::now().toHTTPDateString("%Y-%m-%dT%H-%M-"); + // put this candidate file in our logs directory + auto base = gDirUtilp->getExpandedFilename(LL_PATH_LOGS, basebuff.str()); + S32 sec; + now.split(nullptr, nullptr, nullptr, nullptr, nullptr, &sec); + // Loop over finished filename, incrementing sec until we find one that + // doesn't yet exist. Should rarely loop (only if successive calls within + // same second), may produce (e.g.) sec==61, but avoids collisions and + // preserves chronological filename sort order. + std::string name; + std::error_code ec; + do + { + // base + missing 2-digit seconds, append ".json" + // post-increment sec in case we have to try again + name = stringize(base, std::setw(2), std::setfill('0'), sec++, ".json"); + } while (std::filesystem::exists(fsyspath(name), ec)); + // Ignoring ec means we might potentially return a name that does already + // exist -- but if we can't check its existence, what more can we do? + return name; +} + // WIP simplified copy of display() that does minimal work void display_cube_face() { -- cgit v1.2.3 From c6e6f44f50b4de391000c5b9f781a2f0a5024e76 Mon Sep 17 00:00:00 2001 From: Nat Goodspeed Date: Thu, 12 Sep 2024 09:12:33 -0400 Subject: Give `LLGLSLShader::finishProfile()` a static default string param. `finishProfile()` is called at least once within a `__try` block. If we default its `report_name` parameter to a temporary `std::string`, that temporary must be destroyed when the stack is unwound, which `__try` forbids. --- indra/llrender/llglslshader.cpp | 1 + indra/llrender/llglslshader.h | 7 ++++++- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/indra/llrender/llglslshader.cpp b/indra/llrender/llglslshader.cpp index bb734971d5..56f1533708 100644 --- a/indra/llrender/llglslshader.cpp +++ b/indra/llrender/llglslshader.cpp @@ -65,6 +65,7 @@ U64 LLGLSLShader::sTotalTimeElapsed = 0; U32 LLGLSLShader::sTotalTrianglesDrawn = 0; U64 LLGLSLShader::sTotalSamplesDrawn = 0; U32 LLGLSLShader::sTotalBinds = 0; +std::string LLGLSLShader::sDefaultReportName; //UI shader -- declared here so llui_libtest will link properly LLGLSLShader gUIProgram; diff --git a/indra/llrender/llglslshader.h b/indra/llrender/llglslshader.h index efcbaf42e8..a9b9bfafa8 100644 --- a/indra/llrender/llglslshader.h +++ b/indra/llrender/llglslshader.h @@ -170,7 +170,7 @@ public: static U32 sMaxGLTFNodes; static void initProfile(); - static void finishProfile(const std::string& report_name={}); + static void finishProfile(const std::string& report_name=sDefaultReportName); static void startProfile(); static void stopProfile(); @@ -364,6 +364,11 @@ public: private: void unloadInternal(); + // This must be static because finishProfile() is called at least once + // within a __try block. If we default its report_name parameter to a + // temporary std::string, that temporary must be destroyed when the stack + // is unwound, which __try forbids. + static std::string sDefaultReportName; }; //UI shader (declared here so llui_libtest will link properly) -- cgit v1.2.3 From 0c1b3c8a38542b5aab10527cf411b57642b1f70f Mon Sep 17 00:00:00 2001 From: Nat Goodspeed Date: Thu, 12 Sep 2024 09:24:20 -0400 Subject: Specialize `std::numpunct` to fix broken MS `basic_ostream`. MSVC's `std::basic_ostream` template is not implemented in a general way: it can only be instantiated for certain specific `CHAR` types. Declaring a `std::basic_ostringstream` fails on MSVC with C2941. The ugly workaround from Stack Overflow is to clone-and-edit Microsoft's `std::numpunct` template, locally specializing it for the desired `CHAR` type. --- indra/llcommon/llstring.cpp | 158 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 158 insertions(+) diff --git a/indra/llcommon/llstring.cpp b/indra/llcommon/llstring.cpp index 614075498c..23b12bb98d 100644 --- a/indra/llcommon/llstring.cpp +++ b/indra/llcommon/llstring.cpp @@ -31,10 +31,168 @@ #include "llfasttimer.h" #include "llsd.h" #include +#include #if LL_WINDOWS #include "llwin32headerslean.h" #include // for WideCharToMultiByte + +// From https://stackoverflow.com/a/48716488: +// how to work around MSVC's broken implementation of std::basic_ostream +// (get C2941 otherwise) +// The problem is that the MSVC implementation doesn't generalize for +// arbitrary CHAR. +namespace std +{ + +// The std::numpunct<_Elem> template on which this specialization is based was +// copied 2024-09-11 from xlocnum header with versions: +// FRAMEWORK40VERSION="v4.0" +// FRAMEWORKVERSION="v4.0.30319" +// FRAMEWORKVERSION64="v4.0.30319" +// UCRTVERSION="10.0.22621.0" +// VCTOOLSVERSION="14.40.33807" +// VISUALSTUDIOVERSION="17.0" +// WINDOWSSDKLIBVERSION="10.0.22621.0" +// WINDOWSSDKVERSION="10.0.22621.0" +// Not sure which of the above versions tracks changes to xlocnum. +template <> +class numpunct : public locale::facet // facet for defining numeric punctuation text +{ +private: + friend _Tidy_guard; + +public: + using string_type = basic_string, allocator>; + using char_type = llwchar; + + static locale::id id; // unique facet id + + llwchar decimal_point() const { + return do_decimal_point(); + } + + llwchar thousands_sep() const { + return do_thousands_sep(); + } + + string grouping() const { + return do_grouping(); + } + + string_type falsename() const { + return do_falsename(); + } + + string_type truename() const { + return do_truename(); + } + + explicit numpunct(size_t _Refs = 0) : locale::facet(_Refs) { // construct from current locale + _BEGIN_LOCINFO(_Lobj) + _Init(_Lobj); + if (_Kseparator == 0) { + _Kseparator = // NB: differs from "C" locale + _Maklocchr(',', static_cast(nullptr), _Lobj._Getcvt()); + } + _END_LOCINFO() + } + + numpunct(const _Locinfo& _Lobj, size_t _Refs = 0, bool _Isdef = false) : locale::facet(_Refs) { + _Init(_Lobj, _Isdef); + } + + static size_t _Getcat(const locale::facet** _Ppf = nullptr, const locale* _Ploc = nullptr) { + // return locale category mask and construct standard facet + if (_Ppf && !*_Ppf) { + *_Ppf = new numpunct(_Locinfo(_Ploc->_C_str()), 0, true); + } + return _X_NUMERIC; + } + +protected: + __CLR_OR_THIS_CALL ~numpunct() noexcept override { + _Tidy(); + } + + numpunct(const char* _Locname, size_t _Refs = 0, bool _Isdef = false) : locale::facet(_Refs) { + _BEGIN_LOCINFO(_Lobj(_Locname)) + _Init(_Lobj, _Isdef); + _END_LOCINFO() + } + + template + void _Getvals(_Elem2, const lconv* _Ptr, _Locinfo::_Cvtvec _Cvt) { // get values + _Dp = _Maklocchr(_Ptr->decimal_point[0], static_cast<_Elem2*>(nullptr), _Cvt); + _Kseparator = _Maklocchr(_Ptr->thousands_sep[0], static_cast<_Elem2*>(nullptr), _Cvt); + } + + void _Getvals(wchar_t, const lconv* _Ptr, _Locinfo::_Cvtvec) { // get values + _Dp = static_cast(_Ptr->_W_decimal_point[0]); + _Kseparator = static_cast(_Ptr->_W_thousands_sep[0]); + } + + void _Init(const _Locinfo& _Lobj, bool _Isdef = false) { // initialize from _Lobj + const lconv* _Ptr = _Lobj._Getlconv(); + _Locinfo::_Cvtvec _Cvt = _Lobj._Getcvt(); // conversion information + + _Grouping = nullptr; + _Falsename = nullptr; + _Truename = nullptr; + + _Tidy_guard _Guard{this}; + _Grouping = _Maklocstr(_Isdef ? "" : _Ptr->grouping, static_cast(nullptr), _Lobj._Getcvt()); + _Falsename = _Maklocstr(_Lobj._Getfalse(), static_cast(nullptr), _Cvt); + _Truename = _Maklocstr(_Lobj._Gettrue(), static_cast(nullptr), _Cvt); + _Guard._Target = nullptr; + + if (_Isdef) { // apply defaults for required facets + // _Grouping = _Maklocstr("", static_cast(nullptr), _Cvt); + _Dp = _Maklocchr('.', static_cast(nullptr), _Cvt); + _Kseparator = _Maklocchr(',', static_cast(nullptr), _Cvt); + } else { + _Getvals(llwchar{}, _Ptr, _Cvt); + } + } + + virtual llwchar __CLR_OR_THIS_CALL do_decimal_point() const { + return _Dp; + } + + virtual llwchar __CLR_OR_THIS_CALL do_thousands_sep() const { + return _Kseparator; + } + + virtual string __CLR_OR_THIS_CALL do_grouping() const { + return string{_Grouping}; + } + + virtual string_type __CLR_OR_THIS_CALL do_falsename() const { + return string_type{_Falsename}; + } + + virtual string_type __CLR_OR_THIS_CALL do_truename() const { + return string_type{_Truename}; + } + +private: + void _Tidy() noexcept { // free all storage + _CSTD free(const_cast(_Grouping)); + _CSTD free(const_cast(_Falsename)); + _CSTD free(const_cast(_Truename)); + } + + const char* _Grouping; // grouping string, "" for "C" locale + llwchar _Dp; // decimal point, '.' for "C" locale + llwchar _Kseparator; // thousands separator, '\0' for "C" locale + const llwchar* _Falsename; // name for false, "false" for "C" locale + const llwchar* _Truename; // name for true, "true" for "C" locale +}; + +locale::id numpunct::id; + +} // namespace std + #endif std::string ll_safe_string(const char* in) -- cgit v1.2.3 From b898276a4f480eb5cf3fbc27bea01e2f4a261374 Mon Sep 17 00:00:00 2001 From: Nat Goodspeed Date: Thu, 12 Sep 2024 09:39:06 -0400 Subject: Work around broken MS `std::basic_ostream`. MSVC's `std::basic_ostream` template is not implemented in a general way: it can only be instantiated for certain specific `CHAR` types. Declaring a `std::basic_ostringstream` fails on MSVC with C2941. Fortunately both llstring.cpp functions that build a `LLWString` incrementally have the same characteristics: (a) they each build it one character at a time, and (b) the length of the result `LLWString` won't exceed the known length of the input string. So it works to declare a `std::vector`, `reserve()` the input length and `push_back()` individual characters. Then we can use `LLWString`'s range constructor to immediately allocate the right size. --- indra/llcommon/llstring.cpp | 187 +++++--------------------------------------- 1 file changed, 21 insertions(+), 166 deletions(-) diff --git a/indra/llcommon/llstring.cpp b/indra/llcommon/llstring.cpp index 23b12bb98d..005864a843 100644 --- a/indra/llcommon/llstring.cpp +++ b/indra/llcommon/llstring.cpp @@ -36,163 +36,6 @@ #if LL_WINDOWS #include "llwin32headerslean.h" #include // for WideCharToMultiByte - -// From https://stackoverflow.com/a/48716488: -// how to work around MSVC's broken implementation of std::basic_ostream -// (get C2941 otherwise) -// The problem is that the MSVC implementation doesn't generalize for -// arbitrary CHAR. -namespace std -{ - -// The std::numpunct<_Elem> template on which this specialization is based was -// copied 2024-09-11 from xlocnum header with versions: -// FRAMEWORK40VERSION="v4.0" -// FRAMEWORKVERSION="v4.0.30319" -// FRAMEWORKVERSION64="v4.0.30319" -// UCRTVERSION="10.0.22621.0" -// VCTOOLSVERSION="14.40.33807" -// VISUALSTUDIOVERSION="17.0" -// WINDOWSSDKLIBVERSION="10.0.22621.0" -// WINDOWSSDKVERSION="10.0.22621.0" -// Not sure which of the above versions tracks changes to xlocnum. -template <> -class numpunct : public locale::facet // facet for defining numeric punctuation text -{ -private: - friend _Tidy_guard; - -public: - using string_type = basic_string, allocator>; - using char_type = llwchar; - - static locale::id id; // unique facet id - - llwchar decimal_point() const { - return do_decimal_point(); - } - - llwchar thousands_sep() const { - return do_thousands_sep(); - } - - string grouping() const { - return do_grouping(); - } - - string_type falsename() const { - return do_falsename(); - } - - string_type truename() const { - return do_truename(); - } - - explicit numpunct(size_t _Refs = 0) : locale::facet(_Refs) { // construct from current locale - _BEGIN_LOCINFO(_Lobj) - _Init(_Lobj); - if (_Kseparator == 0) { - _Kseparator = // NB: differs from "C" locale - _Maklocchr(',', static_cast(nullptr), _Lobj._Getcvt()); - } - _END_LOCINFO() - } - - numpunct(const _Locinfo& _Lobj, size_t _Refs = 0, bool _Isdef = false) : locale::facet(_Refs) { - _Init(_Lobj, _Isdef); - } - - static size_t _Getcat(const locale::facet** _Ppf = nullptr, const locale* _Ploc = nullptr) { - // return locale category mask and construct standard facet - if (_Ppf && !*_Ppf) { - *_Ppf = new numpunct(_Locinfo(_Ploc->_C_str()), 0, true); - } - return _X_NUMERIC; - } - -protected: - __CLR_OR_THIS_CALL ~numpunct() noexcept override { - _Tidy(); - } - - numpunct(const char* _Locname, size_t _Refs = 0, bool _Isdef = false) : locale::facet(_Refs) { - _BEGIN_LOCINFO(_Lobj(_Locname)) - _Init(_Lobj, _Isdef); - _END_LOCINFO() - } - - template - void _Getvals(_Elem2, const lconv* _Ptr, _Locinfo::_Cvtvec _Cvt) { // get values - _Dp = _Maklocchr(_Ptr->decimal_point[0], static_cast<_Elem2*>(nullptr), _Cvt); - _Kseparator = _Maklocchr(_Ptr->thousands_sep[0], static_cast<_Elem2*>(nullptr), _Cvt); - } - - void _Getvals(wchar_t, const lconv* _Ptr, _Locinfo::_Cvtvec) { // get values - _Dp = static_cast(_Ptr->_W_decimal_point[0]); - _Kseparator = static_cast(_Ptr->_W_thousands_sep[0]); - } - - void _Init(const _Locinfo& _Lobj, bool _Isdef = false) { // initialize from _Lobj - const lconv* _Ptr = _Lobj._Getlconv(); - _Locinfo::_Cvtvec _Cvt = _Lobj._Getcvt(); // conversion information - - _Grouping = nullptr; - _Falsename = nullptr; - _Truename = nullptr; - - _Tidy_guard _Guard{this}; - _Grouping = _Maklocstr(_Isdef ? "" : _Ptr->grouping, static_cast(nullptr), _Lobj._Getcvt()); - _Falsename = _Maklocstr(_Lobj._Getfalse(), static_cast(nullptr), _Cvt); - _Truename = _Maklocstr(_Lobj._Gettrue(), static_cast(nullptr), _Cvt); - _Guard._Target = nullptr; - - if (_Isdef) { // apply defaults for required facets - // _Grouping = _Maklocstr("", static_cast(nullptr), _Cvt); - _Dp = _Maklocchr('.', static_cast(nullptr), _Cvt); - _Kseparator = _Maklocchr(',', static_cast(nullptr), _Cvt); - } else { - _Getvals(llwchar{}, _Ptr, _Cvt); - } - } - - virtual llwchar __CLR_OR_THIS_CALL do_decimal_point() const { - return _Dp; - } - - virtual llwchar __CLR_OR_THIS_CALL do_thousands_sep() const { - return _Kseparator; - } - - virtual string __CLR_OR_THIS_CALL do_grouping() const { - return string{_Grouping}; - } - - virtual string_type __CLR_OR_THIS_CALL do_falsename() const { - return string_type{_Falsename}; - } - - virtual string_type __CLR_OR_THIS_CALL do_truename() const { - return string_type{_Truename}; - } - -private: - void _Tidy() noexcept { // free all storage - _CSTD free(const_cast(_Grouping)); - _CSTD free(const_cast(_Falsename)); - _CSTD free(const_cast(_Truename)); - } - - const char* _Grouping; // grouping string, "" for "C" locale - llwchar _Dp; // decimal point, '.' for "C" locale - llwchar _Kseparator; // thousands separator, '\0' for "C" locale - const llwchar* _Falsename; // name for false, "false" for "C" locale - const llwchar* _Truename; // name for true, "true" for "C" locale -}; - -locale::id numpunct::id; - -} // namespace std - #endif std::string ll_safe_string(const char* in) @@ -403,8 +246,14 @@ LLWString utf16str_to_wstring(const U16* utf16str, size_t len) { if (len == 0) return {}; - // ostringstream for LLWString - std::basic_ostringstream wout; + // MS doesn't support std::basic_ostringstream; have to work + // around it. + std::vector wout; + // We want to minimize allocations. We don't know how many llwchars we'll + // generate from this utf16str, but we do know the length should be at + // most len. So if we reserve 'len' llwchars, we shouldn't need to expand + // wout incrementally. + wout.reserve(len); S32 i = 0; const U16* chars16 = utf16str; @@ -412,9 +261,9 @@ LLWString utf16str_to_wstring(const U16* utf16str, size_t len) { llwchar cur_char; i += (S32)utf16chars_to_wchar(chars16+i, &cur_char); - wout << cur_char; + wout.push_back(cur_char); } - return wout.str(); + return { wout.begin(), wout.end() }; } // Length in llwchar (UTF-32) of the first len units (16 bits) of the given UTF-16 string. @@ -559,8 +408,14 @@ S32 wstring_utf8_length(const LLWString& wstr) LLWString utf8str_to_wstring(const char* utf8str, size_t len) { - // ostringstream for LLWString - std::basic_ostringstream wout; + // MS doesn't support std::basic_ostringstream; have to work + // around it. + std::vector wout; + // We want to minimize allocations. We don't know how many llwchars we'll + // generate from this utf8str, but we do know the length should be at most + // len. So if we reserve 'len' llwchars, we shouldn't need to expand wout + // incrementally. + wout.reserve(len); S32 i = 0; while (i < len) @@ -603,7 +458,7 @@ LLWString utf8str_to_wstring(const char* utf8str, size_t len) } else { - wout << LL_UNKNOWN_CHAR; + wout.push_back(LL_UNKNOWN_CHAR); ++i; continue; } @@ -640,10 +495,10 @@ LLWString utf8str_to_wstring(const char* utf8str, size_t len) } } - wout << unichar; + wout.push_back(unichar); ++i; } - return wout.str(); + return { wout.begin(), wout.end() }; } std::string wstring_to_utf8str(const llwchar* utf32str, size_t len) -- cgit v1.2.3 From f789bf05053c99d550e6d95e9a512c984ea43ed5 Mon Sep 17 00:00:00 2001 From: Nat Goodspeed Date: Thu, 12 Sep 2024 10:39:05 -0400 Subject: Populate the viewer package's lua/auto subdir as well as require. --- indra/newview/viewer_manifest.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/indra/newview/viewer_manifest.py b/indra/newview/viewer_manifest.py index aa2b0b0e25..c403b57813 100755 --- a/indra/newview/viewer_manifest.py +++ b/indra/newview/viewer_manifest.py @@ -169,8 +169,9 @@ class ViewerManifest(LLManifest): with self.prefix(src_dst="scripts/lua"): self.path("*.lua") self.path("*.xml") - with self.prefix(src_dst='require'): - self.path("*.lua") + for subdir in 'require', 'auto': + with self.prefix(src_dst=subdir): + self.path("*.lua") #build_data.json. Standard with exception handling is fine. If we can't open a new file for writing, we have worse problems #platform is computed above with other arg parsing -- cgit v1.2.3 From 034d13bcd77c3cbba00da1ef6c3c59d22f4a689e Mon Sep 17 00:00:00 2001 From: Nat Goodspeed Date: Thu, 12 Sep 2024 10:39:26 -0400 Subject: Disable happy-path destructor semantics when unwinding C++ stack. If the C++ runtime is already handling an exception, don't try to launch more Lua operations. --- indra/llcommon/lua_function.cpp | 8 +++++++- indra/llcommon/lua_function.h | 5 ++++- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/indra/llcommon/lua_function.cpp b/indra/llcommon/lua_function.cpp index 380e650360..2557fd0cc9 100644 --- a/indra/llcommon/lua_function.cpp +++ b/indra/llcommon/lua_function.cpp @@ -709,6 +709,11 @@ int lua_metaipair(lua_State* L) LuaState::~LuaState() { + // If we're unwinding the stack due to an exception, don't bother trying + // to call any callbacks -- either Lua or C++. + if (std::uncaught_exceptions() != 0) + return; + /*---------------------------- feature flag ----------------------------*/ if (mFeature) /*---------------------------- feature flag ----------------------------*/ @@ -990,7 +995,8 @@ lua_function(atexit, "atexit(function): " *****************************************************************************/ LuaPopper::~LuaPopper() { - if (mCount) + // If we're unwinding the C++ stack due to an exception, don't pop! + if (std::uncaught_exceptions() == 0 && mCount) { lua_pop(mState, mCount); } diff --git a/indra/llcommon/lua_function.h b/indra/llcommon/lua_function.h index 12e74b5d04..10c201c234 100644 --- a/indra/llcommon/lua_function.h +++ b/indra/llcommon/lua_function.h @@ -172,7 +172,10 @@ public: LuaRemover& operator=(const LuaRemover&) = delete; ~LuaRemover() { - lua_remove(mState, mIndex); + // If we're unwinding the C++ stack due to an exception, don't mess + // with the Lua stack! + if (std::uncaught_exceptions() == 0) + lua_remove(mState, mIndex); } private: -- cgit v1.2.3 From 4ae12a08265998055627a0530f6e7ef8da5ca2ed Mon Sep 17 00:00:00 2001 From: Nat Goodspeed Date: Thu, 12 Sep 2024 11:05:24 -0400 Subject: Add LLAgent.teleport() Lua function that wraps existing "LLTeleportHandler" LEAP listener. --- indra/newview/scripts/lua/require/LLAgent.lua | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/indra/newview/scripts/lua/require/LLAgent.lua b/indra/newview/scripts/lua/require/LLAgent.lua index 07ef1e0b0b..9eebede59f 100644 --- a/indra/newview/scripts/lua/require/LLAgent.lua +++ b/indra/newview/scripts/lua/require/LLAgent.lua @@ -71,4 +71,12 @@ function LLAgent.getAnimationInfo(item_id) return leap.request('LLAgent', {op = 'getAnimationInfo', item_id=item_id}).anim_info end +-- Teleport to specified "regionname" at specified region-relative "x", "y", "z". +-- If "regionname" omitted, teleport to GLOBAL coordinates "x", "y", "z". +function LLAgent.teleport(...) + local args = mapargs('regionname,x,y,z', ...) + args.op = 'teleport' + return leap.request('LLTeleportHandler', args) +end + return LLAgent -- cgit v1.2.3 From 499c62637f487b673bac86be566588a8ccde388d Mon Sep 17 00:00:00 2001 From: Nat Goodspeed Date: Thu, 12 Sep 2024 11:14:40 -0400 Subject: Recursively package all of indra/newview/scripts/lua. Instead of trying to continue mirroring the lua subdirectory structure in viewer_manifest.py, and enumerating the relevant file extensions, just pack up the whole subtree. --- indra/newview/viewer_manifest.py | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/indra/newview/viewer_manifest.py b/indra/newview/viewer_manifest.py index c403b57813..9f77dba3bc 100755 --- a/indra/newview/viewer_manifest.py +++ b/indra/newview/viewer_manifest.py @@ -166,12 +166,7 @@ class ViewerManifest(LLManifest): self.path("*/*/*/*.js") self.path("*/*/*.html") - with self.prefix(src_dst="scripts/lua"): - self.path("*.lua") - self.path("*.xml") - for subdir in 'require', 'auto': - with self.prefix(src_dst=subdir): - self.path("*.lua") + self.path('scripts/lua') #build_data.json. Standard with exception handling is fine. If we can't open a new file for writing, we have worse problems #platform is computed above with other arg parsing -- cgit v1.2.3 From a68bb4eb27573a9313169799a188dda3ae765666 Mon Sep 17 00:00:00 2001 From: Nat Goodspeed Date: Thu, 12 Sep 2024 12:27:08 -0400 Subject: Support "LLTeleportHandler" "teleport" regionname="home". --- indra/newview/llurldispatcher.cpp | 9 ++++++++- indra/newview/scripts/lua/require/LLAgent.lua | 3 ++- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/indra/newview/llurldispatcher.cpp b/indra/newview/llurldispatcher.cpp index 39a9f0f8bc..166542324d 100644 --- a/indra/newview/llurldispatcher.cpp +++ b/indra/newview/llurldispatcher.cpp @@ -289,6 +289,8 @@ public: LLEventAPI::add("teleport", "Teleport to specified [\"regionname\"] at\n" "specified region-relative [\"x\"], [\"y\"], [\"z\"].\n" + "If [\"regionname\"] is \"home\", ignore [\"x\"], [\"y\"], [\"z\"]\n" + "and teleport home.\n" "If [\"regionname\"] omitted, teleport to GLOBAL\n" "coordinates [\"x\"], [\"y\"], [\"z\"].", &LLTeleportHandler::from_event); @@ -328,7 +330,12 @@ public: void from_event(const LLSD& params) const { Response response(LLSD(), params); - if (params.has("regionname")) + if (params["regionname"].asString() == "home") + { + gAgent.teleportHome(); + response["message"] = "Teleporting home"; + } + else if (params.has("regionname")) { // region specified, coordinates (if any) are region-local LLVector3 local_pos( diff --git a/indra/newview/scripts/lua/require/LLAgent.lua b/indra/newview/scripts/lua/require/LLAgent.lua index 9eebede59f..5cee998fcd 100644 --- a/indra/newview/scripts/lua/require/LLAgent.lua +++ b/indra/newview/scripts/lua/require/LLAgent.lua @@ -72,11 +72,12 @@ function LLAgent.getAnimationInfo(item_id) end -- Teleport to specified "regionname" at specified region-relative "x", "y", "z". +-- If "regionname" is "home", ignore "x", "y", "z" and teleport home. -- If "regionname" omitted, teleport to GLOBAL coordinates "x", "y", "z". function LLAgent.teleport(...) local args = mapargs('regionname,x,y,z', ...) args.op = 'teleport' - return leap.request('LLTeleportHandler', args) + return leap.request('LLTeleportHandler', args).message end return LLAgent -- cgit v1.2.3 From ab3083819793a30911354670a7929b0d3f7c104c Mon Sep 17 00:00:00 2001 From: Nat Goodspeed Date: Thu, 12 Sep 2024 12:28:05 -0400 Subject: Add a JSON frame profile stats file pretty-printer script. --- scripts/perf/profile_pretty.py | 54 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 54 insertions(+) create mode 100644 scripts/perf/profile_pretty.py diff --git a/scripts/perf/profile_pretty.py b/scripts/perf/profile_pretty.py new file mode 100644 index 0000000000..ca52fe366a --- /dev/null +++ b/scripts/perf/profile_pretty.py @@ -0,0 +1,54 @@ +#!/usr/bin/env python3 +"""\ +@file profile_pretty.py +@author Nat Goodspeed +@date 2024-09-12 +@brief Pretty-print a JSON file from Develop -> Render Tests -> Frame Profile + +$LicenseInfo:firstyear=2024&license=viewerlgpl$ +Copyright (c) 2024, Linden Research, Inc. +$/LicenseInfo$ +""" + +import logsdir +import json +from pathlib import Path +import sys + +class Error(Exception): + pass + +def pretty(path=None): + if not path: + logs = logsdir.logsdir() + profiles = Path(logs).glob('profile.*.json') + sort = [(p.stat().st_mtime, p) for p in profiles] + sort.sort(reverse=True) + try: + path = sort[0][1] + except IndexError: + raise Error(f'No profile.*.json files in {logs}') + # print path to sys.stderr in case user is redirecting stdout + print(path, file=sys.stderr) + + with open(path) as inf: + data = json.load(inf) + json.dump(data, sys.stdout, indent=4) + +def main(*raw_args): + from argparse import ArgumentParser + parser = ArgumentParser(description=""" +%(prog)s pretty-prints a JSON file from Develop -> Render Tests -> Frame Profile. +The file produced by the viewer is a single dense line of JSON. +""") + parser.add_argument('path', nargs='?', + help="""profile filename to pretty-print (default is most recent)""") + + args = parser.parse_args(raw_args) + pretty(args.path) + +if __name__ == "__main__": + try: + sys.exit(main(*sys.argv[1:])) + except (Error, OSError, json.JSONDecodeError) as err: + sys.exit(str(err)) -- cgit v1.2.3 From d60b1f92213ace6a8ab6a4a60cb01a43f45d3955 Mon Sep 17 00:00:00 2001 From: Nat Goodspeed Date: Thu, 12 Sep 2024 13:43:36 -0400 Subject: Add script to convert frame profile JSON file to CSV. Also slightly refactor profile_pretty.py. --- scripts/perf/profile_csv.py | 72 ++++++++++++++++++++++++++++++++++++++++++ scripts/perf/profile_pretty.py | 26 +++++++-------- 2 files changed, 85 insertions(+), 13 deletions(-) create mode 100644 scripts/perf/profile_csv.py diff --git a/scripts/perf/profile_csv.py b/scripts/perf/profile_csv.py new file mode 100644 index 0000000000..273e3b7434 --- /dev/null +++ b/scripts/perf/profile_csv.py @@ -0,0 +1,72 @@ +#!/usr/bin/env python3 +"""\ +@file profile_csv.py +@author Nat Goodspeed +@date 2024-09-12 +@brief Convert a JSON file from Develop -> Render Tests -> Frame Profile to CSV + +$LicenseInfo:firstyear=2024&license=viewerlgpl$ +Copyright (c) 2024, Linden Research, Inc. +$/LicenseInfo$ +""" + +import logsdir +import json +from pathlib import Path +import sys + +class Error(Exception): + pass + +def convert(path, totals=True, unused=True, file=sys.stdout): + with open(path) as inf: + data = json.load(inf) + print('"name", "file1", "file2", "time", "binds", "samples", "triangles"', file=file) + + if totals: + t = data['totals'] + print(f'"totals", "", "", {t["time"]}, {t["binds"]}, {t["samples"]}, {t["triangles"]}', + file=file) + + for sh in data['shaders']: + print(f'"{sh["name"]}", "{sh["files"][0]}", "{sh["files"][1]}", ' + f'{sh["time"]}, {sh["binds"]}, {sh["samples"]}, {sh["triangles"]}', file=file) + + if unused: + for u in data['unused']: + print(f'"{u}", "", "", 0, 0, 0, 0', file=file) + +def main(*raw_args): + from argparse import ArgumentParser + parser = ArgumentParser(description=""" +%(prog)s converts a JSON file from Develop -> Render Tests -> Frame Profile to +a more-or-less equivalent CSV file. It expands the totals stats and unused +shaders list to full shaders lines. +""") + parser.add_argument('-t', '--totals', action='store_false', default=True, + help="""omit totals from CSV file""") + parser.add_argument('-u', '--unused', action='store_false', default=True, + help="""omit unused shaders from CSV file""") + parser.add_argument('path', nargs='?', + help="""profile filename to convert (default is most recent)""") + + args = parser.parse_args(raw_args) + if not args.path: + logs = logsdir.logsdir() + profiles = Path(logs).glob('profile.*.json') + sort = [(p.stat().st_mtime, p) for p in profiles] + sort.sort(reverse=True) + try: + args.path = sort[0][1] + except IndexError: + raise Error(f'No profile.*.json files in {logs}') + # print path to sys.stderr in case user is redirecting stdout + print(args.path, file=sys.stderr) + + convert(args.path, totals=args.totals, unused=args.unused) + +if __name__ == "__main__": + try: + sys.exit(main(*sys.argv[1:])) + except (Error, OSError, json.JSONDecodeError) as err: + sys.exit(str(err)) diff --git a/scripts/perf/profile_pretty.py b/scripts/perf/profile_pretty.py index ca52fe366a..15b6efd94d 100644 --- a/scripts/perf/profile_pretty.py +++ b/scripts/perf/profile_pretty.py @@ -18,19 +18,7 @@ import sys class Error(Exception): pass -def pretty(path=None): - if not path: - logs = logsdir.logsdir() - profiles = Path(logs).glob('profile.*.json') - sort = [(p.stat().st_mtime, p) for p in profiles] - sort.sort(reverse=True) - try: - path = sort[0][1] - except IndexError: - raise Error(f'No profile.*.json files in {logs}') - # print path to sys.stderr in case user is redirecting stdout - print(path, file=sys.stderr) - +def pretty(path): with open(path) as inf: data = json.load(inf) json.dump(data, sys.stdout, indent=4) @@ -45,6 +33,18 @@ The file produced by the viewer is a single dense line of JSON. help="""profile filename to pretty-print (default is most recent)""") args = parser.parse_args(raw_args) + if not args.path: + logs = logsdir.logsdir() + profiles = Path(logs).glob('profile.*.json') + sort = [(p.stat().st_mtime, p) for p in profiles] + sort.sort(reverse=True) + try: + args.path = sort[0][1] + except IndexError: + raise Error(f'No profile.*.json files in {logs}') + # print path to sys.stderr in case user is redirecting stdout + print(args.path, file=sys.stderr) + pretty(args.path) if __name__ == "__main__": -- cgit v1.2.3 From 6c6a42fbbc231bad6a7921c37cdcb82d17dc225f Mon Sep 17 00:00:00 2001 From: Nat Goodspeed Date: Thu, 12 Sep 2024 13:46:30 -0400 Subject: Let test_animation.lua cope with the case of 0 animations. --- indra/newview/scripts/lua/test_animation.lua | 28 ++++++++++++++++------------ 1 file changed, 16 insertions(+), 12 deletions(-) diff --git a/indra/newview/scripts/lua/test_animation.lua b/indra/newview/scripts/lua/test_animation.lua index c16fef4918..37e7254a6c 100644 --- a/indra/newview/scripts/lua/test_animation.lua +++ b/indra/newview/scripts/lua/test_animation.lua @@ -11,18 +11,22 @@ for key in pairs(anims) do table.insert(anim_ids, key) end --- Start playing a random animation -math.randomseed(os.time()) -local random_id = anim_ids[math.random(#anim_ids)] -local anim_info = LLAgent.getAnimationInfo(random_id) +if #anim_ids == 0 then + print("No animations found") +else + -- Start playing a random animation + math.randomseed(os.time()) + local random_id = anim_ids[math.random(#anim_ids)] + local anim_info = LLAgent.getAnimationInfo(random_id) -print("Starting animation locally: " .. anims[random_id].name) -print("Loop: " .. anim_info.is_loop .. " Joints: " .. anim_info.num_joints .. " Duration " .. tonumber(string.format("%.2f", anim_info.duration))) -LLAgent.playAnimation{item_id=random_id} + print("Starting animation locally: " .. anims[random_id].name) + print("Loop: " .. anim_info.is_loop .. " Joints: " .. anim_info.num_joints .. " Duration " .. tonumber(string.format("%.2f", anim_info.duration))) + LLAgent.playAnimation{item_id=random_id} --- Stop animation after 3 sec if it's looped or longer than 3 sec -if anim_info.is_loop == 1 or anim_info.duration > 3 then - LL.sleep(3) - print("Stop animation.") - LLAgent.stopAnimation(random_id) + -- Stop animation after 3 sec if it's looped or longer than 3 sec + if anim_info.is_loop == 1 or anim_info.duration > 3 then + LL.sleep(3) + print("Stop animation.") + LLAgent.stopAnimation(random_id) + end end -- cgit v1.2.3 From 2b851fbd0b4f72f46ec68965b3e06451e20a5b39 Mon Sep 17 00:00:00 2001 From: Nat Goodspeed Date: Thu, 12 Sep 2024 15:08:54 -0400 Subject: Mediate "LLAppViewer" "userQuit" et al. via "mainloop" WorkQueue. Empirically, this works better than engaging the respective LLAppViewer methods directly. --- indra/newview/llappviewerlistener.cpp | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/indra/newview/llappviewerlistener.cpp b/indra/newview/llappviewerlistener.cpp index d02b1b4a79..4690c91b61 100644 --- a/indra/newview/llappviewerlistener.cpp +++ b/indra/newview/llappviewerlistener.cpp @@ -35,6 +35,7 @@ // external library headers // other Linden headers #include "llappviewer.h" +#include "workqueue.h" LLAppViewerListener::LLAppViewerListener(const LLAppViewerGetter& getter): LLEventAPI("LLAppViewer", @@ -56,17 +57,23 @@ LLAppViewerListener::LLAppViewerListener(const LLAppViewerGetter& getter): void LLAppViewerListener::userQuit(const LLSD& event) { LL_INFOS() << "Listener requested user quit" << LL_ENDL; - mAppViewerGetter()->userQuit(); + // Trying to engage this from (e.g.) a Lua-hosting C++ coroutine runs + // afoul of an assert in the logging machinery that LLMutex must be locked + // only from the main coroutine. + LL::WorkQueue::getInstance("mainloop")->post( + [appviewer=mAppViewerGetter()]{ appviewer->userQuit(); }); } void LLAppViewerListener::requestQuit(const LLSD& event) { LL_INFOS() << "Listener requested quit" << LL_ENDL; - mAppViewerGetter()->requestQuit(); + LL::WorkQueue::getInstance("mainloop")->post( + [appviewer=mAppViewerGetter()]{ appviewer->requestQuit(); }); } void LLAppViewerListener::forceQuit(const LLSD& event) { LL_INFOS() << "Listener requested force quit" << LL_ENDL; - mAppViewerGetter()->forceQuit(); + LL::WorkQueue::getInstance("mainloop")->post( + [appviewer=mAppViewerGetter()]{ appviewer->forceQuit(); }); } -- cgit v1.2.3 From 1b7fdac4689e29ec3f64c37f10b843a114d7805d Mon Sep 17 00:00:00 2001 From: Nat Goodspeed Date: Thu, 12 Sep 2024 15:09:24 -0400 Subject: Add frame_profile.lua to TP to known spot and take frame profile. frame_profile.lua teleports home when done. Further add frame_profile bash script to run the specified viewer, automatically log into said known spot, take frame profile and quit. The frame_profile bash script runs frame_profile_quit.lua. frame_profile_quit.lua is derived from frame_profile.lua, but different: it doesn't teleport either way because it assumes autologin to the target location, and because it logs out instead of returning home. --- indra/newview/scripts/lua/frame_profile.lua | 24 +++++++++++++++++++++++ indra/newview/scripts/lua/frame_profile_quit.lua | 25 ++++++++++++++++++++++++ scripts/perf/frame_profile | 4 ++++ 3 files changed, 53 insertions(+) create mode 100644 indra/newview/scripts/lua/frame_profile.lua create mode 100644 indra/newview/scripts/lua/frame_profile_quit.lua create mode 100755 scripts/perf/frame_profile diff --git a/indra/newview/scripts/lua/frame_profile.lua b/indra/newview/scripts/lua/frame_profile.lua new file mode 100644 index 0000000000..3c6353ff68 --- /dev/null +++ b/indra/newview/scripts/lua/frame_profile.lua @@ -0,0 +1,24 @@ +-- Trigger Develop -> Render Tests -> Frame Profile + +LLAgent = require 'LLAgent' +startup = require 'startup' +Timer = (require 'timers').Timer +UI = require 'UI' + +startup.wait('STATE_STARTED') + +-- teleport to http://maps.secondlife.com/secondlife/Bug%20Island/220/224/27 +print(LLAgent.teleport{regionname='Bug Island', x=220, y=224, z=27}) +Timer(10, 'wait') +LLAgent.setCamera{camera_pos={220, 224, 26}, camera_locked=true, + focus_pos ={228, 232, 26}, focus_locked=true} +Timer(1, 'wait') +-- This freezes the viewer for perceptible realtime +UI.popup:tip('starting Render Tests -> Frame Profile') +UI.call("Advanced.ClickRenderProfile") +Timer(1, 'wait') +LLAgent.removeCamParams() +LLAgent.setFollowCamActive(false) + +-- Home, James! +print(LLAgent.teleport('home')) diff --git a/indra/newview/scripts/lua/frame_profile_quit.lua b/indra/newview/scripts/lua/frame_profile_quit.lua new file mode 100644 index 0000000000..e3177a3f67 --- /dev/null +++ b/indra/newview/scripts/lua/frame_profile_quit.lua @@ -0,0 +1,25 @@ +-- Trigger Develop -> Render Tests -> Frame Profile and quit + +LLAgent = require 'LLAgent' +logout = require 'logout' +startup = require 'startup' +Timer = (require 'timers').Timer +UI = require 'UI' + +startup.wait('STATE_STARTED') + +-- Assume we logged into http://maps.secondlife.com/secondlife/Bug%20Island/220/224/27 +-- (see frame_profile bash script) +Timer(10, 'wait') +LLAgent.setCamera{camera_pos={220, 224, 26}, camera_locked=true, + focus_pos ={228, 232, 26}, focus_locked=true} +Timer(1, 'wait') +-- This freezes the viewer for perceptible realtime +UI.popup:tip('starting Render Tests -> Frame Profile') +UI.call("Advanced.ClickRenderProfile") +Timer(1, 'wait') +LLAgent.removeCamParams() +LLAgent.setFollowCamActive(false) + +-- done +logout() diff --git a/scripts/perf/frame_profile b/scripts/perf/frame_profile new file mode 100755 index 0000000000..75bf0a3105 --- /dev/null +++ b/scripts/perf/frame_profile @@ -0,0 +1,4 @@ +#!/usr/bin/env bash + +"$1" --autologin --luafile frame_profile_quit.lua \ + http://maps.secondlife.com/secondlife/Bug%20Island/220/224/27 -- cgit v1.2.3 From d6f3f20af6cccf53746cbf1fdf39bc4e235c4f0d Mon Sep 17 00:00:00 2001 From: Nat Goodspeed Date: Thu, 12 Sep 2024 16:56:05 -0400 Subject: Convenience tweak for passing a Mac "Second Life Mumble.app" bundle --- scripts/perf/frame_profile | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/scripts/perf/frame_profile b/scripts/perf/frame_profile index 75bf0a3105..0a4e0a74ff 100755 --- a/scripts/perf/frame_profile +++ b/scripts/perf/frame_profile @@ -1,4 +1,10 @@ #!/usr/bin/env bash -"$1" --autologin --luafile frame_profile_quit.lua \ +exe="$1" +if [[ "$OSTYPE" == darwin* && -d "$exe" && "$exe" == *.app ]] +then + exe="$(ls "$exe/Contents/MacOS/Second Life "*)" +fi + +"$exe" --autologin --luafile frame_profile_quit.lua \ http://maps.secondlife.com/secondlife/Bug%20Island/220/224/27 -- cgit v1.2.3 From ae2f4f6f6a05e1d6f327887204add2c984999db2 Mon Sep 17 00:00:00 2001 From: Ansariel Date: Fri, 13 Sep 2024 20:40:51 +0200 Subject: Restore LUA debug consoles in viewer menu and tie visibility to feature flag --- indra/newview/skins/default/xui/en/menu_viewer.xml | 31 ++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/indra/newview/skins/default/xui/en/menu_viewer.xml b/indra/newview/skins/default/xui/en/menu_viewer.xml index 324e868bd5..f4864dabef 100644 --- a/indra/newview/skins/default/xui/en/menu_viewer.xml +++ b/indra/newview/skins/default/xui/en/menu_viewer.xml @@ -2583,6 +2583,37 @@ function="World.EnvPreset" parameter="scene monitor" /> + + + + + + + + + + + + + Date: Fri, 13 Sep 2024 16:01:05 -0400 Subject: Add context info to Develop->Render Tests->Frame Profile stats file. Specifically, add the viewer version, the machine ID, the grid, the region name and ID, the parcel name and ID and the timestamp. This is both richer and less fragile than trying to extract that information from the generated filename: e.g. we now have region and parcel names. Instead of making `LLGLSLShader::finishProfile()` mess with file I/O, pass it a reference to a `boost::json::value` to be filled in with statistics, if it's a `boost::json::object`. Otherwise it's `boost::json::null`, meaning no report. Make llviewerdisplay.cpp's `display()` function instantiate a `boost::json::value` to pass to `finishProfile()`. That lets llviewerdisplay.cpp also set the `"context"` entry, with a new `getProfileStatsContext()` function quite similar to `getProfileStatsFilename()`. --- indra/llrender/llglslshader.cpp | 21 ++++--------------- indra/llrender/llglslshader.h | 10 ++++----- indra/newview/llviewerdisplay.cpp | 44 ++++++++++++++++++++++++++++++++++++++- 3 files changed, 52 insertions(+), 23 deletions(-) diff --git a/indra/llrender/llglslshader.cpp b/indra/llrender/llglslshader.cpp index 56f1533708..6ba5463acd 100644 --- a/indra/llrender/llglslshader.cpp +++ b/indra/llrender/llglslshader.cpp @@ -41,8 +41,6 @@ #include "OpenGL/OpenGL.h" #endif -#include - // Print-print list of shader included source files that are linked together via glAttachShader() // i.e. On macOS / OSX the AMD GLSL linker will display an error if a varying is left in an undefined state. #define DEBUG_SHADER_INCLUDES 0 @@ -65,7 +63,7 @@ U64 LLGLSLShader::sTotalTimeElapsed = 0; U32 LLGLSLShader::sTotalTrianglesDrawn = 0; U64 LLGLSLShader::sTotalSamplesDrawn = 0; U32 LLGLSLShader::sTotalBinds = 0; -std::string LLGLSLShader::sDefaultReportName; +boost::json::value LLGLSLShader::sDefaultStats; //UI shader -- declared here so llui_libtest will link properly LLGLSLShader gUIProgram; @@ -120,16 +118,16 @@ struct LLGLSLShaderCompareTimeElapsed }; //static -void LLGLSLShader::finishProfile(const std::string& report_name) +void LLGLSLShader::finishProfile(boost::json::value& statsv) { sProfileEnabled = false; - if (! report_name.empty()) + if (! statsv.is_null()) { std::vector sorted(sInstances.begin(), sInstances.end()); std::sort(sorted.begin(), sorted.end(), LLGLSLShaderCompareTimeElapsed()); - boost::json::object stats; + auto& stats = statsv.as_object(); auto shadersit = stats.emplace("shaders", boost::json::array_kind).first; auto& shaders = shadersit->value().as_array(); bool unbound = false; @@ -174,17 +172,6 @@ void LLGLSLShader::finishProfile(const std::string& report_name) } } } - - std::ofstream outf(report_name); - if (! outf) - { - LL_WARNS() << "Couldn't write to " << std::quoted(report_name) << LL_ENDL; - } - else - { - outf << stats; - LL_INFOS() << "(also dumped to " << std::quoted(report_name) << ")" << LL_ENDL; - } } } diff --git a/indra/llrender/llglslshader.h b/indra/llrender/llglslshader.h index a9b9bfafa8..2d669c70a9 100644 --- a/indra/llrender/llglslshader.h +++ b/indra/llrender/llglslshader.h @@ -170,7 +170,7 @@ public: static U32 sMaxGLTFNodes; static void initProfile(); - static void finishProfile(const std::string& report_name=sDefaultReportName); + static void finishProfile(boost::json::value& stats=sDefaultStats); static void startProfile(); static void stopProfile(); @@ -365,10 +365,10 @@ public: private: void unloadInternal(); // This must be static because finishProfile() is called at least once - // within a __try block. If we default its report_name parameter to a - // temporary std::string, that temporary must be destroyed when the stack - // is unwound, which __try forbids. - static std::string sDefaultReportName; + // within a __try block. If we default its stats parameter to a temporary + // json::value, that temporary must be destroyed when the stack is + // unwound, which __try forbids. + static boost::json::value sDefaultStats; }; //UI shader (declared here so llui_libtest will link properly) diff --git a/indra/newview/llviewerdisplay.cpp b/indra/newview/llviewerdisplay.cpp index fdfe477a6c..aad11d9372 100644 --- a/indra/newview/llviewerdisplay.cpp +++ b/indra/newview/llviewerdisplay.cpp @@ -138,6 +138,7 @@ void render_ui_3d(); void render_ui_2d(); void render_disconnected_background(); +void getProfileStatsContext(boost::json::object& stats); std::string getProfileStatsFilename(); void display_startup() @@ -1040,8 +1041,49 @@ void display(bool rebuild, F32 zoom_factor, int subfield, bool for_snapshot) if (gShaderProfileFrame) { gShaderProfileFrame = false; - LLGLSLShader::finishProfile(getProfileStatsFilename()); + boost::json::value stats{ boost::json::object_kind }; + getProfileStatsContext(stats.as_object()); + LLGLSLShader::finishProfile(stats); + + auto report_name = getProfileStatsFilename(); + std::ofstream outf(report_name); + if (! outf) + { + LL_WARNS() << "Couldn't write to " << std::quoted(report_name) << LL_ENDL; + } + else + { + outf << stats; + LL_INFOS() << "(also dumped to " << std::quoted(report_name) << ")" << LL_ENDL; + } + } +} + +void getProfileStatsContext(boost::json::object& stats) +{ + auto contextit = stats.emplace("context", boost::json::object_kind).first; + auto& context = contextit->value().as_object(); + + auto& versionInfo = LLVersionInfo::instance(); + context.emplace("channel", versionInfo.getChannel()); + context.emplace("version", versionInfo.getVersion()); + unsigned char unique_id[MAC_ADDRESS_BYTES]{}; + LLMachineID::getUniqueID(unique_id, sizeof(unique_id)); + context.emplace("machine", stringize(LL::hexdump(unique_id, sizeof(unique_id)))); + context.emplace("grid", LLGridManager::instance().getGrid()); + LLViewerRegion* region = gAgent.getRegion(); + if (region) + { + context.emplace("region", region->getName()); + context.emplace("regionid", stringize(region->getRegionID())); + } + LLParcel* parcel = LLViewerParcelMgr::instance().getAgentParcel(); + if (parcel) + { + context.emplace("parcel", parcel->getName()); + context.emplace("parcelid", parcel->getLocalID()); } + context.emplace("time", LLDate::now().toHTTPDateString("%Y-%m-%dT%H:%M:%S")); } std::string getProfileStatsFilename() -- cgit v1.2.3 From 439cfc97a81f221daaf8ba13aa5daa87e8511047 Mon Sep 17 00:00:00 2001 From: Nat Goodspeed Date: Fri, 13 Sep 2024 16:32:04 -0400 Subject: Add script to compare a Frame Profile JSON stats file vs. baseline. Extract `latest_file()` logic replicated in profile_pretty.py and profile_csv.py out to logsdir.py, and use for new profile_cmp.py. --- scripts/perf/logsdir.py | 46 ++++++++++++++++++ scripts/perf/profile_cmp.py | 104 +++++++++++++++++++++++++++++++++++++++++ scripts/perf/profile_csv.py | 24 +++------- scripts/perf/profile_pretty.py | 22 ++------- 4 files changed, 160 insertions(+), 36 deletions(-) create mode 100644 scripts/perf/logsdir.py create mode 100644 scripts/perf/profile_cmp.py diff --git a/scripts/perf/logsdir.py b/scripts/perf/logsdir.py new file mode 100644 index 0000000000..c8b498cf78 --- /dev/null +++ b/scripts/perf/logsdir.py @@ -0,0 +1,46 @@ +#!/usr/bin/env python3 +"""\ +@file logsdir.py +@author Nat Goodspeed +@date 2024-09-12 +@brief Locate the Second Life logs directory for the current user on the + current platform. + +$LicenseInfo:firstyear=2024&license=viewerlgpl$ +Copyright (c) 2024, Linden Research, Inc. +$/LicenseInfo$ +""" + +import os +from pathlib import Path +import platform + +class Error(Exception): + pass + +# logic used by SLVersionChecker +def logsdir(): + app = 'SecondLife' + system = platform.system() + if (system == 'Darwin'): + base_dir = os.path.join(os.path.expanduser('~'), + 'Library','Application Support',app) + elif (system == 'Linux'): + base_dir = os.path.join(os.path.expanduser('~'), + '.' + app.lower()) + elif (system == 'Windows'): + appdata = os.getenv('APPDATA') + base_dir = os.path.join(appdata, app) + else: + raise ValueError("Unsupported platform '%s'" % system) + + return os.path.join(base_dir, 'logs') + +def latest_file(dirpath, pattern): + files = Path(dirpath).glob(pattern) + sort = [(p.stat().st_mtime, p) for p in files if p.is_file()] + sort.sort(reverse=True) + try: + return sort[0][1] + except IndexError: + raise Error(f'No {pattern} files in {dirpath}') diff --git a/scripts/perf/profile_cmp.py b/scripts/perf/profile_cmp.py new file mode 100644 index 0000000000..9dbfa3145b --- /dev/null +++ b/scripts/perf/profile_cmp.py @@ -0,0 +1,104 @@ +#!/usr/bin/env python3 +"""\ +@file profile_cmp.py +@author Nat Goodspeed +@date 2024-09-13 +@brief Compare a frame profile stats file with a similar baseline file. + +$LicenseInfo:firstyear=2024&license=viewerlgpl$ +Copyright (c) 2024, Linden Research, Inc. +$/LicenseInfo$ +""" + +from datetime import datetime +import json +from logsdir import Error, latest_file, logsdir +from pathlib import Path +import sys + +# variance that's ignorable +DEFAULT_EPSILON = 0.03 # 3% + +def compare(baseline, test, epsilon=DEFAULT_EPSILON): + if Path(baseline).samefile(test): + print(f'{baseline} same as\n{test}\nAnalysis moot.') + return + + with open(baseline) as inf: + bdata = json.load(inf) + with open(test) as inf: + tdata = json.load(inf) + print(f'baseline {baseline}\ntestfile {test}') + + for k, tv in tdata['context'].items(): + bv = bdata['context'].get(k) + if bv != tv: + print(f'baseline {k}={bv} vs.\ntestfile {k}={tv}') + + btime = bdata['context'].get('time') + ttime = tdata['context'].get('time') + if btime and ttime: + print('testfile newer by', + datetime.fromisoformat(ttime) - datetime.fromisoformat(btime)) + + # The following ignores totals and unused shaders, except to the extent + # that some shaders were used in the baseline but not in the recent test + # or vice-versa. While the viewer considers that a shader has been used if + # 'binds' is nonzero, we exclude any whose 'time' is zero to avoid zero + # division. + bshaders = {s['name']: s for s in bdata['shaders'] if s['time'] and s['samples']} + tshaders = {s['name']: s for s in tdata['shaders'] if s['time']} + + bothshaders = set(bshaders).intersection(tshaders) + deltas = [] + for shader in bothshaders: + bshader = bshaders[shader] + tshader = tshaders[shader] + bthruput = bshader['samples']/bshader['time'] + tthruput = tshader['samples']/tshader['time'] + delta = (tthruput - bthruput)/bthruput + if abs(delta) > epsilon: + deltas.append((delta, shader, bthruput, tthruput)) + + # descending order of performance gain + deltas.sort(reverse=True) + print(f'{len(deltas)} shaders showed nontrivial performance differences ' + '(millon samples/sec):') + namelen = max(len(s[1]) for s in deltas) if deltas else 0 + for delta, shader, bthruput, tthruput in deltas: + print(f' {shader.rjust(namelen)} {delta*100:6.1f}% ' + f'{bthruput/1000000:8.2f} -> {tthruput/1000000:8.2f}') + + tunused = set(bshaders).difference(tshaders) + print(f'{len(tunused)} baseline shaders not used in test:') + for s in tunused: + print(f' {s}') + bunused = set(tshaders).difference(bshaders) + print(f'{len(bunused)} shaders newly used in test:') + for s in bunused: + print(f' {s}') + +def main(*raw_args): + from argparse import ArgumentParser + parser = ArgumentParser(description=""" +%(prog)s compares a baseline JSON file from Develop -> Render Tests -> Frame +Profile to another such file from a more recent test. It identifies shaders +that have gained and lost in throughput. +""") + parser.add_argument('-e', '--epsilon', type=float, default=int(DEFAULT_EPSILON*100), + help="""percent variance considered ignorable (default %(default)s%%)""") + parser.add_argument('baseline', + help="""baseline profile filename to compare against""") + parser.add_argument('test', nargs='?', + help="""test profile filename to compare + (default is most recent)""") + args = parser.parse_args(raw_args) + compare(args.baseline, + args.test or latest_file(logsdir(), 'profile.*.json'), + epsilon=(args.epsilon / 100.)) + +if __name__ == "__main__": + try: + sys.exit(main(*sys.argv[1:])) + except (Error, OSError, json.JSONDecodeError) as err: + sys.exit(str(err)) diff --git a/scripts/perf/profile_csv.py b/scripts/perf/profile_csv.py index 273e3b7434..7a6b2b338e 100644 --- a/scripts/perf/profile_csv.py +++ b/scripts/perf/profile_csv.py @@ -10,17 +10,16 @@ Copyright (c) 2024, Linden Research, Inc. $/LicenseInfo$ """ -import logsdir import json -from pathlib import Path +from logsdir import Error, latest_file, logsdir import sys -class Error(Exception): - pass - def convert(path, totals=True, unused=True, file=sys.stdout): with open(path) as inf: data = json.load(inf) + # print path to sys.stderr in case user is redirecting stdout + print(path, file=sys.stderr) + print('"name", "file1", "file2", "time", "binds", "samples", "triangles"', file=file) if totals: @@ -51,19 +50,8 @@ shaders list to full shaders lines. help="""profile filename to convert (default is most recent)""") args = parser.parse_args(raw_args) - if not args.path: - logs = logsdir.logsdir() - profiles = Path(logs).glob('profile.*.json') - sort = [(p.stat().st_mtime, p) for p in profiles] - sort.sort(reverse=True) - try: - args.path = sort[0][1] - except IndexError: - raise Error(f'No profile.*.json files in {logs}') - # print path to sys.stderr in case user is redirecting stdout - print(args.path, file=sys.stderr) - - convert(args.path, totals=args.totals, unused=args.unused) + convert(args.path or latest_file(logsdir(), 'profile.*.json'), + totals=args.totals, unused=args.unused) if __name__ == "__main__": try: diff --git a/scripts/perf/profile_pretty.py b/scripts/perf/profile_pretty.py index 15b6efd94d..405b14b373 100644 --- a/scripts/perf/profile_pretty.py +++ b/scripts/perf/profile_pretty.py @@ -10,17 +10,15 @@ Copyright (c) 2024, Linden Research, Inc. $/LicenseInfo$ """ -import logsdir import json -from pathlib import Path +from logsdir import Error, latest_file, logsdir import sys -class Error(Exception): - pass - def pretty(path): with open(path) as inf: data = json.load(inf) + # print path to sys.stderr in case user is redirecting stdout + print(path, file=sys.stderr) json.dump(data, sys.stdout, indent=4) def main(*raw_args): @@ -33,19 +31,7 @@ The file produced by the viewer is a single dense line of JSON. help="""profile filename to pretty-print (default is most recent)""") args = parser.parse_args(raw_args) - if not args.path: - logs = logsdir.logsdir() - profiles = Path(logs).glob('profile.*.json') - sort = [(p.stat().st_mtime, p) for p in profiles] - sort.sort(reverse=True) - try: - args.path = sort[0][1] - except IndexError: - raise Error(f'No profile.*.json files in {logs}') - # print path to sys.stderr in case user is redirecting stdout - print(args.path, file=sys.stderr) - - pretty(args.path) + pretty(args.path or latest_file(logsdir(), 'profile.*.json')) if __name__ == "__main__": try: -- cgit v1.2.3 From 30238772354d4a99c2867f35b7e87c4f1a748222 Mon Sep 17 00:00:00 2001 From: Nat Goodspeed Date: Fri, 13 Sep 2024 16:50:33 -0400 Subject: Zap stray trailing space. --- scripts/perf/logsdir.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/perf/logsdir.py b/scripts/perf/logsdir.py index c8b498cf78..5ab45a28b6 100644 --- a/scripts/perf/logsdir.py +++ b/scripts/perf/logsdir.py @@ -25,7 +25,7 @@ def logsdir(): if (system == 'Darwin'): base_dir = os.path.join(os.path.expanduser('~'), 'Library','Application Support',app) - elif (system == 'Linux'): + elif (system == 'Linux'): base_dir = os.path.join(os.path.expanduser('~'), '.' + app.lower()) elif (system == 'Windows'): -- cgit v1.2.3 From 328bdeb3cc26648060a3d7331ccdb80539953f33 Mon Sep 17 00:00:00 2001 From: Nat Goodspeed Date: Fri, 13 Sep 2024 16:52:17 -0400 Subject: Remove autorun 'menus.lua' since menu_viewer.xml handles visibility --- indra/newview/scripts/lua/auto/menus.lua | 51 -------------------------------- 1 file changed, 51 deletions(-) delete mode 100644 indra/newview/scripts/lua/auto/menus.lua diff --git a/indra/newview/scripts/lua/auto/menus.lua b/indra/newview/scripts/lua/auto/menus.lua deleted file mode 100644 index b2f54d83df..0000000000 --- a/indra/newview/scripts/lua/auto/menus.lua +++ /dev/null @@ -1,51 +0,0 @@ --- Inject Lua-related menus into the top menu structure. Run this as a Lua --- script so that turning off the Lua feature also disables these menus. - --- Under Develop -> Consoles, want to present the equivalent of: --- --- --- --- --- --- --- --- --- - -local startup = require 'startup' -local UI = require 'UI' - --- Don't mess with the viewer's menu structure until we've logged in. -startup.wait('STATE_STARTED') - --- Add LUA Debug Console to Develop->Consoles -local pos = 9 -UI.addMenuSeparator{ - parent_menu='Consoles', pos=pos, -} -pos += 1 -UI.addMenuItem{ - parent_menu='Consoles', pos=pos, - name='lua_debug', label='LUA Debug Console', - func='Floater.ToggleOrBringToFront', param='lua_debug', -} -pos += 1 - --- Add LUA Scripts Info to Develop->Consoles -UI.addMenuItem{ - parent_menu='Consoles', pos=pos, - name='lua_scripts', label='LUA Scripts Info', - func='Floater.ToggleOrBringToFront', param='lua_scripts', -} -- cgit v1.2.3 From b89d7ef92ca955ef600729b54353eb235b79038c Mon Sep 17 00:00:00 2001 From: Kitty Barnett Date: Mon, 16 Sep 2024 15:33:27 +0200 Subject: Add the RLVa menu --- indra/newview/app_settings/settings.xml | 11 ++++ indra/newview/llviewercontrol.cpp | 3 + indra/newview/llviewermenu.cpp | 3 + indra/newview/rlvcommon.cpp | 33 +++++++++- indra/newview/rlvcommon.h | 1 + indra/newview/skins/default/xui/en/menu_viewer.xml | 70 ++++++++++++++++++++++ 6 files changed, 119 insertions(+), 2 deletions(-) diff --git a/indra/newview/app_settings/settings.xml b/indra/newview/app_settings/settings.xml index ab577200f5..bb9c1d0057 100644 --- a/indra/newview/app_settings/settings.xml +++ b/indra/newview/app_settings/settings.xml @@ -9908,6 +9908,17 @@ Boolean Value 1 + + RLVaTopLevelMenu + + Comment + Show the RLVa specific menu as a top level menu + Persist + 1 + Type + Boolean + Value + 1 RevokePermsOnStopAnimation diff --git a/indra/newview/llviewercontrol.cpp b/indra/newview/llviewercontrol.cpp index c1bf31ff9a..b9d80fff8e 100644 --- a/indra/newview/llviewercontrol.cpp +++ b/indra/newview/llviewercontrol.cpp @@ -76,6 +76,7 @@ #include "llslurl.h" #include "llstartup.h" #include "llperfstats.h" +#include "rlvcommon.h" // Third party library includes #include @@ -933,6 +934,8 @@ void settings_setup_listeners() setting_setup_signal_listener(gSavedSettings, "TerrainPaintBitDepth", handleSetShaderChanged); setting_setup_signal_listener(gSavedPerAccountSettings, "AvatarHoverOffsetZ", handleAvatarHoverOffsetChanged); + + setting_setup_signal_listener(gSavedSettings, Rlv::Settings::TopLevelMenu, Rlv::Util::menuToggleVisible); } #if TEST_CACHED_CONTROL diff --git a/indra/newview/llviewermenu.cpp b/indra/newview/llviewermenu.cpp index 97d5781566..b8a4cc89fd 100644 --- a/indra/newview/llviewermenu.cpp +++ b/indra/newview/llviewermenu.cpp @@ -147,6 +147,7 @@ #include "llviewershadermgr.h" #include "gltfscenemanager.h" #include "gltf/asset.h" +#include "rlvcommon.h" using namespace LLAvatarAppearanceDefines; @@ -6375,6 +6376,8 @@ void show_debug_menus() gMenuBarView->setItemVisible("Advanced", debug); // gMenuBarView->setItemEnabled("Advanced", debug); // Don't disable Advanced keyboard shortcuts when hidden + Rlv::Util::menuToggleVisible(); + gMenuBarView->setItemVisible("Debug", qamode); gMenuBarView->setItemEnabled("Debug", qamode); diff --git a/indra/newview/rlvcommon.cpp b/indra/newview/rlvcommon.cpp index eda2cdedc8..abb54b5b39 100644 --- a/indra/newview/rlvcommon.cpp +++ b/indra/newview/rlvcommon.cpp @@ -1,15 +1,18 @@ #include "llviewerprecompiledheaders.h" + #include "llagent.h" #include "llchat.h" #include "lldbstrings.h" #include "llversioninfo.h" +#include "llviewermenu.h" #include "llviewerstats.h" #include "message.h" +#include -#include "rlvdefines.h" #include "rlvcommon.h" -#include +#include "llviewercontrol.h" +#include "rlvhandler.h" using namespace Rlv; @@ -48,6 +51,32 @@ std::string Strings::getVersionImplNum() // RlvUtil // +void Util::menuToggleVisible() +{ + bool isTopLevel = gSavedSettings.getBOOL(Settings::TopLevelMenu); + bool isRlvEnabled = RlvHandler::isEnabled(); + + LLMenuGL* menuRLVaMain = gMenuBarView->findChildMenuByName("RLVa Main", false); + LLMenuGL* menuAdvanced = gMenuBarView->findChildMenuByName("Advanced", false); + LLMenuGL* menuRLVaEmbed= menuAdvanced->findChildMenuByName("RLVa Embedded", false); + + gMenuBarView->setItemVisible("RLVa Main", isRlvEnabled && isTopLevel); + menuAdvanced->setItemVisible("RLVa Embedded", isRlvEnabled && !isTopLevel); + + if ( isRlvEnabled && menuRLVaMain && menuRLVaEmbed && + ( (isTopLevel && 1 == menuRLVaMain->getItemCount()) || (!isTopLevel && 1 == menuRLVaEmbed->getItemCount())) ) + { + LLMenuGL* menuFrom = isTopLevel ? menuRLVaEmbed : menuRLVaMain; + LLMenuGL* menuTo = isTopLevel ? menuRLVaMain : menuRLVaEmbed; + while (LLMenuItemGL* pItem = menuFrom->getItem(1)) + { + menuFrom->removeChild(pItem); + menuTo->addChild(pItem); + pItem->updateBranchParent(menuTo); + } + } +} + bool Util::parseStringList(const std::string& strInput, std::vector& optionList, std::string_view strSeparator) { if (!strInput.empty()) diff --git a/indra/newview/rlvcommon.h b/indra/newview/rlvcommon.h index bec3e23e11..d18abcf1ac 100644 --- a/indra/newview/rlvcommon.h +++ b/indra/newview/rlvcommon.h @@ -24,6 +24,7 @@ namespace Rlv namespace Util { bool isValidReplyChannel(S32 nChannel, bool isLoopback = false); + void menuToggleVisible(); bool parseStringList(const std::string& strInput, std::vector& optionList, std::string_view strSeparator = Constants::OptionSeparator); bool sendChatReply(S32 nChannel, const std::string& strUTF8Text); bool sendChatReply(const std::string& strChannel, const std::string& strUTF8Text); diff --git a/indra/newview/skins/default/xui/en/menu_viewer.xml b/indra/newview/skins/default/xui/en/menu_viewer.xml index 40f3e51fca..d04bf87676 100644 --- a/indra/newview/skins/default/xui/en/menu_viewer.xml +++ b/indra/newview/skins/default/xui/en/menu_viewer.xml @@ -1818,6 +1818,71 @@ function="World.EnvPreset" parameter="sl_about" /> + + + + + + + + + + + + + + + + + + + + + + + + + + + + -- cgit v1.2.3 From 3c49c33f1333be9508966bcb8e79adfbaec79e46 Mon Sep 17 00:00:00 2001 From: Kitty Barnett Date: Mon, 16 Sep 2024 15:41:09 +0200 Subject: Don't compose emojis on the RLVa console input --- indra/newview/skins/default/xui/en/floater_rlv_console.xml | 1 + 1 file changed, 1 insertion(+) diff --git a/indra/newview/skins/default/xui/en/floater_rlv_console.xml b/indra/newview/skins/default/xui/en/floater_rlv_console.xml index 928d50cb41..708055d1b6 100644 --- a/indra/newview/skins/default/xui/en/floater_rlv_console.xml +++ b/indra/newview/skins/default/xui/en/floater_rlv_console.xml @@ -66,6 +66,7 @@ left="1" top="1" right="-1" + show_emoji_helper="false" wrap="true" /> -- cgit v1.2.3 From 1154e02fdded191147284997707a3b18ee3b43fd Mon Sep 17 00:00:00 2001 From: Nat Goodspeed Date: Mon, 16 Sep 2024 13:53:11 -0400 Subject: WIP: edits in support of Lua script args --- indra/llcommon/lua_function.cpp | 39 +++++++++++++++++--- indra/llcommon/lua_function.h | 17 +++++++-- indra/newview/llluamanager.cpp | 79 ++++++++++++++++++++++++++++++----------- 3 files changed, 106 insertions(+), 29 deletions(-) diff --git a/indra/llcommon/lua_function.cpp b/indra/llcommon/lua_function.cpp index 2557fd0cc9..014ff3c4c4 100644 --- a/indra/llcommon/lua_function.cpp +++ b/indra/llcommon/lua_function.cpp @@ -60,7 +60,8 @@ namespace namespace lluau { -int dostring(lua_State* L, const std::string& desc, const std::string& text) +int dostring(lua_State* L, const std::string& desc, const std::string& text, + const std::vector& args) { auto r = loadstring(L, desc, text); if (r != LUA_OK) @@ -80,12 +81,22 @@ int dostring(lua_State* L, const std::string& desc, const std::string& text) // stack: compiled chunk, debug.traceback() lua_insert(L, -2); // stack: debug.traceback(), compiled chunk - LuaRemover cleanup(L, -2); + // capture absolute index of debug.traceback() + int traceback = lua_absindex(L, -2); + // remove it from stack on exit + LuaRemover cleanup(L, traceback); + + // push any args passed -- all strings -- script must handle any desired + // conversions + for (const auto& arg : args) + { + lua_pushstdstring(L, arg); + } // It's important to pass LUA_MULTRET as the expected number of return // values: if we pass any fixed number, we discard any returned values // beyond that number. - return lua_pcall(L, 0, LUA_MULTRET, -2); + return lua_pcall(L, int(args.size()), LUA_MULTRET, traceback); } int loadstring(lua_State *L, const std::string &desc, const std::string &text) @@ -804,7 +815,13 @@ bool LuaState::checkLua(const std::string& desc, int r) return true; } -std::pair LuaState::expr(const std::string& desc, const std::string& text) +std::pair LuaState::expr(const std::string& desc, const ScriptCommand& command) +{ + return expr(desc, , command.args); +} + +std::pair LuaState::expr(const std::string& desc, const std::string& text, + const std::vector& args) { /*---------------------------- feature flag ----------------------------*/ if (! mFeature) @@ -827,7 +844,7 @@ std::pair LuaState::expr(const std::string& desc, const std::string& }; LL_INFOS("Lua") << desc << " run" << LL_ENDL; - if (! checkLua(desc, lluau::dostring(mState, desc, text))) + if (! checkLua(desc, lluau::dostring(mState, desc, text, args))) { LL_WARNS("Lua") << desc << " error: " << mError << LL_ENDL; return { -1, mError }; @@ -1049,6 +1066,18 @@ std::pair LuaFunction::getState() return { registry, lookup }; } +/***************************************************************************** +* LuaCommand +*****************************************************************************/ +LuaCommand::LuaCommand(const std::string& command) +{ +} + +bool LuaCommand::found() const +{ + return ; +} + /***************************************************************************** * source_path() *****************************************************************************/ diff --git a/indra/llcommon/lua_function.h b/indra/llcommon/lua_function.h index 10c201c234..a5022db225 100644 --- a/indra/llcommon/lua_function.h +++ b/indra/llcommon/lua_function.h @@ -19,6 +19,7 @@ #include "fsyspath.h" #include "llerror.h" #include "llsd.h" +#include "scriptcommand.h" #include "stringize.h" #include // std::uncaught_exceptions() #include // std::shared_ptr @@ -26,6 +27,7 @@ #include #include #include // std::pair +#include class LuaListener; @@ -55,8 +57,10 @@ namespace lluau // luau removed lua_dostring(), but since we perform the equivalent luau // sequence in multiple places, encapsulate it. desc and text are strings // rather than string_views because dostring() needs pointers to nul- - // terminated char arrays. - int dostring(lua_State* L, const std::string& desc, const std::string& text); + // terminated char arrays. Any args are pushed to the Lua stack before + // calling the Lua chunk in text. + int dostring(lua_State* L, const std::string& desc, const std::string& text, + const std::vector& args={}); int loadstring(lua_State* L, const std::string& desc, const std::string& text); fsyspath source_path(lua_State* L); @@ -92,13 +96,20 @@ public: // expr() is for when we want to capture any results left on the stack // by a Lua expression, possibly including multiple return values. + // Pass: + // desc = description used for logging et al. + // text = Lua chunk to execute, e.g. contents of a script file + // args = arguments, if any, to pass to script file + // Returns: // int < 0 means error, and LLSD::asString() is the error message. // int == 0 with LLSD::isUndefined() means the Lua expression returned no // results. // int == 1 means the Lua expression returned one result. // int > 1 with LLSD::isArray() means the Lua expression returned // multiple results, represented as the entries of the array. - std::pair expr(const std::string& desc, const std::string& text); + std::pair expr(const std::string& desc, const std::string& text, + const std::vector& args={}); + std::pair expr(const std::string& desc, const ScriptCommand& command); operator lua_State*() const { return mState; } diff --git a/indra/newview/llluamanager.cpp b/indra/newview/llluamanager.cpp index 0bd21516bc..c4ce07e492 100644 --- a/indra/newview/llluamanager.cpp +++ b/indra/newview/llluamanager.cpp @@ -192,35 +192,72 @@ void LLLUAmanager::runScriptFile(const std::string &filename, bool autorun, LLCoros::instance().launch(filename, [filename, autorun, result_cb, finished_cb]() { ScriptObserver observer(LLCoros::getName(), filename); - llifstream in_file; - in_file.open(filename.c_str()); + // Use LLStringUtil::getTokens() to parse the script command line + auto tokens = LLStringUtil::getTokens(filename, + " \t\r\n", // drop_delims + "", // no keep_delims + "\"'", // either kind of quotes + "\\"); // backslash escape + +#error Under construction + // TODO: + // - Move this either/or file existence logic to LuaCommand() + // - Accept LuaCommand in all LLLUAmanager::mumbleScriptFile() methods + // - Update all existing mumbleScriptFile() callers + llifstream in_file; + in_file.open(tokens[0]); if (in_file.is_open()) { - if (autorun) - { - sAutorunScriptCount++; - } - sScriptCount++; - - // A script_finished_fn is used to initialize the LuaState. - // It will be called when the LuaState is destroyed. - LuaState L(finished_cb); - std::string text{std::istreambuf_iterator(in_file), {}}; - auto [count, result] = L.expr(filename, text); - if (result_cb) - { - result_cb(count, result); - } + // The first token is in fact the script filename. Now that the + // script file is open, we've consumed that token. The rest are + // command-line arguments. + tokens.erase(tokens.begin()); } else { - auto msg{ stringize("unable to open script file '", filename, "'") }; - LL_WARNS("Lua") << msg << LL_ENDL; - if (result_cb) + // Parsing the command line produced a script file path we can't + // open. Maybe that's because there are spaces in the original + // pathname that were neither quoted nor escaped? See if we can + // open the whole original command line string. + in_file.open(filename); + if (! in_file.is_open()) { - result_cb(-1, msg); + std::ostringstream msgstream; + msgstream << "Can't open script file " << std::quoted(tokens[0]); + if (filename != tokens[0]) + { + msgstream << " or " << std::quoted(filename); + } + auto msg = msgstream.str(); + LL_WARNS("Lua") << msg << LL_ENDL; + if (result_cb) + { + result_cb(-1, msg); + } + return; } + // Here we do have in_file open, using the whole input command + // line as its pathname. Discard any parts of it we mistook for + // command-line arguments. + tokens.clear(); + } + + // Here in_file is open. + if (autorun) + { + sAutorunScriptCount++; + } + sScriptCount++; + + // A script_finished_fn is used to initialize the LuaState. + // It will be called when the LuaState is destroyed. + LuaState L(finished_cb); + std::string text{std::istreambuf_iterator(in_file), {}}; + auto [count, result] = L.expr(filename, text, tokens); + if (result_cb) + { + result_cb(count, result); } }); } -- cgit v1.2.3 From f4b65638879c10c832b3bb8448f82001106ffd11 Mon Sep 17 00:00:00 2001 From: Nat Goodspeed Date: Mon, 16 Sep 2024 17:25:48 -0400 Subject: Add LLFloaterAbout info (esp. GPU info) to Frame Profile stats dump With the About info added, `getProfileStatsContext()` need not redundantly add `"channel"`, `"version"` or `"region"`. Slightly improve the efficiency of `LlsdToJson()` and `LlsdFromJson()` by preallocating the known size of the source array or map. (Unfortunately the C++ `LLSD` class offers us no way to preallocate a map.) In `LLAppViewer::getViewerInfo()`, avoid immediate successive calls to `gAgent.getRegion()`. --- indra/llcommon/llsdjson.cpp | 15 +++++++++++++-- indra/newview/llappviewer.cpp | 4 ++-- indra/newview/llviewerdisplay.cpp | 10 +++++----- 3 files changed, 20 insertions(+), 9 deletions(-) diff --git a/indra/llcommon/llsdjson.cpp b/indra/llcommon/llsdjson.cpp index 5d38e55686..1df2a8f9eb 100644 --- a/indra/llcommon/llsdjson.cpp +++ b/indra/llcommon/llsdjson.cpp @@ -61,12 +61,20 @@ LLSD LlsdFromJson(const boost::json::value& val) result = LLSD(val.as_bool()); break; case boost::json::kind::array: + { result = LLSD::emptyArray(); - for (const auto &element : val.as_array()) + auto& array = val.as_array(); + // allocate elements 0 .. (size() - 1) to avoid incremental allocation + if (! array.empty()) + { + result[array.size() - 1] = LLSD(); + } + for (const auto &element : array) { result.append(LlsdFromJson(element)); } break; + } case boost::json::kind::object: result = LLSD::emptyMap(); for (const auto& element : val.as_object()) @@ -106,6 +114,7 @@ boost::json::value LlsdToJson(const LLSD &val) case LLSD::TypeMap: { boost::json::object& obj = result.emplace_object(); + obj.reserve(val.size()); for (const auto& llsd_dat : llsd::inMap(val)) { obj[llsd_dat.first] = LlsdToJson(llsd_dat.second); @@ -115,6 +124,7 @@ boost::json::value LlsdToJson(const LLSD &val) case LLSD::TypeArray: { boost::json::array& json_array = result.emplace_array(); + json_array.reserve(val.size()); for (const auto& llsd_dat : llsd::inArray(val)) { json_array.push_back(LlsdToJson(llsd_dat)); @@ -123,7 +133,8 @@ boost::json::value LlsdToJson(const LLSD &val) } case LLSD::TypeBinary: default: - LL_ERRS("LlsdToJson") << "Unsupported conversion to JSON from LLSD type (" << val.type() << ")." << LL_ENDL; + LL_ERRS("LlsdToJson") << "Unsupported conversion to JSON from LLSD type (" + << val.type() << ")." << LL_ENDL; break; } diff --git a/indra/newview/llappviewer.cpp b/indra/newview/llappviewer.cpp index 789aaab70d..77797ad5f0 100644 --- a/indra/newview/llappviewer.cpp +++ b/indra/newview/llappviewer.cpp @@ -3351,10 +3351,10 @@ LLSD LLAppViewer::getViewerInfo() const LLVector3d pos = gAgent.getPositionGlobal(); info["POSITION"] = ll_sd_from_vector3d(pos); info["POSITION_LOCAL"] = ll_sd_from_vector3(gAgent.getPosAgentFromGlobal(pos)); - info["REGION"] = gAgent.getRegion()->getName(); + info["REGION"] = region->getName(); boost::regex regex("\\.(secondlife|lindenlab)\\..*"); - info["HOSTNAME"] = boost::regex_replace(gAgent.getRegion()->getSimHostName(), regex, ""); + info["HOSTNAME"] = boost::regex_replace(region->getSimHostName(), regex, ""); info["SERVER_VERSION"] = gLastVersionChannel; LLSLURL slurl; LLAgentUI::buildSLURL(slurl); diff --git a/indra/newview/llviewerdisplay.cpp b/indra/newview/llviewerdisplay.cpp index aad11d9372..f722d0bd1d 100644 --- a/indra/newview/llviewerdisplay.cpp +++ b/indra/newview/llviewerdisplay.cpp @@ -58,6 +58,7 @@ #include "llpostprocess.h" #include "llrender.h" #include "llscenemonitor.h" +#include "llsdjson.h" #include "llselectmgr.h" #include "llsky.h" #include "llspatialpartition.h" @@ -1061,12 +1062,12 @@ void display(bool rebuild, F32 zoom_factor, int subfield, bool for_snapshot) void getProfileStatsContext(boost::json::object& stats) { - auto contextit = stats.emplace("context", boost::json::object_kind).first; + // populate the context with info from LLFloaterAbout + auto contextit = stats.emplace("context", + LlsdToJson(LLAppViewer::instance()->getViewerInfo())).first; auto& context = contextit->value().as_object(); - auto& versionInfo = LLVersionInfo::instance(); - context.emplace("channel", versionInfo.getChannel()); - context.emplace("version", versionInfo.getVersion()); + // then add a few more things unsigned char unique_id[MAC_ADDRESS_BYTES]{}; LLMachineID::getUniqueID(unique_id, sizeof(unique_id)); context.emplace("machine", stringize(LL::hexdump(unique_id, sizeof(unique_id)))); @@ -1074,7 +1075,6 @@ void getProfileStatsContext(boost::json::object& stats) LLViewerRegion* region = gAgent.getRegion(); if (region) { - context.emplace("region", region->getName()); context.emplace("regionid", stringize(region->getRegionID())); } LLParcel* parcel = LLViewerParcelMgr::instance().getAgentParcel(); -- cgit v1.2.3 From 6f4d7c2d6d363aee60d2f3d1fe4ed4a251aaa11b Mon Sep 17 00:00:00 2001 From: Kitty Barnett Date: Tue, 17 Sep 2024 19:12:55 +0200 Subject: Add proper file headers --- indra/newview/rlvactions.cpp | 27 +++++++++++++++++++++++++++ indra/newview/rlvactions.h | 27 +++++++++++++++++++++++++++ indra/newview/rlvcommon.cpp | 27 +++++++++++++++++++++++++++ indra/newview/rlvcommon.h | 27 +++++++++++++++++++++++++++ indra/newview/rlvdefines.h | 27 +++++++++++++++++++++++++++ indra/newview/rlvfloaters.cpp | 27 +++++++++++++++++++++++++++ indra/newview/rlvfloaters.h | 27 +++++++++++++++++++++++++++ indra/newview/rlvhandler.cpp | 27 +++++++++++++++++++++++++++ indra/newview/rlvhandler.h | 27 +++++++++++++++++++++++++++ indra/newview/rlvhelper.cpp | 27 +++++++++++++++++++++++++++ indra/newview/rlvhelper.h | 27 +++++++++++++++++++++++++++ 11 files changed, 297 insertions(+) diff --git a/indra/newview/rlvactions.cpp b/indra/newview/rlvactions.cpp index 1988c99ecf..110beeafc0 100644 --- a/indra/newview/rlvactions.cpp +++ b/indra/newview/rlvactions.cpp @@ -1,3 +1,30 @@ +/** + * @file rlvactions.cpp + * @author Kitty Barnett + * @brief RLVa public facing helper class to easily make RLV checks + * + * $LicenseInfo:firstyear=2024&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2024, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + #include "llviewerprecompiledheaders.h" #include "rlvactions.h" diff --git a/indra/newview/rlvactions.h b/indra/newview/rlvactions.h index ac4fc73339..cb0df95e37 100644 --- a/indra/newview/rlvactions.h +++ b/indra/newview/rlvactions.h @@ -1,3 +1,30 @@ +/** + * @file rlvactions.h + * @author Kitty Barnett + * @brief RLVa public facing helper class to easily make RLV checks + * + * $LicenseInfo:firstyear=2024&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2024, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + #pragma once // ============================================================================ diff --git a/indra/newview/rlvcommon.cpp b/indra/newview/rlvcommon.cpp index abb54b5b39..4140659715 100644 --- a/indra/newview/rlvcommon.cpp +++ b/indra/newview/rlvcommon.cpp @@ -1,3 +1,30 @@ +/** + * @file rlvcommon.h + * @author Kitty Barnett + * @brief RLVa helper functions and constants used throughout the viewer + * + * $LicenseInfo:firstyear=2024&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2024, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + #include "llviewerprecompiledheaders.h" #include "llagent.h" diff --git a/indra/newview/rlvcommon.h b/indra/newview/rlvcommon.h index d18abcf1ac..6f1bbbbdc6 100644 --- a/indra/newview/rlvcommon.h +++ b/indra/newview/rlvcommon.h @@ -1,3 +1,30 @@ +/** + * @file rlvcommon.h + * @author Kitty Barnett + * @brief RLVa helper functions and constants used throughout the viewer + * + * $LicenseInfo:firstyear=2024&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2024, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + #pragma once #include "rlvdefines.h" diff --git a/indra/newview/rlvdefines.h b/indra/newview/rlvdefines.h index 88dffa1127..15baf1ba49 100644 --- a/indra/newview/rlvdefines.h +++ b/indra/newview/rlvdefines.h @@ -1,3 +1,30 @@ +/** + * @file rlvdefines.h + * @author Kitty Barnett + * @brief RLVa common defines, constants and enums + * + * $LicenseInfo:firstyear=2024&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2024, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + #pragma once // ============================================================================ diff --git a/indra/newview/rlvfloaters.cpp b/indra/newview/rlvfloaters.cpp index 8d107b2540..8a074fd14d 100644 --- a/indra/newview/rlvfloaters.cpp +++ b/indra/newview/rlvfloaters.cpp @@ -1,3 +1,30 @@ +/** + * @file rlvfloaters.cpp + * @author Kitty Barnett + * @brief RLVa floaters class implementations + * + * $LicenseInfo:firstyear=2024&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2024, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + #include "llviewerprecompiledheaders.h" #include "llagentdata.h" diff --git a/indra/newview/rlvfloaters.h b/indra/newview/rlvfloaters.h index a599bb051a..1d560f32d4 100644 --- a/indra/newview/rlvfloaters.h +++ b/indra/newview/rlvfloaters.h @@ -1,3 +1,30 @@ +/** + * @file rlvfloaters.h + * @author Kitty Barnett + * @brief RLVa floaters class implementations + * + * $LicenseInfo:firstyear=2024&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2024, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + #pragma once #include "llfloater.h" diff --git a/indra/newview/rlvhandler.cpp b/indra/newview/rlvhandler.cpp index 0a0da0e25d..bf086c1b4b 100644 --- a/indra/newview/rlvhandler.cpp +++ b/indra/newview/rlvhandler.cpp @@ -1,3 +1,30 @@ +/** + * @file rlvhandler.cpp + * @author Kitty Barnett + * @brief RLVa helper classes for internal use only + * + * $LicenseInfo:firstyear=2024&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2024, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + #include "llviewerprecompiledheaders.h" #include "llagent.h" #include "llstartup.h" diff --git a/indra/newview/rlvhandler.h b/indra/newview/rlvhandler.h index 7fa4da767a..38612485b1 100644 --- a/indra/newview/rlvhandler.h +++ b/indra/newview/rlvhandler.h @@ -1,3 +1,30 @@ +/** + * @file rlvhandler.h + * @author Kitty Barnett + * @brief Primary command process and orchestration class + * + * $LicenseInfo:firstyear=2024&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2024, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + #pragma once #include "llchat.h" diff --git a/indra/newview/rlvhelper.cpp b/indra/newview/rlvhelper.cpp index ecb76a1db3..a82de4b9b8 100644 --- a/indra/newview/rlvhelper.cpp +++ b/indra/newview/rlvhelper.cpp @@ -1,3 +1,30 @@ +/** + * @file rlvhelper.cpp + * @author Kitty Barnett + * @brief RLVa helper classes for internal use only + * + * $LicenseInfo:firstyear=2024&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2024, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + #include "llviewerprecompiledheaders.h" #include "lltrans.h" diff --git a/indra/newview/rlvhelper.h b/indra/newview/rlvhelper.h index 644cd0ee22..7f0435f791 100644 --- a/indra/newview/rlvhelper.h +++ b/indra/newview/rlvhelper.h @@ -1,3 +1,30 @@ +/** + * @file rlvhelper.h + * @author Kitty Barnett + * @brief RLVa helper classes for internal use only + * + * $LicenseInfo:firstyear=2024&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2024, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + #pragma once #include "rlvdefines.h" -- cgit v1.2.3 From 576c558759aa84df7b30ee29ca55143719d73028 Mon Sep 17 00:00:00 2001 From: Andrey Lihatskiy Date: Tue, 17 Sep 2024 20:03:03 +0300 Subject: Xcode16 build fix --- .github/workflows/build.yaml | 4 ++-- indra/CMakeLists.txt | 3 +++ indra/llcommon/llqueuedthread.cpp | 4 ++-- indra/llrender/llimagegl.cpp | 2 +- indra/llwindow/llwindowmacosx.cpp | 8 ++++---- indra/newview/gltfscenemanager.cpp | 2 +- indra/newview/llmeshrepository.cpp | 6 +++--- indra/newview/llmeshrepository.h | 4 ++-- indra/newview/llpanelemojicomplete.cpp | 2 +- indra/newview/lltexturefetch.cpp | 6 +++--- indra/newview/llviewermedia.cpp | 4 ++-- indra/newview/llviewerwindow.cpp | 2 +- indra/newview/llvoicewebrtc.cpp | 20 ++++++++++---------- indra/viewer_components/login/lllogin.cpp | 3 +-- 14 files changed, 36 insertions(+), 34 deletions(-) diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index 2cb94c9c90..8103d5c039 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -42,7 +42,7 @@ jobs: needs: setup strategy: matrix: - runner: [windows-large, macos-12-xl] + runner: [windows-large, macos-14-xlarge] configuration: ${{ fromJSON(needs.setup.outputs.configurations) }} runs-on: ${{ matrix.runner }} outputs: @@ -64,7 +64,7 @@ jobs: # autobuild-package.xml. AUTOBUILD_VCS_INFO: "true" AUTOBUILD_VSVER: "170" - DEVELOPER_DIR: "/Applications/Xcode_14.0.1.app/Contents/Developer" + DEVELOPER_DIR: "/Applications/Xcode_16.0.app/Contents/Developer" # Ensure that Linden viewer builds engage Bugsplat. BUGSPLAT_DB: ${{ needs.setup.outputs.bugsplat_db }} build_coverity: false diff --git a/indra/CMakeLists.txt b/indra/CMakeLists.txt index 422927704a..bf0a051799 100644 --- a/indra/CMakeLists.txt +++ b/indra/CMakeLists.txt @@ -29,6 +29,9 @@ else() set( USE_AUTOBUILD_3P ON ) endif() +set(CMAKE_CXX_STANDARD 20) +set(CMAKE_CXX_STANDARD_REQUIRED ON) + include(Variables) include(BuildVersion) diff --git a/indra/llcommon/llqueuedthread.cpp b/indra/llcommon/llqueuedthread.cpp index 1c4ac5a7bf..0196a24b18 100644 --- a/indra/llcommon/llqueuedthread.cpp +++ b/indra/llcommon/llqueuedthread.cpp @@ -146,7 +146,7 @@ size_t LLQueuedThread::updateQueue(F32 max_time_ms) // schedule a call to threadedUpdate for every call to updateQueue if (!isQuitting()) { - mRequestQueue.post([=]() + mRequestQueue.post([=, this]() { LL_PROFILE_ZONE_NAMED_CATEGORY_THREAD("qt - update"); mIdleThread = false; @@ -474,7 +474,7 @@ void LLQueuedThread::processRequest(LLQueuedThread::QueuedRequest* req) #else using namespace std::chrono_literals; auto retry_time = LL::WorkQueue::TimePoint::clock::now() + 16ms; - mRequestQueue.post([=] + mRequestQueue.post([=, this] { LL_PROFILE_ZONE_NAMED("processRequest - retry"); if (LL::WorkQueue::TimePoint::clock::now() < retry_time) diff --git a/indra/llrender/llimagegl.cpp b/indra/llrender/llimagegl.cpp index 67b4ada62f..ba77c6f1a4 100644 --- a/indra/llrender/llimagegl.cpp +++ b/indra/llrender/llimagegl.cpp @@ -1777,7 +1777,7 @@ void LLImageGL::syncToMainThread(LLGLuint new_tex_name) ref(); LL::WorkQueue::postMaybe( mMainQueue, - [=]() + [=, this]() { LL_PROFILE_ZONE_NAMED("cglt - delete callback"); syncTexName(new_tex_name); diff --git a/indra/llwindow/llwindowmacosx.cpp b/indra/llwindow/llwindowmacosx.cpp index 80001b14ee..cc88268c67 100644 --- a/indra/llwindow/llwindowmacosx.cpp +++ b/indra/llwindow/llwindowmacosx.cpp @@ -1040,7 +1040,7 @@ F32 LLWindowMacOSX::getGamma() &greenGamma, &blueMin, &blueMax, - &blueGamma) == noErr) + &blueGamma) == static_cast(noErr)) { // So many choices... // Let's just return the green channel gamma for now. @@ -1091,7 +1091,7 @@ bool LLWindowMacOSX::setGamma(const F32 gamma) &greenGamma, &blueMin, &blueMax, - &blueGamma) != noErr) + &blueGamma) != static_cast(noErr)) { return false; } @@ -1106,7 +1106,7 @@ bool LLWindowMacOSX::setGamma(const F32 gamma) gamma, blueMin, blueMax, - gamma) != noErr) + gamma) != static_cast(noErr)) { return false; } @@ -1158,7 +1158,7 @@ bool LLWindowMacOSX::setCursorPosition(const LLCoordWindow position) newPosition.y = screen_pos.mY; CGSetLocalEventsSuppressionInterval(0.0); - if(CGWarpMouseCursorPosition(newPosition) == noErr) + if(CGWarpMouseCursorPosition(newPosition) == static_cast(noErr)) { result = true; } diff --git a/indra/newview/gltfscenemanager.cpp b/indra/newview/gltfscenemanager.cpp index ce54fa4c12..f043277367 100644 --- a/indra/newview/gltfscenemanager.cpp +++ b/indra/newview/gltfscenemanager.cpp @@ -497,7 +497,7 @@ void GLTFSceneManager::update() LLNewBufferedResourceUploadInfo::uploadFinish_f finish = [this, buffer](LLUUID assetId, LLSD response) { LLAppViewer::instance()->postToMainCoro( - [=]() + [=, this]() { if (mUploadingAsset) { diff --git a/indra/newview/llmeshrepository.cpp b/indra/newview/llmeshrepository.cpp index 1f214344ff..ebfbc435ae 100644 --- a/indra/newview/llmeshrepository.cpp +++ b/indra/newview/llmeshrepository.cpp @@ -548,8 +548,8 @@ LLViewerFetchedTexture* LLMeshUploadThread::FindViewerTexture(const LLImportMate return ppTex ? (*ppTex).get() : NULL; } -volatile S32 LLMeshRepoThread::sActiveHeaderRequests = 0; -volatile S32 LLMeshRepoThread::sActiveLODRequests = 0; +std::atomic LLMeshRepoThread::sActiveHeaderRequests = 0; +std::atomic LLMeshRepoThread::sActiveLODRequests = 0; U32 LLMeshRepoThread::sMaxConcurrentRequests = 1; S32 LLMeshRepoThread::sRequestLowWater = REQUEST2_LOW_WATER_MIN; S32 LLMeshRepoThread::sRequestHighWater = REQUEST2_HIGH_WATER_MIN; @@ -3914,7 +3914,7 @@ void LLMeshRepository::notifyLoadedMeshes() } // erase from background thread - mThread->mWorkQueue.post([=]() + mThread->mWorkQueue.post([=, this]() { mThread->mSkinMap.erase(id); }); diff --git a/indra/newview/llmeshrepository.h b/indra/newview/llmeshrepository.h index b5c2424578..3d25a4dd78 100644 --- a/indra/newview/llmeshrepository.h +++ b/indra/newview/llmeshrepository.h @@ -256,8 +256,8 @@ class LLMeshRepoThread : public LLThread { public: - volatile static S32 sActiveHeaderRequests; - volatile static S32 sActiveLODRequests; + static std::atomic sActiveHeaderRequests; + static std::atomic sActiveLODRequests; static U32 sMaxConcurrentRequests; static S32 sRequestLowWater; static S32 sRequestHighWater; diff --git a/indra/newview/llpanelemojicomplete.cpp b/indra/newview/llpanelemojicomplete.cpp index cb89a5910e..fc1bf6ca93 100644 --- a/indra/newview/llpanelemojicomplete.cpp +++ b/indra/newview/llpanelemojicomplete.cpp @@ -438,7 +438,7 @@ void LLPanelEmojiComplete::updateConstraints() { mRenderRect = getLocalRect(); - mEmojiWidth = (U16)(mIconFont->getWidthF32(u8"\U0001F431") + mPadding * 2); + mEmojiWidth = (U16)(mIconFont->getWidthF32(LLWString(1, 0x1F431).c_str()) + mPadding * 2); if (mVertical) { mEmojiHeight = mIconFont->getLineHeight() + mPadding * 2; diff --git a/indra/newview/lltexturefetch.cpp b/indra/newview/lltexturefetch.cpp index 69c40066b4..ee98728ff1 100644 --- a/indra/newview/lltexturefetch.cpp +++ b/indra/newview/lltexturefetch.cpp @@ -2832,7 +2832,7 @@ bool LLTextureFetch::getRequestFinished(const LLUUID& id, S32& discard_level, bool LLTextureFetch::updateRequestPriority(const LLUUID& id, F32 priority) { LL_PROFILE_ZONE_SCOPED; - mRequestQueue.tryPost([=]() + mRequestQueue.tryPost([=, this]() { LLTextureFetchWorker* worker = getWorker(id); if (worker) @@ -3523,8 +3523,8 @@ TFReqSendMetrics::doWork(LLTextureFetch * fetcher) //if (! gViewerAssetStatsThread1) // return true; - static volatile bool reporting_started(false); - static volatile S32 report_sequence(0); + static std::atomic reporting_started(false); + static std::atomic report_sequence(0); // In mStatsSD, we have a copy we own of the LLSD representation // of the asset stats. Add some additional fields and ship it off. diff --git a/indra/newview/llviewermedia.cpp b/indra/newview/llviewermedia.cpp index 9739cac311..a29c6afe66 100644 --- a/indra/newview/llviewermedia.cpp +++ b/indra/newview/llviewermedia.cpp @@ -2900,14 +2900,14 @@ void LLViewerMediaImpl::update() media_tex->ref(); main_queue->postTo( mTexUpdateQueue, // Worker thread queue - [=]() // work done on update worker thread + [=, this]() // work done on update worker thread { #if LL_IMAGEGL_THREAD_CHECK media_tex->getGLTexture()->mActiveThread = LLThread::currentID(); #endif doMediaTexUpdate(media_tex, data, data_width, data_height, x_pos, y_pos, width, height, true); }, - [=]() // callback to main thread + [=, this]() // callback to main thread { #if LL_IMAGEGL_THREAD_CHECK media_tex->getGLTexture()->mActiveThread = LLThread::currentID(); diff --git a/indra/newview/llviewerwindow.cpp b/indra/newview/llviewerwindow.cpp index 0e7d82fd90..d35a504228 100644 --- a/indra/newview/llviewerwindow.cpp +++ b/indra/newview/llviewerwindow.cpp @@ -1331,7 +1331,7 @@ LLWindowCallbacks::DragNDropResult LLViewerWindow::handleDragNDrop( LLWindow *wi // Check the whitelist, if there's media (otherwise just show it) if (te->getMediaData() == NULL || te->getMediaData()->checkCandidateUrl(url)) { - if ( obj != mDragHoveredObject) + if (obj != mDragHoveredObject.get()) { // Highlight the dragged object LLSelectMgr::getInstance()->unhighlightObjectOnly(mDragHoveredObject); diff --git a/indra/newview/llvoicewebrtc.cpp b/indra/newview/llvoicewebrtc.cpp index 3d684e5a1b..9c0bdf0e18 100644 --- a/indra/newview/llvoicewebrtc.cpp +++ b/indra/newview/llvoicewebrtc.cpp @@ -549,7 +549,7 @@ void LLWebRTCVoiceClient::voiceConnectionCoro() } } LL::WorkQueue::postMaybe(mMainQueue, - [=] { + [=, this] { if (sShuttingDown) { return; @@ -667,7 +667,7 @@ void LLWebRTCVoiceClient::OnDevicesChanged(const llwebrtc::LLWebRTCVoiceDeviceLi { LL::WorkQueue::postMaybe(mMainQueue, - [=] + [=, this] { OnDevicesChangedImpl(render_devices, capture_devices); }); @@ -2203,7 +2203,7 @@ LLVoiceWebRTCConnection::~LLVoiceWebRTCConnection() void LLVoiceWebRTCConnection::OnIceGatheringState(llwebrtc::LLWebRTCSignalingObserver::EIceGatheringState state) { LL::WorkQueue::postMaybe(mMainQueue, - [=] { + [=, this] { LL_DEBUGS("Voice") << "Ice Gathering voice account. " << state << LL_ENDL; switch (state) @@ -2226,7 +2226,7 @@ void LLVoiceWebRTCConnection::OnIceGatheringState(llwebrtc::LLWebRTCSignalingObs // callback from llwebrtc void LLVoiceWebRTCConnection::OnIceCandidate(const llwebrtc::LLWebRTCIceCandidate& candidate) { - LL::WorkQueue::postMaybe(mMainQueue, [=] { mIceCandidates.push_back(candidate); }); + LL::WorkQueue::postMaybe(mMainQueue, [=, this] { mIceCandidates.push_back(candidate); }); } void LLVoiceWebRTCConnection::processIceUpdates() @@ -2344,7 +2344,7 @@ void LLVoiceWebRTCConnection::processIceUpdatesCoro(connectionPtr_t connection) void LLVoiceWebRTCConnection::OnOfferAvailable(const std::string &sdp) { LL::WorkQueue::postMaybe(mMainQueue, - [=] { + [=, this] { if (mShutDown) { return; @@ -2371,7 +2371,7 @@ void LLVoiceWebRTCConnection::OnOfferAvailable(const std::string &sdp) void LLVoiceWebRTCConnection::OnAudioEstablished(llwebrtc::LLWebRTCAudioInterface* audio_interface) { LL::WorkQueue::postMaybe(mMainQueue, - [=] { + [=, this] { if (mShutDown) { return; @@ -2393,7 +2393,7 @@ void LLVoiceWebRTCConnection::OnAudioEstablished(llwebrtc::LLWebRTCAudioInterfac void LLVoiceWebRTCConnection::OnRenegotiationNeeded() { LL::WorkQueue::postMaybe(mMainQueue, - [=] { + [=, this] { LL_DEBUGS("Voice") << "Voice channel requires renegotiation." << LL_ENDL; if (!mShutDown) { @@ -2407,7 +2407,7 @@ void LLVoiceWebRTCConnection::OnRenegotiationNeeded() void LLVoiceWebRTCConnection::OnPeerConnectionClosed() { LL::WorkQueue::postMaybe(mMainQueue, - [=] { + [=, this] { LL_DEBUGS("Voice") << "Peer connection has closed." << LL_ENDL; if (mVoiceConnectionState == VOICE_STATE_WAIT_FOR_CLOSE) { @@ -2879,7 +2879,7 @@ bool LLVoiceWebRTCConnection::connectionStateMachine() // llwebrtc callback void LLVoiceWebRTCConnection::OnDataReceived(const std::string& data, bool binary) { - LL::WorkQueue::postMaybe(mMainQueue, [=] { LLVoiceWebRTCConnection::OnDataReceivedImpl(data, binary); }); + LL::WorkQueue::postMaybe(mMainQueue, [=, this] { LLVoiceWebRTCConnection::OnDataReceivedImpl(data, binary); }); } // @@ -3035,7 +3035,7 @@ void LLVoiceWebRTCConnection::OnDataReceivedImpl(const std::string &data, bool b void LLVoiceWebRTCConnection::OnDataChannelReady(llwebrtc::LLWebRTCDataInterface *data_interface) { LL::WorkQueue::postMaybe(mMainQueue, - [=] { + [=, this] { if (mShutDown) { return; diff --git a/indra/viewer_components/login/lllogin.cpp b/indra/viewer_components/login/lllogin.cpp index feebecf4cb..a6c4afd6c4 100644 --- a/indra/viewer_components/login/lllogin.cpp +++ b/indra/viewer_components/login/lllogin.cpp @@ -127,8 +127,7 @@ void LLLogin::Impl::connect(const std::string& uri, const LLSD& login_params) // Launch a coroutine with our login_() method. Run the coroutine until // its first wait; at that point, return here. - std::string coroname = - LLCoros::instance().launch("LLLogin::Impl::login_", [=]() { loginCoro(uri, login_params); }); + std::string coroname = LLCoros::instance().launch("LLLogin::Impl::login_", [=, this]() { loginCoro(uri, login_params); }); LL_DEBUGS("LLLogin") << " connected with uri '" << uri << "', login_params " << login_params << LL_ENDL; } -- cgit v1.2.3 From fca4227e59d3375e824e0265a08d6b7ed7715632 Mon Sep 17 00:00:00 2001 From: Kitty Barnett Date: Tue, 17 Sep 2024 23:07:29 +0200 Subject: Fix tab vs whitespace line --- indra/newview/llviewermenu.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/indra/newview/llviewermenu.cpp b/indra/newview/llviewermenu.cpp index dbd3478637..bbf2dfad13 100644 --- a/indra/newview/llviewermenu.cpp +++ b/indra/newview/llviewermenu.cpp @@ -6248,7 +6248,7 @@ void show_debug_menus() gMenuBarView->setItemVisible("Advanced", debug); // gMenuBarView->setItemEnabled("Advanced", debug); // Don't disable Advanced keyboard shortcuts when hidden - Rlv::Util::menuToggleVisible(); + Rlv::Util::menuToggleVisible(); gMenuBarView->setItemVisible("Debug", qamode); gMenuBarView->setItemEnabled("Debug", qamode); -- cgit v1.2.3 From e03ad4913fd9dc12d9efcdd3854885a1622277ef Mon Sep 17 00:00:00 2001 From: Kitty Barnett Date: Tue, 17 Sep 2024 23:44:22 +0200 Subject: Mac build fixes: Reapply the template fix in rlvhelper.h + point to LLFloaterReg in the global namespace --- indra/newview/rlvfloaters.h | 3 ++- indra/newview/rlvhelper.h | 18 ++++++++++++------ 2 files changed, 14 insertions(+), 7 deletions(-) diff --git a/indra/newview/rlvfloaters.h b/indra/newview/rlvfloaters.h index 1d560f32d4..8acfa43f28 100644 --- a/indra/newview/rlvfloaters.h +++ b/indra/newview/rlvfloaters.h @@ -32,6 +32,7 @@ #include "rlvdefines.h" class LLChatEntry; +class LLFloaterReg; class LLLayoutPanel; class LLTextEditor; class RlvCommand; @@ -45,7 +46,7 @@ namespace Rlv class FloaterConsole : public LLFloater { - friend class LLFloaterReg; + friend class ::LLFloaterReg; FloaterConsole(const LLSD& sdKey) : LLFloater(sdKey) {} public: diff --git a/indra/newview/rlvhelper.h b/indra/newview/rlvhelper.h index 7f0435f791..f241332594 100644 --- a/indra/newview/rlvhelper.h +++ b/indra/newview/rlvhelper.h @@ -152,14 +152,20 @@ namespace Rlv // // CommandHandler - The actual command handler (Note that a handler is more general than a processor; a handler can - for instance - be used by multiple processors) // + #if LL_WINDOWS + #define RLV_TEMPL_FIX(x) template + #else + #define RLV_TEMPL_FIX(x) template + #endif // LL_WINDOWS + template struct CommandHandler { - template::type> static ECmdRet onCommand(const RlvCommand&, bool&); - template::type> static void onCommandToggle(EBehaviour, bool); - template::type> static ECmdRet onCommand(const RlvCommand&); - template::type> static ECmdRet onCommand(const RlvCommand&, std::string&); + RLV_TEMPL_FIX(typename = typename std::enable_if::type) static ECmdRet onCommand(const RlvCommand&, bool&); + RLV_TEMPL_FIX(typename = typename std::enable_if::type) static void onCommandToggle(EBehaviour, bool); + RLV_TEMPL_FIX(typename = typename std::enable_if::type) static ECmdRet onCommand(const RlvCommand&); + RLV_TEMPL_FIX(typename = typename std::enable_if::type) static ECmdRet onCommand(const RlvCommand&, std::string&); }; // Aliases to improve readability in definitions @@ -179,11 +185,11 @@ namespace Rlv { public: // Default constructor used by behaviour specializations - template::type> + RLV_TEMPL_FIX(typename = typename std::enable_if::type) CommandProcessor(const std::string& strBhvr, U32 nBhvrFlags = 0) : BehaviourInfo(strBhvr, templBhvr, templParamType, nBhvrFlags) {} // Constructor used when we don't want to specialize on behaviour (see BehaviourGenericProcessor) - template::type> + RLV_TEMPL_FIX(typename = typename std::enable_if::type) CommandProcessor(const std::string& strBhvr, EBehaviour eBhvr, U32 nBhvrFlags = 0) : BehaviourInfo(strBhvr, eBhvr, templParamType, nBhvrFlags) {} ECmdRet processCommand(const RlvCommand& rlvCmd) const override { return baseImpl::processCommand(rlvCmd, &handlerImpl::onCommand); } -- cgit v1.2.3 From 50513bab2d6b1823f983c145553b8a6af44c2f28 Mon Sep 17 00:00:00 2001 From: Nat Goodspeed Date: Thu, 19 Sep 2024 15:00:37 -0400 Subject: Make login.lua enhance plain grid='agni' to required form. --- indra/newview/scripts/lua/require/login.lua | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/indra/newview/scripts/lua/require/login.lua b/indra/newview/scripts/lua/require/login.lua index 919434f3a5..37c9093a21 100644 --- a/indra/newview/scripts/lua/require/login.lua +++ b/indra/newview/scripts/lua/require/login.lua @@ -18,16 +18,25 @@ local function ensure_login_state(op) end end +local function fullgrid(grid) + if string.find(grid, '.', 1, true) then + return grid + else + return `util.{grid}.secondlife.com` + end +end + function login.login(...) ensure_login_state('login') local args = mapargs('username,grid,slurl', ...) args.op = 'login' + args.grid = fullgrid(args.grid) return leap.request('LLPanelLogin', args) end function login.savedLogins(grid) ensure_login_state('savedLogins') - return leap.request('LLPanelLogin', {op='savedLogins', grid=grid})['logins'] + return leap.request('LLPanelLogin', {op='savedLogins', grid=fullgrid(grid)})['logins'] end return login -- cgit v1.2.3 From 71fcf45584d7f418f60047eb212ed2a474b39861 Mon Sep 17 00:00:00 2001 From: Brad Linden Date: Thu, 19 Sep 2024 12:05:27 -0700 Subject: Incremented viewer version after release/2024.09-ExtraFPS branch creation --- indra/newview/VIEWER_VERSION.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/indra/newview/VIEWER_VERSION.txt b/indra/newview/VIEWER_VERSION.txt index e0eaaa0bbc..0f9f025fe4 100644 --- a/indra/newview/VIEWER_VERSION.txt +++ b/indra/newview/VIEWER_VERSION.txt @@ -1 +1 @@ -7.1.11 +7.1.12 -- cgit v1.2.3 From cae279b6e1019319f9186c2cea1127b9df149a7b Mon Sep 17 00:00:00 2001 From: Cosmic Linden Date: Wed, 11 Sep 2024 17:10:48 -0700 Subject: secondlife/viewer#2462: Optimize unloading of prims --- indra/newview/llmeshrepository.cpp | 81 ++++++++++++++++++++++++++++++++++-- indra/newview/llmeshrepository.h | 7 +++- indra/newview/llviewerobjectlist.cpp | 5 +++ indra/newview/llvovolume.cpp | 27 +++++++++++- indra/newview/llvovolume.h | 1 + 5 files changed, 116 insertions(+), 5 deletions(-) diff --git a/indra/newview/llmeshrepository.cpp b/indra/newview/llmeshrepository.cpp index 26e2d8f319..2ca94390e5 100644 --- a/indra/newview/llmeshrepository.cpp +++ b/indra/newview/llmeshrepository.cpp @@ -3651,20 +3651,95 @@ S32 LLMeshRepository::update() return static_cast(size); } -void LLMeshRepository::unregisterMesh(LLVOVolume* vobj) +#ifdef SHOW_ASSERT +// Brute-force remove the object from all loading queues. Returns true if +// something was removed. +// This function is used in a debug assert to ensure unregisterMesh and +// unregisterSkinInfo are called as intended. +// *TODO: Consider removing at some point if we feel confident about the code +// working as intended. +bool LLMeshRepository::forceUnregisterMesh(LLVOVolume* vobj) { + LL_PROFILE_ZONE_SCOPED_CATEGORY_VOLUME; + + bool found = false; + for (auto& lod : mLoadingMeshes) { for (auto& param : lod) { - vector_replace_with_last(param.second, vobj); + llassert(std::find(param.second.begin(), param.second.end(), vobj) == param.second.end()); + found = found || vector_replace_with_last(param.second, vobj); } } for (auto& skin_pair : mLoadingSkins) { - vector_replace_with_last(skin_pair.second, vobj); + llassert(std::find(skin_pair.second.begin(), skin_pair.second.end(), vobj) == skin_pair.second.end()); + found = found || vector_replace_with_last(skin_pair.second, vobj); + } + + return found; +} +#endif + +void LLMeshRepository::unregisterMesh(LLVOVolume* vobj, const LLVolumeParams& mesh_params, S32 detail) +{ + LL_PROFILE_ZONE_SCOPED_CATEGORY_VOLUME; + + llassert((mesh_params.getSculptType() & LL_SCULPT_TYPE_MASK) == LL_SCULPT_TYPE_MESH); + llassert(mesh_params.getSculptID().notNull()); + auto& lod = mLoadingMeshes[detail]; + auto param_iter = lod.find(mesh_params.getSculptID()); + if (param_iter != lod.end()) + { + vector_replace_with_last(param_iter->second, vobj); + llassert(!vector_replace_with_last(param_iter->second, vobj)); + if (param_iter->second.empty()) + { + lod.erase(param_iter); + } + } +} + +void LLMeshRepository::unregisterSkinInfo(const LLUUID& mesh_id, LLVOVolume* vobj) +{ + LL_PROFILE_ZONE_SCOPED_CATEGORY_VOLUME; + + llassert(mesh_id.notNull()); + auto skin_pair_iter = mLoadingSkins.find(mesh_id); + if (skin_pair_iter != mLoadingSkins.end()) + { + vector_replace_with_last(skin_pair_iter->second, vobj); + llassert(!vector_replace_with_last(skin_pair_iter->second, vobj)); + if (skin_pair_iter->second.empty()) + { + mLoadingSkins.erase(skin_pair_iter); + } + } +} + +// Lots of dead objects make expensive calls to +// LLMeshRepository::unregisterMesh which may delay shutdown. Avoid this by +// preemptively unregistering all meshes. +// We can also do this safely if all objects are confirmed dead for some other +// reason. +void LLMeshRepository::unregisterAllMeshes() +{ + LL_PROFILE_ZONE_SCOPED_CATEGORY_VOLUME; + + // The size of mLoadingMeshes and mLoadingSkins may be large and thus + // expensive to iterate over in LLVOVolume::~LLVOVolume. + // This is unnecessary during shutdown, so we ignore the referenced objects in the + // least expensive way which is still safe: by clearing these containers. + // Clear now and not in LLMeshRepository::shutdown because + // LLMeshRepository::notifyLoadedMeshes could (depending on invocation + // order) reference a pointer to an object after it has been deleted. + for (auto& lod : mLoadingMeshes) + { + lod.clear(); } + mLoadingSkins.clear(); } S32 LLMeshRepository::loadMesh(LLVOVolume* vobj, const LLVolumeParams& mesh_params, S32 detail, S32 last_lod) diff --git a/indra/newview/llmeshrepository.h b/indra/newview/llmeshrepository.h index b850ade0bb..b8bec7f233 100644 --- a/indra/newview/llmeshrepository.h +++ b/indra/newview/llmeshrepository.h @@ -632,10 +632,15 @@ public: LLMeshRepository(); void init(); + void unregisterAllMeshes(); void shutdown(); S32 update(); - void unregisterMesh(LLVOVolume* volume); +#ifdef SHOW_ASSERT + bool forceUnregisterMesh(LLVOVolume* volume); +#endif + void unregisterMesh(LLVOVolume* vobj, const LLVolumeParams& mesh_params, S32 detail); + void unregisterSkinInfo(const LLUUID& mesh_id, LLVOVolume* vobj); //mesh management functions S32 loadMesh(LLVOVolume* volume, const LLVolumeParams& mesh_params, S32 detail = 0, S32 last_lod = -1); diff --git a/indra/newview/llviewerobjectlist.cpp b/indra/newview/llviewerobjectlist.cpp index 9e1d86faac..6167129077 100644 --- a/indra/newview/llviewerobjectlist.cpp +++ b/indra/newview/llviewerobjectlist.cpp @@ -65,6 +65,7 @@ #include "lltoolmgr.h" #include "lltoolpie.h" #include "llkeyboard.h" +#include "llmeshrepository.h" #include "u64.h" #include "llviewertexturelist.h" #include "lldatapacker.h" @@ -1419,6 +1420,10 @@ void LLViewerObjectList::cleanDeadObjects(bool use_timer) // No dead objects, don't need to scan object list. return; } + if ((LLApp::isExiting()) || (mNumDeadObjects == (S32)mObjects.size())) + { + gMeshRepo.unregisterAllMeshes(); + } LL_PROFILE_ZONE_SCOPED; diff --git a/indra/newview/llvovolume.cpp b/indra/newview/llvovolume.cpp index 7da4358f86..2f2054355a 100644 --- a/indra/newview/llvovolume.cpp +++ b/indra/newview/llvovolume.cpp @@ -250,10 +250,13 @@ LLVOVolume::~LLVOVolume() delete mVolumeImpl; mVolumeImpl = NULL; - gMeshRepo.unregisterMesh(this); + unregisterOldMeshAndSkin(); + llassert(!gMeshRepo.forceUnregisterMesh(this)); if(!mMediaImplList.empty()) { + LL_PROFILE_ZONE_NAMED_CATEGORY_MEDIA("delete volume media list"); + for(U32 i = 0 ; i < mMediaImplList.size() ; i++) { if(mMediaImplList[i].notNull()) @@ -998,6 +1001,28 @@ LLDrawable *LLVOVolume::createDrawable(LLPipeline *pipeline) return mDrawable; } +// Inverse of gMeshRepo.loadMesh and gMeshRepo.getSkinInfo, combined into one function +// Assume a Collada mesh never changes after being set. +void LLVOVolume::unregisterOldMeshAndSkin() +{ + if (mVolumep) + { + const LLVolumeParams& params = mVolumep->getParams(); + if ((params.getSculptType() & LL_SCULPT_TYPE_MASK) == LL_SCULPT_TYPE_MESH) + { + // object is being deleted, so it will no longer need to request + // meshes. + for (S32 lod = 0; lod != LLVolumeLODGroup::NUM_LODS; ++lod) + { + gMeshRepo.unregisterMesh(this, params, lod); + } + // This volume may or may not have a skin + gMeshRepo.unregisterSkinInfo(params.getSculptID(), this); + } + } +} + + bool LLVOVolume::setVolume(const LLVolumeParams ¶ms_in, const S32 detail, bool unique_volume) { LL_PROFILE_ZONE_SCOPED_CATEGORY_VOLUME; diff --git a/indra/newview/llvovolume.h b/indra/newview/llvovolume.h index 6241bf42d6..dfd75aa7d0 100644 --- a/indra/newview/llvovolume.h +++ b/indra/newview/llvovolume.h @@ -227,6 +227,7 @@ public: void setTexture(const S32 face); S32 getIndexInTex(U32 ch) const {return mIndexInTex[ch];} + void unregisterOldMeshAndSkin(); /*virtual*/ bool setVolume(const LLVolumeParams &volume_params, const S32 detail, bool unique_volume = false) override; void updateSculptTexture(); void setIndexInTex(U32 ch, S32 index) { mIndexInTex[ch] = index ;} -- cgit v1.2.3 From 89106b98a104afc0fb68412cfdf00f8e3e008fd0 Mon Sep 17 00:00:00 2001 From: Cosmic Linden Date: Thu, 19 Sep 2024 17:34:28 -0700 Subject: secondlife/viewer#2623: Remove assert --- indra/newview/llmeshrepository.cpp | 32 -------------------------------- indra/newview/llmeshrepository.h | 3 --- indra/newview/llvovolume.cpp | 1 - 3 files changed, 36 deletions(-) diff --git a/indra/newview/llmeshrepository.cpp b/indra/newview/llmeshrepository.cpp index 2ca94390e5..61ccd355a1 100644 --- a/indra/newview/llmeshrepository.cpp +++ b/indra/newview/llmeshrepository.cpp @@ -3651,38 +3651,6 @@ S32 LLMeshRepository::update() return static_cast(size); } -#ifdef SHOW_ASSERT -// Brute-force remove the object from all loading queues. Returns true if -// something was removed. -// This function is used in a debug assert to ensure unregisterMesh and -// unregisterSkinInfo are called as intended. -// *TODO: Consider removing at some point if we feel confident about the code -// working as intended. -bool LLMeshRepository::forceUnregisterMesh(LLVOVolume* vobj) -{ - LL_PROFILE_ZONE_SCOPED_CATEGORY_VOLUME; - - bool found = false; - - for (auto& lod : mLoadingMeshes) - { - for (auto& param : lod) - { - llassert(std::find(param.second.begin(), param.second.end(), vobj) == param.second.end()); - found = found || vector_replace_with_last(param.second, vobj); - } - } - - for (auto& skin_pair : mLoadingSkins) - { - llassert(std::find(skin_pair.second.begin(), skin_pair.second.end(), vobj) == skin_pair.second.end()); - found = found || vector_replace_with_last(skin_pair.second, vobj); - } - - return found; -} -#endif - void LLMeshRepository::unregisterMesh(LLVOVolume* vobj, const LLVolumeParams& mesh_params, S32 detail) { LL_PROFILE_ZONE_SCOPED_CATEGORY_VOLUME; diff --git a/indra/newview/llmeshrepository.h b/indra/newview/llmeshrepository.h index b8bec7f233..6e10493e90 100644 --- a/indra/newview/llmeshrepository.h +++ b/indra/newview/llmeshrepository.h @@ -636,9 +636,6 @@ public: void shutdown(); S32 update(); -#ifdef SHOW_ASSERT - bool forceUnregisterMesh(LLVOVolume* volume); -#endif void unregisterMesh(LLVOVolume* vobj, const LLVolumeParams& mesh_params, S32 detail); void unregisterSkinInfo(const LLUUID& mesh_id, LLVOVolume* vobj); //mesh management functions diff --git a/indra/newview/llvovolume.cpp b/indra/newview/llvovolume.cpp index 2f2054355a..e1eface4e8 100644 --- a/indra/newview/llvovolume.cpp +++ b/indra/newview/llvovolume.cpp @@ -251,7 +251,6 @@ LLVOVolume::~LLVOVolume() mVolumeImpl = NULL; unregisterOldMeshAndSkin(); - llassert(!gMeshRepo.forceUnregisterMesh(this)); if(!mMediaImplList.empty()) { -- cgit v1.2.3 From c3215c1bb07adee2ff6a546e97d4e42165e74b60 Mon Sep 17 00:00:00 2001 From: Andrey Kleshchev Date: Fri, 20 Sep 2024 00:01:25 +0300 Subject: viewer#2576 Crash baning a resident --- indra/newview/llpanelgroupbulk.cpp | 32 +++++++++++--------------------- indra/newview/llpanelgroupbulkban.cpp | 33 ++++++++++++--------------------- indra/newview/llpanelgroupbulkimpl.h | 3 ++- 3 files changed, 25 insertions(+), 43 deletions(-) diff --git a/indra/newview/llpanelgroupbulk.cpp b/indra/newview/llpanelgroupbulk.cpp index 433db74cda..8032e207cd 100644 --- a/indra/newview/llpanelgroupbulk.cpp +++ b/indra/newview/llpanelgroupbulk.cpp @@ -56,6 +56,7 @@ LLPanelGroupBulkImpl::LLPanelGroupBulkImpl(const LLUUID& group_id) : mGroupID(group_id), mBulkAgentList(NULL), mOKButton(NULL), + mAddButton(nullptr), mRemoveButton(NULL), mGroupName(NULL), mLoadingText(), @@ -79,29 +80,18 @@ LLPanelGroupBulkImpl::~LLPanelGroupBulkImpl() } } -// static -void LLPanelGroupBulkImpl::callbackClickAdd(void* userdata) +void LLPanelGroupBulkImpl::callbackClickAdd(LLPanelGroupBulk* panelp) { - if (LLPanelGroupBulk* panelp = (LLPanelGroupBulk*)userdata) - { - // Right now this is hard coded with some knowledge that it is part - // of a floater since the avatar picker needs to be added as a dependent - // floater to the parent floater. - // Soon the avatar picker will be embedded into this panel - // instead of being it's own separate floater. But that is next week. - // This will do for now. -jwolk May 10, 2006 - LLView* button = panelp->findChild("add_button"); - LLFloater* root_floater = gFloaterView->getParentFloater(panelp); - LLFloaterAvatarPicker* picker = LLFloaterAvatarPicker::show( - [&](const uuid_vec_t& agent_ids, const std::vector&) - { - panelp->mImplementation->addUsers(agent_ids); - }, true, false, false, root_floater->getName(), button); - if (picker) + LLFloater* root_floater = gFloaterView->getParentFloater(panelp); + LLFloaterAvatarPicker* picker = LLFloaterAvatarPicker::show( + [this](const uuid_vec_t& agent_ids, const std::vector&) { - root_floater->addDependentFloater(picker); - LLGroupMgr::getInstance()->sendCapGroupMembersRequest(panelp->mImplementation->mGroupID); - } + addUsers(agent_ids); + }, true, false, false, root_floater->getName(), mAddButton); + if (picker) + { + root_floater->addDependentFloater(picker); + LLGroupMgr::getInstance()->sendCapGroupMembersRequest(mGroupID); } } diff --git a/indra/newview/llpanelgroupbulkban.cpp b/indra/newview/llpanelgroupbulkban.cpp index 3c764887a6..1d3edad0f3 100644 --- a/indra/newview/llpanelgroupbulkban.cpp +++ b/indra/newview/llpanelgroupbulkban.cpp @@ -68,35 +68,26 @@ bool LLPanelGroupBulkBan::postBuild() mImplementation->mBulkAgentList->setCommitCallback(LLPanelGroupBulkImpl::callbackSelect, mImplementation); } - LLButton* button = getChild("add_button", recurse); - if ( button ) + mImplementation->mAddButton = getChild("add_button", recurse); + // default to opening avatarpicker automatically + mImplementation->mAddButton->setClickedCallback( + [this](LLUICtrl* ctrl, const LLSD& param) { - // default to opening avatarpicker automatically - // (*impl::callbackClickAdd)((void*)this); - button->setClickedCallback(LLPanelGroupBulkImpl::callbackClickAdd, this); - } + mImplementation->callbackClickAdd(this); + }); mImplementation->mRemoveButton = getChild("remove_button", recurse); - if ( mImplementation->mRemoveButton ) - { - mImplementation->mRemoveButton->setClickedCallback(LLPanelGroupBulkImpl::callbackClickRemove, mImplementation); - mImplementation->mRemoveButton->setEnabled(false); - } + mImplementation->mRemoveButton->setClickedCallback(LLPanelGroupBulkImpl::callbackClickRemove, mImplementation); + mImplementation->mRemoveButton->setEnabled(false); mImplementation->mOKButton = getChild("ban_button", recurse); - if ( mImplementation->mOKButton ) - { - mImplementation->mOKButton->setClickedCallback(LLPanelGroupBulkBan::callbackClickSubmit, this); - mImplementation->mOKButton->setEnabled(false); - } + mImplementation->mOKButton->setClickedCallback(LLPanelGroupBulkBan::callbackClickSubmit, this); + mImplementation->mOKButton->setEnabled(false); - button = getChild("cancel_button", recurse); - if ( button ) - { - button->setClickedCallback(LLPanelGroupBulkImpl::callbackClickCancel, mImplementation); - } + LLButton* button = getChild("cancel_button", recurse); + button->setClickedCallback(LLPanelGroupBulkImpl::callbackClickCancel, mImplementation); mImplementation->mTooManySelected = getString("ban_selection_too_large"); mImplementation->mBanNotPermitted = getString("ban_not_permitted"); diff --git a/indra/newview/llpanelgroupbulkimpl.h b/indra/newview/llpanelgroupbulkimpl.h index 5a479f8117..5515bd6d9a 100644 --- a/indra/newview/llpanelgroupbulkimpl.h +++ b/indra/newview/llpanelgroupbulkimpl.h @@ -44,7 +44,7 @@ public: LLPanelGroupBulkImpl(const LLUUID& group_id); ~LLPanelGroupBulkImpl(); - static void callbackClickAdd(void* userdata); + void callbackClickAdd(LLPanelGroupBulk* panelp); static void callbackClickRemove(void* userdata); static void callbackClickCancel(void* userdata); @@ -70,6 +70,7 @@ public: LLNameListCtrl* mBulkAgentList; LLButton* mOKButton; + LLButton* mAddButton; LLButton* mRemoveButton; LLTextBox* mGroupName; -- cgit v1.2.3 From 4f5d58dd036d06c88c552b8d8ac57dbb1e0b45ac Mon Sep 17 00:00:00 2001 From: Alexander Gavriliuk Date: Thu, 19 Sep 2024 19:30:05 +0200 Subject: #1519 ObjectGrab message includes invalid SurfaceInfo data when in mouselook mode --- indra/newview/lltoolselect.cpp | 9 ++++----- indra/newview/llviewerwindow.cpp | 16 ++++++++-------- 2 files changed, 12 insertions(+), 13 deletions(-) diff --git a/indra/newview/lltoolselect.cpp b/indra/newview/lltoolselect.cpp index 5ccda7d4eb..3bd459f5b0 100644 --- a/indra/newview/lltoolselect.cpp +++ b/indra/newview/lltoolselect.cpp @@ -55,17 +55,17 @@ LLToolSelect::LLToolSelect( LLToolComposite* composite ) : LLTool( std::string("Select"), composite ), mIgnoreGroup( false ) { - } +} // True if you selected an object. bool LLToolSelect::handleMouseDown(S32 x, S32 y, MASK mask) { // do immediate pick query bool pick_rigged = false; //gSavedSettings.getBOOL("AnimatedObjectsAllowLeftClick"); - bool pick_transparent = gSavedSettings.getBOOL("SelectInvisibleObjects"); - bool pick_reflection_probe = gSavedSettings.getBOOL("SelectReflectionProbes"); + static LLCachedControl select_invisible_objects(gSavedSettings, "SelectInvisibleObjects"); + static LLCachedControl select_reflection_probes(gSavedSettings, "SelectReflectionProbes"); - mPick = gViewerWindow->pickImmediate(x, y, pick_transparent, pick_rigged, false, true, pick_reflection_probe); + mPick = gViewerWindow->pickImmediate(x, y, select_invisible_objects, pick_rigged, false, true, select_reflection_probes); // Pass mousedown to agent LLTool::handleMouseDown(x, y, mask); @@ -73,7 +73,6 @@ bool LLToolSelect::handleMouseDown(S32 x, S32 y, MASK mask) return mPick.getObject().notNull(); } - // static LLObjectSelectionHandle LLToolSelect::handleObjectSelection(const LLPickInfo& pick, bool ignore_group, bool temp_select, bool select_root) { diff --git a/indra/newview/llviewerwindow.cpp b/indra/newview/llviewerwindow.cpp index 0e7d82fd90..acb2c85ef8 100644 --- a/indra/newview/llviewerwindow.cpp +++ b/indra/newview/llviewerwindow.cpp @@ -4255,15 +4255,15 @@ void LLViewerWindow::pickAsync( S32 x, bool pick_unselectable, bool pick_reflection_probes) { + static LLCachedControl select_invisible_objects(gSavedSettings, "SelectInvisibleObjects"); // "Show Debug Alpha" means no object actually transparent bool in_build_mode = LLFloaterReg::instanceVisible("build"); - if (LLDrawPoolAlpha::sShowDebugAlpha - || (in_build_mode && gSavedSettings.getBOOL("SelectInvisibleObjects"))) + if (LLDrawPoolAlpha::sShowDebugAlpha || (in_build_mode && select_invisible_objects)) { pick_transparent = true; } - LLPickInfo pick_info(LLCoordGL(x, y_from_bot), mask, pick_transparent, pick_rigged, false, pick_reflection_probes, pick_unselectable, true, callback); + LLPickInfo pick_info(LLCoordGL(x, y_from_bot), mask, pick_transparent, pick_rigged, false, pick_reflection_probes, true, pick_unselectable, callback); schedulePick(pick_info); } @@ -4287,7 +4287,6 @@ void LLViewerWindow::schedulePick(LLPickInfo& pick_info) mWindow->delayInputProcessing(); } - void LLViewerWindow::performPick() { if (!mPicks.empty()) @@ -4321,8 +4320,9 @@ void LLViewerWindow::returnEmptyPicks() // Performs the GL object/land pick. LLPickInfo LLViewerWindow::pickImmediate(S32 x, S32 y_from_bot, bool pick_transparent, bool pick_rigged, bool pick_particle, bool pick_unselectable, bool pick_reflection_probe) { + static LLCachedControl select_invisible_objects(gSavedSettings, "SelectInvisibleObjects"); bool in_build_mode = LLFloaterReg::instanceVisible("build"); - if ((in_build_mode && gSavedSettings.getBOOL("SelectInvisibleObjects")) || LLDrawPoolAlpha::sShowDebugAlpha) + if ((in_build_mode && select_invisible_objects) || LLDrawPoolAlpha::sShowDebugAlpha) { // build mode allows interaction with all transparent objects // "Show Debug Alpha" means no object actually transparent @@ -4330,7 +4330,7 @@ LLPickInfo LLViewerWindow::pickImmediate(S32 x, S32 y_from_bot, bool pick_transp } // shortcut queueing in mPicks and just update mLastPick in place - MASK key_mask = gKeyboard->currentMask(true); + MASK key_mask = gKeyboard->currentMask(true); mLastPick = LLPickInfo(LLCoordGL(x, y_from_bot), key_mask, pick_transparent, pick_rigged, pick_particle, pick_reflection_probe, true, false, NULL); mLastPick.fetchResults(); @@ -6050,14 +6050,14 @@ LLPickInfo::LLPickInfo(const LLCoordGL& mouse_pos, bool pick_rigged, bool pick_particle, bool pick_reflection_probe, - bool pick_uv_coords, + bool pick_surface_info, bool pick_unselectable, void (*pick_callback)(const LLPickInfo& pick_info)) : mMousePt(mouse_pos), mKeyMask(keyboard_mask), mPickCallback(pick_callback), mPickType(PICK_INVALID), - mWantSurfaceInfo(pick_uv_coords), + mWantSurfaceInfo(pick_surface_info), mObjectFace(-1), mUVCoords(-1.f, -1.f), mSTCoords(-1.f, -1.f), -- cgit v1.2.3 From 4274eb591a1b0806f8c73ca16df65ade60db2200 Mon Sep 17 00:00:00 2001 From: Alexander Gavriliuk Date: Wed, 4 Sep 2024 16:31:39 +0200 Subject: #2471 The Destinations ComboBox on Login Screen... (quick fix) --- indra/llui/llcombobox.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/indra/llui/llcombobox.cpp b/indra/llui/llcombobox.cpp index b64794a244..a1c16ccdec 100644 --- a/indra/llui/llcombobox.cpp +++ b/indra/llui/llcombobox.cpp @@ -521,12 +521,16 @@ bool LLComboBox::setCurrentByIndex(S32 index) if (item->getEnabled()) { mList->selectItem(item, -1, true); + LLSD::String label = item->getColumn(0)->getValue().asString(); if (mTextEntry) { - LLSD::String label = item->getColumn(0)->getValue().asString(); mTextEntry->setText(label); mTextEntry->setTentative(false); } + if (!mAllowTextEntry) + { + mButton->setLabel(label); + } mLastSelectedIndex = index; return true; } -- cgit v1.2.3 From fad8b11f480a7e7fc8cae38e0f76de2109a46a99 Mon Sep 17 00:00:00 2001 From: Rye Cogtail Date: Fri, 20 Sep 2024 12:10:06 -0400 Subject: Fix sky settings with reflection probe ambiance of 0 still receiving tonemapping --- indra/newview/pipeline.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/indra/newview/pipeline.cpp b/indra/newview/pipeline.cpp index 14a15eb59f..fe02742aac 100644 --- a/indra/newview/pipeline.cpp +++ b/indra/newview/pipeline.cpp @@ -7070,7 +7070,7 @@ void LLPipeline::tonemap(LLRenderTarget* src, LLRenderTarget* dst) LLSettingsSky::ptr_t psky = LLEnvironment::instance().getCurrentSky(); - bool no_post = gSnapshotNoPost || (buildNoPost && gFloaterTools->isAvailable()); + bool no_post = gSnapshotNoPost || psky->getReflectionProbeAmbiance(should_auto_adjust) == 0.f || (buildNoPost && gFloaterTools->isAvailable()); LLGLSLShader& shader = no_post ? gNoPostTonemapProgram : gDeferredPostTonemapProgram; shader.bind(); -- cgit v1.2.3 From 0e33882b15e0c970e216d1cca4b93a7702e5113b Mon Sep 17 00:00:00 2001 From: Ansariel Hiller Date: Fri, 20 Sep 2024 19:07:02 +0200 Subject: Restore option to change location of existing pick (#2622) --- indra/newview/llpanelprofilepicks.cpp | 42 ++++++++++++++++++++++ indra/newview/llpanelprofilepicks.h | 10 +++++- .../skins/default/xui/en/panel_profile_pick.xml | 20 +++++++++++ 3 files changed, 71 insertions(+), 1 deletion(-) diff --git a/indra/newview/llpanelprofilepicks.cpp b/indra/newview/llpanelprofilepicks.cpp index 08f3d3af5a..11a10ebd2c 100644 --- a/indra/newview/llpanelprofilepicks.cpp +++ b/indra/newview/llpanelprofilepicks.cpp @@ -246,6 +246,8 @@ void LLPanelProfilePicks::onClickNewBtn() select_tab(true). label(pick_panel->getPickName())); updateButtons(); + + pick_panel->addLocationChangedCallbacks(); } void LLPanelProfilePicks::onClickDelete() @@ -571,10 +573,12 @@ void LLPanelProfilePick::setAvatarId(const LLUUID& avatar_id) { mPickName->setEnabled(true); mPickDescription->setEnabled(true); + mSetCurrentLocationButton->setVisible(true); } else { mSnapshotCtrl->setEnabled(false); + mSetCurrentLocationButton->setVisible(false); } } @@ -585,6 +589,7 @@ bool LLPanelProfilePick::postBuild() mSaveButton = getChild("save_changes_btn"); mCreateButton = getChild("create_changes_btn"); mCancelButton = getChild("cancel_changes_btn"); + mSetCurrentLocationButton = getChild("set_to_curr_location_btn"); mSnapshotCtrl = getChild("pick_snapshot"); mSnapshotCtrl->setCommitCallback(boost::bind(&LLPanelProfilePick::onSnapshotChanged, this)); @@ -597,6 +602,7 @@ bool LLPanelProfilePick::postBuild() mSaveButton->setCommitCallback(boost::bind(&LLPanelProfilePick::onClickSave, this)); mCreateButton->setCommitCallback(boost::bind(&LLPanelProfilePick::onClickSave, this)); mCancelButton->setCommitCallback(boost::bind(&LLPanelProfilePick::onClickCancel, this)); + mSetCurrentLocationButton->setCommitCallback(boost::bind(&LLPanelProfilePick::onClickSetLocation, this)); mPickName->setKeystrokeCallback(boost::bind(&LLPanelProfilePick::onPickChanged, this, _1), NULL); mPickName->setEnabled(false); @@ -759,6 +765,32 @@ bool LLPanelProfilePick::isDirty() const return false; } +void LLPanelProfilePick::onClickSetLocation() +{ + // Save location for later use. + setPosGlobal(gAgent.getPositionGlobal()); + + std::string parcel_name, region_name; + + LLParcel* parcel = LLViewerParcelMgr::getInstance()->getAgentParcel(); + if (parcel) + { + mParcelId = parcel->getID(); + parcel_name = parcel->getName(); + } + + LLViewerRegion* region = gAgent.getRegion(); + if (region) + { + region_name = region->getName(); + } + + setPickLocation(createLocationText(getLocationNotice(), parcel_name, region_name, getPosGlobal())); + + mLocationChanged = true; + enableSaveButton(true); +} + void LLPanelProfilePick::onClickSave() { if (mRegionCallbackConnection.connected()) @@ -769,6 +801,10 @@ void LLPanelProfilePick::onClickSave() { mParcelCallbackConnection.disconnect(); } + if (mLocationChanged) + { + onClickSetLocation(); + } sendUpdate(); mLocationChanged = false; @@ -816,6 +852,12 @@ void LLPanelProfilePick::processParcelInfo(const LLParcelData& parcel_data) } } +void LLPanelProfilePick::addLocationChangedCallbacks() +{ + mRegionCallbackConnection = gAgent.addRegionChangedCallback([this]() { onClickSetLocation(); }); + mParcelCallbackConnection = gAgent.addParcelChangedCallback([this]() { onClickSetLocation(); }); +} + void LLPanelProfilePick::sendUpdate() { LLPickData pick_data; diff --git a/indra/newview/llpanelprofilepicks.h b/indra/newview/llpanelprofilepicks.h index e3f50f5576..5e5d4ff81e 100644 --- a/indra/newview/llpanelprofilepicks.h +++ b/indra/newview/llpanelprofilepicks.h @@ -138,6 +138,8 @@ public: void setParcelID(const LLUUID& parcel_id) override { mParcelId = parcel_id; } void setErrorStatus(S32 status, const std::string& reason) override {}; + void addLocationChangedCallbacks(); + protected: /** @@ -199,6 +201,11 @@ public: */ void resetDirty() override; + /** + * Callback for "Set Location" button click + */ + void onClickSetLocation(); + /** * Callback for "Save" and "Create" button click */ @@ -221,6 +228,7 @@ protected: LLTextureCtrl* mSnapshotCtrl; LLLineEditor* mPickName; LLTextEditor* mPickDescription; + LLButton* mSetCurrentLocationButton; LLButton* mSaveButton; LLButton* mCreateButton; LLButton* mCancelButton; @@ -236,7 +244,7 @@ protected: bool mLocationChanged; bool mNewPick; - bool mIsEditing; + bool mIsEditing; void onDescriptionFocusReceived(); }; diff --git a/indra/newview/skins/default/xui/en/panel_profile_pick.xml b/indra/newview/skins/default/xui/en/panel_profile_pick.xml index 024120931f..4f441b9b49 100644 --- a/indra/newview/skins/default/xui/en/panel_profile_pick.xml +++ b/indra/newview/skins/default/xui/en/panel_profile_pick.xml @@ -198,6 +198,26 @@ /> + + + @@ -2465,6 +2464,22 @@ function="World.EnvPreset" + + + + + + diff --git a/indra/newview/tests/llviewerhelputil_test.cpp b/indra/newview/tests/llviewerhelputil_test.cpp index 9ee6625bf1..9a7a5be09f 100644 --- a/indra/newview/tests/llviewerhelputil_test.cpp +++ b/indra/newview/tests/llviewerhelputil_test.cpp @@ -79,6 +79,8 @@ bool LLAgent::isGodlike() const { return false; } LLAgent gAgent; +LLFlycam::LLFlycam() { } + std::string LLWeb::expandURLSubstitutions(const std::string &url, const LLSD &default_subs) { diff --git a/scripts/messages/message_template.msg.sha1 b/scripts/messages/message_template.msg.sha1 index efa5f3cf48..e38780dc92 100755 --- a/scripts/messages/message_template.msg.sha1 +++ b/scripts/messages/message_template.msg.sha1 @@ -1 +1 @@ -d7915d67467e59287857630bd89bf9529d065199 \ No newline at end of file +fb225888b50e4c525a33442538f0fe78bbe9cd43 \ No newline at end of file -- cgit v1.2.3 From e90913c4a926a006467661bb0d2db0a4bdb7a4b9 Mon Sep 17 00:00:00 2001 From: leviathan Date: Sat, 6 Jul 2024 08:43:19 -0700 Subject: add enable_game_control checkbox to GameControl prefs --- .../default/xui/en/panel_preferences_game_control.xml | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/indra/newview/skins/default/xui/en/panel_preferences_game_control.xml b/indra/newview/skins/default/xui/en/panel_preferences_game_control.xml index 1815d98ae0..8c9e15a180 100644 --- a/indra/newview/skins/default/xui/en/panel_preferences_game_control.xml +++ b/indra/newview/skins/default/xui/en/panel_preferences_game_control.xml @@ -9,6 +9,14 @@ name="gamecontrol" top="1" width="517"> + + top="30"/> + top="50"/> + top="70"/> -- cgit v1.2.3 From e74ce9655ed9f1124887aa31aa2e2155ea62d3da Mon Sep 17 00:00:00 2001 From: leviathan Date: Wed, 10 Jul 2024 14:08:17 -0700 Subject: more correct AgentUpdate transmission logic --- indra/llui/llview.cpp | 8 ++--- indra/newview/llagent.cpp | 19 ++---------- indra/newview/llagent.h | 3 +- indra/newview/llappviewer.cpp | 34 ++++++---------------- indra/newview/llviewerinput.cpp | 5 ---- indra/newview/llviewermessage.cpp | 7 ++--- .../skins/default/xui/en/floater_preferences.xml | 18 ++++++------ 7 files changed, 29 insertions(+), 65 deletions(-) diff --git a/indra/llui/llview.cpp b/indra/llui/llview.cpp index 0206e46b57..190ba07ad6 100644 --- a/indra/llui/llview.cpp +++ b/indra/llui/llview.cpp @@ -642,10 +642,10 @@ void LLView::onVisibilityChange ( bool new_visibility ) if(log_visibility_change) { - if (old_visibility!=new_visibility) - { - LLViewerEventRecorder::instance().logVisibilityChange( viewp->getPathname(), viewp->getName(), new_visibility,"widget"); - } + if (old_visibility!=new_visibility) + { + LLViewerEventRecorder::instance().logVisibilityChange( viewp->getPathname(), viewp->getName(), new_visibility,"widget"); + } } if (old_visibility) diff --git a/indra/newview/llagent.cpp b/indra/newview/llagent.cpp index 53929051da..b7257a6c50 100644 --- a/indra/newview/llagent.cpp +++ b/indra/newview/llagent.cpp @@ -4982,28 +4982,15 @@ void LLAgent::renderAutoPilotTarget() } } -void LLAgent::setExternalActionFlags(U32 outer_flags) -{ - if (LLGameControl::willControlAvatar()) - { - // save these flags for later, for when we're ready - // to actually send an AgentUpdate packet - mExternalActionFlags = outer_flags; - mbFlagsDirty = TRUE; - } -} - static U64 g_lastUpdateTime { 0 }; static F32 g_deltaTime { 0.0f }; static S32 g_lastUpdateFrame { 0 }; static S32 g_deltaFrame { 0 }; -void LLAgent::applyExternalActionFlags() +void LLAgent::applyExternalActionFlags(U32 outer_flags) { - if (! LLGameControl::isEnabled() || ! LLGameControl::willControlAvatar()) - { - return; - } + assert(LLGameControl::isEnabled() && LLGameControl::willControlAvatar()); + mExternalActionFlags = outer_flags; // HACK: AGENT_CONTROL_NUDGE_AT_NEG is used to toggle Flycam if ((mExternalActionFlags & AGENT_CONTROL_NUDGE_AT_NEG) > 0) diff --git a/indra/newview/llagent.h b/indra/newview/llagent.h index 80b7c588e3..a69a777e1e 100644 --- a/indra/newview/llagent.h +++ b/indra/newview/llagent.h @@ -511,8 +511,7 @@ public: // computed as a function of input and state, and are sent to the server // to steer its character controller for the avatar. // - void setExternalActionFlags(U32 flags); - void applyExternalActionFlags(); + void applyExternalActionFlags(U32 flags); void updateFlycam(); void pressGameControlButton(U8 button); diff --git a/indra/newview/llappviewer.cpp b/indra/newview/llappviewer.cpp index c99f7365ab..7eded9e174 100644 --- a/indra/newview/llappviewer.cpp +++ b/indra/newview/llappviewer.cpp @@ -4809,18 +4809,17 @@ void LLAppViewer::idle() gAgent.autoPilot(&yaw); } - send_agent_update(false); - - // Some GameControl stuff needs to be executed even when the feature is not enabled + // Some GameControl logic needs to run even when the feature is not enabled // Note: we process game_control before sending AgentUpdate // because it may translate to control flags that control avatar motion. LLGameControl::processEvents(gFocusMgr.getAppHasFocus()); - // trade flags between gAgent and LLGameControl + // get control flags from each side U32 control_flags = gAgent.getControlFlags(); - U32 action_flags = LLGameControl::computeInternalActionFlags(); - LLGameControl::setExternalInput(control_flags, gAgent.getGameControlButtonsFromKeys()); + U32 game_control_action_flags = LLGameControl::computeInternalActionFlags(); + // apply to GameControl + LLGameControl::setExternalInput(control_flags, gAgent.getGameControlButtonsFromKeys()); bool should_send_game_control = LLGameControl::computeFinalStateAndCheckForChanges(); if (LLPanelPreferenceGameControl::isWaitingForInputChannel()) { @@ -4833,28 +4832,13 @@ void LLAppViewer::idle() sendGameControlInput(); } - // This GameControl stuff should NOT be executed when it isn't enabled - if (LLGameControl::isEnabled()) + // apply to AvatarControl + if (LLGameControl::isEnabled() && LLGameControl::willControlAvatar()) { - gAgent.setExternalActionFlags(action_flags); + gAgent.applyExternalActionFlags(game_control_action_flags); } - // When appropriate, update agent location to the simulator. - F32 agent_update_time = agent_update_timer.getElapsedTimeF32(); - F32 agent_force_update_time = mLastAgentForceUpdate + agent_update_time; - bool force_update = gAgent.controlFlagsDirty() - || (mLastAgentControlFlags != gAgent.getControlFlags()) - || (agent_force_update_time > (1.0f / (F32) AGENT_FORCE_UPDATES_PER_SECOND)); - if (force_update || (agent_update_time > (1.0f / (F32) AGENT_UPDATES_PER_SECOND))) - { - gAgent.applyExternalActionFlags(); - LL_PROFILE_ZONE_SCOPED_CATEGORY_NETWORK; - // Send avatar and camera info - mLastAgentControlFlags = gAgent.getControlFlags(); - mLastAgentForceUpdate = force_update ? 0 : agent_force_update_time; - send_agent_update(force_update); - agent_update_timer.reset(); - } + send_agent_update(false); } ////////////////////////////////////// diff --git a/indra/newview/llviewerinput.cpp b/indra/newview/llviewerinput.cpp index 0bbf7a07b3..66042128f2 100644 --- a/indra/newview/llviewerinput.cpp +++ b/indra/newview/llviewerinput.cpp @@ -666,7 +666,6 @@ bool start_gesture( EKeystate s ) bool run_forward(EKeystate s) { - // HACK: we use AGENT_CONTROL_NUDGE_AT_POS to signify "run forward" if (KEYSTATE_UP != s) { if (gAgent.mDoubleTapRunMode != LLAgent::DOUBLETAP_FORWARD) @@ -692,7 +691,6 @@ bool run_forward(EKeystate s) bool run_backward(EKeystate s) { - // HACK: we use AGENT_CONTROL_NUDGE_AT_NEG to signify "run backward" if (KEYSTATE_UP != s) { if (gAgent.mDoubleTapRunMode != LLAgent::DOUBLETAP_BACKWARD) @@ -718,7 +716,6 @@ bool run_backward(EKeystate s) bool run_left(EKeystate s) { - // HACK: we use AGENT_CONTROL_NUDGE_LEFT_POS to signify "run left" if (KEYSTATE_UP != s) { if (gAgent.mDoubleTapRunMode != LLAgent::DOUBLETAP_SLIDELEFT) @@ -769,7 +766,6 @@ bool run_right(EKeystate s) bool toggle_run(EKeystate s) { - // HACK: we use AGENT_CONTROL_FAST_AT to signify "run button" if (KEYSTATE_DOWN != s) return true; bool run = gAgent.getAlwaysRun(); if (run) @@ -788,7 +784,6 @@ bool toggle_run(EKeystate s) bool toggle_sit(EKeystate s) { - // HACK: we use AGENT_CONTROL_SIT_ON_GROUND to signify "sit button" if (KEYSTATE_DOWN != s) return true; if (gAgent.isSitting()) { diff --git a/indra/newview/llviewermessage.cpp b/indra/newview/llviewermessage.cpp index a37e15f8da..da019d60d8 100644 --- a/indra/newview/llviewermessage.cpp +++ b/indra/newview/llviewermessage.cpp @@ -3153,8 +3153,8 @@ void process_crossed_region(LLMessageSystem* msg, void**) } -// sends an AgentUpdate message to the server... or not: -// only when force_send is 'true' OR +// sends an AgentUpdate message to the server... or not +// e.g. only when force_send is 'true' OR // something changed AND the update is not being throttled void send_agent_update(bool force_send, bool send_reliable) { @@ -3220,8 +3220,6 @@ void send_agent_update(bool force_send, bool send_reliable) return; } - bool send_update = force_send || sec_since_last_send > MAX_AGENT_UPDATE_PERIOD; - LLVector3 camera_pos_agent = gAgentCamera.getCameraPositionAgent(); // local to avatar's region LLVector3 camera_at = LLViewerCamera::getInstance()->getAtAxis(); LLQuaternion body_rotation = gAgent.getFrameAgent().getQuaternion(); @@ -3238,6 +3236,7 @@ void send_agent_update(bool force_send, bool send_reliable) flags |= AU_FLAGS_CLIENT_AUTOPILOT; } + bool send_update = force_send || sec_since_last_send > MAX_AGENT_UPDATE_PERIOD; if (!send_update) { // check to see if anything changed diff --git a/indra/newview/skins/default/xui/en/floater_preferences.xml b/indra/newview/skins/default/xui/en/floater_preferences.xml index e0dc538af4..e7fa839001 100644 --- a/indra/newview/skins/default/xui/en/floater_preferences.xml +++ b/indra/newview/skins/default/xui/en/floater_preferences.xml @@ -87,49 +87,49 @@ top="40" width="658"> Date: Wed, 17 Jul 2024 15:25:22 -0700 Subject: even more correct GameControl feature-flag switch --- indra/llui/lltabcontainer.cpp | 28 +++++++----- indra/llwindow/llgamecontrol.cpp | 2 +- indra/newview/app_settings/settings.xml | 2 +- indra/newview/llfloaterpreference.cpp | 53 +++++++++++++++++++--- indra/newview/llfloaterpreference.h | 12 +++-- indra/newview/llviewermenu.cpp | 2 + indra/newview/skins/default/xui/en/menu_viewer.xml | 32 ++++++------- .../default/xui/en/panel_preferences_controls.xml | 2 +- .../xui/en/panel_preferences_game_control.xml | 18 ++------ 9 files changed, 98 insertions(+), 53 deletions(-) diff --git a/indra/llui/lltabcontainer.cpp b/indra/llui/lltabcontainer.cpp index 5e0985c79c..7c9aa42e5f 100644 --- a/indra/llui/lltabcontainer.cpp +++ b/indra/llui/lltabcontainer.cpp @@ -1258,7 +1258,6 @@ void LLTabContainer::removeTabPanel(LLPanel* child) bool has_focus = gFocusMgr.childHasKeyboardFocus(this); - // If the tab being deleted is the selected one, select a different tab. for(std::vector::iterator iter = mTabList.begin(); iter != mTabList.end(); ++iter) { LLTabTuple* tuple = *iter; @@ -1296,6 +1295,7 @@ void LLTabContainer::removeTabPanel(LLPanel* child) // make sure we don't have more locked tabs than we have tabs mLockedTabCount = llmin(getTabCount(), mLockedTabCount); + // If the tab being deleted is the selected one, select a different tab. if (mCurrentTabIdx >= (S32)mTabList.size()) { mCurrentTabIdx = static_cast(mTabList.size()) - 1; @@ -1723,7 +1723,7 @@ void LLTabContainer::reshapeTuple(LLTabTuple* tuple) { S32 image_overlay_width = 0; - if(mCustomIconCtrlUsed) + if (mCustomIconCtrlUsed) { LLCustomButtonIconCtrl* button = dynamic_cast(tuple->mButton); LLIconCtrl* icon_ctrl = button ? button->getIconCtrl() : NULL; @@ -2173,12 +2173,22 @@ S32 LLTabContainer::getTotalTabWidth() const void LLTabContainer::setTabVisibility( LLPanel const *aPanel, bool aVisible ) { - for( tuple_list_t::const_iterator itr = mTabList.begin(); itr != mTabList.end(); ++itr ) + S32 num_tabs = S32(mTabList.size()); + for (S32 i = 0; i < num_tabs; ++i) { - LLTabTuple const *pTT = *itr; - if( pTT->mTabPanel == aPanel ) + LLTabTuple* tuple = mTabList[i]; + if( tuple->mTabPanel == aPanel ) { - pTT->mVisible = aVisible; + if (tuple->mVisible != aVisible) + { + tuple->mVisible = aVisible; + if (aVisible) + { + this->selectTab(i); + this->setVisible(true); + } + updateMaxScrollPos(); + } break; } } @@ -2194,11 +2204,7 @@ void LLTabContainer::setTabVisibility( LLPanel const *aPanel, bool aVisible ) break; } } - - if( foundTab ) - this->setVisible( true ); - else - this->setVisible( false ); + this->setVisible( foundTab ); updateMaxScrollPos(); } diff --git a/indra/llwindow/llgamecontrol.cpp b/indra/llwindow/llgamecontrol.cpp index 0e3782a10e..812c861370 100644 --- a/indra/llwindow/llgamecontrol.cpp +++ b/indra/llwindow/llgamecontrol.cpp @@ -434,7 +434,7 @@ namespace // data state. However, to keep the ambient resend bandwidth low we // expand the resend period at a geometric rate. // - constexpr U64 MSEC_PER_NSEC = 1e6; + constexpr U64 MSEC_PER_NSEC = 1000000; constexpr U64 FIRST_RESEND_PERIOD = 100 * MSEC_PER_NSEC; constexpr U64 RESEND_EXPANSION_RATE = 10; LLGameControl::State g_outerState; // from controller devices diff --git a/indra/newview/app_settings/settings.xml b/indra/newview/app_settings/settings.xml index 2057ddaa0d..36abebc942 100644 --- a/indra/newview/app_settings/settings.xml +++ b/indra/newview/app_settings/settings.xml @@ -2612,7 +2612,7 @@ Value 1 - GameControl + EnableGameControl Comment Enable GameControl feature diff --git a/indra/newview/llfloaterpreference.cpp b/indra/newview/llfloaterpreference.cpp index 3c4eab0282..7ea9f9b4c2 100644 --- a/indra/newview/llfloaterpreference.cpp +++ b/indra/newview/llfloaterpreference.cpp @@ -402,6 +402,7 @@ void LLFloaterPreference::saveAvatarProperties( void ) } } +// static void LLFloaterPreference::saveAvatarPropertiesCoro(const std::string cap_url, bool allow_publish) { LLCore::HttpRequest::policy_t httpPolicy(LLCore::HttpRequest::DEFAULT_POLICY_ID); @@ -439,6 +440,7 @@ bool LLFloaterPreference::postBuild() mDisabledPopups = getChild("disabled_popups"); mEnablePopupBtn = getChild("enable_this_popup"); mDisablePopupBtn = getChild("disable_this_popup"); + setPanelVisibility("game_control", LLGameControl::isEnabled()); gSavedSettings.getControl("ChatFontSize")->getSignal()->connect(boost::bind(&LLFloaterIMSessionTab::processChatHistoryStyleUpdate, false)); @@ -1048,6 +1050,15 @@ void LLFloaterPreference::onBtnCancel(const LLSD& userdata) } } +//static +void LLFloaterPreference::refreshInstance() +{ + if (LLFloaterPreference* instance = LLFloaterReg::findTypedInstance("preferences")) + { + instance->refresh(); + } +} + // static void LLFloaterPreference::updateUserInfo(const std::string& visibility) { @@ -1057,6 +1068,7 @@ void LLFloaterPreference::updateUserInfo(const std::string& visibility) } } +// static void LLFloaterPreference::refreshEnabledGraphics() { if (LLFloaterPreference* instance = LLFloaterReg::findTypedInstance("preferences")) @@ -1292,6 +1304,16 @@ void LLAvatarComplexityControls::setIndirectMaxArc() void LLFloaterPreference::refresh() { + setPanelVisibility("game_control", LLGameControl::isEnabled()); + LLTabContainer* tabcontainer = getChild("pref core"); + for (LLView* view : *tabcontainer->getChildList()) + { + if (LLPanelPreferenceControls* panel = dynamic_cast(view)) + { + panel->refresh(); + break; + } + } LLFloater::refresh(); setMaxNonImpostorsText( gSavedSettings.getU32("RenderAvatarMaxNonImpostors"), @@ -1945,6 +1967,16 @@ void LLFloaterPreference::selectPanel(const LLSD& name) } } +void LLFloaterPreference::setPanelVisibility(const LLSD& name, bool visible) +{ + LLTabContainer * tab_containerp = getChild("pref core"); + LLPanel * panel = tab_containerp->getPanelByName(name.asStringRef()); + if (NULL != panel) + { + tab_containerp->setTabVisibility(panel, visible); + } +} + void LLFloaterPreference::selectPrivacyPanel() { selectPanel("im"); @@ -2532,6 +2564,12 @@ LLPanelPreferenceControls::~LLPanelPreferenceControls() { } +void LLPanelPreferenceControls::refresh() +{ + populateControlTable(); + LLPanelPreference::refresh(); +} + bool LLPanelPreferenceControls::postBuild() { // populate list of controls @@ -2710,7 +2748,10 @@ void LLPanelPreferenceControls::populateControlTable() addControlTableSeparator(); addControlTableRows("control_table_contents_media.xml"); addControlTableSeparator(); - addControlTableRows("control_table_contents_game_control.xml"); + if (LLGameControl::isEnabled()) + { + addControlTableRows("control_table_contents_game_control.xml"); + } } // MODE_THIRD_PERSON; MODE_EDIT_AVATAR; MODE_SITTING else if (mEditingMode < LLKeyConflictHandler::MODE_SAVED_SETTINGS) @@ -2729,7 +2770,10 @@ void LLPanelPreferenceControls::populateControlTable() addControlTableRows("control_table_contents_media.xml"); addControlTableSeparator(); - addControlTableRows("control_table_contents_game_control.xml"); + if (LLGameControl::isEnabled()) + { + addControlTableRows("control_table_contents_game_control.xml"); + } } else { @@ -3440,12 +3484,10 @@ void LLPanelPreferenceGameControl::onCommitNumericValue() bool LLPanelPreferenceGameControl::postBuild() { // Above the tab container - mCheckEnableGameControl = getChild("enable_game_control"); mCheckGameControlToServer = getChild("game_control_to_server"); mCheckGameControlToAgent = getChild("game_control_to_agent"); mCheckAgentToGameControl = getChild("agent_to_game_control"); - mCheckEnableGameControl->setCommitCallback([this](LLUICtrl*, const LLSD&) { updateEnable(); }); mCheckGameControlToAgent->setCommitCallback([this](LLUICtrl*, const LLSD&) { updateActionTableState(); }); mCheckAgentToGameControl->setCommitCallback([this](LLUICtrl*, const LLSD&) { updateActionTableState(); }); @@ -3522,7 +3564,6 @@ bool LLPanelPreferenceGameControl::postBuild() // This function is called before floater is shown void LLPanelPreferenceGameControl::onOpen(const LLSD& key) { - mCheckEnableGameControl->setValue(LLGameControl::isEnabled()); mCheckGameControlToServer->setValue(LLGameControl::getSendToServer()); mCheckGameControlToAgent->setValue(LLGameControl::getControlAgent()); mCheckAgentToGameControl->setValue(LLGameControl::getTranslateAgentActions()); @@ -3882,7 +3923,7 @@ void LLPanelPreferenceGameControl::addActionTableSeparator() void LLPanelPreferenceGameControl::updateEnable() { - bool enabled = mCheckEnableGameControl->get(); + bool enabled = LLGameControl::isEnabled(); LLGameControl::setEnabled(enabled); mCheckGameControlToServer->setEnabled(enabled); diff --git a/indra/newview/llfloaterpreference.h b/indra/newview/llfloaterpreference.h index 1a498c035e..b3872958ac 100644 --- a/indra/newview/llfloaterpreference.h +++ b/indra/newview/llfloaterpreference.h @@ -92,6 +92,8 @@ public: virtual void changed() override; virtual void changed(const LLUUID& session_id, U32 mask) override {}; + static void refreshInstance(); + // static data update, called from message handler static void updateUserInfo(const std::string& visibility); @@ -201,6 +203,7 @@ public: void buildPopupLists(); static void refreshSkin(void* data); void selectPanel(const LLSD& name); + void setPanelVisibility(const LLSD& name, bool visible); void saveGraphicsPreset(std::string& preset); void setRecommendedSettings(); @@ -327,6 +330,8 @@ public: bool postBuild() override; + void refresh() override; + void apply() override; void cancel(const std::vector settings_to_skip = {}) override; void saveSettings() override; @@ -349,6 +354,9 @@ public: void onDefaultKeyBind(bool all_modes) override; void onCancelKeyBind() override; + // Cleans content and then adds content from xml files according to current mEditingMode + void populateControlTable(); + private: // reloads settings, discards current changes, updates table void regenerateControls(); @@ -358,9 +366,6 @@ private: bool addControlTableRows(const std::string &filename); void addControlTableSeparator(); - // Cleans content and then adds content from xml files according to current mEditingMode - void populateControlTable(); - // Updates keybindings from storage to table void updateTable(); @@ -435,7 +440,6 @@ private: void resetButtonMappingsToDefaults(); // Above the tab container - LLCheckBoxCtrl *mCheckEnableGameControl; LLCheckBoxCtrl *mCheckGameControlToServer; // send game_control data to server LLCheckBoxCtrl *mCheckGameControlToAgent; // use game_control data to move avatar LLCheckBoxCtrl *mCheckAgentToGameControl; // translate external avatar actions to game_control data diff --git a/indra/newview/llviewermenu.cpp b/indra/newview/llviewermenu.cpp index a6614542b8..39aa85beea 100644 --- a/indra/newview/llviewermenu.cpp +++ b/indra/newview/llviewermenu.cpp @@ -72,6 +72,7 @@ #include "llfloaterpathfindingcharacters.h" #include "llfloaterpathfindinglinksets.h" #include "llfloaterpay.h" +#include "llfloaterpreference.h" #include "llfloaterreporter.h" #include "llfloatersearch.h" #include "llfloaterscriptdebug.h" @@ -965,6 +966,7 @@ class LLAdvancedToggleExperiment : public view_listener_t if (feature == "GameControl") { LLGameControl::setEnabled(! LLGameControl::isEnabled()); + LLFloaterPreference::refreshInstance(); return true; } return false; diff --git a/indra/newview/skins/default/xui/en/menu_viewer.xml b/indra/newview/skins/default/xui/en/menu_viewer.xml index f6946f5304..da9cc0c98d 100644 --- a/indra/newview/skins/default/xui/en/menu_viewer.xml +++ b/indra/newview/skins/default/xui/en/menu_viewer.xml @@ -2464,22 +2464,6 @@ function="World.EnvPreset" - - - - - - @@ -2505,6 +2489,22 @@ function="World.EnvPreset" name="Develop" tear_off="true" visible="false"> + + + + + + - + top="10"/> + top="30"/> + top="50"/> -- cgit v1.2.3 From 8213a0fb6b3149042d6833c8f0c4a3c1bfdc8bc9 Mon Sep 17 00:00:00 2001 From: leviathan Date: Tue, 27 Aug 2024 16:18:57 -0700 Subject: more correct application of GameControl prefs --- indra/llwindow/llgamecontrol.cpp | 68 +++++++++++++++++++++++------------ indra/llwindow/llgamecontrol.h | 14 ++++++-- indra/newview/llappviewer.cpp | 4 ++- indra/newview/llfloaterpreference.cpp | 55 +++++++++++++++++++--------- indra/newview/llfloaterpreference.h | 7 ++++ 5 files changed, 104 insertions(+), 44 deletions(-) diff --git a/indra/llwindow/llgamecontrol.cpp b/indra/llwindow/llgamecontrol.cpp index 812c861370..c7a27c5558 100644 --- a/indra/llwindow/llgamecontrol.cpp +++ b/indra/llwindow/llgamecontrol.cpp @@ -356,6 +356,7 @@ public: void resetDeviceOptionsToDefaults(); void loadDeviceOptionsFromSettings(); void saveDeviceOptionsToSettings() const; + void setDeviceOptions(const std::string& guid, const LLGameControl::Options& options); void addController(SDL_JoystickID id, const std::string& guid, const std::string& name); void removeController(SDL_JoystickID id); @@ -457,6 +458,7 @@ namespace std::function s_saveString; std::function s_loadObject; std::function s_saveObject; + std::function s_updateUI; std::string SETTING_ENABLE("EnableGameControl"); std::string SETTING_SENDTOSERVER("GameControlToServer"); @@ -549,7 +551,6 @@ LLGameControl::Options::Options() mAxisOptions.resize(NUM_AXES); mAxisMap.resize(NUM_AXES); mButtonMap.resize(NUM_BUTTONS); - resetToDefaults(); } @@ -560,7 +561,6 @@ void LLGameControl::Options::resetToDefaults() mAxisOptions[i].resetToDefaults(); mAxisMap[i] = (U8)i; } - for (size_t i = 0; i < NUM_BUTTONS; ++i) { mButtonMap[i] = (U8)i; @@ -595,18 +595,7 @@ S16 LLGameControl::Options::fixAxisValue(U8 axis, S16 value) const } else { - const AxisOptions& options = mAxisOptions[axis]; - S32 new_value = (S32)value + (S32)options.mOffset; - value = (S16)std::clamp(new_value , -32768, 32767); - if ((value > 0 && value < (S16)options.mDeadZone) || - (value < 0 && value > -(S16)options.mDeadZone)) - { - value = 0; - } - else if (options.mInvert) - { - value = -value; - } + value = mAxisOptions[axis].computeModifiedValue(value); } return value; } @@ -615,7 +604,7 @@ std::string LLGameControl::Options::AxisOptions::saveToString() const { std::list options; - if (mInvert) + if (mMultiplier == -1) { options.push_back("invert:1"); } @@ -715,16 +704,17 @@ void LLGameControl::Options::AxisOptions::loadFromString(std::string options) LL_WARNS("SDL2") << "Invalid axis options: '" << options << "'" << LL_ENDL; } + mMultiplier = 1; std::string invert = pairs["invert"]; if (!invert.empty()) { - if (invert != "1") + if (invert == "1") { - LL_WARNS("SDL2") << "Invalid invert value: '" << invert << "'" << LL_ENDL; + mMultiplier = -1; } else { - mInvert = true; + LL_WARNS("SDL2") << "Invalid invert value: '" << invert << "'" << LL_ENDL; } } @@ -732,13 +722,13 @@ void LLGameControl::Options::AxisOptions::loadFromString(std::string options) if (!dead_zone.empty()) { size_t number = std::stoull(dead_zone); - if (number > MAX_AXIS_DEAD_ZONE || std::to_string(number) != dead_zone) + if (number <= MAX_AXIS_DEAD_ZONE && std::to_string(number) == dead_zone) { - LL_WARNS("SDL2") << "Invalid dead_zone value: '" << dead_zone << "'" << LL_ENDL; + mDeadZone = (U16)number; } else { - mDeadZone = (U16)number; + LL_WARNS("SDL2") << "Invalid dead_zone value: '" << dead_zone << "'" << LL_ENDL; } } @@ -764,11 +754,13 @@ std::string LLGameControl::Options::saveToString(const std::string& name, bool f bool LLGameControl::Options::loadFromString(std::string& name, std::string options) { + resetToDefaults(); return LLGameControl::parseDeviceOptions(options, name, mAxisOptions, mAxisMap, mButtonMap); } bool LLGameControl::Options::loadFromString(std::string options) { + resetToDefaults(); std::string dummy_name; return LLGameControl::parseDeviceOptions(options, dummy_name, mAxisOptions, mAxisMap, mButtonMap); } @@ -916,6 +908,19 @@ void LLGameControllerManager::saveDeviceOptionsToSettings() const } } +void LLGameControllerManager::setDeviceOptions(const std::string& guid, const LLGameControl::Options& options) +{ + // find Device by name + for (LLGameControl::Device& device : mDevices) + { + if (device.getGUID() == guid) + { + device.mOptions = options; + return; + } + } +} + void LLGameControllerManager::addController(SDL_JoystickID id, const std::string& guid, const std::string& name) { llassert(id >= 0); @@ -1468,6 +1473,10 @@ void onControllerDeviceAdded(const SDL_Event& event) } g_manager.addController(id, guid, name); + + // this event could happen while the preferences UI is open + // in which case we need to force it to update + s_updateUI(); } void onControllerDeviceRemoved(const SDL_Event& event) @@ -1476,6 +1485,10 @@ void onControllerDeviceRemoved(const SDL_Event& event) SDL_JoystickID id = event.cdevice.which; g_manager.removeController(id); + + // this event could happen while the preferences UI is open + // in which case we need to force it to update + s_updateUI(); } void onControllerButton(const SDL_Event& event) @@ -1524,7 +1537,8 @@ void LLGameControl::init(const std::string& gamecontrollerdb_path, std::function loadString, std::function saveString, std::function loadObject, - std::function saveObject) + std::function saveObject, + std::function updateUI) { if (g_gameControl) return; @@ -1535,6 +1549,7 @@ void LLGameControl::init(const std::string& gamecontrollerdb_path, llassert(saveString); llassert(loadObject); llassert(saveObject); + llassert(updateUI); int result = SDL_InitSubSystem(SDL_INIT_VIDEO | SDL_INIT_GAMECONTROLLER); if (result < 0) @@ -1572,6 +1587,7 @@ void LLGameControl::init(const std::string& gamecontrollerdb_path, s_saveString = saveString; s_loadObject = loadObject; s_saveObject = saveObject; + s_updateUI = updateUI; loadFromSettings(); } @@ -1692,7 +1708,7 @@ LLGameControl::InputChannel LLGameControl::getActiveInputChannel() else { // scan axes - S16 threshold = std::numeric_limits::max() / 2; + constexpr S16 threshold = std::numeric_limits::max() / 2; for (U8 i = 0; i < 6; ++i) { if (abs(state.mAxes[i]) > threshold) @@ -2099,3 +2115,9 @@ void LLGameControl::saveToSettings() LLSD deviceOptions(g_deviceOptions, true); s_saveObject(SETTING_KNOWNCONTROLLERS, deviceOptions); } + +// static +void LLGameControl::setDeviceOptions(const std::string& guid, const Options& options) +{ + g_manager.setDeviceOptions(guid, options); +} diff --git a/indra/llwindow/llgamecontrol.h b/indra/llwindow/llgamecontrol.h index 9398fb7f66..9dfce4c287 100644 --- a/indra/llwindow/llgamecontrol.h +++ b/indra/llwindow/llgamecontrol.h @@ -178,17 +178,23 @@ public: public: struct AxisOptions { - bool mInvert { false }; + S32 mMultiplier = 1; U16 mDeadZone { 0 }; S16 mOffset { 0 }; void resetToDefaults() { - mInvert = false; + mMultiplier = 1; mDeadZone = 0; mOffset = 0; } + S16 computeModifiedValue(S16 raw_value) const + { + S32 new_value = ((S32)raw_value + S32(mOffset)) * mMultiplier; + return (S16)(std::clamp(new_value, -32768, 32767)); + } + std::string saveToString() const; void loadFromString(std::string options); }; @@ -265,7 +271,8 @@ public: std::function loadString, std::function saveString, std::function loadObject, - std::function saveObject); + std::function saveObject, + std::function updateUI); static void terminate(); static const std::list& getDevices(); @@ -333,5 +340,6 @@ public: static void initByDefault(); static void loadFromSettings(); static void saveToSettings(); + static void setDeviceOptions(const std::string& guid, const Options& options); }; diff --git a/indra/newview/llappviewer.cpp b/indra/newview/llappviewer.cpp index 7eded9e174..475610be36 100644 --- a/indra/newview/llappviewer.cpp +++ b/indra/newview/llappviewer.cpp @@ -47,6 +47,7 @@ #include "llagentwearables.h" #include "lldirpicker.h" #include "llfloaterimcontainer.h" +#include "llfloaterpreference.h" #include "llimprocessing.h" #include "llwindow.h" #include "llviewerstats.h" @@ -1141,7 +1142,8 @@ bool LLAppViewer::init() [&](const std::string& name) -> std::string { return gSavedSettings.getString(name); }, [&](const std::string& name, const std::string& value) { gSavedSettings.setString(name, value); }, [&](const std::string& name) -> LLSD { return gSavedSettings.getLLSD(name); }, - [&](const std::string& name, const LLSD& value) { gSavedSettings.setLLSD(name, value); }); + [&](const std::string& name, const LLSD& value) { gSavedSettings.setLLSD(name, value); }, + [&]() { LLPanelPreferenceGameControl::updateDeviceList(); }); try { diff --git a/indra/newview/llfloaterpreference.cpp b/indra/newview/llfloaterpreference.cpp index 7ea9f9b4c2..109db7e3d7 100644 --- a/indra/newview/llfloaterpreference.cpp +++ b/indra/newview/llfloaterpreference.cpp @@ -1001,7 +1001,8 @@ void LLFloaterPreference::onBtnOK(const LLSD& userdata) LLUIColorTable::instance().saveUserSettings(); gSavedSettings.saveToFile(gSavedSettings.getString("ClientSettingsFile"), true); - LLGameControl::loadFromSettings(); + // save current config to settings + LLGameControl::saveToSettings(); // Only save once logged in and loaded per account settings if (mGotPersonalInfo) @@ -1048,6 +1049,9 @@ void LLFloaterPreference::onBtnCancel(const LLSD& userdata) cancel(); closeFloater(); } + + // restore config from settings + LLGameControl::loadFromSettings(); } //static @@ -2261,12 +2265,12 @@ void LLPanelPreference::cancel(const std::vector settings_to_skip) { for (control_values_map_t::iterator iter = mSavedValues.begin(); iter != mSavedValues.end(); ++iter) -{ + { LLControlVariable* control = iter->first; LLSD ctrl_value = iter->second; if((control->getName() == "InstantMessageLogPath") && (ctrl_value.asString() == "")) - { + { continue; } @@ -3176,6 +3180,15 @@ static LLScrollListCtrl* gSelectedGrid { nullptr }; static LLScrollListItem* gSelectedItem { nullptr }; static LLScrollListCell* gSelectedCell { nullptr }; +// static +void LLPanelPreferenceGameControl::updateDeviceList() +{ + if (gGameControlPanel) + { + gGameControlPanel->updateDeviceListInternal(); + } +} + LLPanelPreferenceGameControl::LLPanelPreferenceGameControl() { gGameControlPanel = this; @@ -3424,34 +3437,39 @@ void LLPanelPreferenceGameControl::onAxisOptionsSelect() if (LLScrollListItem* row = mAxisOptions->getFirstSelected()) { - LLGameControl::Options& deviceOptions = getSelectedDeviceOptions(); + LLGameControl::Options& options = getSelectedDeviceOptions(); S32 row_index = mAxisOptions->getItemIndex(row); - S32 column_index = row->getSelectedCell(); - if (column_index == 1) + { - LLGameControl::Options& deviceOptions = getSelectedDeviceOptions(); - deviceOptions.getAxisOptions()[row_index].mInvert = - row->getColumn(column_index)->getValue().asBoolean(); + // always update invert checkbox value because even though it may have been clicked + // the row does not know its cell has been selected + constexpr S32 invert_checkbox_column = 1; + bool invert = row->getColumn(invert_checkbox_column)->getValue().asBoolean(); + options.getAxisOptions()[row_index].mMultiplier = invert ? -1 : 1; } - else if (column_index == 2 || column_index == 3) + + S32 column_index = row->getSelectedCell(); + if (column_index == 2 || column_index == 3) { fitInRect(mNumericValueEditor, mAxisOptions, row_index, column_index); if (column_index == 2) { mNumericValueEditor->setMinValue(0); mNumericValueEditor->setMaxValue(LLGameControl::MAX_AXIS_DEAD_ZONE); - mNumericValueEditor->setValue(deviceOptions.getAxisOptions()[row_index].mDeadZone); + mNumericValueEditor->setValue(options.getAxisOptions()[row_index].mDeadZone); } else // column_index == 3 { mNumericValueEditor->setMinValue(-LLGameControl::MAX_AXIS_OFFSET); mNumericValueEditor->setMaxValue(LLGameControl::MAX_AXIS_OFFSET); - mNumericValueEditor->setValue(deviceOptions.getAxisOptions()[row_index].mOffset); + mNumericValueEditor->setValue(options.getAxisOptions()[row_index].mOffset); } mNumericValueEditor->setVisible(true); } initCombobox(row, mAxisOptions); + + LLGameControl::setDeviceOptions(mSelectedDeviceGUID, options); } } @@ -3464,7 +3482,7 @@ void LLPanelPreferenceGameControl::onCommitNumericValue() S32 row_index = mAxisOptions->getItemIndex(row); S32 column_index = row->getSelectedCell(); llassert(column_index == 2 || column_index == 3); - if (column_index != 2 && column_index != 3) + if (column_index < 2 || column_index > 3) return; if (column_index == 2) @@ -3574,6 +3592,12 @@ void LLPanelPreferenceGameControl::onOpen(const LLSD& key) populateActionTableCells(); updateActionTableState(); + updateDeviceListInternal(); + updateEnable(); +} + +void LLPanelPreferenceGameControl::updateDeviceListInternal() +{ // Setup the 2nd tab mDeviceOptions.clear(); for (const auto& pair : LLGameControl::getDeviceOptions()) @@ -3590,11 +3614,8 @@ void LLPanelPreferenceGameControl::onOpen(const LLSD& key) mDeviceOptions[device.getGUID()] = { device.getName(), device.saveOptionsToString(true), device.getOptions() }; } } - mCheckShowAllDevices->setValue(false); populateDeviceTitle(); - - updateEnable(); } void LLPanelPreferenceGameControl::populateActionTableRows(const std::string& filename) @@ -3791,7 +3812,7 @@ void LLPanelPreferenceGameControl::populateOptionsTableCells() { LLScrollListItem* row = rows[i]; const LLGameControl::Options::AxisOptions& axis_options = all_axis_options[i]; - row->getColumn(1)->setValue(axis_options.mInvert); + row->getColumn(1)->setValue(axis_options.mMultiplier == -1 ? true : false); setNumericLabel(row->getColumn(2), axis_options.mDeadZone); setNumericLabel(row->getColumn(3), axis_options.mOffset); } diff --git a/indra/newview/llfloaterpreference.h b/indra/newview/llfloaterpreference.h index b3872958ac..57eaa193a3 100644 --- a/indra/newview/llfloaterpreference.h +++ b/indra/newview/llfloaterpreference.h @@ -389,12 +389,19 @@ public: TYPE_NONE }; + static void updateDeviceList(); + LLPanelPreferenceGameControl(); ~LLPanelPreferenceGameControl(); void onOpen(const LLSD& key) override; + + // This function squirrels away the current values of the controls so that + // cancel() can restore them. void saveSettings() override; + void updateDeviceListInternal(); + void onGridSelect(LLUICtrl* ctrl); void onCommitInputChannel(LLUICtrl* ctrl); -- cgit v1.2.3 From 0617923ae7f450ece7288f8a73446c45a8ed32db Mon Sep 17 00:00:00 2001 From: leviathan Date: Tue, 3 Sep 2024 15:38:35 -0700 Subject: remove crashy LLSD ctor used by GameControl --- indra/cmake/00-COMPILE-LINK-RUN.txt | 2 +- indra/cmake/UI.cmake | 3 +- indra/llappearance/CMakeLists.txt | 2 +- indra/llcommon/llsd.h | 14 --- indra/llcommon/llstring.h | 1 + indra/llmath/llsimdmath.h | 11 ++- indra/llui/lltexteditor.h | 1 + indra/llwindow/CMakeLists.txt | 8 +- indra/llwindow/llgamecontrol.cpp | 95 +++++++++++---------- indra/llwindow/llgamecontrol.h | 2 + indra/llwindow/llkeyboard.cpp | 41 ++------- indra/llwindow/llkeyboard.h | 14 +-- indra/llwindow/llkeyboardheadless.cpp | 10 +++ indra/llwindow/llkeyboardheadless.h | 4 +- indra/llwindow/llkeyboardsdl.cpp | 17 ++-- indra/llwindow/llkeyboardsdl.h | 20 ++--- indra/llwindow/llsdl.cpp | 102 ++++++++++++++++++++++ indra/llwindow/llsdl.h | 30 +++++++ indra/llwindow/llwindow.cpp | 3 + indra/llwindow/llwindow.h | 45 +++++----- indra/llwindow/llwindowheadless.h | 115 ++++++++++++------------- indra/llwindow/llwindowmacosx.cpp | 44 +++++----- indra/llwindow/llwindowmacosx.h | 36 ++++---- indra/llwindow/llwindowmesaheadless.h | 114 ++++++++++++------------- indra/llwindow/llwindowsdl.cpp | 46 +++++----- indra/llwindow/llwindowsdl.h | 36 ++++---- indra/llwindow/llwindowwin32.cpp | 44 +++++----- indra/llwindow/llwindowwin32.h | 156 +++++++++++++++++----------------- indra/newview/llagent.h | 2 +- indra/newview/llappviewer.cpp | 13 +-- indra/newview/llappviewerlinux.cpp | 2 +- indra/newview/llfloaterpreference.cpp | 17 +++- indra/newview/llfloaterpreference.h | 6 +- indra/newview/viewer_manifest.py | 9 -- 34 files changed, 585 insertions(+), 480 deletions(-) create mode 100644 indra/llwindow/llsdl.cpp create mode 100644 indra/llwindow/llsdl.h diff --git a/indra/cmake/00-COMPILE-LINK-RUN.txt b/indra/cmake/00-COMPILE-LINK-RUN.txt index 1933072a6d..6cff0c0f28 100644 --- a/indra/cmake/00-COMPILE-LINK-RUN.txt +++ b/indra/cmake/00-COMPILE-LINK-RUN.txt @@ -162,7 +162,7 @@ Linking * Update the autobuild.xml of ALL consumers of the library. In the case of zlib, that meant updating freetype, libpng, openssl, - libxml2, fontconfig, curl, Boost, SDL, llqtwebkit, google-mock and + libxml2, fontconfig, curl, Boost, SDL2, llqtwebkit, google-mock and colladadom. * Confirm by test and observation that the consumers actually use diff --git a/indra/cmake/UI.cmake b/indra/cmake/UI.cmake index 0df62808e7..ae039b4a47 100644 --- a/indra/cmake/UI.cmake +++ b/indra/cmake/UI.cmake @@ -21,9 +21,10 @@ if (LINUX) Xext Xft Xinerama + X11 ll::fontconfig ll::freetype - ll::SDL + ll::SDL2 ll::glib ll::gio ) diff --git a/indra/llappearance/CMakeLists.txt b/indra/llappearance/CMakeLists.txt index c3be8bc78e..c510d01e3a 100644 --- a/indra/llappearance/CMakeLists.txt +++ b/indra/llappearance/CMakeLists.txt @@ -27,7 +27,7 @@ set(llappearance_SOURCE_FILES llviewervisualparam.cpp llavatarappearancedefines.cpp ) - + set(llappearance_HEADER_FILES CMakeLists.txt diff --git a/indra/llcommon/llsd.h b/indra/llcommon/llsd.h index 7fa193e035..e018b400cb 100644 --- a/indra/llcommon/llsd.h +++ b/indra/llcommon/llsd.h @@ -335,20 +335,6 @@ public: { return c ? (*this)[std::string_view(c)] : *this; } - - template - LLSD(const std::map& map, bool exclude_empty = false) - { - assign(emptyMap()); - for (const std::pair& pair : map) - { - LLSD value(pair.second); - if (!exclude_empty || !value.isEmpty()) - { - insert(pair.first, value); - } - } - } //@} /** @name Array Values */ diff --git a/indra/llcommon/llstring.h b/indra/llcommon/llstring.h index 7d0806b22e..b15a85cb2a 100644 --- a/indra/llcommon/llstring.h +++ b/indra/llcommon/llstring.h @@ -36,6 +36,7 @@ //#include #include #include +#include #include #include #include diff --git a/indra/llmath/llsimdmath.h b/indra/llmath/llsimdmath.h index 40953dc2e8..592bb124c2 100644 --- a/indra/llmath/llsimdmath.h +++ b/indra/llmath/llsimdmath.h @@ -31,8 +31,15 @@ #error "Please include llmath.h before this file." #endif -#if ( ( LL_DARWIN || LL_LINUX ) && !(__SSE2__) ) || ( LL_WINDOWS && ( _M_IX86_FP < 2 && ADDRESS_SIZE == 32 ) ) -#error SSE2 not enabled. LLVector4a and related class will not compile. +// the check for this error case must be split into multiple parts +// because some versions of VS complain about '__SSE2__' +//#if ( ( LL_DARWIN || LL_LINUX ) && !(__SSE2__) ) || ( LL_WINDOWS && ( _M_IX86_FP < 2 && ADDRESS_SIZE == 32 ) ) +#if ( ( LL_DARWIN || LL_LINUX ) ) + #if !(__SSE2__) + #error SSE2 not enabled. LLVector4a and related class will not compile. + #endif +#elif ( LL_WINDOWS && ( _M_IX86_FP < 2 && ADDRESS_SIZE == 32 ) ) + #error SSE2 not enabled. LLVector4a and related class will not compile. #endif #if !LL_WINDOWS diff --git a/indra/llui/lltexteditor.h b/indra/llui/lltexteditor.h index 6d45a72deb..cedb79bf62 100644 --- a/indra/llui/lltexteditor.h +++ b/indra/llui/lltexteditor.h @@ -314,6 +314,7 @@ public: void pasteTextWithLinebreaksImpl(const LLWString& clean_string); private: + void pasteTextWithLinebreaksInternal(const LLWString & clean_string); void onKeyStroke(); // Concrete TextCmd sub-classes used by the LLTextEditor base class diff --git a/indra/llwindow/CMakeLists.txt b/indra/llwindow/CMakeLists.txt index ebac55cb9c..e86ef2d578 100644 --- a/indra/llwindow/CMakeLists.txt +++ b/indra/llwindow/CMakeLists.txt @@ -26,6 +26,7 @@ set(llwindow_SOURCE_FILES llgamecontroltranslator.cpp llkeyboard.cpp llkeyboardheadless.cpp + llsdl.cpp llwindowheadless.cpp llwindowcallbacks.cpp llwindow.cpp @@ -39,6 +40,7 @@ set(llwindow_HEADER_FILES llgamecontroltranslator.h llkeyboard.h llkeyboardheadless.h + llsdl.h llwindowheadless.h llwindowcallbacks.h ) @@ -63,7 +65,7 @@ set(llwindow_LINK_LIBRARIES ll::glm ll::glext ll::uilibraries - ll::SDL + ll::SDL2 ll::zlib-ng ) @@ -178,11 +180,11 @@ endif (llwindow_HEADER_FILES) ${viewer_SOURCE_FILES} ) -if (SDL_FOUND) +if (SDL2_FOUND) set_property(TARGET llwindow PROPERTY COMPILE_DEFINITIONS LL_SDL=1 ) -endif (SDL_FOUND) +endif (SDL2_FOUND) target_link_libraries (llwindow ${llwindow_LINK_LIBRARIES}) target_include_directories(llwindow INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}) diff --git a/indra/llwindow/llgamecontrol.cpp b/indra/llwindow/llgamecontrol.cpp index c7a27c5558..01c6f91d25 100644 --- a/indra/llwindow/llgamecontrol.cpp +++ b/indra/llwindow/llgamecontrol.cpp @@ -156,10 +156,6 @@ namespace std { ss << ",version:" << version; } - if (U16 firmware = SDL_JoystickGetFirmwareVersion(joystick)) - { - ss << ",firmware:" << firmware; - } if (const char* serial = SDL_JoystickGetSerial(joystick)) { ss << ",serial:'" << serial << "'"; @@ -190,10 +186,6 @@ namespace std { ss << ",version:" << version; } - if (U16 firmware = SDL_GameControllerGetFirmwareVersion(controller)) - { - ss << ",firmware:" << firmware; - } if (const char* serial = SDL_GameControllerGetSerial(controller)) { ss << ",serial:'" << serial << "'"; @@ -1420,13 +1412,11 @@ void onJoystickDeviceAdded(const SDL_Event& event) SDL_JoystickGUID guid(SDL_JoystickGetDeviceGUID(event.cdevice.which)); SDL_JoystickType type(SDL_JoystickGetDeviceType(event.cdevice.which)); std::string name(std::to_string(SDL_JoystickNameForIndex(event.cdevice.which))); - std::string path(std::to_string(SDL_JoystickPathForIndex(event.cdevice.which))); LL_INFOS("SDL2") << "joystick {id:" << event.cdevice.which << ",guid:'" << guid << "'" << ",type:'" << type << "'" << ",name:'" << name << "'" - << ",path:'" << path << "'" << "}" << LL_ENDL; if (SDL_Joystick* joystick = SDL_JoystickOpen(event.cdevice.which)) @@ -1449,13 +1439,11 @@ void onControllerDeviceAdded(const SDL_Event& event) std::string guid(std::to_string(SDL_JoystickGetDeviceGUID(event.cdevice.which))); SDL_GameControllerType type(SDL_GameControllerTypeForIndex(event.cdevice.which)); std::string name(std::to_string(SDL_GameControllerNameForIndex(event.cdevice.which))); - std::string path(std::to_string(SDL_GameControllerPathForIndex(event.cdevice.which))); LL_INFOS("SDL2") << "controller {id:" << event.cdevice.which << ",guid:'" << guid << "'" << ",type:'" << type << "'" << ",name:'" << name << "'" - << ",path:'" << path << "'" << "}" << LL_ENDL; SDL_JoystickID id = SDL_JoystickGetDeviceInstanceID(event.cdevice.which); @@ -1525,11 +1513,6 @@ bool LLGameControl::isInitialized() return g_gameControl != nullptr; } -void sdl_logger(void *userdata, int category, SDL_LogPriority priority, const char *message) -{ - LL_DEBUGS("SDL2") << "log='" << message << "'" << LL_ENDL; -} - // static void LLGameControl::init(const std::string& gamecontrollerdb_path, std::function loadBoolean, @@ -1551,16 +1534,14 @@ void LLGameControl::init(const std::string& gamecontrollerdb_path, llassert(saveObject); llassert(updateUI); - int result = SDL_InitSubSystem(SDL_INIT_VIDEO | SDL_INIT_GAMECONTROLLER); + int result = SDL_InitSubSystem(SDL_INIT_GAMECONTROLLER | SDL_INIT_SENSOR); if (result < 0) { // This error is critical, we stop working with SDL and return - LL_WARNS("SDL2") << "Error initializing the subsystems : " << SDL_GetError() << LL_ENDL; + LL_WARNS("SDL2") << "Error initializing GameController subsystems : " << SDL_GetError() << LL_ENDL; return; } - SDL_LogSetOutputFunction(&sdl_logger, nullptr); - // The inability to read this file is not critical, we can continue working if (!LLFile::isfile(gamecontrollerdb_path.c_str())) { @@ -1596,7 +1577,6 @@ void LLGameControl::init(const std::string& gamecontrollerdb_path, void LLGameControl::terminate() { g_manager.clear(); - SDL_Quit(); } // static @@ -1638,6 +1618,7 @@ void LLGameControl::clearAllStates() // static void LLGameControl::processEvents(bool app_has_focus) { + // This method used by non-linux platforms which only use SDL for GameController input SDL_Event event; if (!app_has_focus) { @@ -1652,31 +1633,42 @@ void LLGameControl::processEvents(bool app_has_focus) while (g_gameControl && SDL_PollEvent(&event)) { - switch (event.type) - { - case SDL_JOYDEVICEADDED: - onJoystickDeviceAdded(event); - break; - case SDL_JOYDEVICEREMOVED: - onJoystickDeviceRemoved(event); - break; - case SDL_CONTROLLERDEVICEADDED: - onControllerDeviceAdded(event); - break; - case SDL_CONTROLLERDEVICEREMOVED: - onControllerDeviceRemoved(event); - break; - case SDL_CONTROLLERBUTTONDOWN: - /* FALLTHROUGH */ - case SDL_CONTROLLERBUTTONUP: + handleEvent(event, app_has_focus); + } +} + +void LLGameControl::handleEvent(const SDL_Event& event, bool app_has_focus) +{ + switch (event.type) + { + case SDL_JOYDEVICEADDED: + onJoystickDeviceAdded(event); + break; + case SDL_JOYDEVICEREMOVED: + onJoystickDeviceRemoved(event); + break; + case SDL_CONTROLLERDEVICEADDED: + onControllerDeviceAdded(event); + break; + case SDL_CONTROLLERDEVICEREMOVED: + onControllerDeviceRemoved(event); + break; + case SDL_CONTROLLERBUTTONDOWN: + /* FALLTHROUGH */ + case SDL_CONTROLLERBUTTONUP: + if (app_has_focus) + { onControllerButton(event); - break; - case SDL_CONTROLLERAXISMOTION: + } + break; + case SDL_CONTROLLERAXISMOTION: + if (app_has_focus) + { onControllerAxis(event); - break; - default: - break; - } + } + break; + default: + break; } } @@ -2112,7 +2104,18 @@ void LLGameControl::saveToSettings() s_saveString(SETTING_FLYCAMMAPPINGS, g_manager.getFlycamMappings()); g_manager.saveDeviceOptionsToSettings(); - LLSD deviceOptions(g_deviceOptions, true); + + // construct LLSD version of g_deviceOptions but only include non-empty values + LLSD deviceOptions = LLSD::emptyMap(); + for (const auto& data_pair : g_deviceOptions) + { + if (!data_pair.second.empty()) + { + LLSD value(data_pair.second); + deviceOptions.insert(data_pair.first, value); + } + } + s_saveObject(SETTING_KNOWNCONTROLLERS, deviceOptions); } diff --git a/indra/llwindow/llgamecontrol.h b/indra/llwindow/llgamecontrol.h index 9dfce4c287..5472a8ce6d 100644 --- a/indra/llwindow/llgamecontrol.h +++ b/indra/llwindow/llgamecontrol.h @@ -32,6 +32,7 @@ #include "llerror.h" #include "llsingleton.h" #include "stdtypes.h" +#include "SDL2/SDL_events.h" // For reference, here are the RAW indices of the various input channels // of a standard XBox controller. Button (N) is numbered in parentheses, @@ -287,6 +288,7 @@ public: static void clearAllStates(); static void processEvents(bool app_has_focus = true); + static void handleEvent(const SDL_Event& event, bool app_has_focus); static const State& getState(); static InputChannel getActiveInputChannel(); static void getFlycamInputs(std::vector& inputs_out); diff --git a/indra/llwindow/llkeyboard.cpp b/indra/llwindow/llkeyboard.cpp index 44679d3843..cb0c312a1d 100644 --- a/indra/llwindow/llkeyboard.cpp +++ b/indra/llwindow/llkeyboard.cpp @@ -227,7 +227,7 @@ LLKeyboard::NATIVE_KEY_TYPE LLKeyboard::inverseTranslateKey(const KEY translated } -bool LLKeyboard::handleTranslatedKeyDown(KEY translated_key, U32 translated_mask) +bool LLKeyboard::handleTranslatedKeyDown(KEY translated_key, MASK translated_mask) { bool handled = false; bool repeated = false; @@ -255,7 +255,7 @@ bool LLKeyboard::handleTranslatedKeyDown(KEY translated_key, U32 translated_mask } -bool LLKeyboard::handleTranslatedKeyUp(KEY translated_key, U32 translated_mask) +bool LLKeyboard::handleTranslatedKeyUp(KEY translated_key, MASK translated_mask) { bool handled = false; if( mKeyLevel[translated_key] ) @@ -276,58 +276,29 @@ bool LLKeyboard::handleTranslatedKeyUp(KEY translated_key, U32 translated_mask) return handled; } -<<<<<<< HEAD -bool LLKeyboard::handleKeyDown(const U16 key, const U32 mask) -{ - U32 translated_mask = updateModifiers(mask); -======= -bool LLKeyboard::handleKeyDown(const U16 key, const MASK mask) +bool LLKeyboard::handleKeyDown(const NATIVE_KEY_TYPE key, const MASK mask) { - U32 translated_mask = updateModifiers(mask); ->>>>>>> 7733b56eab (Add GameControl UI for per device settings) + MASK translated_mask = updateModifiers(mask); KEY translated_key = 0; bool handled = false; if(translateKey(key, &translated_key)) { handled = handleTranslatedKeyDown(translated_key, translated_mask); } -<<<<<<< HEAD - if (!handled) - { - LLGameControl::onKeyDown(translated_key, translated_mask); - } - -======= ->>>>>>> 7733b56eab (Add GameControl UI for per device settings) return handled; } -<<<<<<< HEAD -bool LLKeyboard::handleKeyUp(const U16 key, const U32 mask) +bool LLKeyboard::handleKeyUp(const NATIVE_KEY_TYPE key, const MASK mask) { - U32 translated_mask = updateModifiers(mask); - -======= -bool LLKeyboard::handleKeyUp(const U16 key, const MASK mask) -{ - U32 translated_mask = updateModifiers(mask); ->>>>>>> 7733b56eab (Add GameControl UI for per device settings) + MASK translated_mask = updateModifiers(mask); KEY translated_key = 0; bool handled = false; if(translateKey(key, &translated_key)) { handled = handleTranslatedKeyUp(translated_key, translated_mask); } -<<<<<<< HEAD - if (!handled) - { - LLGameControl::onKeyUp(translated_key, translated_mask); - } - -======= ->>>>>>> 7733b56eab (Add GameControl UI for per device settings) return handled; } diff --git a/indra/llwindow/llkeyboard.h b/indra/llwindow/llkeyboard.h index d91e023b85..da406b28e8 100644 --- a/indra/llwindow/llkeyboard.h +++ b/indra/llwindow/llkeyboard.h @@ -55,10 +55,12 @@ class LLWindowCallbacks; class LLKeyboard { public: -#ifndef LL_SDL - typedef U16 NATIVE_KEY_TYPE; -#else +#ifdef LL_LINUX + // linux relies on SDL2 which uses U32 for its native key type typedef U32 NATIVE_KEY_TYPE; +#else + // on non-linux platforms we can get by with a smaller native key type + typedef U16 NATIVE_KEY_TYPE; #endif LLKeyboard(); virtual ~LLKeyboard(); @@ -73,9 +75,9 @@ public: bool getKeyRepeated(const KEY key) { return mKeyRepeated[key]; } bool translateKey(const NATIVE_KEY_TYPE os_key, KEY *translated_key); - NATIVE_KEY_TYPE inverseTranslateKey(const KEY translated_key); - bool handleTranslatedKeyUp(KEY translated_key, U32 translated_mask); // Translated into "Linden" keycodes - bool handleTranslatedKeyDown(KEY translated_key, U32 translated_mask); // Translated into "Linden" keycodes + NATIVE_KEY_TYPE inverseTranslateKey(const KEY translated_key); + bool handleTranslatedKeyUp(KEY translated_key, MASK translated_mask); // Translated into "Linden" keycodes + bool handleTranslatedKeyDown(KEY translated_key, MASK translated_mask); // Translated into "Linden" keycodes virtual bool handleKeyUp(const NATIVE_KEY_TYPE key, MASK mask) = 0; virtual bool handleKeyDown(const NATIVE_KEY_TYPE key, MASK mask) = 0; diff --git a/indra/llwindow/llkeyboardheadless.cpp b/indra/llwindow/llkeyboardheadless.cpp index a827424141..0ca8c09f42 100644 --- a/indra/llwindow/llkeyboardheadless.cpp +++ b/indra/llwindow/llkeyboardheadless.cpp @@ -31,6 +31,16 @@ LLKeyboardHeadless::LLKeyboardHeadless() { } +bool LLKeyboardHeadless::handleKeyUp(const LLKeyboard::NATIVE_KEY_TYPE key, MASK mask) +{ + return false; +} + +bool LLKeyboardHeadless::handleKeyDown(const LLKeyboard::NATIVE_KEY_TYPE key, MASK mask) +{ + return false; +} + void LLKeyboardHeadless::resetMaskKeys() { } diff --git a/indra/llwindow/llkeyboardheadless.h b/indra/llwindow/llkeyboardheadless.h index cc31b99d3f..60c4d61e1c 100644 --- a/indra/llwindow/llkeyboardheadless.h +++ b/indra/llwindow/llkeyboardheadless.h @@ -35,8 +35,8 @@ public: LLKeyboardHeadless(); ~LLKeyboardHeadless() {}; - bool handleKeyUp(const U16 key, MASK mask) override; - bool handleKeyDown(const U16 key, MASK mask) override; + bool handleKeyUp(const LLKeyboard::NATIVE_KEY_TYPE key, MASK mask) override; + bool handleKeyDown(const LLKeyboard::NATIVE_KEY_TYPE key, MASK mask) override; void resetMaskKeys() override; MASK currentMask(bool for_mouse_event) override; void scanKeyboard() override; diff --git a/indra/llwindow/llkeyboardsdl.cpp b/indra/llwindow/llkeyboardsdl.cpp index b8b2b311f7..b6666195a6 100644 --- a/indra/llwindow/llkeyboardsdl.cpp +++ b/indra/llwindow/llkeyboardsdl.cpp @@ -26,7 +26,7 @@ #include "linden_common.h" #include "llkeyboardsdl.h" #include "llwindowcallbacks.h" -#include "SDL2/SDL.h" + #include "SDL2/SDL_keycode.h" LLKeyboardSDL::LLKeyboardSDL() @@ -42,7 +42,7 @@ LLKeyboardSDL::LLKeyboardSDL() // Looks like we need to map those despite of SDL_TEXTINPUT handling most of this, but without // the translation lower->upper here accelerators will not work. - U16 cur_char; + LLKeyboard::NATIVE_KEY_TYPE cur_char; for (cur_char = 'A'; cur_char <= 'Z'; cur_char++) { mTranslateKeyMap[cur_char] = cur_char; @@ -201,7 +201,7 @@ MASK LLKeyboardSDL::updateModifiers(const MASK mask) } -static U32 adjustNativekeyFromUnhandledMask(const U16 key, const MASK mask) +U32 adjustNativekeyFromUnhandledMask(const LLKeyboard::NATIVE_KEY_TYPE key, const MASK mask) { // SDL doesn't automatically adjust the keysym according to // whether NUMLOCK is engaged, so we massage the keysym manually. @@ -226,11 +226,11 @@ static U32 adjustNativekeyFromUnhandledMask(const U16 key, const MASK mask) } -bool LLKeyboardSDL::handleKeyDown(const U32 key, const MASK mask) +bool LLKeyboardSDL::handleKeyDown(const LLKeyboard::NATIVE_KEY_TYPE key, const MASK mask) { U32 adjusted_nativekey; KEY translated_key = 0; - U32 translated_mask = MASK_NONE; + MASK translated_mask = MASK_NONE; bool handled = false; adjusted_nativekey = adjustNativekeyFromUnhandledMask(key, mask); @@ -246,7 +246,7 @@ bool LLKeyboardSDL::handleKeyDown(const U32 key, const MASK mask) } -bool LLKeyboardSDL::handleKeyUp(const U32 key, const MASK mask) +bool LLKeyboardSDL::handleKeyUp(const LLKeyboard::NATIVE_KEY_TYPE key, const MASK mask) { U32 adjusted_nativekey; KEY translated_key = 0; @@ -315,12 +315,12 @@ void LLKeyboardSDL::scanKeyboard() } -bool LLKeyboardSDL::translateNumpadKey( const U32 os_key, KEY *translated_key) +bool LLKeyboardSDL::translateNumpadKey( const LLKeyboard::NATIVE_KEY_TYPE os_key, KEY *translated_key) { return translateKey(os_key, translated_key); } -U16 LLKeyboardSDL::inverseTranslateNumpadKey(const KEY translated_key) +LLKeyboard::NATIVE_KEY_TYPE LLKeyboardSDL::inverseTranslateNumpadKey(const KEY translated_key) { return inverseTranslateKey(translated_key); } @@ -498,7 +498,6 @@ enum class WindowsVK : U32 }; std::map< U32, U32 > mSDL2_to_Win; -std::set< U32 > mIgnoreSDL2Keys; U32 LLKeyboardSDL::mapSDL2toWin( U32 aSymbol ) { diff --git a/indra/llwindow/llkeyboardsdl.h b/indra/llwindow/llkeyboardsdl.h index fb08fd218b..7671e4c859 100644 --- a/indra/llwindow/llkeyboardsdl.h +++ b/indra/llwindow/llkeyboardsdl.h @@ -33,22 +33,22 @@ class LLKeyboardSDL : public LLKeyboard { public: LLKeyboardSDL(); - /*virtual*/ ~LLKeyboardSDL() {}; + ~LLKeyboardSDL() {}; - /*virtual*/ bool handleKeyUp(const U32 key, MASK mask); - /*virtual*/ bool handleKeyDown(const U32 key, MASK mask); - /*virtual*/ void resetMaskKeys(); - /*virtual*/ MASK currentMask(bool for_mouse_event); - /*virtual*/ void scanKeyboard(); + bool handleKeyUp(const LLKeyboard::NATIVE_KEY_TYPE key, MASK mask) override; + bool handleKeyDown(const LLKeyboard::NATIVE_KEY_TYPE key, MASK mask) override; + void resetMaskKeys() override; + MASK currentMask(bool for_mouse_event) override; + void scanKeyboard() override; protected: MASK updateModifiers(const MASK mask) override; void setModifierKeyLevel( KEY key, bool new_state ); - bool translateNumpadKey( const U32 os_key, KEY *translated_key ); - U16 inverseTranslateNumpadKey(const KEY translated_key); + bool translateNumpadKey( const LLKeyboard::NATIVE_KEY_TYPE os_key, KEY *translated_key ); + LLKeyboard::NATIVE_KEY_TYPE inverseTranslateNumpadKey(const KEY translated_key); private: - std::map mTranslateNumpadMap; // special map for translating OS keys to numpad keys - std::map mInvTranslateNumpadMap; // inverse of the above + std::map mTranslateNumpadMap; // special map for translating OS keys to numpad keys + std::map mInvTranslateNumpadMap; // inverse of the above public: static U32 mapSDL2toWin( U32 ); diff --git a/indra/llwindow/llsdl.cpp b/indra/llwindow/llsdl.cpp new file mode 100644 index 0000000000..6161bd2972 --- /dev/null +++ b/indra/llwindow/llsdl.cpp @@ -0,0 +1,102 @@ +/** + * @file llsdl.cpp + * @brief SDL2 initialization + * + * $LicenseInfo:firstyear=2007&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2010, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#include +#include + +#include "SDL2/SDL.h" + +#include "llerror.h" +#include "llwindow.h" + +void sdl_logger(void *userdata, int category, SDL_LogPriority priority, const char *message) +{ + LL_DEBUGS("SDL2") << "log='" << message << "'" << LL_ENDL; +} + +void init_sdl() +{ + SDL_version c_sdl_version; + SDL_VERSION(&c_sdl_version); + LL_INFOS() << "Compiled against SDL " + << int(c_sdl_version.major) << "." + << int(c_sdl_version.minor) << "." + << int(c_sdl_version.patch) << LL_ENDL; + SDL_version r_sdl_version; + SDL_GetVersion(&r_sdl_version); + LL_INFOS() << "Running with SDL " + << int(r_sdl_version.major) << "." + << int(r_sdl_version.minor) << "." + << int(r_sdl_version.patch) << LL_ENDL; +#ifdef LL_LINUX + // For linux we SDL_INIT_VIDEO and _AUDIO + std::initializer_list > hintList = + { + {SDL_HINT_VIDEO_X11_NET_WM_BYPASS_COMPOSITOR,"0"}, + {SDL_HINT_MOUSE_FOCUS_CLICKTHROUGH,"1"}, + {SDL_HINT_IME_INTERNAL_EDITING,"1"} + }; + + for (auto hint: hintList) + { + SDL_SetHint(std::get<0>(hint), std::get<1>(hint)); + } + + std::initializer_list> initList= + { {SDL_INIT_VIDEO,"SDL_INIT_VIDEO", true}, + {SDL_INIT_AUDIO,"SDL_INIT_AUDIO", false}, + }; +#else + // For non-linux platforms we still SDL_INIT_VIDEO because it is a pre-requisite + // for SDL_INIT_GAMECONTROLLER. + std::initializer_list> initList= + { {SDL_INIT_VIDEO,"SDL_INIT_VIDEO", false}, + }; +#endif // LL_LINUX + // We SDL_INIT_GAMECONTROLLER later in the startup process to make it + // more likely we'll catch initial SDL_CONTROLLERDEVICEADDED events. + + for (auto subSystem : initList) + { + if (SDL_InitSubSystem(std::get<0>(subSystem)) < 0) + { + LL_WARNS() << "SDL_InitSubSystem for " << std::get<1>(subSystem) << " failed " << SDL_GetError() << LL_ENDL; + + if (std::get<2>(subSystem)) + { + OSMessageBox("SDL_Init() failure", "error", OSMB_OK); + return; + } + } + } + + SDL_LogSetOutputFunction(&sdl_logger, nullptr); +} + +void quit_sdl() +{ + SDL_Quit(); +} diff --git a/indra/llwindow/llsdl.h b/indra/llwindow/llsdl.h new file mode 100644 index 0000000000..9fc8de129c --- /dev/null +++ b/indra/llwindow/llsdl.h @@ -0,0 +1,30 @@ +/** + * @file llsdl.h + * @brief SDL2 initialization + * + * $LicenseInfo:firstyear=2007&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2010, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#pragma once + +void init_sdl(); +void quit_sdl(); diff --git a/indra/llwindow/llwindow.cpp b/indra/llwindow/llwindow.cpp index 861fd10e30..93ac58ca6f 100644 --- a/indra/llwindow/llwindow.cpp +++ b/indra/llwindow/llwindow.cpp @@ -39,6 +39,7 @@ #include "llerror.h" #include "llkeyboard.h" +#include "llsdl.h" #include "llwindowcallbacks.h" @@ -415,6 +416,7 @@ LLWindow* LLWindowManager::createWindow( if (use_gl) { + init_sdl(); #if LL_WINDOWS new_window = new LLWindowWin32(callbacks, title, name, x, y, width, height, flags, @@ -462,6 +464,7 @@ bool LLWindowManager::destroyWindow(LLWindow* window) window->close(); sWindowList.erase(window); + quit_sdl(); delete window; diff --git a/indra/llwindow/llwindow.h b/indra/llwindow/llwindow.h index fcc4fd863a..e74142c7df 100644 --- a/indra/llwindow/llwindow.h +++ b/indra/llwindow/llwindow.h @@ -24,8 +24,7 @@ * $/LicenseInfo$ */ -#ifndef LL_LLWINDOW_H -#define LL_LLWINDOW_H +#pragma once #include "llrect.h" #include "llcoord.h" @@ -33,6 +32,7 @@ #include "llcursortypes.h" #include "llinstancetracker.h" #include "llsd.h" +#include "llsdl.h" class LLSplashScreen; class LLPreeditor; @@ -63,16 +63,16 @@ public: virtual void show() = 0; virtual void hide() = 0; virtual void close() = 0; - virtual bool getVisible() = 0; - virtual bool getMinimized() = 0; - virtual bool getMaximized() = 0; + virtual bool getVisible() const = 0; + virtual bool getMinimized() const = 0; + virtual bool getMaximized() const = 0; virtual bool maximize() = 0; virtual void minimize() = 0; virtual void restore() = 0; - bool getFullscreen() { return mFullscreen; }; - virtual bool getPosition(LLCoordScreen *position) = 0; - virtual bool getSize(LLCoordScreen *size) = 0; - virtual bool getSize(LLCoordWindow *size) = 0; + virtual bool getFullscreen() const { return mFullscreen; }; + virtual bool getPosition(LLCoordScreen *position) const = 0; + virtual bool getSize(LLCoordScreen *size) const = 0; + virtual bool getSize(LLCoordWindow *size) const = 0; virtual bool setPosition(LLCoordScreen position) = 0; bool setSize(LLCoordScreen size); bool setSize(LLCoordWindow size); @@ -93,7 +93,7 @@ public: virtual bool setCursorPosition(LLCoordWindow position) = 0; virtual bool getCursorPosition(LLCoordWindow *position) = 0; #if LL_WINDOWS - virtual bool getCursorDelta(LLCoordCommon* delta) = 0; + virtual bool getCursorDelta(LLCoordCommon* delta) const = 0; #endif virtual void showCursor() = 0; virtual void hideCursor() = 0; @@ -135,14 +135,14 @@ public: virtual bool copyTextToPrimary(const LLWString &src); virtual void flashIcon(F32 seconds) = 0; - virtual F32 getGamma() = 0; + virtual F32 getGamma() const = 0; virtual bool setGamma(const F32 gamma) = 0; // Set the gamma virtual void setFSAASamples(const U32 fsaa_samples) = 0; //set number of FSAA samples - virtual U32 getFSAASamples() = 0; + virtual U32 getFSAASamples() const = 0; virtual bool restoreGamma() = 0; // Restore original gamma table (before updating gamma) - virtual ESwapMethod getSwapMethod() { return mSwapMethod; } + ESwapMethod getSwapMethod() { return mSwapMethod; } virtual void processMiscNativeEvents(); - virtual void gatherInput() = 0; + virtual void gatherInput(bool app_has_focus) = 0; virtual void delayInputProcessing() = 0; virtual void swapBuffers() = 0; virtual void bringToFront() = 0; @@ -151,12 +151,12 @@ public: // handy coordinate space conversion routines // NB: screen to window and vice verse won't work on width/height coordinate pairs, // as the conversion must take into account left AND right border widths, etc. - virtual bool convertCoords( LLCoordScreen from, LLCoordWindow *to) = 0; - virtual bool convertCoords( LLCoordWindow from, LLCoordScreen *to) = 0; - virtual bool convertCoords( LLCoordWindow from, LLCoordGL *to) = 0; - virtual bool convertCoords( LLCoordGL from, LLCoordWindow *to) = 0; - virtual bool convertCoords( LLCoordScreen from, LLCoordGL *to) = 0; - virtual bool convertCoords( LLCoordGL from, LLCoordScreen *to) = 0; + virtual bool convertCoords( LLCoordScreen from, LLCoordWindow *to) const = 0; + virtual bool convertCoords( LLCoordWindow from, LLCoordScreen *to) const = 0; + virtual bool convertCoords( LLCoordWindow from, LLCoordGL *to) const = 0; + virtual bool convertCoords( LLCoordGL from, LLCoordWindow *to) const = 0; + virtual bool convertCoords( LLCoordScreen from, LLCoordGL *to) const = 0; + virtual bool convertCoords( LLCoordGL from, LLCoordScreen *to) const = 0; // query supported resolutions virtual LLWindowResolution* getSupportedResolutions(S32 &num_resolutions) = 0; @@ -189,7 +189,7 @@ public: static std::vector getDynamicFallbackFontList(); // Provide native key event data - virtual LLSD getNativeKeyData() { return LLSD::emptyMap(); } + virtual LLSD getNativeKeyData() const { return LLSD::emptyMap(); } // Get system UI size based on DPI (for 96 DPI UI size should be 1.0) virtual F32 getSystemUISize() { return 1.0; } @@ -206,7 +206,7 @@ public: return false; }; - virtual S32 getRefreshRate() { return mRefreshRate; } + virtual S32 getRefreshRate() const { return mRefreshRate; } protected: LLWindow(LLWindowCallbacks* callbacks, bool fullscreen, U32 flags); virtual ~LLWindow(); @@ -328,4 +328,3 @@ extern const S32 gURLProtocolWhitelistCount; extern const std::string gURLProtocolWhitelist[]; //extern const std::string gURLProtocolWhitelistHandler[]; -#endif // _LL_window_h_ diff --git a/indra/llwindow/llwindowheadless.h b/indra/llwindow/llwindowheadless.h index 5696b69a59..96654b8838 100644 --- a/indra/llwindow/llwindowheadless.h +++ b/indra/llwindow/llwindowheadless.h @@ -32,77 +32,70 @@ class LLWindowHeadless : public LLWindow { public: - /*virtual*/ void show() override {} - /*virtual*/ void hide() override {} - /*virtual*/ void close() override {} - /*virtual*/ bool getVisible() override {return false;} - /*virtual*/ bool getMinimized() override {return false;} - /*virtual*/ bool getMaximized() override {return false;} - /*virtual*/ bool maximize() override {return false;} - /*virtual*/ void minimize() override {} - /*virtual*/ void restore() override {} - // TODO: LLWindow::getFullscreen() is (intentionally?) NOT virtual. - // Apparently the coder of LLWindowHeadless didn't realize that. Is it a - // mistake to shadow the base-class method with an LLWindowHeadless - // override when called on the subclass, yet call the base-class method - // when indirecting through a polymorphic pointer or reference? - bool getFullscreen() {return false;} - /*virtual*/ bool getPosition(LLCoordScreen *position) override {return false;} - /*virtual*/ bool getSize(LLCoordScreen *size) override {return false;} - /*virtual*/ bool getSize(LLCoordWindow *size) override {return false;} - /*virtual*/ bool setPosition(LLCoordScreen position) override {return false;} - /*virtual*/ bool setSizeImpl(LLCoordScreen size) override {return false;} - /*virtual*/ bool setSizeImpl(LLCoordWindow size) override {return false;} - /*virtual*/ bool switchContext(bool fullscreen, const LLCoordScreen &size, bool enable_vsync, const LLCoordScreen * const posp = NULL) override {return false;} + void show() override {} + void hide() override {} + void close() override {} + bool getVisible() const override {return false;} + bool getMinimized() const override {return false;} + bool getMaximized() const override {return false;} + bool maximize() override {return false;} + void minimize() override {} + void restore() override {} + bool getFullscreen() const override {return false;}; + bool getPosition(LLCoordScreen *position) const override {return false;} + bool getSize(LLCoordScreen *size) const override {return false;} + bool getSize(LLCoordWindow *size) const override {return false;} + bool setPosition(LLCoordScreen position) override {return false;} + bool setSizeImpl(LLCoordScreen size) override {return false;} + bool setSizeImpl(LLCoordWindow size) override {return false;} + bool switchContext(bool fullscreen, const LLCoordScreen &size, bool enable_vsync, const LLCoordScreen * const posp = NULL) override {return false;} void* createSharedContext() override { return nullptr; } void makeContextCurrent(void*) override {} void destroySharedContext(void*) override {} - /*virtual*/ void toggleVSync(bool enable_vsync) override { } - /*virtual*/ bool setCursorPosition(LLCoordWindow position) override {return false;} - /*virtual*/ bool getCursorPosition(LLCoordWindow *position) override {return false;} + void toggleVSync(bool enable_vsync) override { } + bool setCursorPosition(LLCoordWindow position) override {return false;} + bool getCursorPosition(LLCoordWindow *position) override {return false;} #if LL_WINDOWS - /*virtual*/ bool getCursorDelta(LLCoordCommon* delta) override { return false; } + bool getCursorDelta(LLCoordCommon* delta) const override { return false; } #endif - /*virtual*/ void showCursor() override {} - /*virtual*/ void hideCursor() override {} - /*virtual*/ void showCursorFromMouseMove() override {} - /*virtual*/ void hideCursorUntilMouseMove() override {} - /*virtual*/ bool isCursorHidden() override {return false;} - /*virtual*/ void updateCursor() override {} - //virtual ECursorType getCursor() override { return mCurrentCursor; } - /*virtual*/ void captureMouse() override {} - /*virtual*/ void releaseMouse() override {} - /*virtual*/ void setMouseClipping( bool b ) override {} - /*virtual*/ bool isClipboardTextAvailable() override {return false; } - /*virtual*/ bool pasteTextFromClipboard(LLWString &dst) override {return false; } - /*virtual*/ bool copyTextToClipboard(const LLWString &src) override {return false; } - /*virtual*/ void flashIcon(F32 seconds) override {} - /*virtual*/ F32 getGamma() override {return 1.0f; } - /*virtual*/ bool setGamma(const F32 gamma) override {return false; } // Set the gamma - /*virtual*/ void setFSAASamples(const U32 fsaa_samples) override { } - /*virtual*/ U32 getFSAASamples() override { return 0; } - /*virtual*/ bool restoreGamma() override {return false; } // Restore original gamma table (before updating gamma) - //virtual ESwapMethod getSwapMethod() override { return mSwapMethod; } - /*virtual*/ void gatherInput() override {} - /*virtual*/ void delayInputProcessing() override {} - /*virtual*/ void swapBuffers() override; + void showCursor() override {} + void hideCursor() override {} + void showCursorFromMouseMove() override {} + void hideCursorUntilMouseMove() override {} + bool isCursorHidden() override {return false;} + void updateCursor() override {} + void captureMouse() override {} + void releaseMouse() override {} + void setMouseClipping( bool b ) override {} + bool isClipboardTextAvailable() override {return false; } + bool pasteTextFromClipboard(LLWString &dst) override {return false; } + bool copyTextToClipboard(const LLWString &src) override {return false; } + void flashIcon(F32 seconds) override {} + F32 getGamma() const override {return 1.0f; } + bool setGamma(const F32 gamma) override {return false; } // Set the gamma + void setFSAASamples(const U32 fsaa_samples) override { } + U32 getFSAASamples() const override { return 0; } + bool restoreGamma() override {return false; } // Restore original gamma table (before updating gamma) + void gatherInput(bool app_has_focus) override {} + void delayInputProcessing() override {} + void swapBuffers() override; // handy coordinate space conversion routines - /*virtual*/ bool convertCoords(LLCoordScreen from, LLCoordWindow *to) override { return false; } - /*virtual*/ bool convertCoords(LLCoordWindow from, LLCoordScreen *to) override { return false; } - /*virtual*/ bool convertCoords(LLCoordWindow from, LLCoordGL *to) override { return false; } - /*virtual*/ bool convertCoords(LLCoordGL from, LLCoordWindow *to) override { return false; } - /*virtual*/ bool convertCoords(LLCoordScreen from, LLCoordGL *to) override { return false; } - /*virtual*/ bool convertCoords(LLCoordGL from, LLCoordScreen *to) override { return false; } + bool convertCoords(LLCoordScreen from, LLCoordWindow *to) const override { return false; } + bool convertCoords(LLCoordWindow from, LLCoordScreen *to) const override { return false; } + bool convertCoords(LLCoordWindow from, LLCoordGL *to) const override { return false; } + bool convertCoords(LLCoordGL from, LLCoordWindow *to) const override { return false; } + bool convertCoords(LLCoordScreen from, LLCoordGL *to) const override { return false; } + bool convertCoords(LLCoordGL from, LLCoordScreen *to) const override { return false; } - /*virtual*/ LLWindowResolution* getSupportedResolutions(S32 &num_resolutions) override { return NULL; } - /*virtual*/ F32 getNativeAspectRatio() override { return 1.0f; } - /*virtual*/ F32 getPixelAspectRatio() override { return 1.0f; } - /*virtual*/ void setNativeAspectRatio(F32 ratio) override {} + LLWindowResolution* getSupportedResolutions(S32 &num_resolutions) override { return NULL; } + F32 getNativeAspectRatio() override { return 1.0f; } + F32 getPixelAspectRatio() override { return 1.0f; } + void setNativeAspectRatio(F32 ratio) override {} - /*virtual*/ void *getPlatformWindow() override { return 0; } - /*virtual*/ void bringToFront() override {} + void *getPlatformWindow() override { return 0; } + void bringToFront() override {} LLWindowHeadless(LLWindowCallbacks* callbacks, const std::string& title, const std::string& name, diff --git a/indra/llwindow/llwindowmacosx.cpp b/indra/llwindow/llwindowmacosx.cpp index e95ad4d970..1883c6c9c1 100644 --- a/indra/llwindow/llwindowmacosx.cpp +++ b/indra/llwindow/llwindowmacosx.cpp @@ -28,6 +28,7 @@ #include "llwindowmacosx.h" +#include "llgamecontrol.h" #include "llkeyboardmacosx.h" #include "llwindowcallbacks.h" #include "llpreeditor.h" @@ -625,7 +626,7 @@ void LLWindowMacOSX::updateMouseDeltas(float* deltas) } } -void LLWindowMacOSX::getMouseDeltas(float* delta) +void LLWindowMacOSX::getMouseDeltas(float* delta) const { delta[0] = mCursorLastEventDeltaX; delta[1] = mCursorLastEventDeltaY; @@ -845,7 +846,7 @@ bool LLWindowMacOSX::isValid() return (mWindow != NULL); } -bool LLWindowMacOSX::getVisible() +bool LLWindowMacOSX::getVisible() const { bool result = false; @@ -860,12 +861,12 @@ bool LLWindowMacOSX::getVisible() return(result); } -bool LLWindowMacOSX::getMinimized() +bool LLWindowMacOSX::getMinimized() const { return mMinimized; } -bool LLWindowMacOSX::getMaximized() +bool LLWindowMacOSX::getMaximized() const { return mMaximized; } @@ -879,17 +880,13 @@ bool LLWindowMacOSX::maximize() return mMaximized; } -bool LLWindowMacOSX::getFullscreen() -{ - return mFullscreen; -} - -void LLWindowMacOSX::gatherInput() +void LLWindowMacOSX::gatherInput(bool app_has_focus) { updateCursor(); + LLGameControl::processEvents(app_has_focus); } -bool LLWindowMacOSX::getPosition(LLCoordScreen *position) +bool LLWindowMacOSX::getPosition(LLCoordScreen *position) const { S32 err = -1; @@ -916,7 +913,7 @@ bool LLWindowMacOSX::getPosition(LLCoordScreen *position) return (err == noErr); } -bool LLWindowMacOSX::getSize(LLCoordScreen *size) +bool LLWindowMacOSX::getSize(LLCoordScreen *size) const { S32 err = -1; @@ -942,7 +939,7 @@ bool LLWindowMacOSX::getSize(LLCoordScreen *size) return (err == noErr); } -bool LLWindowMacOSX::getSize(LLCoordWindow *size) +bool LLWindowMacOSX::getSize(LLCoordWindow *size) const { S32 err = -1; @@ -1016,7 +1013,7 @@ void LLWindowMacOSX::restoreGLContext() CGLSetCurrentContext(mContext); } -F32 LLWindowMacOSX::getGamma() +F32 LLWindowMacOSX::getGamma() const { F32 result = 2.2; // Default to something sane @@ -1050,7 +1047,7 @@ F32 LLWindowMacOSX::getGamma() return result; } -U32 LLWindowMacOSX::getFSAASamples() +U32 LLWindowMacOSX::getFSAASamples() const { return mFSAASamples; } @@ -1376,21 +1373,21 @@ LLWindow::LLWindowResolution* LLWindowMacOSX::getSupportedResolutions(S32 &num_r return mSupportedResolutions; } -bool LLWindowMacOSX::convertCoords(LLCoordGL from, LLCoordWindow *to) +bool LLWindowMacOSX::convertCoords(LLCoordGL from, LLCoordWindow *to) const { to->mX = from.mX; to->mY = from.mY; return true; } -bool LLWindowMacOSX::convertCoords(LLCoordWindow from, LLCoordGL* to) +bool LLWindowMacOSX::convertCoords(LLCoordWindow from, LLCoordGL* to) const { to->mX = from.mX; to->mY = from.mY; return true; } -bool LLWindowMacOSX::convertCoords(LLCoordScreen from, LLCoordWindow* to) +bool LLWindowMacOSX::convertCoords(LLCoordScreen from, LLCoordWindow* to) const { if(mWindow) { @@ -1409,7 +1406,7 @@ bool LLWindowMacOSX::convertCoords(LLCoordScreen from, LLCoordWindow* to) return false; } -bool LLWindowMacOSX::convertCoords(LLCoordWindow from, LLCoordScreen *to) +bool LLWindowMacOSX::convertCoords(LLCoordWindow from, LLCoordScreen *to) const { if(mWindow) { @@ -1428,14 +1425,14 @@ bool LLWindowMacOSX::convertCoords(LLCoordWindow from, LLCoordScreen *to) return false; } -bool LLWindowMacOSX::convertCoords(LLCoordScreen from, LLCoordGL *to) +bool LLWindowMacOSX::convertCoords(LLCoordScreen from, LLCoordGL *to) const { LLCoordWindow window_coord; return(convertCoords(from, &window_coord) && convertCoords(window_coord, to)); } -bool LLWindowMacOSX::convertCoords(LLCoordGL from, LLCoordScreen *to) +bool LLWindowMacOSX::convertCoords(LLCoordGL from, LLCoordScreen *to) const { LLCoordWindow window_coord; @@ -2321,7 +2318,7 @@ bool LLWindowMacOSX::getInputDevices(U32 device_type_filter, return return_value; } -LLSD LLWindowMacOSX::getNativeKeyData() +LLSD LLWindowMacOSX::getNativeKeyData() const { LLSD result = LLSD::emptyMap(); @@ -2505,6 +2502,7 @@ void LLWindowMacOSX::interruptLanguageTextInput() commitCurrentPreedit(mGLView); } +// static std::vector LLWindowMacOSX::getDisplaysResolutionList() { std::vector resolution_list; @@ -2534,7 +2532,7 @@ std::vector LLWindowMacOSX::getDisplaysResolutionList() return resolution_list; } -//static +// static std::vector LLWindowMacOSX::getDynamicFallbackFontList() { // Fonts previously in getFontListSans() have moved to fonts.xml. diff --git a/indra/llwindow/llwindowmacosx.h b/indra/llwindow/llwindowmacosx.h index 211ae872c6..14a56a038e 100644 --- a/indra/llwindow/llwindowmacosx.h +++ b/indra/llwindow/llwindowmacosx.h @@ -47,16 +47,15 @@ public: void show() override; void hide() override; void close() override; - bool getVisible() override; - bool getMinimized() override; - bool getMaximized() override; + bool getVisible() const override; + bool getMinimized() const override; + bool getMaximized() const override; bool maximize() override; void minimize() override; void restore() override; - bool getFullscreen(); - bool getPosition(LLCoordScreen *position) override; - bool getSize(LLCoordScreen *size) override; - bool getSize(LLCoordWindow *size) override; + bool getPosition(LLCoordScreen *position) const override; + bool getSize(LLCoordScreen *size) const override; + bool getSize(LLCoordWindow *size) const override; bool setPosition(LLCoordScreen position) override; bool setSizeImpl(LLCoordScreen size) override; bool setSizeImpl(LLCoordWindow size) override; @@ -77,23 +76,22 @@ public: bool pasteTextFromClipboard(LLWString &dst) override; bool copyTextToClipboard(const LLWString & src) override; void flashIcon(F32 seconds) override; - F32 getGamma() override; + F32 getGamma() const override; bool setGamma(const F32 gamma) override; // Set the gamma - U32 getFSAASamples() override; + U32 getFSAASamples() const override; void setFSAASamples(const U32 fsaa_samples) override; bool restoreGamma() override; // Restore original gamma table (before updating gamma) - ESwapMethod getSwapMethod() override { return mSwapMethod; } - void gatherInput() override; + void gatherInput(bool app_has_focus) override; void delayInputProcessing() override {}; void swapBuffers() override; // handy coordinate space conversion routines - bool convertCoords(LLCoordScreen from, LLCoordWindow *to) override; - bool convertCoords(LLCoordWindow from, LLCoordScreen *to) override; - bool convertCoords(LLCoordWindow from, LLCoordGL *to) override; - bool convertCoords(LLCoordGL from, LLCoordWindow *to) override; - bool convertCoords(LLCoordScreen from, LLCoordGL *to) override; - bool convertCoords(LLCoordGL from, LLCoordScreen *to) override; + bool convertCoords(LLCoordScreen from, LLCoordWindow *to) const override; + bool convertCoords(LLCoordWindow from, LLCoordScreen *to) const override; + bool convertCoords(LLCoordWindow from, LLCoordGL *to) const override; + bool convertCoords(LLCoordGL from, LLCoordWindow *to) const override; + bool convertCoords(LLCoordScreen from, LLCoordGL *to) const override; + bool convertCoords(LLCoordGL from, LLCoordScreen *to) const override; LLWindowResolution* getSupportedResolutions(S32 &num_resolutions) override; F32 getNativeAspectRatio() override; @@ -125,14 +123,14 @@ public: static std::vector getDynamicFallbackFontList(); // Provide native key event data - LLSD getNativeKeyData() override; + LLSD getNativeKeyData() const override; void* getWindow() { return mWindow; } LLWindowCallbacks* getCallbacks() { return mCallbacks; } LLPreeditor* getPreeditor() { return mPreeditor; } void updateMouseDeltas(float* deltas); - void getMouseDeltas(float* delta); + void getMouseDeltas(float* delta) const; void handleDragNDrop(std::string url, LLWindowCallbacks::DragNDropAction action); diff --git a/indra/llwindow/llwindowmesaheadless.h b/indra/llwindow/llwindowmesaheadless.h index 0bf8c46a30..0a29ddfa5b 100644 --- a/indra/llwindow/llwindowmesaheadless.h +++ b/indra/llwindow/llwindowmesaheadless.h @@ -36,64 +36,64 @@ class LLWindowMesaHeadless : public LLWindow { public: - /*virtual*/ void show() {}; - /*virtual*/ void hide() {}; - /*virtual*/ void close() {}; - /*virtual*/ bool getVisible() {return false;}; - /*virtual*/ bool getMinimized() {return false;}; - /*virtual*/ bool getMaximized() {return false;}; - /*virtual*/ bool maximize() {return false;}; - /*virtual*/ void minimize() {}; - /*virtual*/ void restore() {}; - /*virtual*/ bool getFullscreen() {return false;}; - /*virtual*/ bool getPosition(LLCoordScreen *position) {return false;}; - /*virtual*/ bool getSize(LLCoordScreen *size) {return false;}; - /*virtual*/ bool getSize(LLCoordWindow *size) {return false;}; - /*virtual*/ bool setPosition(LLCoordScreen position) {return false;}; - /*virtual*/ bool setSizeImpl(LLCoordScreen size) {return false;}; - /*virtual*/ bool switchContext(bool fullscreen, const LLCoordScreen &size, bool disable_vsync, const LLCoordScreen * const posp = NULL) {return false;}; - /*virtual*/ bool setCursorPosition(LLCoordWindow position) {return false;}; - /*virtual*/ bool getCursorPosition(LLCoordWindow *position) {return false;}; - /*virtual*/ void showCursor() {}; - /*virtual*/ void hideCursor() {}; - /*virtual*/ void showCursorFromMouseMove() {}; - /*virtual*/ void hideCursorUntilMouseMove() {}; - /*virtual*/ bool isCursorHidden() {return false;}; - /*virtual*/ void updateCursor() {}; - //virtual ECursorType getCursor() { return mCurrentCursor; }; - /*virtual*/ void captureMouse() {}; - /*virtual*/ void releaseMouse() {}; - /*virtual*/ void setMouseClipping( bool b ) {}; - /*virtual*/ bool isClipboardTextAvailable() {return false; }; - /*virtual*/ bool pasteTextFromClipboard(LLWString &dst) {return false; }; - /*virtual*/ bool copyTextToClipboard(const LLWString &src) {return false; }; - /*virtual*/ void flashIcon(F32 seconds) {}; - /*virtual*/ F32 getGamma() {return 1.0f; }; - /*virtual*/ bool setGamma(const F32 gamma) {return false; }; // Set the gamma - /*virtual*/ bool restoreGamma() {return false; }; // Restore original gamma table (before updating gamma) - /*virtual*/ void setFSAASamples(const U32 fsaa_samples) { /* FSAA not supported yet on Mesa headless.*/ } - /*virtual*/ U32 getFSAASamples() { return 0; } - //virtual ESwapMethod getSwapMethod() { return mSwapMethod; } - /*virtual*/ void gatherInput() {}; - /*virtual*/ void delayInputProcessing() {}; - /*virtual*/ void swapBuffers(); - /*virtual*/ void restoreGLContext() {}; + void show() override {}; + void hide() override {}; + void close() override {}; + bool getVisible() override {return false;}; + bool getMinimized() override {return false;}; + bool getMaximized() override {return false;}; + bool maximize() override {return false;}; + void minimize() override {}; + void restore() override {}; + bool getFullscreen() override {return false;}; + bool getPosition(LLCoordScreen *position) override {return false;}; + bool getSize(LLCoordScreen *size) override {return false;}; + bool getSize(LLCoordWindow *size) override {return false;}; + bool setPosition(LLCoordScreen position) override {return false;}; + bool setSizeImpl(LLCoordScreen size) override {return false;}; + bool switchContext(bool fullscreen, const LLCoordScreen &size, bool disable_vsync, const LLCoordScreen * const posp = NULL) override {return false;}; + bool setCursorPosition(LLCoordWindow position) override {return false;}; + bool getCursorPosition(LLCoordWindow *position) override {return false;}; + void showCursor() override {}; + void hideCursor() override {}; + void showCursorFromMouseMove() override {}; + void hideCursorUntilMouseMove() override {}; + bool isCursorHidden() override {return false;}; + void updateCursor() override {}; + //ECursorType getCursor() override { return mCurrentCursor; }; + void captureMouse() override {}; + void releaseMouse() override {}; + void setMouseClipping( bool b ) override {}; + bool isClipboardTextAvailable() override {return false; }; + bool pasteTextFromClipboard(LLWString &dst) override {return false; }; + bool copyTextToClipboard(const LLWString &src) override {return false; }; + void flashIcon(F32 seconds) override {}; + F32 getGamma() override {return 1.0f; }; + bool setGamma(const F32 gamma) override {return false; }; // Set the gamma + bool restoreGamma() override {return false; }; // Restore original gamma table (before updating gamma) + void setFSAASamples(const U32 fsaa_samples) override { /* FSAA not supported yet on Mesa headless.*/ } + U32 getFSAASamples() override { return 0; } + //ESwapMethod getSwapMethod() override { return mSwapMethod; } + void gatherInput(bool app_has_focus) override {}; + void delayInputProcessing() override {}; + void swapBuffers() override; + void restoreGLContext() override {}; // handy coordinate space conversion routines - /*virtual*/ bool convertCoords(LLCoordScreen from, LLCoordWindow *to) { return false; }; - /*virtual*/ bool convertCoords(LLCoordWindow from, LLCoordScreen *to) { return false; }; - /*virtual*/ bool convertCoords(LLCoordWindow from, LLCoordGL *to) { return false; }; - /*virtual*/ bool convertCoords(LLCoordGL from, LLCoordWindow *to) { return false; }; - /*virtual*/ bool convertCoords(LLCoordScreen from, LLCoordGL *to) { return false; }; - /*virtual*/ bool convertCoords(LLCoordGL from, LLCoordScreen *to) { return false; }; + bool convertCoords(LLCoordScreen from, LLCoordWindow *to) override { return false; }; + bool convertCoords(LLCoordWindow from, LLCoordScreen *to) override { return false; }; + bool convertCoords(LLCoordWindow from, LLCoordGL *to) override { return false; }; + bool convertCoords(LLCoordGL from, LLCoordWindow *to) override { return false; }; + bool convertCoords(LLCoordScreen from, LLCoordGL *to) override { return false; }; + bool convertCoords(LLCoordGL from, LLCoordScreen *to) override { return false; }; - /*virtual*/ LLWindowResolution* getSupportedResolutions(S32 &num_resolutions) { return NULL; }; - /*virtual*/ F32 getNativeAspectRatio() { return 1.0f; }; - /*virtual*/ F32 getPixelAspectRatio() { return 1.0f; }; - /*virtual*/ void setNativeAspectRatio(F32 ratio) {} + LLWindowResolution* getSupportedResolutions(S32 &num_resolutions) override { return NULL; }; + F32 getNativeAspectRatio() override { return 1.0f; }; + F32 getPixelAspectRatio() override { return 1.0f; }; + void setNativeAspectRatio(F32 ratio) override {} - /*virtual*/ void *getPlatformWindow() { return 0; }; - /*virtual*/ void bringToFront() {}; + void *getPlatformWindow() override { return 0; }; + void bringToFront() override {}; LLWindowMesaHeadless(LLWindowCallbacks* callbacks, const std::string& title, const std::string& name, S32 x, S32 y, S32 width, S32 height, @@ -112,9 +112,9 @@ public: LLSplashScreenMesaHeadless() {}; virtual ~LLSplashScreenMesaHeadless() {}; - /*virtual*/ void showImpl() {}; - /*virtual*/ void updateImpl(const std::string& mesg) {}; - /*virtual*/ void hideImpl() {}; + void showImpl() override {}; + void updateImpl(const std::string& mesg) override {}; + void hideImpl() override {}; }; diff --git a/indra/llwindow/llwindowsdl.cpp b/indra/llwindow/llwindowsdl.cpp index ec0972eaae..770170179a 100644 --- a/indra/llwindow/llwindowsdl.cpp +++ b/indra/llwindow/llwindowsdl.cpp @@ -37,6 +37,7 @@ #include "llstring.h" #include "lldir.h" #include "llfindlocale.h" +#include "llgamecontrol.h" #ifdef LL_GLIB #include @@ -246,8 +247,10 @@ void LLWindowSDL::tryFindFullscreenSize( int &width, int &height ) bool LLWindowSDL::createContext(int x, int y, int width, int height, int bits, bool fullscreen, bool enable_vsync) { - //bool glneedsinit = false; - + if (width == 0) + width = 1024; + if (height == 0) + width = 768; LL_INFOS() << "createContext, fullscreen=" << fullscreen << " size=" << width << "x" << height << LL_ENDL; @@ -687,7 +690,7 @@ bool LLWindowSDL::isValid() return (mWindow != NULL); } -bool LLWindowSDL::getVisible() +bool LLWindowSDL::getVisible() const { bool result = false; if (mWindow) @@ -701,7 +704,7 @@ bool LLWindowSDL::getVisible() return result; } -bool LLWindowSDL::getMinimized() +bool LLWindowSDL::getMinimized() const { bool result = false; if (mWindow) @@ -715,7 +718,7 @@ bool LLWindowSDL::getMinimized() return result; } -bool LLWindowSDL::getMaximized() +bool LLWindowSDL::getMaximized() const { bool result = false; if (mWindow) @@ -740,12 +743,7 @@ bool LLWindowSDL::maximize() return FALSE; } -bool LLWindowSDL::getFullscreen() -{ - return mFullscreen; -} - -bool LLWindowSDL::getPosition(LLCoordScreen *position) +bool LLWindowSDL::getPosition(LLCoordScreen *position) const { if (mWindow) { @@ -755,7 +753,7 @@ bool LLWindowSDL::getPosition(LLCoordScreen *position) return false; } -bool LLWindowSDL::getSize(LLCoordScreen *size) +bool LLWindowSDL::getSize(LLCoordScreen *size) const { if (mSurface) { @@ -767,7 +765,7 @@ bool LLWindowSDL::getSize(LLCoordScreen *size) return (false); } -bool LLWindowSDL::getSize(LLCoordWindow *size) +bool LLWindowSDL::getSize(LLCoordWindow *size) const { if (mSurface) { @@ -833,7 +831,7 @@ void LLWindowSDL::swapBuffers() LL_PROFILER_GPU_COLLECT; } -U32 LLWindowSDL::getFSAASamples() +U32 LLWindowSDL::getFSAASamples() const { return mFSAASamples; } @@ -843,7 +841,7 @@ void LLWindowSDL::setFSAASamples(const U32 samples) mFSAASamples = samples; } -F32 LLWindowSDL::getGamma() +F32 LLWindowSDL::getGamma() const { return 1.f / mGamma; } @@ -1142,7 +1140,7 @@ LLWindow::LLWindowResolution* LLWindowSDL::getSupportedResolutions(S32 &num_reso return mSupportedResolutions; } -bool LLWindowSDL::convertCoords(LLCoordGL from, LLCoordWindow *to) +bool LLWindowSDL::convertCoords(LLCoordGL from, LLCoordWindow *to) const { if (!to) return false; @@ -1153,7 +1151,7 @@ bool LLWindowSDL::convertCoords(LLCoordGL from, LLCoordWindow *to) return true; } -bool LLWindowSDL::convertCoords(LLCoordWindow from, LLCoordGL* to) +bool LLWindowSDL::convertCoords(LLCoordWindow from, LLCoordGL* to) const { if (!to) return false; @@ -1164,7 +1162,7 @@ bool LLWindowSDL::convertCoords(LLCoordWindow from, LLCoordGL* to) return true; } -bool LLWindowSDL::convertCoords(LLCoordScreen from, LLCoordWindow* to) +bool LLWindowSDL::convertCoords(LLCoordScreen from, LLCoordWindow* to) const { if (!to) return false; @@ -1175,7 +1173,7 @@ bool LLWindowSDL::convertCoords(LLCoordScreen from, LLCoordWindow* to) return (true); } -bool LLWindowSDL::convertCoords(LLCoordWindow from, LLCoordScreen *to) +bool LLWindowSDL::convertCoords(LLCoordWindow from, LLCoordScreen *to) const { if (!to) return false; @@ -1186,14 +1184,14 @@ bool LLWindowSDL::convertCoords(LLCoordWindow from, LLCoordScreen *to) return (true); } -bool LLWindowSDL::convertCoords(LLCoordScreen from, LLCoordGL *to) +bool LLWindowSDL::convertCoords(LLCoordScreen from, LLCoordGL *to) const { LLCoordWindow window_coord; return(convertCoords(from, &window_coord) && convertCoords(window_coord, to)); } -bool LLWindowSDL::convertCoords(LLCoordGL from, LLCoordScreen *to) +bool LLWindowSDL::convertCoords(LLCoordGL from, LLCoordScreen *to) const { LLCoordWindow window_coord; @@ -1415,7 +1413,7 @@ void LLWindowSDL::processMiscNativeEvents() } } -void LLWindowSDL::gatherInput() +void LLWindowSDL::gatherInput(bool app_has_focus) { SDL_Event event; @@ -1618,7 +1616,7 @@ void LLWindowSDL::gatherInput() } break; default: - //LL_INFOS() << "Unhandled SDL event type " << event.type << LL_ENDL; + LLGameControl::handleEvent(event, app_has_focus); break; } } @@ -1942,7 +1940,7 @@ bool LLWindowSDL::dialogColorPicker( F32 *r, F32 *g, F32 *b) Make the raw keyboard data available - used to poke through to LLQtWebKit so that Qt/Webkit has access to the virtual keycodes etc. that it needs */ -LLSD LLWindowSDL::getNativeKeyData() +LLSD LLWindowSDL::getNativeKeyData() const { LLSD result = LLSD::emptyMap(); diff --git a/indra/llwindow/llwindowsdl.h b/indra/llwindow/llwindowsdl.h index 609d8a6f49..a85b7c11e7 100644 --- a/indra/llwindow/llwindowsdl.h +++ b/indra/llwindow/llwindowsdl.h @@ -55,11 +55,11 @@ public: void close() override; - bool getVisible() override; + bool getVisible() const override; - bool getMinimized() override; + bool getMinimized() const override; - bool getMaximized() override; + bool getMaximized() const override; bool maximize() override; @@ -67,13 +67,11 @@ public: void restore() override; - bool getFullscreen(); + bool getPosition(LLCoordScreen *position) const override; - bool getPosition(LLCoordScreen *position) override; + bool getSize(LLCoordScreen *size) const override; - bool getSize(LLCoordScreen *size) override; - - bool getSize(LLCoordWindow *size) override; + bool getSize(LLCoordWindow *size) const override; bool setPosition(LLCoordScreen position) override; @@ -122,19 +120,19 @@ public: void flashIcon(F32 seconds) override; - F32 getGamma() override; + F32 getGamma() const override; bool setGamma(const F32 gamma) override; // Set the gamma - U32 getFSAASamples() override; + + U32 getFSAASamples() const override; void setFSAASamples(const U32 samples) override; bool restoreGamma() override; // Restore original gamma table (before updating gamma) - ESwapMethod getSwapMethod() override { return mSwapMethod; } void processMiscNativeEvents() override; - void gatherInput() override; + void gatherInput(bool app_has_focus) override; void swapBuffers() override; @@ -143,17 +141,17 @@ public: void delayInputProcessing() override {}; // handy coordinate space conversion routines - bool convertCoords(LLCoordScreen from, LLCoordWindow *to) override; + bool convertCoords(LLCoordScreen from, LLCoordWindow *to) const override; - bool convertCoords(LLCoordWindow from, LLCoordScreen *to) override; + bool convertCoords(LLCoordWindow from, LLCoordScreen *to) const override; - bool convertCoords(LLCoordWindow from, LLCoordGL *to) override; + bool convertCoords(LLCoordWindow from, LLCoordGL *to) const override; - bool convertCoords(LLCoordGL from, LLCoordWindow *to) override; + bool convertCoords(LLCoordGL from, LLCoordWindow *to) const override; - bool convertCoords(LLCoordScreen from, LLCoordGL *to) override; + bool convertCoords(LLCoordScreen from, LLCoordGL *to) const override; - bool convertCoords(LLCoordGL from, LLCoordScreen *to) override; + bool convertCoords(LLCoordGL from, LLCoordScreen *to) const override; LLWindowResolution *getSupportedResolutions(S32 &num_resolutions) override; @@ -218,7 +216,7 @@ protected: bool isValid() override; - LLSD getNativeKeyData() override; + LLSD getNativeKeyData() const override; void initCursors(); diff --git a/indra/llwindow/llwindowwin32.cpp b/indra/llwindow/llwindowwin32.cpp index b19fa13b41..f8294f063f 100644 --- a/indra/llwindow/llwindowwin32.cpp +++ b/indra/llwindow/llwindowwin32.cpp @@ -31,6 +31,7 @@ #include "llwindowwin32.h" // LLWindow library includes +#include "llgamecontrol.h" #include "llkeyboardwin32.h" #include "lldragdropwin32.h" #include "llpreeditor.h" @@ -982,17 +983,17 @@ bool LLWindowWin32::isValid() return (mWindowHandle != NULL); } -bool LLWindowWin32::getVisible() +bool LLWindowWin32::getVisible() const { return (mWindowHandle && IsWindowVisible(mWindowHandle)); } -bool LLWindowWin32::getMinimized() +bool LLWindowWin32::getMinimized() const { return (mWindowHandle && IsIconic(mWindowHandle)); } -bool LLWindowWin32::getMaximized() +bool LLWindowWin32::getMaximized() const { return (mWindowHandle && IsZoomed(mWindowHandle)); } @@ -1017,26 +1018,21 @@ bool LLWindowWin32::maximize() return true; } -bool LLWindowWin32::getFullscreen() -{ - return mFullscreen; -} - -bool LLWindowWin32::getPosition(LLCoordScreen *position) +bool LLWindowWin32::getPosition(LLCoordScreen *position) const { position->mX = mRect.left; position->mY = mRect.top; return true; } -bool LLWindowWin32::getSize(LLCoordScreen *size) +bool LLWindowWin32::getSize(LLCoordScreen *size) const { size->mX = mRect.right - mRect.left; size->mY = mRect.bottom - mRect.top; return true; } -bool LLWindowWin32::getSize(LLCoordWindow *size) +bool LLWindowWin32::getSize(LLCoordWindow *size) const { size->mX = mClientRect.right - mClientRect.left; size->mY = mClientRect.bottom - mClientRect.top; @@ -1974,7 +1970,7 @@ bool LLWindowWin32::getCursorPosition(LLCoordWindow *position) return true; } -bool LLWindowWin32::getCursorDelta(LLCoordCommon* delta) +bool LLWindowWin32::getCursorDelta(LLCoordCommon* delta) const { if (delta == nullptr) { @@ -2162,7 +2158,7 @@ void LLWindowWin32::delayInputProcessing() } -void LLWindowWin32::gatherInput() +void LLWindowWin32::gatherInput(bool app_has_focus) { ASSERT_MAIN_THREAD(); LL_PROFILE_ZONE_SCOPED_CATEGORY_WIN32; @@ -2242,6 +2238,8 @@ void LLWindowWin32::gatherInput() mInputProcessingPaused = false; updateCursor(); + + LLGameControl::processEvents(app_has_focus); } static LLTrace::BlockTimerStatHandle FTM_KEYHANDLER("Handle Keyboard"); @@ -3112,7 +3110,7 @@ LRESULT CALLBACK LLWindowWin32::mainWindowProc(HWND h_wnd, UINT u_msg, WPARAM w_ return ret; } -bool LLWindowWin32::convertCoords(LLCoordGL from, LLCoordWindow *to) +bool LLWindowWin32::convertCoords(LLCoordGL from, LLCoordWindow *to) const { S32 client_height; RECT client_rect; @@ -3132,7 +3130,7 @@ bool LLWindowWin32::convertCoords(LLCoordGL from, LLCoordWindow *to) return true; } -bool LLWindowWin32::convertCoords(LLCoordWindow from, LLCoordGL* to) +bool LLWindowWin32::convertCoords(LLCoordWindow from, LLCoordGL* to) const { S32 client_height; RECT client_rect; @@ -3151,7 +3149,7 @@ bool LLWindowWin32::convertCoords(LLCoordWindow from, LLCoordGL* to) return true; } -bool LLWindowWin32::convertCoords(LLCoordScreen from, LLCoordWindow* to) +bool LLWindowWin32::convertCoords(LLCoordScreen from, LLCoordWindow* to) const { POINT mouse_point; @@ -3168,7 +3166,7 @@ bool LLWindowWin32::convertCoords(LLCoordScreen from, LLCoordWindow* to) return result; } -bool LLWindowWin32::convertCoords(LLCoordWindow from, LLCoordScreen *to) +bool LLWindowWin32::convertCoords(LLCoordWindow from, LLCoordScreen *to) const { POINT mouse_point; @@ -3185,7 +3183,7 @@ bool LLWindowWin32::convertCoords(LLCoordWindow from, LLCoordScreen *to) return result; } -bool LLWindowWin32::convertCoords(LLCoordScreen from, LLCoordGL *to) +bool LLWindowWin32::convertCoords(LLCoordScreen from, LLCoordGL *to) const { LLCoordWindow window_coord; @@ -3199,7 +3197,7 @@ bool LLWindowWin32::convertCoords(LLCoordScreen from, LLCoordGL *to) return true; } -bool LLWindowWin32::convertCoords(LLCoordGL from, LLCoordScreen *to) +bool LLWindowWin32::convertCoords(LLCoordGL from, LLCoordScreen *to) const { LLCoordWindow window_coord; @@ -3318,7 +3316,7 @@ void LLWindowWin32::setMouseClipping( bool b ) } } -bool LLWindowWin32::getClientRectInScreenSpace( RECT* rectp ) +bool LLWindowWin32::getClientRectInScreenSpace( RECT* rectp ) const { bool success = false; @@ -3362,7 +3360,7 @@ void LLWindowWin32::flashIcon(F32 seconds) }); } -F32 LLWindowWin32::getGamma() +F32 LLWindowWin32::getGamma() const { return mCurrentGamma; } @@ -3424,7 +3422,7 @@ void LLWindowWin32::setFSAASamples(const U32 fsaa_samples) mFSAASamples = fsaa_samples; } -U32 LLWindowWin32::getFSAASamples() +U32 LLWindowWin32::getFSAASamples() const { return mFSAASamples; } @@ -3787,7 +3785,7 @@ void LLWindowWin32::openFolder(const std::string &path) Make the raw keyboard data available - used to poke through to LLQtWebKit so that Qt/Webkit has access to the virtual keycodes etc. that it needs */ -LLSD LLWindowWin32::getNativeKeyData() +LLSD LLWindowWin32::getNativeKeyData() const { LLSD result = LLSD::emptyMap(); diff --git a/indra/llwindow/llwindowwin32.h b/indra/llwindow/llwindowwin32.h index e38cfe7ebc..f4964d064e 100644 --- a/indra/llwindow/llwindowwin32.h +++ b/indra/llwindow/llwindowwin32.h @@ -45,84 +45,82 @@ typedef void (*LLW32MsgCallback)(const MSG &msg); class LLWindowWin32 : public LLWindow { public: - /*virtual*/ void show(); - /*virtual*/ void hide(); - /*virtual*/ void close(); - /*virtual*/ bool getVisible(); - /*virtual*/ bool getMinimized(); - /*virtual*/ bool getMaximized(); - /*virtual*/ bool maximize(); - /*virtual*/ void minimize(); - /*virtual*/ void restore(); - /*virtual*/ bool getFullscreen(); - /*virtual*/ bool getPosition(LLCoordScreen *position); - /*virtual*/ bool getSize(LLCoordScreen *size); - /*virtual*/ bool getSize(LLCoordWindow *size); - /*virtual*/ bool setPosition(LLCoordScreen position); - /*virtual*/ bool setSizeImpl(LLCoordScreen size); - /*virtual*/ bool setSizeImpl(LLCoordWindow size); - /*virtual*/ bool switchContext(bool fullscreen, const LLCoordScreen &size, bool enable_vsync, const LLCoordScreen * const posp = NULL); - /*virtual*/ void setTitle(const std::string title); + void show() override; + void hide() override; + void close() override; + bool getVisible() const override; + bool getMinimized() const override; + bool getMaximized() const override; + bool maximize() override; + void minimize() override; + void restore() override; + bool getPosition(LLCoordScreen *position) const override; + bool getSize(LLCoordScreen *size) const override; + bool getSize(LLCoordWindow *size) const override; + bool setPosition(LLCoordScreen position) override; + bool setSizeImpl(LLCoordScreen size) override; + bool setSizeImpl(LLCoordWindow size) override; + bool switchContext(bool fullscreen, const LLCoordScreen &size, bool enable_vsync, const LLCoordScreen * const posp = NULL) override; + void setTitle(const std::string title) override; void* createSharedContext() override; void makeContextCurrent(void* context) override; void destroySharedContext(void* context) override; - /*virtual*/ void toggleVSync(bool enable_vsync); - /*virtual*/ bool setCursorPosition(LLCoordWindow position); - /*virtual*/ bool getCursorPosition(LLCoordWindow *position); - /*virtual*/ bool getCursorDelta(LLCoordCommon* delta); - /*virtual*/ void showCursor(); - /*virtual*/ void hideCursor(); - /*virtual*/ void showCursorFromMouseMove(); - /*virtual*/ void hideCursorUntilMouseMove(); - /*virtual*/ bool isCursorHidden(); - /*virtual*/ void updateCursor(); - /*virtual*/ ECursorType getCursor() const; - /*virtual*/ void captureMouse(); - /*virtual*/ void releaseMouse(); - /*virtual*/ void setMouseClipping( bool b ); - /*virtual*/ bool isClipboardTextAvailable(); - /*virtual*/ bool pasteTextFromClipboard(LLWString &dst); - /*virtual*/ bool copyTextToClipboard(const LLWString &src); - /*virtual*/ void flashIcon(F32 seconds); - /*virtual*/ F32 getGamma(); - /*virtual*/ bool setGamma(const F32 gamma); // Set the gamma - /*virtual*/ void setFSAASamples(const U32 fsaa_samples); - /*virtual*/ U32 getFSAASamples(); - /*virtual*/ bool restoreGamma(); // Restore original gamma table (before updating gamma) - /*virtual*/ ESwapMethod getSwapMethod() { return mSwapMethod; } - /*virtual*/ void gatherInput(); - /*virtual*/ void delayInputProcessing(); - /*virtual*/ void swapBuffers(); - /*virtual*/ void restoreGLContext() {}; + void toggleVSync(bool enable_vsync) override; + bool setCursorPosition(LLCoordWindow position) override; + bool getCursorPosition(LLCoordWindow *position) override; + bool getCursorDelta(LLCoordCommon* delta) const override; + void showCursor() override; + void hideCursor() override; + void showCursorFromMouseMove() override; + void hideCursorUntilMouseMove() override; + bool isCursorHidden() override; + void updateCursor() override; + ECursorType getCursor() const override; + void captureMouse() override; + void releaseMouse() override; + void setMouseClipping( bool b ) override; + bool isClipboardTextAvailable() override; + bool pasteTextFromClipboard(LLWString &dst) override; + bool copyTextToClipboard(const LLWString &src) override; + void flashIcon(F32 seconds) override; + F32 getGamma() const override; + bool setGamma(const F32 gamma) override; // Set the gamma + void setFSAASamples(const U32 fsaa_samples) override; + U32 getFSAASamples() const override; + bool restoreGamma() override; // Restore original gamma table (before updating gamma) + void gatherInput(bool app_has_focus) override; + void delayInputProcessing() override; + void swapBuffers() override; + void restoreGLContext() {}; // handy coordinate space conversion routines - /*virtual*/ bool convertCoords(LLCoordScreen from, LLCoordWindow *to); - /*virtual*/ bool convertCoords(LLCoordWindow from, LLCoordScreen *to); - /*virtual*/ bool convertCoords(LLCoordWindow from, LLCoordGL *to); - /*virtual*/ bool convertCoords(LLCoordGL from, LLCoordWindow *to); - /*virtual*/ bool convertCoords(LLCoordScreen from, LLCoordGL *to); - /*virtual*/ bool convertCoords(LLCoordGL from, LLCoordScreen *to); - - /*virtual*/ LLWindowResolution* getSupportedResolutions(S32 &num_resolutions); - /*virtual*/ F32 getNativeAspectRatio(); - /*virtual*/ F32 getPixelAspectRatio(); - /*virtual*/ void setNativeAspectRatio(F32 ratio) { mOverrideAspectRatio = ratio; } - - /*virtual*/ bool dialogColorPicker(F32 *r, F32 *g, F32 *b ); - - /*virtual*/ void *getPlatformWindow(); - /*virtual*/ void bringToFront(); - /*virtual*/ void focusClient(); - - /*virtual*/ void allowLanguageTextInput(LLPreeditor *preeditor, bool b); - /*virtual*/ void setLanguageTextInput( const LLCoordGL & pos ); - /*virtual*/ void updateLanguageTextInputArea(); - /*virtual*/ void interruptLanguageTextInput(); - /*virtual*/ void spawnWebBrowser(const std::string& escaped_url, bool async); + bool convertCoords(LLCoordScreen from, LLCoordWindow *to) const override; + bool convertCoords(LLCoordWindow from, LLCoordScreen *to) const override; + bool convertCoords(LLCoordWindow from, LLCoordGL *to) const override; + bool convertCoords(LLCoordGL from, LLCoordWindow *to) const override; + bool convertCoords(LLCoordScreen from, LLCoordGL *to) const override; + bool convertCoords(LLCoordGL from, LLCoordScreen *to) const override; + + LLWindowResolution* getSupportedResolutions(S32 &num_resolutions) override; + F32 getNativeAspectRatio() override; + F32 getPixelAspectRatio() override; + void setNativeAspectRatio(F32 ratio) override { mOverrideAspectRatio = ratio; } + + bool dialogColorPicker(F32 *r, F32 *g, F32 *b ) override; + + void *getPlatformWindow() override; + void bringToFront() override; + void focusClient() override; + + void allowLanguageTextInput(LLPreeditor *preeditor, bool b) override; + void setLanguageTextInput( const LLCoordGL & pos ) override; + void updateLanguageTextInputArea() override; + void interruptLanguageTextInput() override; + void spawnWebBrowser(const std::string& escaped_url, bool async) override; void openFolder(const std::string &path) override; - /*virtual*/ F32 getSystemUISize(); + F32 getSystemUISize() override; LLWindowCallbacks::DragNDropResult completeDragNDropRequest( const LLCoordGL gl_coord, const MASK mask, LLWindowCallbacks::DragNDropAction action, const std::string url ); @@ -130,13 +128,13 @@ public: static std::vector getDynamicFallbackFontList(); static void setDPIAwareness(); - /*virtual*/ void* getDirectInput8(); - /*virtual*/ bool getInputDevices(U32 device_type_filter, + void* getDirectInput8() override; + bool getInputDevices(U32 device_type_filter, std::function osx_callback, void* win_callback, - void* userdata); + void* userdata) override; - U32 getRawWParam() { return mRawWParam; } + U32 getRawWParam() const { return mRawWParam; } protected: LLWindowWin32(LLWindowCallbacks* callbacks, @@ -149,7 +147,7 @@ protected: HCURSOR loadColorCursor(LPCTSTR name); bool isValid(); void moveWindow(const LLCoordScreen& position,const LLCoordScreen& size); - virtual LLSD getNativeKeyData(); + LLSD getNativeKeyData() const override; // Changes display resolution. Returns true if successful bool setDisplayResolution(S32 width, S32 height, S32 bits, S32 refresh); @@ -175,7 +173,7 @@ protected: // Platform specific methods // - bool getClientRectInScreenSpace(RECT* rectp); + bool getClientRectInScreenSpace(RECT* rectp) const; static LRESULT CALLBACK mainWindowProc(HWND h_wnd, UINT u_msg, WPARAM w_param, LPARAM l_param); @@ -266,9 +264,9 @@ public: LLSplashScreenWin32(); virtual ~LLSplashScreenWin32(); - /*virtual*/ void showImpl(); - /*virtual*/ void updateImpl(const std::string& mesg); - /*virtual*/ void hideImpl(); + void showImpl() override; + void updateImpl(const std::string& mesg) override; + void hideImpl() override; #if LL_WINDOWS static LRESULT CALLBACK windowProc(HWND h_wnd, UINT u_msg, diff --git a/indra/newview/llagent.h b/indra/newview/llagent.h index a69a777e1e..448ee575f5 100644 --- a/indra/newview/llagent.h +++ b/indra/newview/llagent.h @@ -498,7 +498,7 @@ private: // mControlFlags is a bitmask of behavior instructions for compact // transmission to the server. It does NOT represent "input", rather // the consequences of it, which will sometimes depend on "state". - U32 mControlFlags; // Replacement for the mFooKey's + U32 mControlFlags; //-------------------------------------------------------------------- // GameControls diff --git a/indra/newview/llappviewer.cpp b/indra/newview/llappviewer.cpp index 475610be36..75cd189980 100644 --- a/indra/newview/llappviewer.cpp +++ b/indra/newview/llappviewer.cpp @@ -1531,7 +1531,7 @@ bool LLAppViewer::doFrame() LL_WARNS() << " Someone took over my signal/exception handler (post messagehandling)!" << LL_ENDL; } - gViewerWindow->getWindow()->gatherInput(); + gViewerWindow->getWindow()->gatherInput(gFocusMgr.getAppHasFocus()); } //memory leaking simulation @@ -4811,11 +4811,6 @@ void LLAppViewer::idle() gAgent.autoPilot(&yaw); } - // Some GameControl logic needs to run even when the feature is not enabled - // Note: we process game_control before sending AgentUpdate - // because it may translate to control flags that control avatar motion. - LLGameControl::processEvents(gFocusMgr.getAppHasFocus()); - // get control flags from each side U32 control_flags = gAgent.getControlFlags(); U32 game_control_action_flags = LLGameControl::computeInternalActionFlags(); @@ -4841,6 +4836,12 @@ void LLAppViewer::idle() } send_agent_update(false); + + // After calling send_agent_update() in the mainloop we always clear + // the agent's ephemeral ControlFlags (whether an AgentUpdate was + // actually sent or not) because these will be recomputed based on + // real-time key/controller input and resubmitted next frame. + gAgent.resetControlFlags(); } ////////////////////////////////////// diff --git a/indra/newview/llappviewerlinux.cpp b/indra/newview/llappviewerlinux.cpp index 38f2f1ae7f..7ce74649e2 100644 --- a/indra/newview/llappviewerlinux.cpp +++ b/indra/newview/llappviewerlinux.cpp @@ -129,7 +129,7 @@ int main( int argc, char **argv ) return -1; } - // Run the application main loop + // Run the application main loop while (! viewer_app_ptr->frame()) {} diff --git a/indra/newview/llfloaterpreference.cpp b/indra/newview/llfloaterpreference.cpp index 109db7e3d7..ec725a8ace 100644 --- a/indra/newview/llfloaterpreference.cpp +++ b/indra/newview/llfloaterpreference.cpp @@ -3506,8 +3506,21 @@ bool LLPanelPreferenceGameControl::postBuild() mCheckGameControlToAgent = getChild("game_control_to_agent"); mCheckAgentToGameControl = getChild("agent_to_game_control"); - mCheckGameControlToAgent->setCommitCallback([this](LLUICtrl*, const LLSD&) { updateActionTableState(); }); - mCheckAgentToGameControl->setCommitCallback([this](LLUICtrl*, const LLSD&) { updateActionTableState(); }); + mCheckGameControlToServer->setCommitCallback([this](LLUICtrl*, const LLSD&) + { + LLGameControl::setSendToServer(mCheckGameControlToServer->getValue()); + updateActionTableState(); + }); + mCheckGameControlToAgent->setCommitCallback([this](LLUICtrl*, const LLSD&) + { + LLGameControl::setControlAgent(mCheckGameControlToAgent->getValue()); + updateActionTableState(); + }); + mCheckAgentToGameControl->setCommitCallback([this](LLUICtrl*, const LLSD&) + { + LLGameControl::setTranslateAgentActions(mCheckAgentToGameControl->getValue()); + updateActionTableState(); + }); getChild("game_control_tabs")->setCommitCallback([this](LLUICtrl*, const LLSD&) { clearSelectionState(); }); getChild("device_settings_tabs")->setCommitCallback([this](LLUICtrl*, const LLSD&) { clearSelectionState(); }); diff --git a/indra/newview/llfloaterpreference.h b/indra/newview/llfloaterpreference.h index 57eaa193a3..33d1cb0c87 100644 --- a/indra/newview/llfloaterpreference.h +++ b/indra/newview/llfloaterpreference.h @@ -447,9 +447,9 @@ private: void resetButtonMappingsToDefaults(); // Above the tab container - LLCheckBoxCtrl *mCheckGameControlToServer; // send game_control data to server - LLCheckBoxCtrl *mCheckGameControlToAgent; // use game_control data to move avatar - LLCheckBoxCtrl *mCheckAgentToGameControl; // translate external avatar actions to game_control data + LLCheckBoxCtrl* mCheckGameControlToServer; // send game_control data to server + LLCheckBoxCtrl* mCheckGameControlToAgent; // use game_control data to move avatar + LLCheckBoxCtrl* mCheckAgentToGameControl; // translate external avatar actions to game_control data // 1st tab "Channel mappings" LLPanel* mTabChannelMappings; diff --git a/indra/newview/viewer_manifest.py b/indra/newview/viewer_manifest.py index 39b81f2395..efc90b8991 100755 --- a/indra/newview/viewer_manifest.py +++ b/indra/newview/viewer_manifest.py @@ -1420,15 +1420,6 @@ class Linux_x86_64_Manifest(LinuxManifest): self.path_optional("libjemalloc*.so") - self.path("libdb*.so") - self.path("libuuid.so*") - self.path("libdirectfb-1.*.so.*") - self.path("libfusion-1.*.so.*") - self.path("libdirect-1.*.so.*") - self.path("libopenjp2.so*") - self.path("libdirectfb-1.4.so.5") - self.path("libfusion-1.4.so.5") - self.path("libdirect-1.4.so.5*") self.path("libalut.so*") self.path("libopenal.so*") self.path("libopenal.so", "libvivoxoal.so.1") # vivox's sdk expects this soname -- cgit v1.2.3 From b2970a13ff701171ed72961b3d987c57b37396d1 Mon Sep 17 00:00:00 2001 From: Rye Cogtail Date: Thu, 3 Oct 2024 03:01:27 -0400 Subject: Fix AMD gpu detection under linux --- indra/llrender/llgl.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/indra/llrender/llgl.cpp b/indra/llrender/llgl.cpp index a9627a87d3..b4fe711859 100644 --- a/indra/llrender/llgl.cpp +++ b/indra/llrender/llgl.cpp @@ -1140,7 +1140,11 @@ bool LLGLManager::initGL() // Trailing space necessary to keep "nVidia Corpor_ati_on" cards // from being recognized as ATI. // NOTE: AMD has been pretty good about not breaking this check, do not rename without good reason - if (mGLVendor.substr(0,4) == "ATI ") + if (mGLVendor.substr(0,4) == "ATI " +#if LL_LINUX + || mGLVendor.find("AMD") != std::string::npos +#endif //LL_LINUX + ) { mGLVendorShort = "AMD"; // *TODO: Fix this? -- cgit v1.2.3 From 21cfd8c2f45cc11037583cf20b7ca3cef09dea88 Mon Sep 17 00:00:00 2001 From: Rye Cogtail Date: Thu, 3 Oct 2024 01:52:14 -0400 Subject: Fix GL init on Linux/SDL on various combinations of GPU driver --- indra/llwindow/llwindowsdl.cpp | 29 ++++++----------------------- 1 file changed, 6 insertions(+), 23 deletions(-) diff --git a/indra/llwindow/llwindowsdl.cpp b/indra/llwindow/llwindowsdl.cpp index ec0972eaae..eb8e2dbc3c 100644 --- a/indra/llwindow/llwindowsdl.cpp +++ b/indra/llwindow/llwindowsdl.cpp @@ -322,34 +322,17 @@ bool LLWindowSDL::createContext(int x, int y, int width, int height, int bits, b // Setup default backing colors GLint redBits{8}, greenBits{8}, blueBits{8}, alphaBits{8}; - GLint depthBits{(bits <= 16) ? 16 : 24}, stencilBits{8}; - - if (getenv("LL_GL_NO_STENCIL")) - stencilBits = 0; + GLint depthBits{24}, stencilBits{8}; SDL_GL_SetAttribute(SDL_GL_RED_SIZE, redBits); SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE, greenBits); SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, blueBits); SDL_GL_SetAttribute(SDL_GL_ALPHA_SIZE, alphaBits); SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, depthBits); - - // We need stencil support for a few (minor) things. - if (stencilBits) - SDL_GL_SetAttribute(SDL_GL_STENCIL_SIZE, stencilBits); + SDL_GL_SetAttribute(SDL_GL_STENCIL_SIZE, stencilBits); SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1); - if (LLRender::sGLCoreProfile) - { - SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE); - } - - // This is requesting a minimum context version - int major_gl_version = 3; - int minor_gl_version = 2; - SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, major_gl_version); - SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, minor_gl_version); - U32 context_flags = 0; if (gDebugGL) { @@ -380,7 +363,7 @@ bool LLWindowSDL::createContext(int x, int y, int width, int height, int bits, b { LL_WARNS() << "Failed to make context current. SDL: " << SDL_GetError() << LL_ENDL; setupFailure("GL Context failed to set current failure", "Error", OSMB_OK); - return FALSE; + return false; } mSurface = SDL_GetWindowSurface(mWindow); @@ -695,7 +678,7 @@ bool LLWindowSDL::getVisible() Uint32 flags = SDL_GetWindowFlags(mWindow); if (flags & SDL_WINDOW_SHOWN) { - result = TRUE; + result = true; } } return result; @@ -735,9 +718,9 @@ bool LLWindowSDL::maximize() if (mWindow) { SDL_MaximizeWindow(mWindow); - return TRUE; + return true; } - return FALSE; + return false; } bool LLWindowSDL::getFullscreen() -- cgit v1.2.3 From ea650ac073aeb03ab99b88c41f51ce636ce37982 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Thu, 3 Oct 2024 12:08:26 -0700 Subject: fix GameControl save settings, fix linux build --- indra/cmake/LLWindow.cmake | 5 ++++ indra/llcommon/llthreadsafequeue.h | 5 +++- indra/llwindow/llgamecontrol.cpp | 14 ++++------ indra/llwindow/llgamecontrol.h | 6 ++--- indra/llwindow/llkeyboard.h | 2 +- indra/newview/llagent.cpp | 1 + indra/newview/llfloaterpreference.cpp | 5 ++-- indra/newview/llfloaterpreference.h | 48 +++++++++++++++++------------------ indra/newview/llflycam.cpp | 4 +-- 9 files changed, 47 insertions(+), 43 deletions(-) diff --git a/indra/cmake/LLWindow.cmake b/indra/cmake/LLWindow.cmake index b2c1792df1..a5791f1bef 100644 --- a/indra/cmake/LLWindow.cmake +++ b/indra/cmake/LLWindow.cmake @@ -6,3 +6,8 @@ include(Prebuilt) include(SDL2) include_guard() + +if (LINUX) + # linux uses SDL2 for window and keyboard + target_compile_definitions( ll::SDL2 INTERFACE LL_USE_SDL_KEYBOARD=1 ) +endif (LINUX) diff --git a/indra/llcommon/llthreadsafequeue.h b/indra/llcommon/llthreadsafequeue.h index 034e3f7897..7a5eb5b33d 100644 --- a/indra/llcommon/llthreadsafequeue.h +++ b/indra/llcommon/llthreadsafequeue.h @@ -452,7 +452,10 @@ ElementT LLThreadSafeQueue::pop(void) // so we can finish draining the queue. pop_result popped = pop_(lock1, value); if (popped == POPPED) - return std::move(value); + // don't use std::move when returning local value because + // it prevents the compiler from optimizing with copy elision + //return std::move(value); + return value; // Once the queue is DONE, there will never be any more coming. if (popped == DONE) diff --git a/indra/llwindow/llgamecontrol.cpp b/indra/llwindow/llgamecontrol.cpp index 01c6f91d25..9d3c854ca2 100644 --- a/indra/llwindow/llgamecontrol.cpp +++ b/indra/llwindow/llgamecontrol.cpp @@ -41,11 +41,6 @@ namespace std { - string to_string(const char* text) - { - return text ? string(text) : LLStringUtil::null; - } - string to_string(const SDL_JoystickGUID& guid) { char buffer[33] = { 0 }; @@ -149,7 +144,7 @@ namespace std SDL_JoystickGUID guid = SDL_JoystickGetGUID(joystick); ss << ",guid:'" << guid << "'"; ss << ",type:'" << SDL_JoystickGetType(joystick) << "'"; - ss << ",name:'" << std::to_string(SDL_JoystickName(joystick)) << "'"; + ss << ",name:'" << ll_safe_string(SDL_JoystickName(joystick)) << "'"; ss << ",vendor:" << SDL_JoystickGetVendor(joystick); ss << ",product:" << SDL_JoystickGetProduct(joystick); if (U16 version = SDL_JoystickGetProductVersion(joystick)) @@ -179,7 +174,7 @@ namespace std stringstream ss; ss << "{type:'" << SDL_GameControllerGetType(controller) << "'"; - ss << ",name:'" << std::to_string(SDL_GameControllerName(controller)) << "'"; + ss << ",name:'" << ll_safe_string(SDL_GameControllerName(controller)) << "'"; ss << ",vendor:" << SDL_GameControllerGetVendor(controller); ss << ",product:" << SDL_GameControllerGetProduct(controller); if (U16 version = SDL_GameControllerGetProductVersion(controller)) @@ -1411,7 +1406,7 @@ void onJoystickDeviceAdded(const SDL_Event& event) { SDL_JoystickGUID guid(SDL_JoystickGetDeviceGUID(event.cdevice.which)); SDL_JoystickType type(SDL_JoystickGetDeviceType(event.cdevice.which)); - std::string name(std::to_string(SDL_JoystickNameForIndex(event.cdevice.which))); + std::string name(ll_safe_string(SDL_JoystickNameForIndex(event.cdevice.which))); LL_INFOS("SDL2") << "joystick {id:" << event.cdevice.which << ",guid:'" << guid << "'" @@ -1438,7 +1433,7 @@ void onControllerDeviceAdded(const SDL_Event& event) { std::string guid(std::to_string(SDL_JoystickGetDeviceGUID(event.cdevice.which))); SDL_GameControllerType type(SDL_GameControllerTypeForIndex(event.cdevice.which)); - std::string name(std::to_string(SDL_GameControllerNameForIndex(event.cdevice.which))); + std::string name(ll_safe_string(SDL_GameControllerNameForIndex(event.cdevice.which))); LL_INFOS("SDL2") << "controller {id:" << event.cdevice.which << ",guid:'" << guid << "'" @@ -1514,6 +1509,7 @@ bool LLGameControl::isInitialized() } // static +// TODO: find a cleaner way to provide callbacks to LLGameControl void LLGameControl::init(const std::string& gamecontrollerdb_path, std::function loadBoolean, std::function saveBoolean, diff --git a/indra/llwindow/llgamecontrol.h b/indra/llwindow/llgamecontrol.h index 5472a8ce6d..a4b47cc47f 100644 --- a/indra/llwindow/llgamecontrol.h +++ b/indra/llwindow/llgamecontrol.h @@ -40,10 +40,10 @@ // // leftpaddle rightpaddle // _______ _______ -// / 4+ '-. .-' 5+ \ +// / 4+ '-. .-' 5+ \. // leftshoulder _(9)_________'-.____ ____.-'_________(10) rightshoulder -// / _________ \_________/ \ -// / / 1- \ (3) \ +// / _________ \_________/ \. +// / / 1- \ (3) \. // | | | (4) (5) (6) Y | // | |0- (7) 0+| _________ (2)X B(1) | // | | | / 3- \ A | diff --git a/indra/llwindow/llkeyboard.h b/indra/llwindow/llkeyboard.h index da406b28e8..c092f55685 100644 --- a/indra/llwindow/llkeyboard.h +++ b/indra/llwindow/llkeyboard.h @@ -55,7 +55,7 @@ class LLWindowCallbacks; class LLKeyboard { public: -#ifdef LL_LINUX +#ifdef LL_USE_SDL_KEYBOARD // linux relies on SDL2 which uses U32 for its native key type typedef U32 NATIVE_KEY_TYPE; #else diff --git a/indra/newview/llagent.cpp b/indra/newview/llagent.cpp index b7257a6c50..ca35608175 100644 --- a/indra/newview/llagent.cpp +++ b/indra/newview/llagent.cpp @@ -4989,6 +4989,7 @@ static S32 g_deltaFrame { 0 }; void LLAgent::applyExternalActionFlags(U32 outer_flags) { + llassert(LLCoros::on_main_thread_main_coro()); assert(LLGameControl::isEnabled() && LLGameControl::willControlAvatar()); mExternalActionFlags = outer_flags; diff --git a/indra/newview/llfloaterpreference.cpp b/indra/newview/llfloaterpreference.cpp index ec725a8ace..0c96c93d31 100644 --- a/indra/newview/llfloaterpreference.cpp +++ b/indra/newview/llfloaterpreference.cpp @@ -647,7 +647,7 @@ void LLFloaterPreference::cancel(const std::vector settings_to_skip { if (LLPanelPreference* panel = dynamic_cast(view)) { - panel->cancel(); + panel->cancel(settings_to_skip); } } // hide joystick pref floater @@ -3223,8 +3223,6 @@ void LLPanelPreferenceGameControl::saveSettings() }; // Use string formatting functions provided by class LLGameControl: - // stringifyAnalogMappings(), stringifyBinaryMappings(), stringifyFlycamMappings() - if (LLControlVariable* analogMappings = gSavedSettings.getControl("AnalogChannelMappings")) { analogMappings->set(LLGameControl::stringifyAnalogMappings(getChannel)); @@ -3496,6 +3494,7 @@ void LLPanelPreferenceGameControl::onCommitNumericValue() deviceOptions.getAxisOptions()[row_index].mOffset = (S16)value; } setNumericLabel(row->getColumn(column_index), value); + LLGameControl::setDeviceOptions(mSelectedDeviceGUID, deviceOptions); } } diff --git a/indra/newview/llfloaterpreference.h b/indra/newview/llfloaterpreference.h index 33d1cb0c87..e06e758e3a 100644 --- a/indra/newview/llfloaterpreference.h +++ b/indra/newview/llfloaterpreference.h @@ -447,38 +447,38 @@ private: void resetButtonMappingsToDefaults(); // Above the tab container - LLCheckBoxCtrl* mCheckGameControlToServer; // send game_control data to server - LLCheckBoxCtrl* mCheckGameControlToAgent; // use game_control data to move avatar - LLCheckBoxCtrl* mCheckAgentToGameControl; // translate external avatar actions to game_control data + LLCheckBoxCtrl* mCheckGameControlToServer { nullptr }; // send game_control data to server + LLCheckBoxCtrl* mCheckGameControlToAgent { nullptr }; // use game_control data to move avatar + LLCheckBoxCtrl* mCheckAgentToGameControl { nullptr }; // translate external avatar actions to game_control data // 1st tab "Channel mappings" - LLPanel* mTabChannelMappings; - LLScrollListCtrl* mActionTable; + LLPanel* mTabChannelMappings { nullptr }; + LLScrollListCtrl* mActionTable { nullptr }; // 2nd tab "Device settings" - LLPanel* mTabDeviceSettings; - LLTextBox* mNoDeviceMessage; - LLTextBox* mDevicePrompt; - LLTextBox* mSingleDevice; - LLComboBox* mDeviceList; - LLCheckBoxCtrl* mCheckShowAllDevices; - LLPanel* mPanelDeviceSettings; - LLPanel* mTabAxisOptions; - LLScrollListCtrl* mAxisOptions; - LLPanel* mTabAxisMappings; - LLScrollListCtrl* mAxisMappings; - LLPanel* mTabButtonMappings; - LLScrollListCtrl* mButtonMappings; - - LLButton* mResetToDefaults; + LLPanel* mTabDeviceSettings { nullptr }; + LLTextBox* mNoDeviceMessage { nullptr }; + LLTextBox* mDevicePrompt { nullptr }; + LLTextBox* mSingleDevice { nullptr }; + LLComboBox* mDeviceList { nullptr }; + LLCheckBoxCtrl* mCheckShowAllDevices { nullptr }; + LLPanel* mPanelDeviceSettings { nullptr }; + LLPanel* mTabAxisOptions { nullptr }; + LLScrollListCtrl* mAxisOptions { nullptr }; + LLPanel* mTabAxisMappings { nullptr }; + LLScrollListCtrl* mAxisMappings { nullptr }; + LLPanel* mTabButtonMappings { nullptr }; + LLScrollListCtrl* mButtonMappings { nullptr }; + + LLButton* mResetToDefaults { nullptr }; // Numeric value editor - LLSpinCtrl* mNumericValueEditor; + LLSpinCtrl* mNumericValueEditor { nullptr }; // Channel selectors - LLComboBox* mAnalogChannelSelector; - LLComboBox* mBinaryChannelSelector; - LLComboBox* mAxisSelector; + LLComboBox* mAnalogChannelSelector { nullptr }; + LLComboBox* mBinaryChannelSelector { nullptr }; + LLComboBox* mAxisSelector { nullptr }; struct DeviceOptions { diff --git a/indra/newview/llflycam.cpp b/indra/newview/llflycam.cpp index 9bd854a3cc..eb5bfecf73 100644 --- a/indra/newview/llflycam.cpp +++ b/indra/newview/llflycam.cpp @@ -51,7 +51,7 @@ void LLFlycam::getTransform(LLVector3& position_out, LLQuaternion& rotation_out) // 'view' is expected to be in radians void LLFlycam::setView(F32 view) { - mView = std::min(std::max(view, MIN_FIELD_OF_VIEW), MAX_FIELD_OF_VIEW); + mView = std::clamp(view, MIN_FIELD_OF_VIEW, MAX_FIELD_OF_VIEW); } @@ -126,7 +126,7 @@ void LLFlycam::integrate(F32 delta_time) { // Note: we subtract the delta because "positive" zoom (e.g. "zoom in") // produces smaller view angle - mView = std::min(std::max(mView - delta_time * mZoomRate, MIN_FIELD_OF_VIEW), MAX_FIELD_OF_VIEW); + mView = std::clamp(mView - delta_time * mZoomRate, MIN_FIELD_OF_VIEW, MAX_FIELD_OF_VIEW); } if (needs_renormalization) -- cgit v1.2.3 From b33beb0fdb503544b1fd26da0341d5b406ffd8e4 Mon Sep 17 00:00:00 2001 From: Alexander Gavriliuk Date: Thu, 3 Oct 2024 19:05:17 +0200 Subject: #2408 The long covenant with emojis (crash fix) --- indra/llui/llstyle.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/indra/llui/llstyle.cpp b/indra/llui/llstyle.cpp index f1d57a2273..aafcfbc143 100644 --- a/indra/llui/llstyle.cpp +++ b/indra/llui/llstyle.cpp @@ -66,9 +66,9 @@ LLStyle* LLStyle::makeCopy() const copy->mDropShadow = mDropShadow; copy->mFontName = mFontName; copy->mLink = mLink; - copy->mColor = mColor; - copy->mReadOnlyColor = mReadOnlyColor; - copy->mSelectedColor = mSelectedColor; + copy->mColor.set(mColor.get()); + copy->mReadOnlyColor.set(mReadOnlyColor.get()); + copy->mSelectedColor.set(mSelectedColor.get()); copy->mFont = mFont; copy->mImagep = mImagep; copy->mAlpha = mAlpha; -- cgit v1.2.3 From 736cc7ea19b984216834b5b95ed66a18d94cd79b Mon Sep 17 00:00:00 2001 From: Rye Date: Thu, 3 Oct 2024 14:43:15 -0700 Subject: Update havok packages and enable linux64 Release builds --- .github/workflows/build.yaml | 5 +---- autobuild.xml | 51 +++++++++++++++++++++++++++++--------------- indra/CMakeLists.txt | 22 +++++++++---------- indra/cmake/00-Common.cmake | 4 ++-- indra/cmake/Havok.cmake | 37 ++++++++++++++++---------------- indra/cmake/bugsplat.cmake | 8 +++---- indra/newview/CMakeLists.txt | 2 +- 7 files changed, 71 insertions(+), 58 deletions(-) diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index 1804da749f..d69cfc78e3 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -46,11 +46,8 @@ jobs: needs: setup strategy: matrix: - runner: [windows-large, macos-12-xl] + runner: [windows-large, macos-12-xl, linux-large] configuration: ${{ fromJSON(needs.setup.outputs.configurations) }} - include: - - runner: linux-large - configuration: ReleaseOS runs-on: ${{ matrix.runner }} outputs: viewer_channel: ${{ steps.build.outputs.viewer_channel }} diff --git a/autobuild.xml b/autobuild.xml index 4f70212fa8..35702cf3cf 100644 --- a/autobuild.xml +++ b/autobuild.xml @@ -862,11 +862,11 @@ creds github hash - ae2c2a215b1bc2e3f37a67e301926dc405902d1a + 1648aeb68395cba38f9326c671609d6730cbcc28 hash_algorithm sha1 url - https://api.github.com/repos/secondlife/3p-havok-source/releases/assets/136778143 + https://api.github.com/repos/secondlife/3p-havok-source/releases/assets/196725921 name darwin64 @@ -878,11 +878,11 @@ creds github hash - ebfb82b6143874e7938b9d1e8a70d0a2e28aa818 + 702ad28b6dbace2a68260d69f6d1768d163b5a6f hash_algorithm sha1 url - https://api.github.com/repos/secondlife/3p-havok-source/releases/assets/108912599 + https://api.github.com/repos/secondlife/3p-havok-source/releases/assets/196725902 name linux64 @@ -894,11 +894,11 @@ creds github hash - 0393dd75c58f7046bed47e62a8884a78cb02a5c3 + ea980f372981fe7685f91a111da2785825d473ed hash_algorithm sha1 url - https://api.github.com/repos/secondlife/3p-havok-source/releases/assets/136778145 + https://api.github.com/repos/secondlife/3p-havok-source/releases/assets/196725911 name windows64 @@ -1431,11 +1431,11 @@ creds github hash - 9e59c93c7110e87b4ff3db330f11a23c50e5000f + 7facda95e2f00c260513f3d4db42588fa8ba703c hash_algorithm sha1 url - https://api.github.com/repos/secondlife/llphysicsextensions_source/releases/assets/178910560 + https://api.github.com/repos/secondlife/llphysicsextensions_source/releases/assets/196289774 name darwin64 @@ -1447,11 +1447,11 @@ creds github hash - 7ed994db5bafa9a7ad09a1b53da850a84715c65e + 01d08f13c7bc8d1b95b0330fa6833b7d8274e4d0 hash_algorithm sha1 url - https://api.github.com/repos/secondlife/llphysicsextensions_source/releases/assets/178910561 + https://api.github.com/repos/secondlife/llphysicsextensions_source/releases/assets/196289775 name linux64 @@ -1463,11 +1463,11 @@ creds github hash - 66824c02e0e5eabbfbe37bfb173360195f89697c + 6d00345c7d3471bc5f7c1218e014dd0f1a2c069b hash_algorithm sha1 url - https://api.github.com/repos/secondlife/llphysicsextensions_source/releases/assets/178910562 + https://api.github.com/repos/secondlife/llphysicsextensions_source/releases/assets/196289778 name windows64 @@ -1480,7 +1480,7 @@ copyright Copyright (c) 2010, Linden Research, Inc. version - 1.0.66e6919 + 1.0.11137145495 name llphysicsextensions_source @@ -3203,8 +3203,7 @@ Copyright (c) 2012, 2014, 2015, 2016 nghttp2 contributors options -G - Ninja - -DUSE_OPENAL:BOOL=ON + Unix Makefiles arguments @@ -3214,7 +3213,16 @@ Copyright (c) 2012, 2014, 2015, 2016 nghttp2 contributors build command - ninja + cmake + options + + --build + . + --config + Release + --parallel + $AUTOBUILD_CPU_COUNT + default True @@ -3235,7 +3243,16 @@ Copyright (c) 2012, 2014, 2015, 2016 nghttp2 contributors build command - ninja + cmake + options + + --build + . + --config + Release + --parallel + $AUTOBUILD_CPU_COUNT + name ReleaseOS diff --git a/indra/CMakeLists.txt b/indra/CMakeLists.txt index 422927704a..3be8ebafa8 100644 --- a/indra/CMakeLists.txt +++ b/indra/CMakeLists.txt @@ -82,17 +82,17 @@ if (ENABLE_MEDIA_PLUGINS) add_subdirectory(${LIBS_OPEN_PREFIX}media_plugins) endif (ENABLE_MEDIA_PLUGINS) -if (LINUX) - if (INSTALL_PROPRIETARY) - include(LLAppearanceUtility) - add_subdirectory(${LLAPPEARANCEUTILITY_SRC_DIR} ${LLAPPEARANCEUTILITY_BIN_DIR}) - endif (INSTALL_PROPRIETARY) -elseif (WINDOWS) - # cmake EXISTS requires an absolute path, see indra/cmake/Variables.cmake - if (EXISTS ${VIEWER_DIR}win_setup) - add_subdirectory(${VIEWER_DIR}win_setup) - endif (EXISTS ${VIEWER_DIR}win_setup) -endif (LINUX) +# if (LINUX) +# if (INSTALL_PROPRIETARY) +# include(LLAppearanceUtility) +# add_subdirectory(${LLAPPEARANCEUTILITY_SRC_DIR} ${LLAPPEARANCEUTILITY_BIN_DIR}) +# endif (INSTALL_PROPRIETARY) +# elseif (WINDOWS) +# # cmake EXISTS requires an absolute path, see indra/cmake/Variables.cmake +# if (EXISTS ${VIEWER_DIR}win_setup) +# add_subdirectory(${VIEWER_DIR}win_setup) +# endif (EXISTS ${VIEWER_DIR}win_setup) +# endif (LINUX) if (WINDOWS) # cmake EXISTS requires an absolute path, see indra/cmake/Variables.cmake diff --git a/indra/cmake/00-Common.cmake b/indra/cmake/00-Common.cmake index be38574041..fc18ffebb5 100644 --- a/indra/cmake/00-Common.cmake +++ b/indra/cmake/00-Common.cmake @@ -178,7 +178,7 @@ if (DARWIN) set(CLANG_DISABLE_FATAL_WARNINGS OFF) set(CMAKE_CXX_LINK_FLAGS "-Wl,-headerpad_max_install_names,-search_paths_first") set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_CXX_LINK_FLAGS}") - set(DARWIN_extra_cstar_flags "-Wno-unused-local-typedef -Wno-deprecated-declarations") + set(DARWIN_extra_cstar_flags "-Wno-deprecated-declarations") # Ensure that CMAKE_CXX_FLAGS has the correct -g debug information format -- # see Variables.cmake. string(REPLACE "-gdwarf-2" "-g${CMAKE_XCODE_ATTRIBUTE_DEBUG_INFORMATION_FORMAT}" @@ -197,7 +197,7 @@ if (DARWIN) endif(DARWIN) if(LINUX OR DARWIN) - add_compile_options(-Wall -Wno-sign-compare -Wno-trigraphs -Wno-reorder -Wno-unused-but-set-variable -Wno-unused-variable) + add_compile_options(-Wall -Wno-sign-compare -Wno-trigraphs -Wno-reorder -Wno-unused-but-set-variable -Wno-unused-variable -Wno-unused-local-typedef) if (CMAKE_CXX_COMPILER_ID STREQUAL "GNU") add_compile_options(-Wno-stringop-truncation -Wno-parentheses -Wno-c++20-compat) diff --git a/indra/cmake/Havok.cmake b/indra/cmake/Havok.cmake index c544440adc..64e33f6c83 100644 --- a/indra/cmake/Havok.cmake +++ b/indra/cmake/Havok.cmake @@ -27,26 +27,26 @@ else (LL_DEBUG_HAVOK) endif (LL_DEBUG_HAVOK) set(HAVOK_LIBS - hkBase - hkCompat + hkgpConvexDecomposition hkGeometryUtilities - hkInternal hkSerialize hkSceneData hkpCollide hkpUtilities hkpConstraintSolver hkpDynamics - hkpInternal - hkaiInternal hkaiPathfinding hkaiAiPhysicsBridge - hkcdInternal hkcdCollide hkpVehicle hkVisualize hkaiVisualize - hkgpConvexDecomposition + hkaiInternal + hkcdInternal + hkpInternal + hkInternal + hkCompat + hkBase ) unset(HK_DEBUG_LIBRARIES) @@ -60,16 +60,22 @@ if (DEBUG_PREBUILT) # but making it pretty is a lot more work message(STATUS "${ARGN}") endfunction(DEBUG_MESSAGE) + function(DEBUG_EXEC_FUNC) + execute_process(COMMAND ${ARGN}) + endfunction(DEBUG_EXEC_FUNC) else (DEBUG_PREBUILT) # without DEBUG_PREBUILT, DEBUG_MESSAGE() is a no-op function(DEBUG_MESSAGE) endfunction(DEBUG_MESSAGE) + function(DEBUG_EXEC_FUNC) + execute_process(COMMAND ${ARGN} OUTPUT_QUIET) + endfunction(DEBUG_EXEC_FUNC) endif (DEBUG_PREBUILT) # DEBUG_EXEC() reports each execute_process() before invoking function(DEBUG_EXEC) DEBUG_MESSAGE(${ARGN}) - execute_process(COMMAND ${ARGN}) + DEBUG_EXEC_FUNC(${ARGN}) endfunction(DEBUG_EXEC) # *TODO: Figure out why we need to extract like this... @@ -79,9 +85,7 @@ foreach(HAVOK_LIB ${HAVOK_LIBS}) find_library(HAVOK_RELWITHDEBINFO_LIB_${HAVOK_LIB} ${HAVOK_LIB} PATHS ${HAVOK_RELWITHDEBINFO_LIBRARY_PATH}) if(LINUX) - set(debug_dir "${HAVOK_DEBUG_LIBRARY_PATH}/${HAVOK_LIB}") set(release_dir "${HAVOK_RELEASE_LIBRARY_PATH}/${HAVOK_LIB}") - set(relwithdebinfo_dir "${HAVOK_RELWITHDEBINFO_LIBRARY_PATH}/${HAVOK_LIB}") # Try to avoid extracting havok library each time we run cmake. if("${havok_${HAVOK_LIB}_extracted}" STREQUAL "" AND EXISTS "${PREBUILD_TRACKING_DIR}/havok_${HAVOK_LIB}_extracted") @@ -92,8 +96,8 @@ foreach(HAVOK_LIB ${HAVOK_LIBS}) if(${PREBUILD_TRACKING_DIR}/havok_source_installed IS_NEWER_THAN ${PREBUILD_TRACKING_DIR}/havok_${HAVOK_LIB}_extracted OR NOT ${havok_${HAVOK_LIB}_extracted} EQUAL 0) DEBUG_MESSAGE("Extracting ${HAVOK_LIB}...") - foreach(lib ${debug_dir} ${release_dir} ${relwithdebinfo_dir}) - DEBUG_EXEC("mkdir" ${lib}) + foreach(lib ${release_dir}) + DEBUG_EXEC("mkdir" "-p" ${lib}) DEBUG_EXEC("ar" "-xv" "../lib${HAVOK_LIB}.a" WORKING_DIRECTORY ${lib}) endforeach(lib) @@ -104,17 +108,12 @@ foreach(HAVOK_LIB ${HAVOK_LIBS}) endif() - file(GLOB extracted_debug "${debug_dir}/*.o") file(GLOB extracted_release "${release_dir}/*.o") - file(GLOB extracted_relwithdebinfo "${relwithdebinfo_dir}/*.o") - - DEBUG_MESSAGE("extracted_debug ${debug_dir}/*.o") DEBUG_MESSAGE("extracted_release ${release_dir}/*.o") - DEBUG_MESSAGE("extracted_relwithdebinfo ${relwithdebinfo_dir}/*.o") - list(APPEND HK_DEBUG_LIBRARIES ${extracted_debug}) + list(APPEND HK_DEBUG_LIBRARIES ${extracted_release}) list(APPEND HK_RELEASE_LIBRARIES ${extracted_release}) - list(APPEND HK_RELWITHDEBINFO_LIBRARIES ${extracted_relwithdebinfo}) + list(APPEND HK_RELWITHDEBINFO_LIBRARIES ${extracted_release}) else(LINUX) # Win32 list(APPEND HK_DEBUG_LIBRARIES ${HAVOK_DEBUG_LIB_${HAVOK_LIB}}) diff --git a/indra/cmake/bugsplat.cmake b/indra/cmake/bugsplat.cmake index 509981d72c..0798d4f51f 100644 --- a/indra/cmake/bugsplat.cmake +++ b/indra/cmake/bugsplat.cmake @@ -1,13 +1,13 @@ -if (INSTALL_PROPRIETARY) +if (INSTALL_PROPRIETARY AND NOT LINUX) # Note that viewer_manifest.py makes decision based on BUGSPLAT_DB and not USE_BUGSPLAT if (BUGSPLAT_DB) set(USE_BUGSPLAT ON CACHE BOOL "Use the BugSplat crash reporting system") else (BUGSPLAT_DB) set(USE_BUGSPLAT OFF CACHE BOOL "Use the BugSplat crash reporting system") endif (BUGSPLAT_DB) -else (INSTALL_PROPRIETARY) +else (INSTALL_PROPRIETARY AND NOT LINUX) set(USE_BUGSPLAT OFF CACHE BOOL "Use the BugSplat crash reporting system") -endif (INSTALL_PROPRIETARY) +endif (INSTALL_PROPRIETARY AND NOT LINUX) include_guard() add_library( ll::bugsplat INTERFACE IMPORTED ) @@ -36,6 +36,6 @@ if (USE_BUGSPLAT) set_property( TARGET ll::bugsplat APPEND PROPERTY INTERFACE_COMPILE_DEFINITIONS LL_BUGSPLAT) else() - set(BUGSPLAT_DB "" CACHE STRING "BugSplat crash database name") + set(BUGSPLAT_DB "" CACHE STRING "BugSplat crash database name" FORCE) endif (USE_BUGSPLAT) diff --git a/indra/newview/CMakeLists.txt b/indra/newview/CMakeLists.txt index 29dbceedac..5b38fcfe62 100644 --- a/indra/newview/CMakeLists.txt +++ b/indra/newview/CMakeLists.txt @@ -1870,7 +1870,7 @@ else (WINDOWS) # Linux set_target_properties(${VIEWER_BINARY_NAME} PROPERTIES - LINK_FLAGS_RELEASE "${LINK_FLAGS_RELEASE} -Wl,--Map=${VIEWER_BINARY_NAME}.MAP" + LINK_FLAGS_RELEASE "${LINK_FLAGS_RELEASE} -no-pie -Wl,--Map=${VIEWER_BINARY_NAME}.MAP" ) endif (WINDOWS) -- cgit v1.2.3 From 8cfcd8e135a03b12ca949e3d3f1217c166355f56 Mon Sep 17 00:00:00 2001 From: Rye Cogtail Date: Mon, 30 Sep 2024 17:49:07 -0700 Subject: Use cmake build command instead of devenv to reduce GHA build time --- autobuild.xml | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/autobuild.xml b/autobuild.xml index 35702cf3cf..6802f5d530 100644 --- a/autobuild.xml +++ b/autobuild.xml @@ -3294,15 +3294,15 @@ Copyright (c) 2012, 2014, 2015, 2016 nghttp2 contributors build command - devenv + cmake options - /build - RelWithDebInfo|${AUTOBUILD_WIN_VSPLATFORM|NOTWIN} - - arguments - - SecondLife.sln + --build + . + --config + RelWithDebInfo + --parallel + $AUTOBUILD_CPU_COUNT default @@ -3369,15 +3369,15 @@ Copyright (c) 2012, 2014, 2015, 2016 nghttp2 contributors build command - devenv + cmake options - /build - Release|${AUTOBUILD_WIN_VSPLATFORM|NOTWIN} - - arguments - - SecondLife.sln + --build + . + --config + Release + --parallel + $AUTOBUILD_CPU_COUNT name -- cgit v1.2.3 From bb93375c2ed8ed3eb7eed3105af15a7adf0223b2 Mon Sep 17 00:00:00 2001 From: Rye Cogtail Date: Mon, 30 Sep 2024 22:18:54 -0700 Subject: Default OpenAL audio backend on so all build configurations have audio --- autobuild.xml | 5 ----- indra/cmake/OPENAL.cmake | 6 +----- 2 files changed, 1 insertion(+), 10 deletions(-) diff --git a/autobuild.xml b/autobuild.xml index 6802f5d530..321abfce25 100644 --- a/autobuild.xml +++ b/autobuild.xml @@ -2992,7 +2992,6 @@ Copyright (c) 2012, 2014, 2015, 2016 nghttp2 contributors -DADDRESS_SIZE:STRING=$AUTOBUILD_ADDRSIZE -DROOT_PROJECT_NAME:STRING=SecondLife -DINSTALL_PROPRIETARY=TRUE - -DUSE_OPENAL:BOOL=ON build @@ -3034,7 +3033,6 @@ Copyright (c) 2012, 2014, 2015, 2016 nghttp2 contributors -DADDRESS_SIZE:STRING=$AUTOBUILD_ADDRSIZE -DROOT_PROJECT_NAME:STRING=SecondLife -DINSTALL_PROPRIETARY=TRUE - -DUSE_OPENAL:BOOL=ON build @@ -3237,7 +3235,6 @@ Copyright (c) 2012, 2014, 2015, 2016 nghttp2 contributors -G Ninja - -DUSE_OPENAL:BOOL=ON build @@ -3322,7 +3319,6 @@ Copyright (c) 2012, 2014, 2015, 2016 nghttp2 contributors ${AUTOBUILD_WIN_VSPLATFORM|NOTWIN} -DINSTALL_PROPRIETARY=FALSE -DUSE_KDU=FALSE - -DUSE_OPENAL:BOOL=ON arguments @@ -3396,7 +3392,6 @@ Copyright (c) 2012, 2014, 2015, 2016 nghttp2 contributors -DUNATTENDED:BOOL=ON -DINSTALL_PROPRIETARY=FALSE -DUSE_KDU=FALSE - -DUSE_OPENAL:BOOL=ON arguments diff --git a/indra/cmake/OPENAL.cmake b/indra/cmake/OPENAL.cmake index 347dd02cd7..355907bcf5 100644 --- a/indra/cmake/OPENAL.cmake +++ b/indra/cmake/OPENAL.cmake @@ -4,11 +4,7 @@ include(Prebuilt) include_guard() -# ND: Turn this off by default, the openal code in the viewer isn't very well maintained, seems -# to have memory leaks, has no option to play music streams -# It probably makes sense to to completely remove it - -set(USE_OPENAL OFF CACHE BOOL "Enable OpenAL") +set(USE_OPENAL ON CACHE BOOL "Enable OpenAL") # ND: To streamline arguments passed, switch from OPENAL to USE_OPENAL # To not break all old build scripts convert old arguments but warn about it if(OPENAL) -- cgit v1.2.3 From 5787e8edbb0c24d4649094a5939849986d6e8775 Mon Sep 17 00:00:00 2001 From: Rye Cogtail Date: Mon, 30 Sep 2024 22:21:06 -0700 Subject: Update openal-soft package with linux pipewire support --- autobuild.xml | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/autobuild.xml b/autobuild.xml index 321abfce25..2e7d120509 100644 --- a/autobuild.xml +++ b/autobuild.xml @@ -2112,11 +2112,11 @@ Copyright (c) 2012, 2014, 2015, 2016 nghttp2 contributors archive hash - 191e4ef07a35f7147708415465191ce7622e3012 + 32371131845ad85bf35e89f7a8ec073b89eeb3f1 hash_algorithm sha1 url - https://github.com/secondlife/3p-openal-soft/releases/download/v1.23.1-8668009/openal-1.23.1-darwin64-8979520327.tar.zst + https://github.com/secondlife/3p-openal-soft/releases/download/v1.23.1-0247dd8/openal-1.23.1-darwin64-11115781501.tar.zst name darwin64 @@ -2126,11 +2126,11 @@ Copyright (c) 2012, 2014, 2015, 2016 nghttp2 contributors archive hash - 3bd8c9028ef42bdb43c7422e7d324e213fdb081e + 14856fb33e205138731865b2d1bb680104f83dd3 hash_algorithm sha1 url - https://github.com/secondlife/3p-openal-soft/releases/download/v1.23.1-8668009/openal-1.23.1-linux64-8979520327.tar.zst + https://github.com/secondlife/3p-openal-soft/releases/download/v1.23.1-0247dd8/openal-1.23.1-linux64-11115781501.tar.zst name linux64 @@ -2140,11 +2140,11 @@ Copyright (c) 2012, 2014, 2015, 2016 nghttp2 contributors archive hash - 4b849609abec790e89be5fad8ddee3717ee301c4 + c84665726c23fa6f5d25c7a46f5e9d0153834a2a hash_algorithm sha1 url - https://github.com/secondlife/3p-openal-soft/releases/download/v1.23.1-8668009/openal-1.23.1-windows64-8979520327.tar.zst + https://github.com/secondlife/3p-openal-soft/releases/download/v1.23.1-0247dd8/openal-1.23.1-windows64-11115781501.tar.zst name windows64 -- cgit v1.2.3 From 2792885637d0c61bfcea090cf72dab0d192680da Mon Sep 17 00:00:00 2001 From: Rye Date: Wed, 2 Oct 2024 11:08:39 -0700 Subject: Unify linux Release and ReleaseOS back to Unix Makefiles cmake generator --- autobuild.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/autobuild.xml b/autobuild.xml index 2e7d120509..5584da51e9 100644 --- a/autobuild.xml +++ b/autobuild.xml @@ -3234,7 +3234,7 @@ Copyright (c) 2012, 2014, 2015, 2016 nghttp2 contributors options -G - Ninja + Unix Makefiles build -- cgit v1.2.3 From 322b4fb9c4c37787bd219dd97012f121309d591a Mon Sep 17 00:00:00 2001 From: Cosmic Linden Date: Thu, 3 Oct 2024 16:43:04 -0700 Subject: secondlife/viewer#2472: Feedback Disable if not supported for OpenGL version Minimize performance footguns Simplify for clause --- indra/llrender/llgltexture.cpp | 9 +++++++++ indra/newview/app_settings/settings.xml | 13 ++++++++++++- indra/newview/llviewertexturelist.cpp | 27 +++++++++++++++++++-------- indra/newview/llviewertexturelist.h | 2 +- 4 files changed, 41 insertions(+), 10 deletions(-) diff --git a/indra/llrender/llgltexture.cpp b/indra/llrender/llgltexture.cpp index 9a2b2ef524..bb4205b319 100644 --- a/indra/llrender/llgltexture.cpp +++ b/indra/llrender/llgltexture.cpp @@ -170,6 +170,13 @@ bool LLGLTexture::createGLTexture(S32 discard_level, const LLImageRaw* imageraw, void LLGLTexture::getGLObjectLabel(std::string& label, bool& error) const { + // GL_VERSION_4_3 + if (gGLManager.mGLVersion < 4.29f) + { + error = true; + label.clear(); + return; + } if (!mGLTexturep) { error = true; @@ -194,6 +201,8 @@ void LLGLTexture::getGLObjectLabel(std::string& label, bool& error) const std::string LLGLTexture::setGLObjectLabel(const std::string& prefix, bool append_texname) const { + if (gGLManager.mGLVersion < 4.29f) { return ""; } // GL_VERSION_4_3 + llassert(mGLTexturep); if (mGLTexturep) { diff --git a/indra/newview/app_settings/settings.xml b/indra/newview/app_settings/settings.xml index 5d3f3d58c6..4d0cffa2a9 100644 --- a/indra/newview/app_settings/settings.xml +++ b/indra/newview/app_settings/settings.xml @@ -7935,7 +7935,18 @@ RenderDebugTextureLabel Comment - Enable texture labels via glObjectLabel. Requires restart for some features. + Enable texture labels via glObjectLabel. + Persist + 0 + Type + Boolean + Value + 0 + + RenderDebugTextureLabelLocalFiles + + Comment + Enumerate textures with local file names. Doesn't do anything until RenderDebugTextureLabel is set to true. Requires restart. Persist 1 Type diff --git a/indra/newview/llviewertexturelist.cpp b/indra/newview/llviewertexturelist.cpp index 99df814f32..4c13105d5d 100644 --- a/indra/newview/llviewertexturelist.cpp +++ b/indra/newview/llviewertexturelist.cpp @@ -447,7 +447,7 @@ LLViewerFetchedTexture* LLViewerTextureList::getImageFromFile(const std::string& std::string url = "file://" + full_path; LLViewerFetchedTexture* tex = getImageFromUrl(url, f_type, usemipmaps, boost_priority, texture_type, internal_format, primary_format, force_id); - static LLCachedControl debug_texture_label(gSavedSettings, "RenderDebugTextureLabel", false); + static LLCachedControl debug_texture_label(gSavedSettings, "RenderDebugTextureLabelLocalFiles", false); if (debug_texture_label()) { gTextureList.mNameTextureList.push_back(LLViewerTextureList::NameElement(tex, filename)); @@ -1160,12 +1160,24 @@ void LLViewerTextureList::updateImagesNameTextures() auto it = mNameTextureList.begin(); while (it != mNameTextureList.end()) // For ALL textures needing names { - if (it->mTex->hasGLTexture()) + LLViewerFetchedTexture* tex = it->mTex; + // Check that the texture is in the list first (otherwise it may be a dead pointer) + // A raw pointer ensures textures are cleaned up when this code isn't running. + const bool alive = mImageList.find(tex) != mImageList.end(); + + if (alive) { - if(it->mTex->getTexName()) + if (tex->hasGLTexture()) { - it->mTex->setGLObjectLabel(it->mPrefix, true); - it = mNameTextureList.erase(it); // Assume no rename needed + if(tex->getTexName()) + { + tex->setGLObjectLabel(it->mPrefix, true); + it = mNameTextureList.erase(it); // Assume no rename needed + } + else + { + ++it; // Not ready + } } else { @@ -1174,7 +1186,7 @@ void LLViewerTextureList::updateImagesNameTextures() } else { - ++it; // Not ready + it = mNameTextureList.erase(it); // Remove dead pointer } } } @@ -1190,9 +1202,8 @@ void LLViewerTextureList::labelAll() std::string label; bool error; - for (image_list_t::iterator it = mImageList.begin(); it != mImageList.end(); ++it) + for (LLViewerFetchedTexture* image : mImageList) { - LLViewerFetchedTexture* image = *it; image->getGLObjectLabel(label, error); if (!error && label.empty()) { diff --git a/indra/newview/llviewertexturelist.h b/indra/newview/llviewertexturelist.h index 6424e5c3f0..08dd2d0f7f 100644 --- a/indra/newview/llviewertexturelist.h +++ b/indra/newview/llviewertexturelist.h @@ -220,7 +220,7 @@ public: struct NameElement { NameElement(LLViewerFetchedTexture* tex, const std::string& prefix) : mTex(tex), mPrefix(prefix) {} - LLPointer mTex; + LLViewerFetchedTexture* mTex; std::string mPrefix; }; std::vector mNameTextureList; -- cgit v1.2.3 From de95bd3187b61ca1152c14bc9bd112da61027d4c Mon Sep 17 00:00:00 2001 From: Cosmic Linden Date: Thu, 3 Oct 2024 18:25:37 -0700 Subject: secondlife/viewer#2472: Also don't persist local file enumeration beyond next session --- indra/llrender/llgl.cpp | 1 + indra/llrender/llgl.h | 1 + indra/newview/app_settings/settings.xml | 4 ++-- indra/newview/llappviewer.cpp | 8 ++++++++ indra/newview/llviewertexturelist.cpp | 3 +-- 5 files changed, 13 insertions(+), 4 deletions(-) diff --git a/indra/llrender/llgl.cpp b/indra/llrender/llgl.cpp index 15fb289de3..a60f1efbdf 100644 --- a/indra/llrender/llgl.cpp +++ b/indra/llrender/llgl.cpp @@ -65,6 +65,7 @@ bool gDebugSession = false; bool gDebugGLSession = false; +bool gDebugTextureLabelLocalFilesSession = false; bool gClothRipple = false; bool gHeadlessClient = false; bool gNonInteractive = false; diff --git a/indra/llrender/llgl.h b/indra/llrender/llgl.h index f5b1e8d786..8d7ac56d79 100644 --- a/indra/llrender/llgl.h +++ b/indra/llrender/llgl.h @@ -48,6 +48,7 @@ extern bool gDebugGL; extern bool gDebugSession; extern bool gDebugGLSession; +extern bool gDebugTextureLabelLocalFilesSession; extern llofstream gFailLog; #define LL_GL_ERRS LL_ERRS("RenderState") diff --git a/indra/newview/app_settings/settings.xml b/indra/newview/app_settings/settings.xml index 4d0cffa2a9..cb32789cad 100644 --- a/indra/newview/app_settings/settings.xml +++ b/indra/newview/app_settings/settings.xml @@ -7943,10 +7943,10 @@ Value 0 - RenderDebugTextureLabelLocalFiles + RenderDebugTextureLabelLocalFilesSession Comment - Enumerate textures with local file names. Doesn't do anything until RenderDebugTextureLabel is set to true. Requires restart. + Enumerate textures with local file names. Doesn't do anything until RenderDebugTextureLabel is set to true. Requires restart AND applies on the next session only. Persist 1 Type diff --git a/indra/newview/llappviewer.cpp b/indra/newview/llappviewer.cpp index 521e6eee1a..7fc97558f1 100644 --- a/indra/newview/llappviewer.cpp +++ b/indra/newview/llappviewer.cpp @@ -2860,6 +2860,14 @@ bool LLAppViewer::initConfiguration() gSavedSettings.setBOOL("RenderDebugGLSession", false); } + if (gSavedSettings.getBOOL("RenderDebugTextureLabelLocalFilesSession")) + { + gDebugTextureLabelLocalFilesSession = true; + // Texture label accounting for local files takes up memory in the + // background, so don't persist. + gSavedSettings.setBOOL("RenderDebugTextureLabelLocalFilesSession", false); + } + const LLControlVariable* skinfolder = gSavedSettings.getControl("SkinCurrent"); if (skinfolder && LLStringUtil::null != skinfolder->getValue().asString()) { diff --git a/indra/newview/llviewertexturelist.cpp b/indra/newview/llviewertexturelist.cpp index 4c13105d5d..752cfb3884 100644 --- a/indra/newview/llviewertexturelist.cpp +++ b/indra/newview/llviewertexturelist.cpp @@ -447,8 +447,7 @@ LLViewerFetchedTexture* LLViewerTextureList::getImageFromFile(const std::string& std::string url = "file://" + full_path; LLViewerFetchedTexture* tex = getImageFromUrl(url, f_type, usemipmaps, boost_priority, texture_type, internal_format, primary_format, force_id); - static LLCachedControl debug_texture_label(gSavedSettings, "RenderDebugTextureLabelLocalFiles", false); - if (debug_texture_label()) + if (gDebugTextureLabelLocalFilesSession) { gTextureList.mNameTextureList.push_back(LLViewerTextureList::NameElement(tex, filename)); } -- cgit v1.2.3 From dce44872e441f2437777fde95a5a3451782f42a1 Mon Sep 17 00:00:00 2001 From: Nat Goodspeed Date: Fri, 4 Oct 2024 10:52:09 -0400 Subject: Restore macos-14-large runner for Xcode 16 compiler. Also restore notice messages about the selected viewer channel. --- .github/workflows/build.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index f515046da8..bfad0e467b 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -46,7 +46,7 @@ jobs: needs: setup strategy: matrix: - runner: [windows-large, macos-12-xl, linux-large] + runner: [windows-large, macos-14-xlarge, linux-large] configuration: ${{ fromJSON(needs.setup.outputs.configurations) }} include: - runner: linux-large @@ -250,7 +250,7 @@ jobs: export viewer_channel="Second Life Test" fi fi - echo "viewer_channel=$viewer_channel" + echo "::notice::$RUNNER_OS viewer_channel=$viewer_channel" echo "viewer_channel=$viewer_channel" >> "$GITHUB_OUTPUT" # On windows we need to point the build to the correct python # as neither CMake's FindPython nor our custom Python.cmake module -- cgit v1.2.3 From 78644fafdbaaf732492efba7624bc20ddeaef977 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Fri, 4 Oct 2024 08:28:26 -0700 Subject: repair rebase: remove duplicate SDL initialization --- indra/llwindow/llwindowsdl.cpp | 44 ------------------------------------------ 1 file changed, 44 deletions(-) diff --git a/indra/llwindow/llwindowsdl.cpp b/indra/llwindow/llwindowsdl.cpp index 770170179a..f88feadbc1 100644 --- a/indra/llwindow/llwindowsdl.cpp +++ b/indra/llwindow/llwindowsdl.cpp @@ -258,50 +258,6 @@ bool LLWindowSDL::createContext(int x, int y, int width, int height, int bits, b mGrabbyKeyFlags = 0; mReallyCapturedCount = 0; - std::initializer_list > hintList = - { - {SDL_HINT_VIDEO_X11_NET_WM_BYPASS_COMPOSITOR,"0"}, - {SDL_HINT_MOUSE_FOCUS_CLICKTHROUGH,"1"}, - {SDL_HINT_IME_INTERNAL_EDITING,"1"} - }; - - for( auto hint: hintList ) - { - SDL_SetHint( std::get<0>(hint), std::get<1>(hint)); - } - - std::initializer_list> initList= - { {SDL_INIT_VIDEO,"SDL_INIT_VIDEO", true}, - {SDL_INIT_AUDIO,"SDL_INIT_AUDIO", false}, - {SDL_INIT_GAMECONTROLLER,"SDL_INIT_GAMECONTROLLER", false}, - {SDL_INIT_SENSOR,"SDL_INIT_SENSOR", false} - }; - - for( auto subSystem : initList) - { - if( SDL_InitSubSystem( std::get<0>(subSystem) ) < 0 ) - { - LL_WARNS() << "SDL_InitSubSystem for " << std::get<1>(subSystem) << " failed " << SDL_GetError() << LL_ENDL; - - if( std::get<2>(subSystem)) - setupFailure("SDL_Init() failure", "error", OSMB_OK); - - } - } - - SDL_version c_sdl_version; - SDL_VERSION(&c_sdl_version); - LL_INFOS() << "Compiled against SDL " - << int(c_sdl_version.major) << "." - << int(c_sdl_version.minor) << "." - << int(c_sdl_version.patch) << LL_ENDL; - SDL_version r_sdl_version; - SDL_GetVersion(&r_sdl_version); - LL_INFOS() << " Running against SDL " - << int(r_sdl_version.major) << "." - << int(r_sdl_version.minor) << "." - << int(r_sdl_version.patch) << LL_ENDL; - if (width == 0) width = 1024; if (height == 0) -- cgit v1.2.3 From 5836d8835f88af8b82154a48bcf1281937ed1f04 Mon Sep 17 00:00:00 2001 From: Mnikolenko Productengine Date: Fri, 4 Oct 2024 18:33:28 +0300 Subject: Lua api to get info about nearby avatars and objects --- indra/newview/llagentlistener.cpp | 117 ++++++++++++++++++++++ indra/newview/llagentlistener.h | 4 + indra/newview/scripts/lua/require/LLAgent.lua | 36 +++++++ indra/newview/scripts/lua/require/UI.lua | 6 +- indra/newview/scripts/lua/test_nearby_avatars.lua | 25 +++++ 5 files changed, 186 insertions(+), 2 deletions(-) create mode 100644 indra/newview/scripts/lua/test_nearby_avatars.lua diff --git a/indra/newview/llagentlistener.cpp b/indra/newview/llagentlistener.cpp index 14e443ec4e..f00173dc13 100644 --- a/indra/newview/llagentlistener.cpp +++ b/indra/newview/llagentlistener.cpp @@ -32,11 +32,14 @@ #include "llagent.h" #include "llagentcamera.h" +#include "llavatarname.h" +#include "llavatarnamecache.h" #include "llvoavatar.h" #include "llcommandhandler.h" #include "llinventorymodel.h" #include "llslurl.h" #include "llurldispatcher.h" +#include "llviewercontrol.h" #include "llviewernetwork.h" #include "llviewerobject.h" #include "llviewerobjectlist.h" @@ -47,6 +50,7 @@ #include "lltoolgrab.h" #include "llhudeffectlookat.h" #include "llagentcamera.h" +#include "resultset.h" #include static const F64 PLAY_ANIM_THROTTLE_PERIOD = 1.f; @@ -176,6 +180,25 @@ LLAgentListener::LLAgentListener(LLAgent &agent) "Return information about [\"item_id\"] animation", &LLAgentListener::getAnimationInfo, llsd::map("item_id", LLSD(), "reply", LLSD())); + + add("getID", + "Return your own avatar ID", + &LLAgentListener::getID, + llsd::map("reply", LLSD())); + + add("getNearbyAvatarsList", + "Return result set key [\"result\"] for nearby avatars in a range of [\"dist\"]\n" + "if [\"dist\"] is not specified, 'RenderFarClip' setting is used\n" + "reply contains \"result\" table with \"id\", \"name\", \"global_pos\", \"region_pos\" fields", + &LLAgentListener::getNearbyAvatarsList, + llsd::map("reply", LLSD())); + + add("getNearbyObjectsList", + "Return result set key [\"result\"] for nearby objects in a range of [\"dist\"]\n" + "if [\"dist\"] is not specified, 'RenderFarClip' setting is used\n" + "reply contains \"result\" table with \"id\", \"global_pos\", \"region_pos\" fields", + &LLAgentListener::getNearbyObjectsList, + llsd::map("reply", LLSD())); } void LLAgentListener::requestTeleport(LLSD const & event_data) const @@ -693,3 +716,97 @@ void LLAgentListener::getAnimationInfo(LLSD const &event_data) "priority", motion->getPriority()); } } + +void LLAgentListener::getID(LLSD const& event_data) +{ + Response response(llsd::map("id", gAgentID), event_data); +} + +struct AvResultSet : public LL::ResultSet +{ + AvResultSet() : LL::ResultSet("nearby_avatars") {} + std::vector mAvatars; + + int getLength() const override { return narrow(mAvatars.size()); } + LLSD getSingle(int index) const override + { + auto av = mAvatars[index]; + LLAvatarName av_name; + LLAvatarNameCache::get(av->getID(), &av_name); + LLVector3 region_pos = av->getCharacterPosition(); + return llsd::map("id", av->getID(), + "global_pos", ll_sd_from_vector3d(av->getPosGlobalFromAgent(region_pos)), + "region_pos", ll_sd_from_vector3(region_pos), + "name", av_name.getUserName()); + } +}; + +struct ObjResultSet : public LL::ResultSet +{ + ObjResultSet() : LL::ResultSet("nearby_objects") {} + std::vector mObjects; + + int getLength() const override { return narrow(mObjects.size()); } + LLSD getSingle(int index) const override + { + auto obj = mObjects[index]; + return llsd::map("id", obj->getID(), + "global_pos", ll_sd_from_vector3d(obj->getPositionGlobal()), + "region_pos", ll_sd_from_vector3(obj->getPositionRegion())); + } +}; + + +F32 get_search_radius(LLSD const& event_data) +{ + static LLCachedControl render_far_clip(gSavedSettings, "RenderFarClip", 64); + F32 dist = render_far_clip; + if (event_data.has("dist")) + { + dist = llclamp((F32)event_data["dist"].asReal(), 1, 512); + } + return dist * dist; +} + +void LLAgentListener::getNearbyAvatarsList(LLSD const& event_data) +{ + Response response(LLSD(), event_data); + auto avresult = new AvResultSet; + + F32 radius = get_search_radius(event_data); + LLVector3d agent_pos = gAgent.getPositionGlobal(); + for (LLCharacter* character : LLCharacter::sInstances) + { + LLVOAvatar* avatar = (LLVOAvatar*)character; + if (!avatar->isDead() && !avatar->isControlAvatar() && !avatar->isSelf()) + { + if ((dist_vec_squared(avatar->getPositionGlobal(), agent_pos) <= radius)) + { + avresult->mAvatars.push_back(avatar); + } + } + } + response["result"] = avresult->getKeyLength(); +} + +void LLAgentListener::getNearbyObjectsList(LLSD const& event_data) +{ + Response response(LLSD(), event_data); + auto objresult = new ObjResultSet; + + F32 radius = get_search_radius(event_data); + S32 num_objects = gObjectList.getNumObjects(); + LLVector3d agent_pos = gAgent.getPositionGlobal(); + for (S32 i = 0; i < num_objects; ++i) + { + LLViewerObject* object = gObjectList.getObject(i); + if (object && object->getVolume() && !object->isAttachment()) + { + if ((dist_vec_squared(object->getPositionGlobal(), agent_pos) <= radius)) + { + objresult->mObjects.push_back(object); + } + } + } + response["result"] = objresult->getKeyLength(); +} diff --git a/indra/newview/llagentlistener.h b/indra/newview/llagentlistener.h index 05724ff443..f1e85f0923 100644 --- a/indra/newview/llagentlistener.h +++ b/indra/newview/llagentlistener.h @@ -67,6 +67,10 @@ private: void stopAnimation(LLSD const &event_data); void getAnimationInfo(LLSD const &event_data); + void getID(LLSD const& event_data); + void getNearbyAvatarsList(LLSD const& event_data); + void getNearbyObjectsList(LLSD const& event_data); + LLViewerObject * findObjectClosestTo( const LLVector3 & position, bool sit_target = false ) const; private: diff --git a/indra/newview/scripts/lua/require/LLAgent.lua b/indra/newview/scripts/lua/require/LLAgent.lua index 4a1132fe7e..70f3cdfffb 100644 --- a/indra/newview/scripts/lua/require/LLAgent.lua +++ b/indra/newview/scripts/lua/require/LLAgent.lua @@ -1,8 +1,26 @@ local leap = require 'leap' local mapargs = require 'mapargs' +local result_view = require 'result_view' + +local function result(keys) + local result_table = { + result=result_view(keys.result), + -- call result_table:close() to release result sets before garbage + -- collection or script completion + close = function(self) + result_view.close(keys.result[1]) + end + } + -- When the result_table is destroyed, close its result_views. + return LL.setdtor('LLAgent result', result_table, result_table.close) +end local LLAgent = {} +function LLAgent.getID() + return leap.request('LLAgent', {op = 'getID'}).id +end + function LLAgent.getRegionPosition() return leap.request('LLAgent', {op = 'getPosition'}).region end @@ -95,6 +113,24 @@ function LLAgent.requestStand() leap.send('LLAgent', {op = 'requestStand'}) end +-- Get the nearby avatars in a range of provided "dist", +-- if "dist" is not specified, "RenderFarClip" setting is used +-- reply will contain "result" table with following fields: +-- "id", "global_pos", "region_pos", "name" +function LLAgent.getNearbyAvatarsList(...) + local args = mapargs('dist', ...) + args.op = 'getNearbyAvatarsList' + return result(leap.request('LLAgent', args)) +end + +-- reply will contain "result" table with following fields: +-- "id", "global_pos", "region_pos" +function LLAgent.getNearbyObjectsList(...) + local args = mapargs('dist', ...) + args.op = 'getNearbyObjectsList' + return result(leap.request('LLAgent', args)) +end + -- *************************************************************************** -- Autopilot -- *************************************************************************** diff --git a/indra/newview/scripts/lua/require/UI.lua b/indra/newview/scripts/lua/require/UI.lua index cf2695917e..34f3fb75eb 100644 --- a/indra/newview/scripts/lua/require/UI.lua +++ b/indra/newview/scripts/lua/require/UI.lua @@ -222,8 +222,10 @@ function UI.hideFloater(floater_name) leap.send("LLFloaterReg", {op = "hideInstance", name = floater_name}) end -function UI.toggleFloater(floater_name) - leap.send("LLFloaterReg", {op = "toggleInstance", name = floater_name}) +function UI.toggleFloater(...) + local args = mapargs('name,key', ...) + args.op = 'toggleInstance' + leap.send("LLFloaterReg", args) end function UI.isFloaterVisible(floater_name) diff --git a/indra/newview/scripts/lua/test_nearby_avatars.lua b/indra/newview/scripts/lua/test_nearby_avatars.lua new file mode 100644 index 0000000000..c7fa686076 --- /dev/null +++ b/indra/newview/scripts/lua/test_nearby_avatars.lua @@ -0,0 +1,25 @@ +inspect = require 'inspect' +LLAgent = require 'LLAgent' + +-- Get the list of nearby avatars and print the info +local nearby_avatars = LLAgent.getNearbyAvatarsList() +if nearby_avatars.result.length == 0 then + print("No avatars nearby") +else + print("Nearby avatars:") + for _, av in pairs(nearby_avatars.result) do + print(av.name ..' ID: ' .. av.id ..' Global pos:' .. inspect(av.global_pos)) + end +end + +-- Get the list of nearby objects in 3m range and print the info +local nearby_objects = LLAgent.getNearbyObjectsList(3) +if nearby_objects.result.length == 0 then + print("No objects nearby") +else + print("Nearby objects:") + local pos={} + for _, obj in pairs(nearby_objects.result) do + print('ID: ' .. obj.id ..' Global pos:' .. inspect(obj.global_pos)) + end +end -- cgit v1.2.3 From 8a44149b481bf118d30a457514711c6ba4cf29b6 Mon Sep 17 00:00:00 2001 From: Mnikolenko Productengine Date: Fri, 4 Oct 2024 18:47:34 +0300 Subject: remove trailing whitespace --- indra/newview/llagentlistener.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/indra/newview/llagentlistener.cpp b/indra/newview/llagentlistener.cpp index f00173dc13..f6029e7297 100644 --- a/indra/newview/llagentlistener.cpp +++ b/indra/newview/llagentlistener.cpp @@ -750,7 +750,7 @@ struct ObjResultSet : public LL::ResultSet LLSD getSingle(int index) const override { auto obj = mObjects[index]; - return llsd::map("id", obj->getID(), + return llsd::map("id", obj->getID(), "global_pos", ll_sd_from_vector3d(obj->getPositionGlobal()), "region_pos", ll_sd_from_vector3(obj->getPositionRegion())); } -- cgit v1.2.3 From d76e8dd61120fd82dbfbed786d803d16af7d6d4d Mon Sep 17 00:00:00 2001 From: Andrey Kleshchev Date: Thu, 3 Oct 2024 23:08:52 +0300 Subject: viewer-private#300 Fix inconsistency with multiple textures --- indra/newview/lltexturectrl.cpp | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/indra/newview/lltexturectrl.cpp b/indra/newview/lltexturectrl.cpp index 35057a910a..f125d81430 100644 --- a/indra/newview/lltexturectrl.cpp +++ b/indra/newview/lltexturectrl.cpp @@ -1029,8 +1029,11 @@ void LLFloaterTexturePicker::onBtnCancel(void* userdata) void LLFloaterTexturePicker::onBtnSelect(void* userdata) { LLFloaterTexturePicker* self = (LLFloaterTexturePicker*) userdata; - if (self->mOnFloaterCommitCallback) + if (self->mViewModel->isDirty() && self->mOnFloaterCommitCallback) { + // If nothing changed, don't commit. + // ex: can overwrite multiple original textures with a single one. + // or resubmit something thus overriding some other source of change self->commitCallback(LLTextureCtrl::TEXTURE_SELECT); } self->closeFloater(); @@ -1067,8 +1070,18 @@ void LLFloaterTexturePicker::onSelectionChange(const std::dequeisDirty(); setImageIDFromItem(itemp, false); - mViewModel->setDirty(); // *TODO: shouldn't we be using setValue() here? + if (user_action) + { + mViewModel->setDirty(); // *TODO: shouldn't we be using setValue() here? + setTentative( false ); + } + else if (!was_dirty) + { + // setImageIDFromItem could have dropped the flag + mViewModel->resetDirty(); + } if(!mPreviewSettingChanged) { -- cgit v1.2.3 From 4339ad9a528c82946ce40ec2bd240b41d104f338 Mon Sep 17 00:00:00 2001 From: Andrey Kleshchev Date: Thu, 3 Oct 2024 23:10:17 +0300 Subject: viewer-private#300 Fix inconsistency of finding inventory id when picker was in a different mode --- indra/newview/lltexturectrl.cpp | 33 +++++++++++++++++++-------------- indra/newview/lltexturectrl.h | 1 + 2 files changed, 20 insertions(+), 14 deletions(-) diff --git a/indra/newview/lltexturectrl.cpp b/indra/newview/lltexturectrl.cpp index f125d81430..db3e6335ec 100644 --- a/indra/newview/lltexturectrl.cpp +++ b/indra/newview/lltexturectrl.cpp @@ -185,7 +185,8 @@ LLFloaterTexturePicker::LLFloaterTexturePicker( mSetImageAssetIDCallback(NULL), mOnUpdateImageStatsCallback(NULL), mBakeTextureEnabled(false), - mInventoryPickType(pick_type) + mInventoryPickType(pick_type), + mSelectionSource(PICKER_UNKNOWN) { mCanApplyImmediately = can_apply_immediately; buildFromFile("floater_texture_ctrl.xml"); @@ -203,6 +204,7 @@ void LLFloaterTexturePicker::setImageID(const LLUUID& image_id, bool set_selecti mNoCopyTextureSelected = false; mViewModel->setDirty(); // *TODO: shouldn't we be using setValue() here? mImageAssetID = image_id; + mSelectionSource = PICKER_UNKNOWN; if (LLAvatarAppearanceDefines::LLAvatarAppearanceDictionary::isBakedImageId(mImageAssetID)) { @@ -211,6 +213,7 @@ void LLFloaterTexturePicker::setImageID(const LLUUID& image_id, bool set_selecti mModeSelector->selectByValue(2); onModeSelect(0,this); } + mSelectionSource = PICKER_BAKE; } else { @@ -257,6 +260,7 @@ void LLFloaterTexturePicker::setImageID(const LLUUID& image_id, bool set_selecti getChild("apply_immediate_check")->setValue(false); mNoCopyTextureSelected = true; } + mSelectionSource = PICKER_INVENTORY; } if (set_selection) @@ -276,6 +280,7 @@ void LLFloaterTexturePicker::setImageIDFromItem(const LLInventoryItem* itemp, bo asset_id = BLANK_MATERIAL_ASSET_ID; } setImageID(asset_id, set_selection); + mSelectionSource = PICKER_INVENTORY; } void LLFloaterTexturePicker::setActive( bool active ) @@ -890,10 +895,15 @@ void LLFloaterTexturePicker::commitCallback(LLTextureCtrl::ETexturePickOp op) { return; } + LLUUID asset_id = mImageAssetID; LLUUID inventory_id; LLUUID tracking_id; - LLPickerSource mode = (LLPickerSource)mModeSelector->getValue().asInteger(); + LLPickerSource mode = mSelectionSource; + if (mode == PICKER_UNKNOWN) + { + mode = (LLPickerSource)mModeSelector->getValue().asInteger(); + } switch (mode) { @@ -919,11 +929,13 @@ void LLFloaterTexturePicker::commitCallback(LLTextureCtrl::ETexturePickOp op) } else { + // Item's asset id changed? mode = PICKER_UNKNOWN; // source of id unknown } } else { + // Item could have been removed from inventory mode = PICKER_UNKNOWN; // source of id unknown } break; @@ -947,6 +959,7 @@ void LLFloaterTexturePicker::commitCallback(LLTextureCtrl::ETexturePickOp op) } else { + // List could have been emptied, with local image still selected asset_id = mImageAssetID; mode = PICKER_UNKNOWN; // source of id unknown } @@ -1000,18 +1013,6 @@ void LLFloaterTexturePicker::onBtnNone(void* userdata) self->commitIfImmediateSet(); } -/* -// static -void LLFloaterTexturePicker::onBtnRevert(void* userdata) -{ - LLFloaterTexturePicker* self = (LLFloaterTexturePicker*) userdata; - self->setImageID( self->mOriginalImageAssetID ); - // TODO: Change this to tell the owner to cancel. It needs to be - // smart enough to restore multi-texture selections. - self->mOwner->onFloaterCommit(); - self->mViewModel->resetDirty(); -}*/ - // static void LLFloaterTexturePicker::onBtnCancel(void* userdata) { @@ -1228,6 +1229,8 @@ void LLFloaterTexturePicker::onLocalScrollCommit(LLUICtrl* ctrl, void* userdata) inworld_id = LLLocalBitmapMgr::getInstance()->getWorldID(tracking_id); } + self->mSelectionSource = PICKER_LOCAL; + if (self->mSetImageAssetIDCallback) { self->mSetImageAssetIDCallback(inworld_id); @@ -1325,6 +1328,7 @@ void LLFloaterTexturePicker::onBakeTextureSelect(LLUICtrl* ctrl, void *user_data // only commit intentional selections, not implicit ones self->commitIfImmediateSet(); } + self->mSelectionSource = PICKER_BAKE; } void LLFloaterTexturePicker::setCanApply(bool can_preview, bool can_apply, bool inworld_image) @@ -1640,6 +1644,7 @@ void LLFloaterTexturePicker::onTextureSelect( const LLTextureEntry& te ) // no copy texture mNoCopyTextureSelected = true; } + mSelectionSource = PICKER_INVENTORY; commitIfImmediateSet(); } diff --git a/indra/newview/lltexturectrl.h b/indra/newview/lltexturectrl.h index df5e763139..e4d877e229 100644 --- a/indra/newview/lltexturectrl.h +++ b/indra/newview/lltexturectrl.h @@ -447,6 +447,7 @@ private: S32 mMaxDim; S32 mMinDim; EPickInventoryType mInventoryPickType; + LLPickerSource mSelectionSource; texture_selected_callback mTextureSelectedCallback; -- cgit v1.2.3 From d904d54b8fe9957cd7754eff59978ede6e8111c2 Mon Sep 17 00:00:00 2001 From: Nat Goodspeed Date: Fri, 4 Oct 2024 12:53:38 -0400 Subject: Use approximate equality for failing v3dmath test. Modernize lltut.h. Allow both F32 and F64 for ensure_approximately_equals() etc. Use std::string_view instead of const char* for message string to allow passing std::string as well. Use stringize() instead of explicit std::stringstream idiom. --- indra/llmath/tests/v3dmath_test.cpp | 13 ++--------- indra/test/lltut.h | 45 ++++++++++++++----------------------- 2 files changed, 19 insertions(+), 39 deletions(-) diff --git a/indra/llmath/tests/v3dmath_test.cpp b/indra/llmath/tests/v3dmath_test.cpp index db08419012..36ad1067a2 100644 --- a/indra/llmath/tests/v3dmath_test.cpp +++ b/indra/llmath/tests/v3dmath_test.cpp @@ -504,14 +504,11 @@ namespace tut template<> template<> void v3dmath_object::test<24>() { -#if LL_WINDOWS && _MSC_VER < 1400 - skip("This fails on VS2003!"); -#else F64 x = 10., y = 20., z = -15.; F64 angle1, angle2; LLVector3d vec3Da(x,y,z), vec3Db(x,y,z); angle1 = angle_between(vec3Da, vec3Db); - ensure("1:angle_between: Fail ", (0 == angle1)); + ensure_approximately_equals_range("1:angle_between: Fail ", angle1, 0., 1.5e-8); F64 x1 = -1., y1 = -20., z1 = -1.; vec3Da.clearVec(); vec3Da.setVec(x1,y1,z1); @@ -520,12 +517,6 @@ namespace tut vec3Da.normVec(); F64 angle = vec3Db*vec3Da; angle = acos(angle); -#if LL_WINDOWS && _MSC_VER > 1900 - skip("This fails on VS2017!"); -#else - ensure("2:angle_between: Fail ", (angle == angle2)); -#endif - -#endif + ensure_equals("2:angle_between: Fail ", angle, angle2); } } diff --git a/indra/test/lltut.h b/indra/test/lltut.h index fbf60444be..3855eae7a2 100644 --- a/indra/test/lltut.h +++ b/indra/test/lltut.h @@ -30,8 +30,10 @@ #define LL_LLTUT_H #include "is_approx_equal_fraction.h" // instead of llmath.h +#include "stringize.h" #include #include +#include #include class LLDate; @@ -89,49 +91,38 @@ namespace tut // The functions BELOW this point actually consume tut.hpp functionality. namespace tut { - inline void ensure_approximately_equals(const char* msg, F64 actual, F64 expected, U32 frac_bits) + template // replace with C++20 floating-point concept + inline void ensure_approximately_equals(std::string_view msg, F actual, F expected, U32 frac_bits) { if(!is_approx_equal_fraction(actual, expected, frac_bits)) { - std::stringstream ss; - ss << (msg?msg:"") << (msg?": ":"") << "not equal actual: " << actual << " expected: " << expected; - throw tut::failure(ss.str().c_str()); + throw tut::failure(stringize(msg, (msg.empty()?"":": "), "not equal actual: ", + actual, " expected: ", expected)); } } - inline void ensure_approximately_equals(const char* msg, F32 actual, F32 expected, U32 frac_bits) + template // replace with C++20 floating-point concept + inline void ensure_approximately_equals(F actual, F expected, U32 frac_bits) { - if(!is_approx_equal_fraction(actual, expected, frac_bits)) - { - std::stringstream ss; - ss << (msg?msg:"") << (msg?": ":"") << "not equal actual: " << actual << " expected: " << expected; - throw tut::failure(ss.str().c_str()); - } - } - - inline void ensure_approximately_equals(F32 actual, F32 expected, U32 frac_bits) - { - ensure_approximately_equals(NULL, actual, expected, frac_bits); + ensure_approximately_equals("", actual, expected, frac_bits); } - inline void ensure_approximately_equals_range(const char *msg, F32 actual, F32 expected, F32 delta) + template // replace with C++20 floating-point concept + inline void ensure_approximately_equals_range(std::string_view msg, F actual, F expected, F delta) { if (fabs(actual-expected)>delta) { - std::stringstream ss; - ss << (msg?msg:"") << (msg?": ":"") << "not equal actual: " << actual << " expected: " << expected << " tolerance: " << delta; - throw tut::failure(ss.str().c_str()); + throw tut::failure(stringize(msg, (msg.empty()?"":": "), "not equal actual: ", + actual, " expected: ", expected, " tolerance: ", delta)); } } - inline void ensure_memory_matches(const char* msg,const void* actual, U32 actual_len, const void* expected,U32 expected_len) + inline void ensure_memory_matches(std::string_view msg,const void* actual, U32 actual_len, const void* expected,U32 expected_len) { if((expected_len != actual_len) || (std::memcmp(actual, expected, actual_len) != 0)) { - std::stringstream ss; - ss << (msg?msg:"") << (msg?": ":"") << "not equal"; - throw tut::failure(ss.str().c_str()); + throw tut::failure(stringize(msg, (msg.empty()?"":": "), "not equal")); } } @@ -141,13 +132,11 @@ namespace tut } template - void ensure_not_equals(const char* msg,const Q& actual,const T& expected) + void ensure_not_equals(std::string_view msg,const Q& actual,const T& expected) { if( expected == actual ) { - std::stringstream ss; - ss << (msg?msg:"") << (msg?": ":"") << "both equal " << expected; - throw tut::failure(ss.str().c_str()); + throw tut::failure(stringize(msg, (msg.empty()?"":": "), "both equal ", expected)); } } -- cgit v1.2.3 From 16e4747dcd04c78b438570ee6dea4ccc52e7485c Mon Sep 17 00:00:00 2001 From: Brad Linden Date: Fri, 4 Oct 2024 10:32:00 -0700 Subject: Make linux build failures not block other platforms. See "Example: Preventing a specific failing matrix job from failing a workflow run" https://docs.github.com/en/actions/writing-workflows/workflow-syntax-for-github-actions#example-preventing-a-specific-failing-matrix-job-from-failing-a-workflow-run --- .github/workflows/build.yaml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index 1804da749f..4ab627bdfe 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -44,6 +44,7 @@ jobs: fi build: needs: setup + continue-on-error: ${{ matrix.experimental }} strategy: matrix: runner: [windows-large, macos-12-xl] @@ -51,6 +52,7 @@ jobs: include: - runner: linux-large configuration: ReleaseOS + experimental: true runs-on: ${{ matrix.runner }} outputs: viewer_channel: ${{ steps.build.outputs.viewer_channel }} -- cgit v1.2.3 From 0d0ec4c20dd8de752b80ab4d271eac1fa9a2f76d Mon Sep 17 00:00:00 2001 From: Rye Date: Fri, 4 Oct 2024 11:11:36 -0700 Subject: Fix GHA build config error --- .github/workflows/build.yaml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index 7df105e2d5..a287f29b95 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -47,8 +47,9 @@ jobs: continue-on-error: ${{ matrix.experimental }} strategy: matrix: - runner: [windows-large, macos-12-xl, linux-large] + runner: [windows-large, macos-12-xl] configuration: ${{ fromJSON(needs.setup.outputs.configurations) }} + experimental: [false] include: - runner: linux-large experimental: true -- cgit v1.2.3 From 6c51a262d8b576355622eadeef156ef4e6135266 Mon Sep 17 00:00:00 2001 From: Brad Linden <46733234+brad-linden@users.noreply.github.com> Date: Fri, 4 Oct 2024 17:39:04 -0700 Subject: Fix mac link error using GL 4.3 functions (#2813) --- indra/llrender/llgltexture.cpp | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/indra/llrender/llgltexture.cpp b/indra/llrender/llgltexture.cpp index bb4205b319..c2298d5377 100644 --- a/indra/llrender/llgltexture.cpp +++ b/indra/llrender/llgltexture.cpp @@ -190,6 +190,13 @@ void LLGLTexture::getGLObjectLabel(std::string& label, bool& error) const label.clear(); return; } + +#if LL_DARWIN + // apple doesn't support GL after 4.1 so should have hit the above early out, but make the compiler happy here + error = true; + label.clear(); + return; +#else static GLsizei max_length = 0; if (max_length == 0) { glGetIntegerv(GL_MAX_LABEL_LENGTH, &max_length); } static char * clabel = new char[max_length+1]; @@ -197,12 +204,13 @@ void LLGLTexture::getGLObjectLabel(std::string& label, bool& error) const glGetObjectLabel(GL_TEXTURE, texname, max_length+1, &length, clabel); error = false; label.assign(clabel, length); +#endif } std::string LLGLTexture::setGLObjectLabel(const std::string& prefix, bool append_texname) const { +#ifndef LL_DARWIN // apple doesn't support GL > 4.1 if (gGLManager.mGLVersion < 4.29f) { return ""; } // GL_VERSION_4_3 - llassert(mGLTexturep); if (mGLTexturep) { @@ -236,6 +244,7 @@ std::string LLGLTexture::setGLObjectLabel(const std::string& prefix, bool append } } } +#endif return ""; } -- cgit v1.2.3 From 39eb250e3926e123794a939e861e62d96916e92e Mon Sep 17 00:00:00 2001 From: Ansariel Hiller Date: Sat, 5 Oct 2024 02:41:14 +0200 Subject: More cleanup of LLMath and the color classes in particular (#2810) --- indra/llmath/camera.h | 27 -- indra/llmath/coordframe.h | 27 -- indra/llmath/llcalc.cpp | 11 - indra/llmath/llcalc.h | 7 +- indra/llmath/llcalcparser.h | 1 - indra/llmath/llcoordframe.h | 4 +- indra/llmath/llinterp.h | 2 +- indra/llmath/llline.h | 2 +- indra/llmath/m3math.h | 1 - indra/llmath/v3color.cpp | 122 ++++---- indra/llmath/v3color.h | 425 +++++++++++++------------- indra/llmath/v3colorutil.h | 68 ++--- indra/llmath/v4color.cpp | 393 ++++++++++++------------ indra/llmath/v4color.h | 706 +++++++++++++++++++++----------------------- indra/llmath/v4coloru.cpp | 67 +---- indra/llmath/v4coloru.h | 371 +++++++++-------------- indra/llmath/xform.h | 2 +- 17 files changed, 980 insertions(+), 1256 deletions(-) delete mode 100644 indra/llmath/camera.h delete mode 100644 indra/llmath/coordframe.h diff --git a/indra/llmath/camera.h b/indra/llmath/camera.h deleted file mode 100644 index 14a08c5985..0000000000 --- a/indra/llmath/camera.h +++ /dev/null @@ -1,27 +0,0 @@ -/** - * @file camera.h - * @brief Legacy wrapper header. - * - * $LicenseInfo:firstyear=2000&license=viewerlgpl$ - * Second Life Viewer Source Code - * Copyright (C) 2010, Linden Research, Inc. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; - * version 2.1 of the License only. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * - * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA - * $/LicenseInfo$ - */ - -#include "llcamera.h" diff --git a/indra/llmath/coordframe.h b/indra/llmath/coordframe.h deleted file mode 100644 index e7395b1212..0000000000 --- a/indra/llmath/coordframe.h +++ /dev/null @@ -1,27 +0,0 @@ -/** - * @file coordframe.h - * @brief Legacy wrapper header. - * - * $LicenseInfo:firstyear=2000&license=viewerlgpl$ - * Second Life Viewer Source Code - * Copyright (C) 2010, Linden Research, Inc. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; - * version 2.1 of the License only. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * - * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA - * $/LicenseInfo$ - */ - -#include "llcoordframe.h" diff --git a/indra/llmath/llcalc.cpp b/indra/llmath/llcalc.cpp index e060b5df58..311e1e7059 100644 --- a/indra/llmath/llcalc.cpp +++ b/indra/llmath/llcalc.cpp @@ -116,17 +116,6 @@ void LLCalc::clearAllVariables() mVariables.clear(); } -/* -void LLCalc::updateVariables(LLSD& vars) -{ - LLSD::map_iterator cIt = vars.beginMap(); - for(; cIt != vars.endMap(); cIt++) - { - setVar(cIt->first, (F32)(LLSD::Real)cIt->second); - } -} -*/ - bool LLCalc::evalString(const std::string& expression, F32& result) { std::string expr_upper = expression; diff --git a/indra/llmath/llcalc.h b/indra/llmath/llcalc.h index 09672eb13b..14797de996 100644 --- a/indra/llmath/llcalc.h +++ b/indra/llmath/llcalc.h @@ -73,10 +73,9 @@ public: void setVar(const std::string& name, const F32& value); void clearVar(const std::string& name); void clearAllVariables(); -// void updateVariables(LLSD& vars); bool evalString(const std::string& expression, F32& result); - std::string::size_type getLastErrorPos() { return mLastErrorPos; } + std::string::size_type getLastErrorPos() const { return mLastErrorPos; } static LLCalc* getInstance(); static void cleanUp(); @@ -89,10 +88,6 @@ private: calc_map_t mConstants; calc_map_t mVariables; - // *TODO: Add support for storing user defined variables, and stored functions. - // Will need UI work, and a means to save them between sessions. -// calc_map_t mUserVariables; - // "There shall be only one" static LLCalc* sInstance; }; diff --git a/indra/llmath/llcalcparser.h b/indra/llmath/llcalcparser.h index ec7f6a2cb6..b7f84a6546 100644 --- a/indra/llmath/llcalcparser.h +++ b/indra/llmath/llcalcparser.h @@ -182,7 +182,6 @@ private: LLCalc::calc_map_t* mConstants; LLCalc::calc_map_t* mVariables; -// LLCalc::calc_map_t* mUserVariables; F32& mResult; }; diff --git a/indra/llmath/llcoordframe.h b/indra/llmath/llcoordframe.h index aaa701f792..391d58dc27 100644 --- a/indra/llmath/llcoordframe.h +++ b/indra/llmath/llcoordframe.h @@ -61,7 +61,7 @@ public: //LLCoordFrame(const F32 *origin, const F32 *rotation); // Assumes "origin" is 1x3 and "rotation" is 1x9 array //LLCoordFrame(const F32 *origin_and_rotation); // Assumes "origin_and_rotation" is 1x12 array - bool isFinite() { return mOrigin.isFinite() && mXAxis.isFinite() && mYAxis.isFinite() && mZAxis.isFinite(); } + bool isFinite() const { return mOrigin.isFinite() && mXAxis.isFinite() && mYAxis.isFinite() && mZAxis.isFinite(); } void reset(); void resetAxes(); @@ -118,8 +118,6 @@ public: // Same as above, except it also includes the translation of the LLFrame // LLMatrix4 getMatrix4() const; // Returns position and axes in 4x4 matrix - // Returns matrix which expresses point in local frame in the parent frame - void getMatrixToParent(LLMatrix4 &mat) const; // Returns matrix which expresses point in parent frame in the local frame void getMatrixToLocal(LLMatrix4 &mat) const; // Returns matrix which expresses point in parent frame in the local frame diff --git a/indra/llmath/llinterp.h b/indra/llmath/llinterp.h index f4faa82a82..ef6a5e638d 100644 --- a/indra/llmath/llinterp.h +++ b/indra/llmath/llinterp.h @@ -354,7 +354,7 @@ void LLInterpAttractor::update(const F32 time) ////////////////////////////// // -// LLInterpFucn derived class implementation. +// LLInterpFunc derived class implementation. // diff --git a/indra/llmath/llline.h b/indra/llmath/llline.h index 33c1eb61a4..e98e173d1f 100644 --- a/indra/llmath/llline.h +++ b/indra/llmath/llline.h @@ -33,7 +33,7 @@ #include "stdtypes.h" #include "v3math.h" -const F32 DEFAULT_INTERSECTION_ERROR = 0.000001f; +constexpr F32 DEFAULT_INTERSECTION_ERROR = 0.000001f; class LLLine { diff --git a/indra/llmath/m3math.h b/indra/llmath/m3math.h index cd14290246..22b11d13b1 100644 --- a/indra/llmath/m3math.h +++ b/indra/llmath/m3math.h @@ -119,7 +119,6 @@ class LLMatrix3 // foo.rotate(bar) // foo = foo * bar // That is, foo.rotate(bar) multiplies foo by bar FROM THE RIGHT - const LLMatrix3& rotate(const F32 angle, const F32 x, const F32 y, const F32 z); // Rotate matrix by rotating angle radians about (x, y, z) const LLMatrix3& rotate(const F32 angle, const LLVector3 &vec); // Rotate matrix by rotating angle radians about vec const LLMatrix3& rotate(const F32 roll, const F32 pitch, const F32 yaw); // Rotate matrix by roll (about x), pitch (about y), and yaw (about z) const LLMatrix3& rotate(const LLQuaternion &q); // Transform matrix by Euler angles and translating by pos diff --git a/indra/llmath/v3color.cpp b/indra/llmath/v3color.cpp index 4367b993f8..08b3795020 100644 --- a/indra/llmath/v3color.cpp +++ b/indra/llmath/v3color.cpp @@ -32,74 +32,79 @@ LLColor3 LLColor3::white(1.0f, 1.0f, 1.0f); LLColor3 LLColor3::black(0.0f, 0.0f, 0.0f); -LLColor3 LLColor3::grey (0.5f, 0.5f, 0.5f); +LLColor3 LLColor3::grey(0.5f, 0.5f, 0.5f); -LLColor3::LLColor3(const LLColor4 &a) +LLColor3::LLColor3(const LLColor4& a) { - mV[0] = a.mV[0]; - mV[1] = a.mV[1]; - mV[2] = a.mV[2]; + mV[VRED] = a.mV[VRED]; + mV[VGREEN] = a.mV[VGREEN]; + mV[VBLUE] = a.mV[VBLUE]; } -LLColor3::LLColor3(const LLVector4 &a) +LLColor3::LLColor3(const LLVector4& a) { - mV[0] = a.mV[0]; - mV[1] = a.mV[1]; - mV[2] = a.mV[2]; + mV[VRED] = a.mV[VRED]; + mV[VGREEN] = a.mV[VGREEN]; + mV[VBLUE] = a.mV[VBLUE]; } -LLColor3::LLColor3(const LLSD &sd) +LLColor3::LLColor3(const LLSD& sd) { setValue(sd); } -const LLColor3& LLColor3::operator=(const LLColor4 &a) +const LLColor3& LLColor3::operator=(const LLColor4& a) { - mV[0] = a.mV[0]; - mV[1] = a.mV[1]; - mV[2] = a.mV[2]; + mV[VRED] = a.mV[VRED]; + mV[VGREEN] = a.mV[VGREEN]; + mV[VBLUE] = a.mV[VBLUE]; return (*this); } -std::ostream& operator<<(std::ostream& s, const LLColor3 &a) +std::ostream& operator<<(std::ostream& s, const LLColor3& a) { s << "{ " << a.mV[VRED] << ", " << a.mV[VGREEN] << ", " << a.mV[VBLUE] << " }"; return s; } -static F32 hueToRgb ( F32 val1In, F32 val2In, F32 valHUeIn ) +static F32 hueToRgb(F32 val1In, F32 val2In, F32 valHUeIn) { - if ( valHUeIn < 0.0f ) valHUeIn += 1.0f; - if ( valHUeIn > 1.0f ) valHUeIn -= 1.0f; - if ( ( 6.0f * valHUeIn ) < 1.0f ) return ( val1In + ( val2In - val1In ) * 6.0f * valHUeIn ); - if ( ( 2.0f * valHUeIn ) < 1.0f ) return ( val2In ); - if ( ( 3.0f * valHUeIn ) < 2.0f ) return ( val1In + ( val2In - val1In ) * ( ( 2.0f / 3.0f ) - valHUeIn ) * 6.0f ); - return ( val1In ); + if (valHUeIn < 0.0f) + valHUeIn += 1.0f; + if (valHUeIn > 1.0f) + valHUeIn -= 1.0f; + if ((6.0f * valHUeIn) < 1.0f) + return (val1In + (val2In - val1In) * 6.0f * valHUeIn); + if ((2.0f * valHUeIn) < 1.0f) + return (val2In); + if ((3.0f * valHUeIn) < 2.0f) + return (val1In + (val2In - val1In) * ((2.0f / 3.0f) - valHUeIn) * 6.0f); + return (val1In); } -void LLColor3::setHSL ( F32 hValIn, F32 sValIn, F32 lValIn) +void LLColor3::setHSL(F32 hValIn, F32 sValIn, F32 lValIn) { - if ( sValIn < 0.00001f ) + if (sValIn < 0.00001f) { - mV[VRED] = lValIn; + mV[VRED] = lValIn; mV[VGREEN] = lValIn; - mV[VBLUE] = lValIn; + mV[VBLUE] = lValIn; } else { F32 interVal1; F32 interVal2; - if ( lValIn < 0.5f ) - interVal2 = lValIn * ( 1.0f + sValIn ); + if (lValIn < 0.5f) + interVal2 = lValIn * (1.0f + sValIn); else - interVal2 = ( lValIn + sValIn ) - ( sValIn * lValIn ); + interVal2 = (lValIn + sValIn) - (sValIn * lValIn); interVal1 = 2.0f * lValIn - interVal2; - mV[VRED] = hueToRgb ( interVal1, interVal2, hValIn + ( 1.f / 3.f ) ); - mV[VGREEN] = hueToRgb ( interVal1, interVal2, hValIn ); - mV[VBLUE] = hueToRgb ( interVal1, interVal2, hValIn - ( 1.f / 3.f ) ); + mV[VRED] = hueToRgb(interVal1, interVal2, hValIn + (1.f / 3.f)); + mV[VGREEN] = hueToRgb(interVal1, interVal2, hValIn); + mV[VBLUE] = hueToRgb(interVal1, interVal2, hValIn - (1.f / 3.f)); } } @@ -109,45 +114,48 @@ void LLColor3::calcHSL(F32* hue, F32* saturation, F32* luminance) const F32 var_G = mV[VGREEN]; F32 var_B = mV[VBLUE]; - F32 var_Min = ( var_R < ( var_G < var_B ? var_G : var_B ) ? var_R : ( var_G < var_B ? var_G : var_B ) ); - F32 var_Max = ( var_R > ( var_G > var_B ? var_G : var_B ) ? var_R : ( var_G > var_B ? var_G : var_B ) ); + F32 var_Min = (var_R < (var_G < var_B ? var_G : var_B) ? var_R : (var_G < var_B ? var_G : var_B)); + F32 var_Max = (var_R > (var_G > var_B ? var_G : var_B) ? var_R : (var_G > var_B ? var_G : var_B)); F32 del_Max = var_Max - var_Min; - F32 L = ( var_Max + var_Min ) / 2.0f; + F32 L = (var_Max + var_Min) / 2.0f; F32 H = 0.0f; F32 S = 0.0f; - if ( del_Max == 0.0f ) + if (del_Max == 0.0f) { - H = 0.0f; - S = 0.0f; + H = 0.0f; + S = 0.0f; } else { - if ( L < 0.5 ) - S = del_Max / ( var_Max + var_Min ); + if (L < 0.5) + S = del_Max / (var_Max + var_Min); else - S = del_Max / ( 2.0f - var_Max - var_Min ); + S = del_Max / (2.0f - var_Max - var_Min); - F32 del_R = ( ( ( var_Max - var_R ) / 6.0f ) + ( del_Max / 2.0f ) ) / del_Max; - F32 del_G = ( ( ( var_Max - var_G ) / 6.0f ) + ( del_Max / 2.0f ) ) / del_Max; - F32 del_B = ( ( ( var_Max - var_B ) / 6.0f ) + ( del_Max / 2.0f ) ) / del_Max; + F32 del_R = (((var_Max - var_R) / 6.0f) + (del_Max / 2.0f)) / del_Max; + F32 del_G = (((var_Max - var_G) / 6.0f) + (del_Max / 2.0f)) / del_Max; + F32 del_B = (((var_Max - var_B) / 6.0f) + (del_Max / 2.0f)) / del_Max; - if ( var_R >= var_Max ) + if (var_R >= var_Max) H = del_B - del_G; - else - if ( var_G >= var_Max ) - H = ( 1.0f / 3.0f ) + del_R - del_B; - else - if ( var_B >= var_Max ) - H = ( 2.0f / 3.0f ) + del_G - del_R; - - if ( H < 0.0f ) H += 1.0f; - if ( H > 1.0f ) H -= 1.0f; + else if (var_G >= var_Max) + H = (1.0f / 3.0f) + del_R - del_B; + else if (var_B >= var_Max) + H = (2.0f / 3.0f) + del_G - del_R; + + if (H < 0.0f) + H += 1.0f; + if (H > 1.0f) + H -= 1.0f; } - if (hue) *hue = H; - if (saturation) *saturation = S; - if (luminance) *luminance = L; + if (hue) + *hue = H; + if (saturation) + *saturation = S; + if (luminance) + *luminance = L; } diff --git a/indra/llmath/v3color.h b/indra/llmath/v3color.h index f7af469e66..3763fc6725 100644 --- a/indra/llmath/v3color.h +++ b/indra/llmath/v3color.h @@ -33,12 +33,12 @@ class LLVector4; #include "llerror.h" #include "llmath.h" #include "llsd.h" -#include "v3math.h" // needed for linearColor3v implemtation below +#include "v3math.h" // needed for linearColor3v implemtation below #include // LLColor3 = |r g b| -static const U32 LENGTHOFCOLOR3 = 3; +static constexpr U32 LENGTHOFCOLOR3 = 3; class LLColor3 { @@ -50,44 +50,43 @@ public: static LLColor3 grey; public: - LLColor3(); // Initializes LLColor3 to (0, 0, 0) - LLColor3(F32 r, F32 g, F32 b); // Initializes LLColor3 to (r, g, b) - LLColor3(const F32 *vec); // Initializes LLColor3 to (vec[0]. vec[1], vec[2]) - LLColor3(const char *color_string); // html format color ie "#FFDDEE" - explicit LLColor3(const LLColor4& color4); // "explicit" to avoid automatic conversion - explicit LLColor3(const LLVector4& vector4); // "explicit" to avoid automatic conversion + LLColor3(); // Initializes LLColor3 to (0, 0, 0) + LLColor3(F32 r, F32 g, F32 b); // Initializes LLColor3 to (r, g, b) + LLColor3(const F32* vec); // Initializes LLColor3 to (vec[0]. vec[1], vec[2]) + LLColor3(const char* color_string); // html format color ie "#FFDDEE" + explicit LLColor3(const LLColor4& color4); // "explicit" to avoid automatic conversion + explicit LLColor3(const LLVector4& vector4); // "explicit" to avoid automatic conversion LLColor3(const LLSD& sd); - LLSD getValue() const { LLSD ret; - ret[0] = mV[0]; - ret[1] = mV[1]; - ret[2] = mV[2]; + ret[0] = mV[VRED]; + ret[1] = mV[VGREEN]; + ret[2] = mV[VBLUE]; return ret; } void setValue(const LLSD& sd) { - mV[0] = (F32) sd[0].asReal();; - mV[1] = (F32) sd[1].asReal();; - mV[2] = (F32) sd[2].asReal();; + mV[VRED] = (F32)sd[0].asReal(); + mV[VGREEN] = (F32)sd[1].asReal(); + mV[VBLUE] = (F32)sd[2].asReal(); } void setHSL(F32 hue, F32 saturation, F32 luminance); void calcHSL(F32* hue, F32* saturation, F32* luminance) const; - const LLColor3& setToBlack(); // Clears LLColor3 to (0, 0, 0) - const LLColor3& setToWhite(); // Zero LLColor3 to (0, 0, 0) + const LLColor3& setToBlack(); // Clears LLColor3 to (0, 0, 0) + const LLColor3& setToWhite(); // Zero LLColor3 to (0, 0, 0) - const LLColor3& setVec(F32 x, F32 y, F32 z); // deprecated - const LLColor3& setVec(const LLColor3 &vec); // deprecated - const LLColor3& setVec(const F32 *vec); // deprecated + const LLColor3& setVec(F32 x, F32 y, F32 z); // deprecated + const LLColor3& setVec(const LLColor3& vec); // deprecated + const LLColor3& setVec(const F32* vec); // deprecated - const LLColor3& set(F32 x, F32 y, F32 z); // Sets LLColor3 to (x, y, z) - const LLColor3& set(const LLColor3 &vec); // Sets LLColor3 to vec - const LLColor3& set(const F32 *vec); // Sets LLColor3 to vec + const LLColor3& set(F32 x, F32 y, F32 z); // Sets LLColor3 to (x, y, z) + const LLColor3& set(const LLColor3& vec); // Sets LLColor3 to vec + const LLColor3& set(const F32* vec); // Sets LLColor3 to vec // set from a vector of unknown type and size // may leave some data unmodified @@ -99,414 +98,390 @@ public: template void write(std::vector& v) const; - F32 magVec() const; // deprecated - F32 magVecSquared() const; // deprecated - F32 normVec(); // deprecated + F32 magVec() const; // deprecated + F32 magVecSquared() const; // deprecated + F32 normVec(); // deprecated - F32 length() const; // Returns magnitude of LLColor3 - F32 lengthSquared() const; // Returns magnitude squared of LLColor3 - F32 normalize(); // Normalizes and returns the magnitude of LLColor3 + F32 length() const; // Returns magnitude of LLColor3 + F32 lengthSquared() const; // Returns magnitude squared of LLColor3 + F32 normalize(); // Normalizes and returns the magnitude of LLColor3 - F32 brightness() const; // Returns brightness of LLColor3 + F32 brightness() const; // Returns brightness of LLColor3 - const LLColor3& operator=(const LLColor4 &a); + const LLColor3& operator=(const LLColor4& a); - LL_FORCE_INLINE LLColor3 divide(const LLColor3 &col2) + LL_FORCE_INLINE LLColor3 divide(const LLColor3& col2) const { - return LLColor3( - mV[0] / col2.mV[0], - mV[1] / col2.mV[1], - mV[2] / col2.mV[2] ); + return LLColor3(mV[VRED] / col2.mV[VRED], mV[VGREEN] / col2.mV[VGREEN], mV[VBLUE] / col2.mV[VBLUE]); } - LL_FORCE_INLINE LLColor3 color_norm() + LL_FORCE_INLINE LLColor3 color_norm() const { F32 l = length(); - return LLColor3( - mV[0] / l, - mV[1] / l, - mV[2] / l ); + return LLColor3(mV[VRED] / l, mV[VGREEN] / l, mV[VBLUE] / l); } - friend std::ostream& operator<<(std::ostream& s, const LLColor3 &a); // Print a - friend LLColor3 operator+(const LLColor3 &a, const LLColor3 &b); // Return vector a + b - friend LLColor3 operator-(const LLColor3 &a, const LLColor3 &b); // Return vector a minus b + friend std::ostream& operator<<(std::ostream& s, const LLColor3& a); // Print a + friend LLColor3 operator+(const LLColor3& a, const LLColor3& b); // Return vector a + b + friend LLColor3 operator-(const LLColor3& a, const LLColor3& b); // Return vector a minus b - friend const LLColor3& operator+=(LLColor3 &a, const LLColor3 &b); // Return vector a + b - friend const LLColor3& operator-=(LLColor3 &a, const LLColor3 &b); // Return vector a minus b - friend const LLColor3& operator*=(LLColor3 &a, const LLColor3 &b); + friend const LLColor3& operator+=(LLColor3& a, const LLColor3& b); // Return vector a + b + friend const LLColor3& operator-=(LLColor3& a, const LLColor3& b); // Return vector a minus b + friend const LLColor3& operator*=(LLColor3& a, const LLColor3& b); - friend LLColor3 operator*(const LLColor3 &a, const LLColor3 &b); // Return component wise a * b - friend LLColor3 operator*(const LLColor3 &a, F32 k); // Return a times scaler k - friend LLColor3 operator*(F32 k, const LLColor3 &a); // Return a times scaler k + friend LLColor3 operator*(const LLColor3& a, const LLColor3& b); // Return component wise a * b + friend LLColor3 operator*(const LLColor3& a, F32 k); // Return a times scaler k + friend LLColor3 operator*(F32 k, const LLColor3& a); // Return a times scaler k - friend bool operator==(const LLColor3 &a, const LLColor3 &b); // Return a == b - friend bool operator!=(const LLColor3 &a, const LLColor3 &b); // Return a != b + friend bool operator==(const LLColor3& a, const LLColor3& b); // Return a == b + friend bool operator!=(const LLColor3& a, const LLColor3& b); // Return a != b - friend const LLColor3& operator*=(LLColor3 &a, F32 k); // Return a times scaler k + friend const LLColor3& operator*=(LLColor3& a, F32 k); // Return a times scaler k - friend LLColor3 operator-(const LLColor3 &a); // Return vector 1-rgb (inverse) + friend LLColor3 operator-(const LLColor3& a); // Return vector 1-rgb (inverse) inline void clamp(); - inline void exp(); // Do an exponential on the color + inline void exp(); // Do an exponential on the color }; -LLColor3 lerp(const LLColor3 &a, const LLColor3 &b, F32 u); - +LLColor3 lerp(const LLColor3& a, const LLColor3& b, F32 u); void LLColor3::clamp() { // Clamp the color... - if (mV[0] < 0.f) + if (mV[VRED] < 0.f) { - mV[0] = 0.f; + mV[VRED] = 0.f; } - else if (mV[0] > 1.f) + else if (mV[VRED] > 1.f) { - mV[0] = 1.f; + mV[VRED] = 1.f; } - if (mV[1] < 0.f) + if (mV[VGREEN] < 0.f) { - mV[1] = 0.f; + mV[VGREEN] = 0.f; } - else if (mV[1] > 1.f) + else if (mV[VGREEN] > 1.f) { - mV[1] = 1.f; + mV[VGREEN] = 1.f; } - if (mV[2] < 0.f) + if (mV[VBLUE] < 0.f) { - mV[2] = 0.f; + mV[VBLUE] = 0.f; } - else if (mV[2] > 1.f) + else if (mV[VBLUE] > 1.f) { - mV[2] = 1.f; + mV[VBLUE] = 1.f; } } // Non-member functions -F32 distVec(const LLColor3 &a, const LLColor3 &b); // Returns distance between a and b -F32 distVec_squared(const LLColor3 &a, const LLColor3 &b);// Returns distance squared between a and b +F32 distVec(const LLColor3& a, const LLColor3& b); // Returns distance between a and b +F32 distVec_squared(const LLColor3& a, const LLColor3& b); // Returns distance squared between a and b -inline LLColor3::LLColor3(void) +inline LLColor3::LLColor3() { - mV[0] = 0.f; - mV[1] = 0.f; - mV[2] = 0.f; + mV[VRED] = 0.f; + mV[VGREEN] = 0.f; + mV[VBLUE] = 0.f; } inline LLColor3::LLColor3(F32 r, F32 g, F32 b) { - mV[VRED] = r; + mV[VRED] = r; mV[VGREEN] = g; - mV[VBLUE] = b; + mV[VBLUE] = b; } - -inline LLColor3::LLColor3(const F32 *vec) +inline LLColor3::LLColor3(const F32* vec) { - mV[VRED] = vec[VRED]; + mV[VRED] = vec[VRED]; mV[VGREEN] = vec[VGREEN]; - mV[VBLUE] = vec[VBLUE]; + mV[VBLUE] = vec[VBLUE]; } inline LLColor3::LLColor3(const char* color_string) // takes a string of format "RRGGBB" where RR is hex 00..FF { - if (strlen(color_string) < 6) /* Flawfinder: ignore */ + if (strlen(color_string) < 6) /* Flawfinder: ignore */ { - mV[0] = 0.f; - mV[1] = 0.f; - mV[2] = 0.f; + mV[VRED] = 0.f; + mV[VGREEN] = 0.f; + mV[VBLUE] = 0.f; return; } char tempstr[7]; - strncpy(tempstr,color_string,6); /* Flawfinder: ignore */ + strncpy(tempstr, color_string, 6); /* Flawfinder: ignore */ tempstr[6] = '\0'; - mV[VBLUE] = (F32)strtol(&tempstr[4],NULL,16)/255.f; + mV[VBLUE] = (F32)strtol(&tempstr[4], nullptr, 16) / 255.f; tempstr[4] = '\0'; - mV[VGREEN] = (F32)strtol(&tempstr[2],NULL,16)/255.f; + mV[VGREEN] = (F32)strtol(&tempstr[2], nullptr, 16) / 255.f; tempstr[2] = '\0'; - mV[VRED] = (F32)strtol(&tempstr[0],NULL,16)/255.f; + mV[VRED] = (F32)strtol(&tempstr[0], nullptr, 16) / 255.f; } -inline const LLColor3& LLColor3::setToBlack(void) +inline const LLColor3& LLColor3::setToBlack() { - mV[0] = 0.f; - mV[1] = 0.f; - mV[2] = 0.f; + mV[VRED] = 0.f; + mV[VGREEN] = 0.f; + mV[VBLUE] = 0.f; return (*this); } -inline const LLColor3& LLColor3::setToWhite(void) +inline const LLColor3& LLColor3::setToWhite() { - mV[0] = 1.f; - mV[1] = 1.f; - mV[2] = 1.f; + mV[VRED] = 1.f; + mV[VGREEN] = 1.f; + mV[VBLUE] = 1.f; return (*this); } -inline const LLColor3& LLColor3::set(F32 r, F32 g, F32 b) +inline const LLColor3& LLColor3::set(F32 r, F32 g, F32 b) { - mV[0] = r; - mV[1] = g; - mV[2] = b; + mV[VRED] = r; + mV[VGREEN] = g; + mV[VBLUE] = b; return (*this); } -inline const LLColor3& LLColor3::set(const LLColor3 &vec) +inline const LLColor3& LLColor3::set(const LLColor3& vec) { - mV[0] = vec.mV[0]; - mV[1] = vec.mV[1]; - mV[2] = vec.mV[2]; + mV[VRED] = vec.mV[VRED]; + mV[VGREEN] = vec.mV[VGREEN]; + mV[VBLUE] = vec.mV[VBLUE]; return (*this); } -inline const LLColor3& LLColor3::set(const F32 *vec) +inline const LLColor3& LLColor3::set(const F32* vec) { - mV[0] = vec[0]; - mV[1] = vec[1]; - mV[2] = vec[2]; + mV[VRED] = vec[0]; + mV[VGREEN] = vec[1]; + mV[VBLUE] = vec[2]; return (*this); } // deprecated -inline const LLColor3& LLColor3::setVec(F32 r, F32 g, F32 b) +inline const LLColor3& LLColor3::setVec(F32 r, F32 g, F32 b) { - mV[0] = r; - mV[1] = g; - mV[2] = b; + mV[VRED] = r; + mV[VGREEN] = g; + mV[VBLUE] = b; return (*this); } // deprecated -inline const LLColor3& LLColor3::setVec(const LLColor3 &vec) +inline const LLColor3& LLColor3::setVec(const LLColor3& vec) { - mV[0] = vec.mV[0]; - mV[1] = vec.mV[1]; - mV[2] = vec.mV[2]; + mV[VRED] = vec.mV[VRED]; + mV[VGREEN] = vec.mV[VGREEN]; + mV[VBLUE] = vec.mV[VBLUE]; return (*this); } // deprecated -inline const LLColor3& LLColor3::setVec(const F32 *vec) +inline const LLColor3& LLColor3::setVec(const F32* vec) { - mV[0] = vec[0]; - mV[1] = vec[1]; - mV[2] = vec[2]; + mV[VRED] = vec[0]; + mV[VGREEN] = vec[1]; + mV[VBLUE] = vec[2]; return (*this); } -inline F32 LLColor3::brightness(void) const +inline F32 LLColor3::brightness() const { - return (mV[0] + mV[1] + mV[2]) / 3.0f; + return (mV[VRED] + mV[VGREEN] + mV[VBLUE]) / 3.0f; } -inline F32 LLColor3::length(void) const +inline F32 LLColor3::length() const { - return (F32) sqrt(mV[0]*mV[0] + mV[1]*mV[1] + mV[2]*mV[2]); + return sqrt(mV[VRED] * mV[VRED] + mV[VGREEN] * mV[VGREEN] + mV[VBLUE] * mV[VBLUE]); } -inline F32 LLColor3::lengthSquared(void) const +inline F32 LLColor3::lengthSquared() const { - return mV[0]*mV[0] + mV[1]*mV[1] + mV[2]*mV[2]; + return mV[VRED] * mV[VRED] + mV[VGREEN] * mV[VGREEN] + mV[VBLUE] * mV[VBLUE]; } -inline F32 LLColor3::normalize(void) +inline F32 LLColor3::normalize() { - F32 mag = (F32) sqrt(mV[0]*mV[0] + mV[1]*mV[1] + mV[2]*mV[2]); + F32 mag = sqrt(mV[VRED] * mV[VRED] + mV[VGREEN] * mV[VGREEN] + mV[VBLUE] * mV[VBLUE]); F32 oomag; if (mag) { - oomag = 1.f/mag; - mV[0] *= oomag; - mV[1] *= oomag; - mV[2] *= oomag; + oomag = 1.f / mag; + mV[VRED] *= oomag; + mV[VGREEN] *= oomag; + mV[VBLUE] *= oomag; } - return (mag); + return mag; } // deprecated -inline F32 LLColor3::magVec(void) const +inline F32 LLColor3::magVec() const { - return (F32) sqrt(mV[0]*mV[0] + mV[1]*mV[1] + mV[2]*mV[2]); + return sqrt(mV[VRED] * mV[VRED] + mV[VGREEN] * mV[VGREEN] + mV[VBLUE] * mV[VBLUE]); } // deprecated -inline F32 LLColor3::magVecSquared(void) const +inline F32 LLColor3::magVecSquared() const { - return mV[0]*mV[0] + mV[1]*mV[1] + mV[2]*mV[2]; + return mV[VRED] * mV[VRED] + mV[VGREEN] * mV[VGREEN] + mV[VBLUE] * mV[VBLUE]; } // deprecated -inline F32 LLColor3::normVec(void) +inline F32 LLColor3::normVec() { - F32 mag = (F32) sqrt(mV[0]*mV[0] + mV[1]*mV[1] + mV[2]*mV[2]); + F32 mag = sqrt(mV[VRED] * mV[VRED] + mV[VGREEN] * mV[VGREEN] + mV[VBLUE] * mV[VBLUE]); F32 oomag; if (mag) { - oomag = 1.f/mag; - mV[0] *= oomag; - mV[1] *= oomag; - mV[2] *= oomag; + oomag = 1.f / mag; + mV[VRED] *= oomag; + mV[VGREEN] *= oomag; + mV[VBLUE] *= oomag; } - return (mag); + return mag; } inline void LLColor3::exp() { #if 0 - mV[0] = ::exp(mV[0]); - mV[1] = ::exp(mV[1]); - mV[2] = ::exp(mV[2]); + mV[VRED] = ::exp(mV[VRED]); + mV[VGREEN] = ::exp(mV[VGREEN]); + mV[VBLUE] = ::exp(mV[VBLUE]); #else - mV[0] = (F32)LL_FAST_EXP(mV[0]); - mV[1] = (F32)LL_FAST_EXP(mV[1]); - mV[2] = (F32)LL_FAST_EXP(mV[2]); + mV[VRED] = (F32)LL_FAST_EXP(mV[VRED]); + mV[VGREEN] = (F32)LL_FAST_EXP(mV[VGREEN]); + mV[VBLUE] = (F32)LL_FAST_EXP(mV[VBLUE]); #endif } - -inline LLColor3 operator+(const LLColor3 &a, const LLColor3 &b) +inline LLColor3 operator+(const LLColor3& a, const LLColor3& b) { - return LLColor3( - a.mV[0] + b.mV[0], - a.mV[1] + b.mV[1], - a.mV[2] + b.mV[2]); + return LLColor3(a.mV[VRED] + b.mV[VRED], a.mV[VGREEN] + b.mV[VGREEN], a.mV[VBLUE] + b.mV[VBLUE]); } -inline LLColor3 operator-(const LLColor3 &a, const LLColor3 &b) +inline LLColor3 operator-(const LLColor3& a, const LLColor3& b) { - return LLColor3( - a.mV[0] - b.mV[0], - a.mV[1] - b.mV[1], - a.mV[2] - b.mV[2]); + return LLColor3(a.mV[VRED] - b.mV[VRED], a.mV[VGREEN] - b.mV[VGREEN], a.mV[VBLUE] - b.mV[VBLUE]); } -inline LLColor3 operator*(const LLColor3 &a, const LLColor3 &b) +inline LLColor3 operator*(const LLColor3& a, const LLColor3& b) { - return LLColor3( - a.mV[0] * b.mV[0], - a.mV[1] * b.mV[1], - a.mV[2] * b.mV[2]); + return LLColor3(a.mV[VRED] * b.mV[VRED], a.mV[VGREEN] * b.mV[VGREEN], a.mV[VBLUE] * b.mV[VBLUE]); } -inline LLColor3 operator*(const LLColor3 &a, F32 k) +inline LLColor3 operator*(const LLColor3& a, F32 k) { - return LLColor3( a.mV[0] * k, a.mV[1] * k, a.mV[2] * k ); + return LLColor3(a.mV[VRED] * k, a.mV[VGREEN] * k, a.mV[VBLUE] * k); } -inline LLColor3 operator*(F32 k, const LLColor3 &a) +inline LLColor3 operator*(F32 k, const LLColor3& a) { - return LLColor3( a.mV[0] * k, a.mV[1] * k, a.mV[2] * k ); + return LLColor3(a.mV[VRED] * k, a.mV[VGREEN] * k, a.mV[VBLUE] * k); } -inline bool operator==(const LLColor3 &a, const LLColor3 &b) +inline bool operator==(const LLColor3& a, const LLColor3& b) { - return ( (a.mV[0] == b.mV[0]) - &&(a.mV[1] == b.mV[1]) - &&(a.mV[2] == b.mV[2])); + return ((a.mV[VRED] == b.mV[VRED]) && (a.mV[VGREEN] == b.mV[VGREEN]) && (a.mV[VBLUE] == b.mV[VBLUE])); } -inline bool operator!=(const LLColor3 &a, const LLColor3 &b) +inline bool operator!=(const LLColor3& a, const LLColor3& b) { - return ( (a.mV[0] != b.mV[0]) - ||(a.mV[1] != b.mV[1]) - ||(a.mV[2] != b.mV[2])); + return ((a.mV[VRED] != b.mV[VRED]) || (a.mV[VGREEN] != b.mV[VGREEN]) || (a.mV[VBLUE] != b.mV[VBLUE])); } -inline const LLColor3 &operator*=(LLColor3 &a, const LLColor3 &b) +inline const LLColor3& operator*=(LLColor3& a, const LLColor3& b) { - a.mV[0] *= b.mV[0]; - a.mV[1] *= b.mV[1]; - a.mV[2] *= b.mV[2]; + a.mV[VRED] *= b.mV[VRED]; + a.mV[VGREEN] *= b.mV[VGREEN]; + a.mV[VBLUE] *= b.mV[VBLUE]; return a; } -inline const LLColor3& operator+=(LLColor3 &a, const LLColor3 &b) +inline const LLColor3& operator+=(LLColor3& a, const LLColor3& b) { - a.mV[0] += b.mV[0]; - a.mV[1] += b.mV[1]; - a.mV[2] += b.mV[2]; + a.mV[VRED] += b.mV[VRED]; + a.mV[VGREEN] += b.mV[VGREEN]; + a.mV[VBLUE] += b.mV[VBLUE]; return a; } -inline const LLColor3& operator-=(LLColor3 &a, const LLColor3 &b) +inline const LLColor3& operator-=(LLColor3& a, const LLColor3& b) { - a.mV[0] -= b.mV[0]; - a.mV[1] -= b.mV[1]; - a.mV[2] -= b.mV[2]; + a.mV[VRED] -= b.mV[VRED]; + a.mV[VGREEN] -= b.mV[VGREEN]; + a.mV[VBLUE] -= b.mV[VBLUE]; return a; } -inline const LLColor3& operator*=(LLColor3 &a, F32 k) +inline const LLColor3& operator*=(LLColor3& a, F32 k) { - a.mV[0] *= k; - a.mV[1] *= k; - a.mV[2] *= k; + a.mV[VRED] *= k; + a.mV[VGREEN] *= k; + a.mV[VBLUE] *= k; return a; } -inline LLColor3 operator-(const LLColor3 &a) +inline LLColor3 operator-(const LLColor3& a) { - return LLColor3( - 1.f - a.mV[0], - 1.f - a.mV[1], - 1.f - a.mV[2] ); + return LLColor3(1.f - a.mV[VRED], 1.f - a.mV[VGREEN], 1.f - a.mV[VBLUE]); } // Non-member functions -inline F32 distVec(const LLColor3 &a, const LLColor3 &b) +inline F32 distVec(const LLColor3& a, const LLColor3& b) { - F32 x = a.mV[0] - b.mV[0]; - F32 y = a.mV[1] - b.mV[1]; - F32 z = a.mV[2] - b.mV[2]; - return (F32) sqrt( x*x + y*y + z*z ); + F32 x = a.mV[VRED] - b.mV[VRED]; + F32 y = a.mV[VGREEN] - b.mV[VGREEN]; + F32 z = a.mV[VBLUE] - b.mV[VBLUE]; + return sqrt(x * x + y * y + z * z); } -inline F32 distVec_squared(const LLColor3 &a, const LLColor3 &b) +inline F32 distVec_squared(const LLColor3& a, const LLColor3& b) { - F32 x = a.mV[0] - b.mV[0]; - F32 y = a.mV[1] - b.mV[1]; - F32 z = a.mV[2] - b.mV[2]; - return x*x + y*y + z*z; + F32 x = a.mV[VRED] - b.mV[VRED]; + F32 y = a.mV[VGREEN] - b.mV[VGREEN]; + F32 z = a.mV[VBLUE] - b.mV[VBLUE]; + return x * x + y * y + z * z; } -inline LLColor3 lerp(const LLColor3 &a, const LLColor3 &b, F32 u) +inline LLColor3 lerp(const LLColor3& a, const LLColor3& b, F32 u) { - return LLColor3( - a.mV[VX] + (b.mV[VX] - a.mV[VX]) * u, - a.mV[VY] + (b.mV[VY] - a.mV[VY]) * u, - a.mV[VZ] + (b.mV[VZ] - a.mV[VZ]) * u); + return LLColor3(a.mV[VX] + (b.mV[VX] - a.mV[VX]) * u, a.mV[VY] + (b.mV[VY] - a.mV[VY]) * u, a.mV[VZ] + (b.mV[VZ] - a.mV[VZ]) * u); } -inline const LLColor3 srgbColor3(const LLColor3 &a) { +inline const LLColor3 srgbColor3(const LLColor3& a) +{ LLColor3 srgbColor; - srgbColor.mV[0] = linearTosRGB(a.mV[0]); - srgbColor.mV[1] = linearTosRGB(a.mV[1]); - srgbColor.mV[2] = linearTosRGB(a.mV[2]); + srgbColor.mV[VRED] = linearTosRGB(a.mV[VRED]); + srgbColor.mV[VGREEN] = linearTosRGB(a.mV[VGREEN]); + srgbColor.mV[VBLUE] = linearTosRGB(a.mV[VBLUE]); return srgbColor; } -inline const LLColor3 linearColor3p(const F32* v) { +inline const LLColor3 linearColor3p(const F32* v) +{ LLColor3 linearColor; - linearColor.mV[0] = sRGBtoLinear(v[0]); - linearColor.mV[1] = sRGBtoLinear(v[1]); - linearColor.mV[2] = sRGBtoLinear(v[2]); + linearColor.mV[VRED] = sRGBtoLinear(v[0]); + linearColor.mV[VGREEN] = sRGBtoLinear(v[1]); + linearColor.mV[VBLUE] = sRGBtoLinear(v[2]); return linearColor; } template -inline const LLColor3 linearColor3(const T& a) { +inline const LLColor3 linearColor3(const T& a) +{ return linearColor3p(a.mV); } template -inline const LLVector3 linearColor3v(const T& a) { +inline const LLVector3 linearColor3v(const T& a) +{ return LLVector3(linearColor3p(a.mV).mV); } diff --git a/indra/llmath/v3colorutil.h b/indra/llmath/v3colorutil.h index 62005f76a0..1378d46450 100644 --- a/indra/llmath/v3colorutil.h +++ b/indra/llmath/v3colorutil.h @@ -29,59 +29,46 @@ #include "v3color.h" -inline LLColor3 componentDiv(LLColor3 const &left, LLColor3 const & right) +inline LLColor3 componentDiv(const LLColor3& left, const LLColor3& right) { - return LLColor3(left.mV[0] / right.mV[0], - left.mV[1] / right.mV[1], - left.mV[2] / right.mV[2]); + return LLColor3(left.mV[VRED] / right.mV[VRED], left.mV[VGREEN] / right.mV[VGREEN], left.mV[VBLUE] / right.mV[VBLUE]); } - -inline LLColor3 componentMult(LLColor3 const &left, LLColor3 const & right) +inline LLColor3 componentMult(const LLColor3& left, const LLColor3& right) { - return LLColor3(left.mV[0] * right.mV[0], - left.mV[1] * right.mV[1], - left.mV[2] * right.mV[2]); + return LLColor3(left.mV[VRED] * right.mV[VRED], left.mV[VGREEN] * right.mV[VGREEN], left.mV[VBLUE] * right.mV[VBLUE]); } - -inline LLColor3 componentExp(LLColor3 const &v) +inline LLColor3 componentExp(const LLColor3& v) { - return LLColor3(exp(v.mV[0]), - exp(v.mV[1]), - exp(v.mV[2])); + return LLColor3(exp(v.mV[VRED]), exp(v.mV[VGREEN]), exp(v.mV[VBLUE])); } -inline LLColor3 componentPow(LLColor3 const &v, F32 exponent) +inline LLColor3 componentPow(const LLColor3& v, F32 exponent) { - return LLColor3(pow(v.mV[0], exponent), - pow(v.mV[1], exponent), - pow(v.mV[2], exponent)); + return LLColor3(pow(v.mV[VRED], exponent), pow(v.mV[VGREEN], exponent), pow(v.mV[VBLUE], exponent)); } -inline LLColor3 componentSaturate(LLColor3 const &v) +inline LLColor3 componentSaturate(const LLColor3& v) { - return LLColor3(std::max(std::min(v.mV[0], 1.f), 0.f), - std::max(std::min(v.mV[1], 1.f), 0.f), - std::max(std::min(v.mV[2], 1.f), 0.f)); + return LLColor3(std::max(std::min(v.mV[VRED], 1.f), 0.f), + std::max(std::min(v.mV[VGREEN], 1.f), 0.f), + std::max(std::min(v.mV[VBLUE], 1.f), 0.f)); } - -inline LLColor3 componentSqrt(LLColor3 const &v) +inline LLColor3 componentSqrt(const LLColor3& v) { - return LLColor3(sqrt(v.mV[0]), - sqrt(v.mV[1]), - sqrt(v.mV[2])); + return LLColor3(sqrt(v.mV[VRED]), sqrt(v.mV[VGREEN]), sqrt(v.mV[VBLUE])); } -inline void componentMultBy(LLColor3 & left, LLColor3 const & right) +inline void componentMultBy(LLColor3& left, const LLColor3& right) { - left.mV[0] *= right.mV[0]; - left.mV[1] *= right.mV[1]; - left.mV[2] *= right.mV[2]; + left.mV[VRED] *= right.mV[VRED]; + left.mV[VGREEN] *= right.mV[VGREEN]; + left.mV[VBLUE] *= right.mV[VBLUE]; } -inline LLColor3 colorMix(LLColor3 const & left, LLColor3 const & right, F32 amount) +inline LLColor3 colorMix(const LLColor3& left, const LLColor3& right, F32 amount) { return (left + ((right - left) * amount)); } @@ -91,25 +78,24 @@ inline LLColor3 smear(F32 val) return LLColor3(val, val, val); } -inline F32 color_intens(const LLColor3 &col) +inline F32 color_intens(const LLColor3& col) { - return col.mV[0] + col.mV[1] + col.mV[2]; + return col.mV[VRED] + col.mV[VGREEN] + col.mV[VBLUE]; } -inline F32 color_max(const LLColor3 &col) +inline F32 color_max(const LLColor3& col) { - return llmax(col.mV[0], col.mV[1], col.mV[2]); + return llmax(col.mV[VRED], col.mV[VGREEN], col.mV[VBLUE]); } -inline F32 color_max(const LLColor4 &col) +inline F32 color_max(const LLColor4& col) { - return llmax(col.mV[0], col.mV[1], col.mV[2]); + return llmax(col.mV[VRED], col.mV[VGREEN], col.mV[VBLUE]); } - -inline F32 color_min(const LLColor3 &col) +inline F32 color_min(const LLColor3& col) { - return llmin(col.mV[0], col.mV[1], col.mV[2]); + return llmin(col.mV[VRED], col.mV[VGREEN], col.mV[VBLUE]); } #endif diff --git a/indra/llmath/v4color.cpp b/indra/llmath/v4color.cpp index ad13656bbd..0fdf2883b2 100644 --- a/indra/llmath/v4color.cpp +++ b/indra/llmath/v4color.cpp @@ -38,6 +38,7 @@ ////////////////////////////////////////////////////////////////////////////// +// clang-format off LLColor4 LLColor4::red( 1.f, 0.f, 0.f, 1.f); LLColor4 LLColor4::green( 0.f, 1.f, 0.f, 1.f); LLColor4 LLColor4::blue( 0.f, 0.f, 1.f, 1.f); @@ -52,6 +53,7 @@ LLColor4 LLColor4::orange( 1.f, 0.5, 0.f, 1.f ); LLColor4 LLColor4::purple( 0.6f, 0.2f, 0.8f, 1.0f); LLColor4 LLColor4::pink( 1.0f, 0.5f, 0.8f, 1.0f); LLColor4 LLColor4::transparent( 0.f, 0.f, 0.f, 0.f ); +// clang-format on ////////////////////////////////////////////////////////////////////////////// @@ -124,65 +126,64 @@ LLColor4 LLColor4::cyan6(0.2f, 0.6f, 0.6f, 1.0f); // conversion LLColor4::operator LLColor4U() const { - return LLColor4U( - (U8)llclampb(ll_round(mV[VRED]*255.f)), - (U8)llclampb(ll_round(mV[VGREEN]*255.f)), - (U8)llclampb(ll_round(mV[VBLUE]*255.f)), - (U8)llclampb(ll_round(mV[VALPHA]*255.f))); + return LLColor4U((U8)llclampb(ll_round(mV[VRED] * 255.f)), + (U8)llclampb(ll_round(mV[VGREEN] * 255.f)), + (U8)llclampb(ll_round(mV[VBLUE] * 255.f)), + (U8)llclampb(ll_round(mV[VALPHA] * 255.f))); } -LLColor4::LLColor4(const LLColor3 &vec, F32 a) +LLColor4::LLColor4(const LLColor3& vec, F32 a) { - mV[VRED] = vec.mV[VRED]; + mV[VRED] = vec.mV[VRED]; mV[VGREEN] = vec.mV[VGREEN]; - mV[VBLUE] = vec.mV[VBLUE]; + mV[VBLUE] = vec.mV[VBLUE]; mV[VALPHA] = a; } LLColor4::LLColor4(const LLColor4U& color4u) { - const F32 SCALE = 1.f/255.f; - mV[VRED] = color4u.mV[VRED] * SCALE; - mV[VGREEN] = color4u.mV[VGREEN] * SCALE; - mV[VBLUE] = color4u.mV[VBLUE] * SCALE; - mV[VALPHA] = color4u.mV[VALPHA] * SCALE; + constexpr F32 SCALE = 1.f / 255.f; + mV[VRED] = color4u.mV[VRED] * SCALE; + mV[VGREEN] = color4u.mV[VGREEN] * SCALE; + mV[VBLUE] = color4u.mV[VBLUE] * SCALE; + mV[VALPHA] = color4u.mV[VALPHA] * SCALE; } LLColor4::LLColor4(const LLVector4& vector4) { - mV[VRED] = vector4.mV[VRED]; + mV[VRED] = vector4.mV[VRED]; mV[VGREEN] = vector4.mV[VGREEN]; - mV[VBLUE] = vector4.mV[VBLUE]; + mV[VBLUE] = vector4.mV[VBLUE]; mV[VALPHA] = vector4.mV[VALPHA]; } const LLColor4& LLColor4::set(const LLColor4U& color4u) { - const F32 SCALE = 1.f/255.f; - mV[VRED] = color4u.mV[VRED] * SCALE; - mV[VGREEN] = color4u.mV[VGREEN] * SCALE; - mV[VBLUE] = color4u.mV[VBLUE] * SCALE; - mV[VALPHA] = color4u.mV[VALPHA] * SCALE; + constexpr F32 SCALE = 1.f / 255.f; + mV[VRED] = color4u.mV[VRED] * SCALE; + mV[VGREEN] = color4u.mV[VGREEN] * SCALE; + mV[VBLUE] = color4u.mV[VBLUE] * SCALE; + mV[VALPHA] = color4u.mV[VALPHA] * SCALE; return (*this); } -const LLColor4& LLColor4::set(const LLColor3 &vec) +const LLColor4& LLColor4::set(const LLColor3& vec) { - mV[VRED] = vec.mV[VRED]; + mV[VRED] = vec.mV[VRED]; mV[VGREEN] = vec.mV[VGREEN]; - mV[VBLUE] = vec.mV[VBLUE]; + mV[VBLUE] = vec.mV[VBLUE]; -// no change to alpha! -// mV[VALPHA] = 1.f; + // no change to alpha! + // mV[VALPHA] = 1.f; return (*this); } -const LLColor4& LLColor4::set(const LLColor3 &vec, F32 a) +const LLColor4& LLColor4::set(const LLColor3& vec, F32 a) { - mV[VRED] = vec.mV[VRED]; + mV[VRED] = vec.mV[VRED]; mV[VGREEN] = vec.mV[VGREEN]; - mV[VBLUE] = vec.mV[VBLUE]; + mV[VBLUE] = vec.mV[VBLUE]; mV[VALPHA] = a; return (*this); } @@ -190,33 +191,33 @@ const LLColor4& LLColor4::set(const LLColor3 &vec, F32 a) // deprecated -- use set() const LLColor4& LLColor4::setVec(const LLColor4U& color4u) { - const F32 SCALE = 1.f/255.f; - mV[VRED] = color4u.mV[VRED] * SCALE; - mV[VGREEN] = color4u.mV[VGREEN] * SCALE; - mV[VBLUE] = color4u.mV[VBLUE] * SCALE; - mV[VALPHA] = color4u.mV[VALPHA] * SCALE; + constexpr F32 SCALE = 1.f / 255.f; + mV[VRED] = color4u.mV[VRED] * SCALE; + mV[VGREEN] = color4u.mV[VGREEN] * SCALE; + mV[VBLUE] = color4u.mV[VBLUE] * SCALE; + mV[VALPHA] = color4u.mV[VALPHA] * SCALE; return (*this); } // deprecated -- use set() -const LLColor4& LLColor4::setVec(const LLColor3 &vec) +const LLColor4& LLColor4::setVec(const LLColor3& vec) { - mV[VRED] = vec.mV[VRED]; + mV[VRED] = vec.mV[VRED]; mV[VGREEN] = vec.mV[VGREEN]; - mV[VBLUE] = vec.mV[VBLUE]; + mV[VBLUE] = vec.mV[VBLUE]; -// no change to alpha! -// mV[VALPHA] = 1.f; + // no change to alpha! + // mV[VALPHA] = 1.f; return (*this); } // deprecated -- use set() -const LLColor4& LLColor4::setVec(const LLColor3 &vec, F32 a) +const LLColor4& LLColor4::setVec(const LLColor3& vec, F32 a) { - mV[VRED] = vec.mV[VRED]; + mV[VRED] = vec.mV[VRED]; mV[VGREEN] = vec.mV[VGREEN]; - mV[VBLUE] = vec.mV[VBLUE]; + mV[VBLUE] = vec.mV[VBLUE]; mV[VALPHA] = a; return (*this); } @@ -228,110 +229,110 @@ void LLColor4::setValue(const LLSD& sd) F32 val; bool out_of_range = false; val = sd[0].asReal(); - mV[0] = llclamp(val, 0.f, 1.f); - out_of_range = mV[0] != val; + mV[VRED] = llclamp(val, 0.f, 1.f); + out_of_range = mV[VRED] != val; val = sd[1].asReal(); - mV[1] = llclamp(val, 0.f, 1.f); - out_of_range |= mV[1] != val; + mV[VGREEN] = llclamp(val, 0.f, 1.f); + out_of_range |= mV[VGREEN] != val; val = sd[2].asReal(); - mV[2] = llclamp(val, 0.f, 1.f); - out_of_range |= mV[2] != val; + mV[VBLUE] = llclamp(val, 0.f, 1.f); + out_of_range |= mV[VBLUE] != val; val = sd[3].asReal(); - mV[3] = llclamp(val, 0.f, 1.f); - out_of_range |= mV[3] != val; + mV[VALPHA] = llclamp(val, 0.f, 1.f); + out_of_range |= mV[VALPHA] != val; if (out_of_range) { LL_WARNS() << "LLSD color value out of range!" << LL_ENDL; } #else - mV[0] = (F32) sd[0].asReal(); - mV[1] = (F32) sd[1].asReal(); - mV[2] = (F32) sd[2].asReal(); - mV[3] = (F32) sd[3].asReal(); + mV[VRED] = (F32)sd[0].asReal(); + mV[VGREEN] = (F32)sd[1].asReal(); + mV[VBLUE] = (F32)sd[2].asReal(); + mV[VALPHA] = (F32)sd[3].asReal(); #endif } -const LLColor4& LLColor4::operator=(const LLColor3 &a) +const LLColor4& LLColor4::operator=(const LLColor3& a) { - mV[VRED] = a.mV[VRED]; + mV[VRED] = a.mV[VRED]; mV[VGREEN] = a.mV[VGREEN]; - mV[VBLUE] = a.mV[VBLUE]; + mV[VBLUE] = a.mV[VBLUE]; -// converting from an rgb sets a=1 (opaque) + // converting from an rgb sets a=1 (opaque) mV[VALPHA] = 1.f; return (*this); } - -std::ostream& operator<<(std::ostream& s, const LLColor4 &a) +std::ostream& operator<<(std::ostream& s, const LLColor4& a) { s << "{ " << a.mV[VRED] << ", " << a.mV[VGREEN] << ", " << a.mV[VBLUE] << ", " << a.mV[VALPHA] << " }"; return s; } -bool operator==(const LLColor4 &a, const LLColor3 &b) +bool operator==(const LLColor4& a, const LLColor3& b) { - return ( (a.mV[VRED] == b.mV[VRED]) - &&(a.mV[VGREEN] == b.mV[VGREEN]) - &&(a.mV[VBLUE] == b.mV[VBLUE])); + return ((a.mV[VRED] == b.mV[VRED]) && (a.mV[VGREEN] == b.mV[VGREEN]) && (a.mV[VBLUE] == b.mV[VBLUE])); } -bool operator!=(const LLColor4 &a, const LLColor3 &b) +bool operator!=(const LLColor4& a, const LLColor3& b) { - return ( (a.mV[VRED] != b.mV[VRED]) - ||(a.mV[VGREEN] != b.mV[VGREEN]) - ||(a.mV[VBLUE] != b.mV[VBLUE])); + return ((a.mV[VRED] != b.mV[VRED]) || (a.mV[VGREEN] != b.mV[VGREEN]) || (a.mV[VBLUE] != b.mV[VBLUE])); } -LLColor3 vec4to3(const LLColor4 &vec) +LLColor3 vec4to3(const LLColor4& vec) { - LLColor3 temp(vec.mV[VRED], vec.mV[VGREEN], vec.mV[VBLUE]); + LLColor3 temp(vec.mV[VRED], vec.mV[VGREEN], vec.mV[VBLUE]); return temp; } -LLColor4 vec3to4(const LLColor3 &vec) +LLColor4 vec3to4(const LLColor3& vec) { - LLColor3 temp(vec.mV[VRED], vec.mV[VGREEN], vec.mV[VBLUE]); + LLColor3 temp(vec.mV[VRED], vec.mV[VGREEN], vec.mV[VBLUE]); return temp; } -static F32 hueToRgb ( F32 val1In, F32 val2In, F32 valHUeIn ) +static F32 hueToRgb(F32 val1In, F32 val2In, F32 valHUeIn) { - if ( valHUeIn < 0.0f ) valHUeIn += 1.0f; - if ( valHUeIn > 1.0f ) valHUeIn -= 1.0f; - if ( ( 6.0f * valHUeIn ) < 1.0f ) return ( val1In + ( val2In - val1In ) * 6.0f * valHUeIn ); - if ( ( 2.0f * valHUeIn ) < 1.0f ) return ( val2In ); - if ( ( 3.0f * valHUeIn ) < 2.0f ) return ( val1In + ( val2In - val1In ) * ( ( 2.0f / 3.0f ) - valHUeIn ) * 6.0f ); - return ( val1In ); + if (valHUeIn < 0.0f) + valHUeIn += 1.0f; + if (valHUeIn > 1.0f) + valHUeIn -= 1.0f; + if ((6.0f * valHUeIn) < 1.0f) + return (val1In + (val2In - val1In) * 6.0f * valHUeIn); + if ((2.0f * valHUeIn) < 1.0f) + return (val2In); + if ((3.0f * valHUeIn) < 2.0f) + return (val1In + (val2In - val1In) * ((2.0f / 3.0f) - valHUeIn) * 6.0f); + return (val1In); } -void LLColor4::setHSL ( F32 hValIn, F32 sValIn, F32 lValIn) +void LLColor4::setHSL(F32 hValIn, F32 sValIn, F32 lValIn) { - if ( sValIn < 0.00001f ) + if (sValIn < 0.00001f) { - mV[VRED] = lValIn; + mV[VRED] = lValIn; mV[VGREEN] = lValIn; - mV[VBLUE] = lValIn; + mV[VBLUE] = lValIn; } else { F32 interVal1; F32 interVal2; - if ( lValIn < 0.5f ) - interVal2 = lValIn * ( 1.0f + sValIn ); + if (lValIn < 0.5f) + interVal2 = lValIn * (1.0f + sValIn); else - interVal2 = ( lValIn + sValIn ) - ( sValIn * lValIn ); + interVal2 = (lValIn + sValIn) - (sValIn * lValIn); interVal1 = 2.0f * lValIn - interVal2; - mV[VRED] = hueToRgb ( interVal1, interVal2, hValIn + ( 1.f / 3.f ) ); - mV[VGREEN] = hueToRgb ( interVal1, interVal2, hValIn ); - mV[VBLUE] = hueToRgb ( interVal1, interVal2, hValIn - ( 1.f / 3.f ) ); + mV[VRED] = hueToRgb(interVal1, interVal2, hValIn + (1.f / 3.f)); + mV[VGREEN] = hueToRgb(interVal1, interVal2, hValIn); + mV[VBLUE] = hueToRgb(interVal1, interVal2, hValIn - (1.f / 3.f)); } } @@ -341,58 +342,61 @@ void LLColor4::calcHSL(F32* hue, F32* saturation, F32* luminance) const F32 var_G = mV[VGREEN]; F32 var_B = mV[VBLUE]; - F32 var_Min = ( var_R < ( var_G < var_B ? var_G : var_B ) ? var_R : ( var_G < var_B ? var_G : var_B ) ); - F32 var_Max = ( var_R > ( var_G > var_B ? var_G : var_B ) ? var_R : ( var_G > var_B ? var_G : var_B ) ); + F32 var_Min = (var_R < (var_G < var_B ? var_G : var_B) ? var_R : (var_G < var_B ? var_G : var_B)); + F32 var_Max = (var_R > (var_G > var_B ? var_G : var_B) ? var_R : (var_G > var_B ? var_G : var_B)); F32 del_Max = var_Max - var_Min; - F32 L = ( var_Max + var_Min ) / 2.0f; + F32 L = (var_Max + var_Min) / 2.0f; F32 H = 0.0f; F32 S = 0.0f; - if ( del_Max == 0.0f ) + if (del_Max == 0.0f) { - H = 0.0f; - S = 0.0f; + H = 0.0f; + S = 0.0f; } else { - if ( L < 0.5 ) - S = del_Max / ( var_Max + var_Min ); + if (L < 0.5) + S = del_Max / (var_Max + var_Min); else - S = del_Max / ( 2.0f - var_Max - var_Min ); + S = del_Max / (2.0f - var_Max - var_Min); - F32 del_R = ( ( ( var_Max - var_R ) / 6.0f ) + ( del_Max / 2.0f ) ) / del_Max; - F32 del_G = ( ( ( var_Max - var_G ) / 6.0f ) + ( del_Max / 2.0f ) ) / del_Max; - F32 del_B = ( ( ( var_Max - var_B ) / 6.0f ) + ( del_Max / 2.0f ) ) / del_Max; + F32 del_R = (((var_Max - var_R) / 6.0f) + (del_Max / 2.0f)) / del_Max; + F32 del_G = (((var_Max - var_G) / 6.0f) + (del_Max / 2.0f)) / del_Max; + F32 del_B = (((var_Max - var_B) / 6.0f) + (del_Max / 2.0f)) / del_Max; - if ( var_R >= var_Max ) + if (var_R >= var_Max) H = del_B - del_G; - else - if ( var_G >= var_Max ) - H = ( 1.0f / 3.0f ) + del_R - del_B; - else - if ( var_B >= var_Max ) - H = ( 2.0f / 3.0f ) + del_G - del_R; - - if ( H < 0.0f ) H += 1.0f; - if ( H > 1.0f ) H -= 1.0f; + else if (var_G >= var_Max) + H = (1.0f / 3.0f) + del_R - del_B; + else if (var_B >= var_Max) + H = (2.0f / 3.0f) + del_G - del_R; + + if (H < 0.0f) + H += 1.0f; + if (H > 1.0f) + H -= 1.0f; } - if (hue) *hue = H; - if (saturation) *saturation = S; - if (luminance) *luminance = L; + if (hue) + *hue = H; + if (saturation) + *saturation = S; + if (luminance) + *luminance = L; } // static bool LLColor4::parseColor(const std::string& buf, LLColor4* color) { - if( buf.empty() || color == nullptr) + if (buf.empty() || color == nullptr) { return false; } - boost_tokenizer tokens(buf, boost::char_separator(", ")); + boost_tokenizer tokens(buf, boost::char_separator(", ")); boost_tokenizer::iterator token_iter = tokens.begin(); if (token_iter == tokens.end()) { @@ -401,16 +405,16 @@ bool LLColor4::parseColor(const std::string& buf, LLColor4* color) // Grab the first token into a string, since we don't know // if this is a float or a color name. - std::string color_name( (*token_iter) ); + std::string color_name((*token_iter)); ++token_iter; if (token_iter != tokens.end()) { // There are more tokens to read. This must be a vector. LLColor4 v; - LLStringUtil::convertToF32( color_name, v.mV[VRED] ); - LLStringUtil::convertToF32( *token_iter, v.mV[VGREEN] ); - v.mV[VBLUE] = 0.0f; + LLStringUtil::convertToF32(color_name, v.mV[VRED]); + LLStringUtil::convertToF32(*token_iter, v.mV[VGREEN]); + v.mV[VBLUE] = 0.0f; v.mV[VALPHA] = 1.0f; ++token_iter; @@ -422,283 +426,284 @@ bool LLColor4::parseColor(const std::string& buf, LLColor4* color) else { // There is a z-component. - LLStringUtil::convertToF32( *token_iter, v.mV[VBLUE] ); + LLStringUtil::convertToF32(*token_iter, v.mV[VBLUE]); ++token_iter; if (token_iter != tokens.end()) { // There is an alpha component. - LLStringUtil::convertToF32( *token_iter, v.mV[VALPHA] ); + LLStringUtil::convertToF32(*token_iter, v.mV[VALPHA]); } } // Make sure all values are between 0 and 1. if (v.mV[VRED] > 1.f || v.mV[VGREEN] > 1.f || v.mV[VBLUE] > 1.f || v.mV[VALPHA] > 1.f) { - v = v * (1.f / 255.f); + constexpr F32 SCALE{ 1.f / 255.f }; + v *= SCALE; } - color->set( v ); + color->set(v); } else // Single value. Read as a named color. { // We have a color name - if ( "red" == color_name ) + if ("red" == color_name) { color->set(LLColor4::red); } - else if ( "red1" == color_name ) + else if ("red1" == color_name) { color->set(LLColor4::red1); } - else if ( "red2" == color_name ) + else if ("red2" == color_name) { color->set(LLColor4::red2); } - else if ( "red3" == color_name ) + else if ("red3" == color_name) { color->set(LLColor4::red3); } - else if ( "red4" == color_name ) + else if ("red4" == color_name) { color->set(LLColor4::red4); } - else if ( "red5" == color_name ) + else if ("red5" == color_name) { color->set(LLColor4::red5); } - else if( "green" == color_name ) + else if ("green" == color_name) { color->set(LLColor4::green); } - else if( "green1" == color_name ) + else if ("green1" == color_name) { color->set(LLColor4::green1); } - else if( "green2" == color_name ) + else if ("green2" == color_name) { color->set(LLColor4::green2); } - else if( "green3" == color_name ) + else if ("green3" == color_name) { color->set(LLColor4::green3); } - else if( "green4" == color_name ) + else if ("green4" == color_name) { color->set(LLColor4::green4); } - else if( "green5" == color_name ) + else if ("green5" == color_name) { color->set(LLColor4::green5); } - else if( "green6" == color_name ) + else if ("green6" == color_name) { color->set(LLColor4::green6); } - else if( "blue" == color_name ) + else if ("blue" == color_name) { color->set(LLColor4::blue); } - else if( "blue1" == color_name ) + else if ("blue1" == color_name) { color->set(LLColor4::blue1); } - else if( "blue2" == color_name ) + else if ("blue2" == color_name) { color->set(LLColor4::blue2); } - else if( "blue3" == color_name ) + else if ("blue3" == color_name) { color->set(LLColor4::blue3); } - else if( "blue4" == color_name ) + else if ("blue4" == color_name) { color->set(LLColor4::blue4); } - else if( "blue5" == color_name ) + else if ("blue5" == color_name) { color->set(LLColor4::blue5); } - else if( "blue6" == color_name ) + else if ("blue6" == color_name) { color->set(LLColor4::blue6); } - else if( "black" == color_name ) + else if ("black" == color_name) { color->set(LLColor4::black); } - else if( "white" == color_name ) + else if ("white" == color_name) { color->set(LLColor4::white); } - else if( "yellow" == color_name ) + else if ("yellow" == color_name) { color->set(LLColor4::yellow); } - else if( "yellow1" == color_name ) + else if ("yellow1" == color_name) { color->set(LLColor4::yellow1); } - else if( "yellow2" == color_name ) + else if ("yellow2" == color_name) { color->set(LLColor4::yellow2); } - else if( "yellow3" == color_name ) + else if ("yellow3" == color_name) { color->set(LLColor4::yellow3); } - else if( "yellow4" == color_name ) + else if ("yellow4" == color_name) { color->set(LLColor4::yellow4); } - else if( "yellow5" == color_name ) + else if ("yellow5" == color_name) { color->set(LLColor4::yellow5); } - else if( "yellow6" == color_name ) + else if ("yellow6" == color_name) { color->set(LLColor4::yellow6); } - else if( "magenta" == color_name ) + else if ("magenta" == color_name) { color->set(LLColor4::magenta); } - else if( "magenta1" == color_name ) + else if ("magenta1" == color_name) { color->set(LLColor4::magenta1); } - else if( "magenta2" == color_name ) + else if ("magenta2" == color_name) { color->set(LLColor4::magenta2); } - else if( "magenta3" == color_name ) + else if ("magenta3" == color_name) { color->set(LLColor4::magenta3); } - else if( "magenta4" == color_name ) + else if ("magenta4" == color_name) { color->set(LLColor4::magenta4); } - else if( "purple" == color_name ) + else if ("purple" == color_name) { color->set(LLColor4::purple); } - else if( "purple1" == color_name ) + else if ("purple1" == color_name) { color->set(LLColor4::purple1); } - else if( "purple2" == color_name ) + else if ("purple2" == color_name) { color->set(LLColor4::purple2); } - else if( "purple3" == color_name ) + else if ("purple3" == color_name) { color->set(LLColor4::purple3); } - else if( "purple4" == color_name ) + else if ("purple4" == color_name) { color->set(LLColor4::purple4); } - else if( "purple5" == color_name ) + else if ("purple5" == color_name) { color->set(LLColor4::purple5); } - else if( "purple6" == color_name ) + else if ("purple6" == color_name) { color->set(LLColor4::purple6); } - else if( "pink" == color_name ) + else if ("pink" == color_name) { color->set(LLColor4::pink); } - else if( "pink1" == color_name ) + else if ("pink1" == color_name) { color->set(LLColor4::pink1); } - else if( "pink2" == color_name ) + else if ("pink2" == color_name) { color->set(LLColor4::pink2); } - else if( "cyan" == color_name ) + else if ("cyan" == color_name) { color->set(LLColor4::cyan); } - else if( "cyan1" == color_name ) + else if ("cyan1" == color_name) { color->set(LLColor4::cyan1); } - else if( "cyan2" == color_name ) + else if ("cyan2" == color_name) { color->set(LLColor4::cyan2); } - else if( "cyan3" == color_name ) + else if ("cyan3" == color_name) { color->set(LLColor4::cyan3); } - else if( "cyan4" == color_name ) + else if ("cyan4" == color_name) { color->set(LLColor4::cyan4); } - else if( "cyan5" == color_name ) + else if ("cyan5" == color_name) { color->set(LLColor4::cyan5); } - else if( "cyan6" == color_name ) + else if ("cyan6" == color_name) { color->set(LLColor4::cyan6); } - else if( "smoke" == color_name ) + else if ("smoke" == color_name) { color->set(LLColor4::smoke); } - else if( "grey" == color_name ) + else if ("grey" == color_name) { color->set(LLColor4::grey); } - else if( "grey1" == color_name ) + else if ("grey1" == color_name) { color->set(LLColor4::grey1); } - else if( "grey2" == color_name ) + else if ("grey2" == color_name) { color->set(LLColor4::grey2); } - else if( "grey3" == color_name ) + else if ("grey3" == color_name) { color->set(LLColor4::grey3); } - else if( "grey4" == color_name ) + else if ("grey4" == color_name) { color->set(LLColor4::grey4); } - else if( "orange" == color_name ) + else if ("orange" == color_name) { color->set(LLColor4::orange); } - else if( "orange1" == color_name ) + else if ("orange1" == color_name) { color->set(LLColor4::orange1); } - else if( "orange2" == color_name ) + else if ("orange2" == color_name) { color->set(LLColor4::orange2); } - else if( "orange3" == color_name ) + else if ("orange3" == color_name) { color->set(LLColor4::orange3); } - else if( "orange4" == color_name ) + else if ("orange4" == color_name) { color->set(LLColor4::orange4); } - else if( "orange5" == color_name ) + else if ("orange5" == color_name) { color->set(LLColor4::orange5); } - else if( "orange6" == color_name ) + else if ("orange6" == color_name) { color->set(LLColor4::orange6); } - else if ( "clear" == color_name ) + else if ("clear" == color_name) { color->set(0.f, 0.f, 0.f, 0.f); } @@ -714,21 +719,21 @@ bool LLColor4::parseColor(const std::string& buf, LLColor4* color) // static bool LLColor4::parseColor4(const std::string& buf, LLColor4* value) { - if( buf.empty() || value == nullptr) + if (buf.empty() || value == nullptr) { return false; } LLColor4 v; - S32 count = sscanf( buf.c_str(), "%f, %f, %f, %f", v.mV + 0, v.mV + 1, v.mV + 2, v.mV + 3 ); - if (1 == count ) + S32 count = sscanf(buf.c_str(), "%f, %f, %f, %f", v.mV + 0, v.mV + 1, v.mV + 2, v.mV + 3); + if (1 == count) { // try this format - count = sscanf( buf.c_str(), "%f %f %f %f", v.mV + 0, v.mV + 1, v.mV + 2, v.mV + 3 ); + count = sscanf(buf.c_str(), "%f %f %f %f", v.mV + 0, v.mV + 1, v.mV + 2, v.mV + 3); } - if( 4 == count ) + if (4 == count) { - value->setVec( v ); + value->setVec(v); return true; } diff --git a/indra/llmath/v4color.h b/indra/llmath/v4color.h index cafdbd9d7c..a26db0428b 100644 --- a/indra/llmath/v4color.h +++ b/indra/llmath/v4color.h @@ -28,7 +28,6 @@ #define LL_V4COLOR_H #include "llerror.h" -//#include "vmath.h" #include "llmath.h" #include "llsd.h" @@ -38,213 +37,212 @@ class LLVector4; // LLColor4 = |x y z w| -static const U32 LENGTHOFCOLOR4 = 4; +static constexpr U32 LENGTHOFCOLOR4 = 4; -static const U32 MAX_LENGTH_OF_COLOR_NAME = 15; //Give plenty of room for additional colors... +static constexpr U32 MAX_LENGTH_OF_COLOR_NAME = 15; // Give plenty of room for additional colors... class LLColor4 { - public: - F32 mV[LENGTHOFCOLOR4]; - LLColor4(); // Initializes LLColor4 to (0, 0, 0, 1) - LLColor4(F32 r, F32 g, F32 b); // Initializes LLColor4 to (r, g, b, 1) - LLColor4(F32 r, F32 g, F32 b, F32 a); // Initializes LLColor4 to (r. g, b, a) - LLColor4(const LLColor3 &vec, F32 a = 1.f); // Initializes LLColor4 to (vec, a) - explicit LLColor4(const LLSD& sd); - explicit LLColor4(const F32 *vec); // Initializes LLColor4 to (vec[0]. vec[1], vec[2], 1) - explicit LLColor4(U32 clr); // Initializes LLColor4 to (r=clr>>24, etc)) - explicit LLColor4(const LLColor4U& color4u); // "explicit" to avoid automatic conversion - explicit LLColor4(const LLVector4& vector4); // "explicit" to avoid automatic conversion - - LLSD getValue() const - { - LLSD ret; - ret[0] = mV[0]; - ret[1] = mV[1]; - ret[2] = mV[2]; - ret[3] = mV[3]; - return ret; - } - - void setValue(const LLSD& sd); - - void setHSL(F32 hue, F32 saturation, F32 luminance); - void calcHSL(F32* hue, F32* saturation, F32* luminance) const; - - const LLColor4& setToBlack(); // zero LLColor4 to (0, 0, 0, 1) - const LLColor4& setToWhite(); // zero LLColor4 to (0, 0, 0, 1) - - const LLColor4& setVec(F32 r, F32 g, F32 b, F32 a); // deprecated -- use set() - const LLColor4& setVec(F32 r, F32 g, F32 b); // deprecated -- use set() - const LLColor4& setVec(const LLColor4 &vec); // deprecated -- use set() - const LLColor4& setVec(const LLColor3 &vec); // deprecated -- use set() - const LLColor4& setVec(const LLColor3 &vec, F32 a); // deprecated -- use set() - const LLColor4& setVec(const F32 *vec); // deprecated -- use set() - const LLColor4& setVec(const LLColor4U& color4u); // deprecated -- use set() - - const LLColor4& set(F32 r, F32 g, F32 b, F32 a); // Sets LLColor4 to (r, g, b, a) - const LLColor4& set(F32 r, F32 g, F32 b); // Sets LLColor4 to (r, g, b) (no change in a) - const LLColor4& set(const LLColor4 &vec); // Sets LLColor4 to vec - const LLColor4& set(const LLColor3 &vec); // Sets LLColor4 to LLColor3 vec (no change in alpha) - const LLColor4& set(const LLColor3 &vec, F32 a); // Sets LLColor4 to LLColor3 vec, with alpha specified - const LLColor4& set(const F32 *vec); // Sets LLColor4 to vec - const LLColor4& set(const F64 *vec); // Sets LLColor4 to (double)vec - const LLColor4& set(const LLColor4U& color4u); // Sets LLColor4 to color4u, rescaled. - - // set from a vector of unknown type and size - // may leave some data unmodified - template - const LLColor4& set(const std::vector& v); - - // write to a vector of unknown type and size - // maye leave some data unmodified - template - void write(std::vector& v) const; - - const LLColor4& setAlpha(F32 a); - - F32 magVec() const; // deprecated -- use length() - F32 magVecSquared() const; // deprecated -- use lengthSquared() - F32 normVec(); // deprecated -- use normalize() - - F32 length() const; // Returns magnitude of LLColor4 - F32 lengthSquared() const; // Returns magnitude squared of LLColor4 - F32 normalize(); // deprecated -- use normalize() - - bool isOpaque() { return mV[VALPHA] == 1.f; } - - F32 operator[](int idx) const { return mV[idx]; } - F32 &operator[](int idx) { return mV[idx]; } - - const LLColor4& operator=(const LLColor3 &a); // Assigns vec3 to vec4 and returns vec4 - - bool operator<(const LLColor4& rhs) const; - friend std::ostream& operator<<(std::ostream& s, const LLColor4 &a); // Print a - friend LLColor4 operator+(const LLColor4 &a, const LLColor4 &b); // Return vector a + b - friend LLColor4 operator-(const LLColor4 &a, const LLColor4 &b); // Return vector a minus b - friend LLColor4 operator*(const LLColor4 &a, const LLColor4 &b); // Return component wise a * b - friend LLColor4 operator*(const LLColor4 &a, F32 k); // Return rgb times scaler k (no alpha change) - friend LLColor4 operator/(const LLColor4 &a, F32 k); // Return rgb divided by scalar k (no alpha change) - friend LLColor4 operator*(F32 k, const LLColor4 &a); // Return rgb times scaler k (no alpha change) - friend LLColor4 operator%(const LLColor4 &a, F32 k); // Return alpha times scaler k (no rgb change) - friend LLColor4 operator%(F32 k, const LLColor4 &a); // Return alpha times scaler k (no rgb change) - - friend bool operator==(const LLColor4 &a, const LLColor4 &b); // Return a == b - friend bool operator!=(const LLColor4 &a, const LLColor4 &b); // Return a != b - - friend bool operator==(const LLColor4 &a, const LLColor3 &b); // Return a == b - friend bool operator!=(const LLColor4 &a, const LLColor3 &b); // Return a != b - - friend const LLColor4& operator+=(LLColor4 &a, const LLColor4 &b); // Return vector a + b - friend const LLColor4& operator-=(LLColor4 &a, const LLColor4 &b); // Return vector a minus b - friend const LLColor4& operator*=(LLColor4 &a, F32 k); // Return rgb times scaler k (no alpha change) - friend const LLColor4& operator%=(LLColor4 &a, F32 k); // Return alpha times scaler k (no rgb change) - - friend const LLColor4& operator*=(LLColor4 &a, const LLColor4 &b); // Doesn't multiply alpha! (for lighting) - - // conversion - operator LLColor4U() const; - - // Basic color values. - static LLColor4 red; - static LLColor4 green; - static LLColor4 blue; - static LLColor4 black; - static LLColor4 white; - static LLColor4 yellow; - static LLColor4 magenta; - static LLColor4 cyan; - static LLColor4 smoke; - static LLColor4 grey; - static LLColor4 orange; - static LLColor4 purple; - static LLColor4 pink; - static LLColor4 transparent; - - // Extra color values. - static LLColor4 grey1; - static LLColor4 grey2; - static LLColor4 grey3; - static LLColor4 grey4; - - static LLColor4 red1; - static LLColor4 red2; - static LLColor4 red3; - static LLColor4 red4; - static LLColor4 red5; - - static LLColor4 green1; - static LLColor4 green2; - static LLColor4 green3; - static LLColor4 green4; - static LLColor4 green5; - static LLColor4 green6; - - static LLColor4 blue1; - static LLColor4 blue2; - static LLColor4 blue3; - static LLColor4 blue4; - static LLColor4 blue5; - static LLColor4 blue6; - - static LLColor4 yellow1; - static LLColor4 yellow2; - static LLColor4 yellow3; - static LLColor4 yellow4; - static LLColor4 yellow5; - static LLColor4 yellow6; - static LLColor4 yellow7; - static LLColor4 yellow8; - static LLColor4 yellow9; - - static LLColor4 orange1; - static LLColor4 orange2; - static LLColor4 orange3; - static LLColor4 orange4; - static LLColor4 orange5; - static LLColor4 orange6; - - static LLColor4 magenta1; - static LLColor4 magenta2; - static LLColor4 magenta3; - static LLColor4 magenta4; - - static LLColor4 purple1; - static LLColor4 purple2; - static LLColor4 purple3; - static LLColor4 purple4; - static LLColor4 purple5; - static LLColor4 purple6; - - static LLColor4 pink1; - static LLColor4 pink2; - - static LLColor4 cyan1; - static LLColor4 cyan2; - static LLColor4 cyan3; - static LLColor4 cyan4; - static LLColor4 cyan5; - static LLColor4 cyan6; - - static bool parseColor(const std::string& buf, LLColor4* color); - static bool parseColor4(const std::string& buf, LLColor4* color); - - inline void clamp(); -}; +public: + F32 mV[LENGTHOFCOLOR4]; + LLColor4(); // Initializes LLColor4 to (0, 0, 0, 1) + LLColor4(F32 r, F32 g, F32 b); // Initializes LLColor4 to (r, g, b, 1) + LLColor4(F32 r, F32 g, F32 b, F32 a); // Initializes LLColor4 to (r. g, b, a) + LLColor4(const LLColor3& vec, F32 a = 1.f); // Initializes LLColor4 to (vec, a) + explicit LLColor4(const LLSD& sd); + explicit LLColor4(const F32* vec); // Initializes LLColor4 to (vec[0]. vec[1], vec[2], 1) + explicit LLColor4(U32 clr); // Initializes LLColor4 to (r=clr>>24, etc)) + explicit LLColor4(const LLColor4U& color4u); // "explicit" to avoid automatic conversion + explicit LLColor4(const LLVector4& vector4); // "explicit" to avoid automatic conversion + + LLSD getValue() const + { + LLSD ret; + ret[0] = mV[VRED]; + ret[1] = mV[VGREEN]; + ret[2] = mV[VBLUE]; + ret[3] = mV[VALPHA]; + return ret; + } + void setValue(const LLSD& sd); + + void setHSL(F32 hue, F32 saturation, F32 luminance); + void calcHSL(F32* hue, F32* saturation, F32* luminance) const; + + const LLColor4& setToBlack(); // zero LLColor4 to (0, 0, 0, 1) + const LLColor4& setToWhite(); // zero LLColor4 to (0, 0, 0, 1) + + const LLColor4& setVec(F32 r, F32 g, F32 b, F32 a); // deprecated -- use set() + const LLColor4& setVec(F32 r, F32 g, F32 b); // deprecated -- use set() + const LLColor4& setVec(const LLColor4& vec); // deprecated -- use set() + const LLColor4& setVec(const LLColor3& vec); // deprecated -- use set() + const LLColor4& setVec(const LLColor3& vec, F32 a); // deprecated -- use set() + const LLColor4& setVec(const F32* vec); // deprecated -- use set() + const LLColor4& setVec(const LLColor4U& color4u); // deprecated -- use set() + + const LLColor4& set(F32 r, F32 g, F32 b, F32 a); // Sets LLColor4 to (r, g, b, a) + const LLColor4& set(F32 r, F32 g, F32 b); // Sets LLColor4 to (r, g, b) (no change in a) + const LLColor4& set(const LLColor4& vec); // Sets LLColor4 to vec + const LLColor4& set(const LLColor3& vec); // Sets LLColor4 to LLColor3 vec (no change in alpha) + const LLColor4& set(const LLColor3& vec, F32 a); // Sets LLColor4 to LLColor3 vec, with alpha specified + const LLColor4& set(const F32* vec); // Sets LLColor4 to vec + const LLColor4& set(const F64* vec); // Sets LLColor4 to (double)vec + const LLColor4& set(const LLColor4U& color4u); // Sets LLColor4 to color4u, rescaled. + + // set from a vector of unknown type and size + // may leave some data unmodified + template + const LLColor4& set(const std::vector& v); + + // write to a vector of unknown type and size + // maye leave some data unmodified + template + void write(std::vector& v) const; + + const LLColor4& setAlpha(F32 a); + + F32 magVec() const; // deprecated -- use length() + F32 magVecSquared() const; // deprecated -- use lengthSquared() + F32 normVec(); // deprecated -- use normalize() + + F32 length() const; // Returns magnitude of LLColor4 + F32 lengthSquared() const; // Returns magnitude squared of LLColor4 + F32 normalize(); // deprecated -- use normalize() + + bool isOpaque() const { return mV[VALPHA] == 1.f; } + + F32 operator[](int idx) const { return mV[idx]; } + F32& operator[](int idx) { return mV[idx]; } + + const LLColor4& operator=(const LLColor3& a); // Assigns vec3 to vec4 and returns vec4 + + bool operator<(const LLColor4& rhs) const; + friend std::ostream& operator<<(std::ostream& s, const LLColor4& a); // Print a + friend LLColor4 operator+(const LLColor4& a, const LLColor4& b); // Return vector a + b + friend LLColor4 operator-(const LLColor4& a, const LLColor4& b); // Return vector a minus b + friend LLColor4 operator*(const LLColor4& a, const LLColor4& b); // Return component wise a * b + friend LLColor4 operator*(const LLColor4& a, F32 k); // Return rgb times scaler k (no alpha change) + friend LLColor4 operator/(const LLColor4& a, F32 k); // Return rgb divided by scalar k (no alpha change) + friend LLColor4 operator*(F32 k, const LLColor4& a); // Return rgb times scaler k (no alpha change) + friend LLColor4 operator%(const LLColor4& a, F32 k); // Return alpha times scaler k (no rgb change) + friend LLColor4 operator%(F32 k, const LLColor4& a); // Return alpha times scaler k (no rgb change) + + friend bool operator==(const LLColor4& a, const LLColor4& b); // Return a == b + friend bool operator!=(const LLColor4& a, const LLColor4& b); // Return a != b + + friend bool operator==(const LLColor4& a, const LLColor3& b); // Return a == b + friend bool operator!=(const LLColor4& a, const LLColor3& b); // Return a != b + + friend const LLColor4& operator+=(LLColor4& a, const LLColor4& b); // Return vector a + b + friend const LLColor4& operator-=(LLColor4& a, const LLColor4& b); // Return vector a minus b + friend const LLColor4& operator*=(LLColor4& a, F32 k); // Return rgb times scaler k (no alpha change) + friend const LLColor4& operator%=(LLColor4& a, F32 k); // Return alpha times scaler k (no rgb change) + + friend const LLColor4& operator*=(LLColor4& a, const LLColor4& b); // Doesn't multiply alpha! (for lighting) + + // conversion + operator LLColor4U() const; + + // Basic color values. + static LLColor4 red; + static LLColor4 green; + static LLColor4 blue; + static LLColor4 black; + static LLColor4 white; + static LLColor4 yellow; + static LLColor4 magenta; + static LLColor4 cyan; + static LLColor4 smoke; + static LLColor4 grey; + static LLColor4 orange; + static LLColor4 purple; + static LLColor4 pink; + static LLColor4 transparent; + + // Extra color values. + static LLColor4 grey1; + static LLColor4 grey2; + static LLColor4 grey3; + static LLColor4 grey4; + + static LLColor4 red1; + static LLColor4 red2; + static LLColor4 red3; + static LLColor4 red4; + static LLColor4 red5; + + static LLColor4 green1; + static LLColor4 green2; + static LLColor4 green3; + static LLColor4 green4; + static LLColor4 green5; + static LLColor4 green6; + + static LLColor4 blue1; + static LLColor4 blue2; + static LLColor4 blue3; + static LLColor4 blue4; + static LLColor4 blue5; + static LLColor4 blue6; + + static LLColor4 yellow1; + static LLColor4 yellow2; + static LLColor4 yellow3; + static LLColor4 yellow4; + static LLColor4 yellow5; + static LLColor4 yellow6; + static LLColor4 yellow7; + static LLColor4 yellow8; + static LLColor4 yellow9; + + static LLColor4 orange1; + static LLColor4 orange2; + static LLColor4 orange3; + static LLColor4 orange4; + static LLColor4 orange5; + static LLColor4 orange6; + + static LLColor4 magenta1; + static LLColor4 magenta2; + static LLColor4 magenta3; + static LLColor4 magenta4; + + static LLColor4 purple1; + static LLColor4 purple2; + static LLColor4 purple3; + static LLColor4 purple4; + static LLColor4 purple5; + static LLColor4 purple6; + + static LLColor4 pink1; + static LLColor4 pink2; + + static LLColor4 cyan1; + static LLColor4 cyan2; + static LLColor4 cyan3; + static LLColor4 cyan4; + static LLColor4 cyan5; + static LLColor4 cyan6; + + static bool parseColor(const std::string& buf, LLColor4* color); + static bool parseColor4(const std::string& buf, LLColor4* color); + + inline void clamp(); +}; // Non-member functions -F32 distVec(const LLColor4 &a, const LLColor4 &b); // Returns distance between a and b -F32 distVec_squared(const LLColor4 &a, const LLColor4 &b); // Returns distance squared between a and b -LLColor3 vec4to3(const LLColor4 &vec); -LLColor4 vec3to4(const LLColor3 &vec); -LLColor4 lerp(const LLColor4 &a, const LLColor4 &b, F32 u); +F32 distVec(const LLColor4& a, const LLColor4& b); // Returns distance between a and b +F32 distVec_squared(const LLColor4& a, const LLColor4& b); // Returns distance squared between a and b +LLColor3 vec4to3(const LLColor4& vec); +LLColor4 vec3to4(const LLColor3& vec); +LLColor4 lerp(const LLColor4& a, const LLColor4& b, F32 u); -inline LLColor4::LLColor4(void) +inline LLColor4::LLColor4() { - mV[VRED] = 0.f; + mV[VRED] = 0.f; mV[VGREEN] = 0.f; - mV[VBLUE] = 0.f; + mV[VBLUE] = 0.f; mV[VALPHA] = 1.f; } @@ -255,149 +253,146 @@ inline LLColor4::LLColor4(const LLSD& sd) inline LLColor4::LLColor4(F32 r, F32 g, F32 b) { - mV[VRED] = r; + mV[VRED] = r; mV[VGREEN] = g; - mV[VBLUE] = b; + mV[VBLUE] = b; mV[VALPHA] = 1.f; } inline LLColor4::LLColor4(F32 r, F32 g, F32 b, F32 a) { - mV[VRED] = r; + mV[VRED] = r; mV[VGREEN] = g; - mV[VBLUE] = b; + mV[VBLUE] = b; mV[VALPHA] = a; } inline LLColor4::LLColor4(U32 clr) { - mV[VRED] = (clr&0xff) * (1.0f/255.0f); - mV[VGREEN] = ((clr>>8)&0xff) * (1.0f/255.0f); - mV[VBLUE] = ((clr>>16)&0xff) * (1.0f/255.0f); - mV[VALPHA] = (clr>>24) * (1.0f/255.0f); + mV[VRED] = (clr & 0xff) * (1.0f / 255.0f); + mV[VGREEN] = ((clr >> 8) & 0xff) * (1.0f / 255.0f); + mV[VBLUE] = ((clr >> 16) & 0xff) * (1.0f / 255.0f); + mV[VALPHA] = (clr >> 24) * (1.0f / 255.0f); } - -inline LLColor4::LLColor4(const F32 *vec) +inline LLColor4::LLColor4(const F32* vec) { - mV[VRED] = vec[VRED]; + mV[VRED] = vec[VRED]; mV[VGREEN] = vec[VGREEN]; - mV[VBLUE] = vec[VBLUE]; + mV[VBLUE] = vec[VBLUE]; mV[VALPHA] = vec[VALPHA]; } -inline const LLColor4& LLColor4::setToBlack(void) +inline const LLColor4& LLColor4::setToBlack(void) { - mV[VRED] = 0.f; + mV[VRED] = 0.f; mV[VGREEN] = 0.f; - mV[VBLUE] = 0.f; + mV[VBLUE] = 0.f; mV[VALPHA] = 1.f; return (*this); } -inline const LLColor4& LLColor4::setToWhite(void) +inline const LLColor4& LLColor4::setToWhite(void) { - mV[VRED] = 1.f; + mV[VRED] = 1.f; mV[VGREEN] = 1.f; - mV[VBLUE] = 1.f; + mV[VBLUE] = 1.f; mV[VALPHA] = 1.f; return (*this); } -inline const LLColor4& LLColor4::set(F32 x, F32 y, F32 z) +inline const LLColor4& LLColor4::set(F32 x, F32 y, F32 z) { - mV[VRED] = x; + mV[VRED] = x; mV[VGREEN] = y; - mV[VBLUE] = z; + mV[VBLUE] = z; -// no change to alpha! -// mV[VALPHA] = 1.f; + // no change to alpha! + // mV[VALPHA] = 1.f; return (*this); } -inline const LLColor4& LLColor4::set(F32 x, F32 y, F32 z, F32 a) +inline const LLColor4& LLColor4::set(F32 x, F32 y, F32 z, F32 a) { - mV[VRED] = x; + mV[VRED] = x; mV[VGREEN] = y; - mV[VBLUE] = z; + mV[VBLUE] = z; mV[VALPHA] = a; return (*this); } -inline const LLColor4& LLColor4::set(const LLColor4 &vec) +inline const LLColor4& LLColor4::set(const LLColor4& vec) { - mV[VRED] = vec.mV[VRED]; + mV[VRED] = vec.mV[VRED]; mV[VGREEN] = vec.mV[VGREEN]; - mV[VBLUE] = vec.mV[VBLUE]; + mV[VBLUE] = vec.mV[VBLUE]; mV[VALPHA] = vec.mV[VALPHA]; return (*this); } - -inline const LLColor4& LLColor4::set(const F32 *vec) +inline const LLColor4& LLColor4::set(const F32* vec) { - mV[VRED] = vec[VRED]; + mV[VRED] = vec[VRED]; mV[VGREEN] = vec[VGREEN]; - mV[VBLUE] = vec[VBLUE]; + mV[VBLUE] = vec[VBLUE]; mV[VALPHA] = vec[VALPHA]; return (*this); } -inline const LLColor4& LLColor4::set(const F64 *vec) +inline const LLColor4& LLColor4::set(const F64* vec) { - mV[VRED] = static_cast(vec[VRED]); + mV[VRED] = static_cast(vec[VRED]); mV[VGREEN] = static_cast(vec[VGREEN]); - mV[VBLUE] = static_cast(vec[VBLUE]); + mV[VBLUE] = static_cast(vec[VBLUE]); mV[VALPHA] = static_cast(vec[VALPHA]); return (*this); } // deprecated -inline const LLColor4& LLColor4::setVec(F32 x, F32 y, F32 z) +inline const LLColor4& LLColor4::setVec(F32 x, F32 y, F32 z) { - mV[VRED] = x; + mV[VRED] = x; mV[VGREEN] = y; - mV[VBLUE] = z; + mV[VBLUE] = z; -// no change to alpha! -// mV[VALPHA] = 1.f; + // no change to alpha! + // mV[VALPHA] = 1.f; return (*this); } // deprecated -inline const LLColor4& LLColor4::setVec(F32 x, F32 y, F32 z, F32 a) +inline const LLColor4& LLColor4::setVec(F32 x, F32 y, F32 z, F32 a) { - mV[VRED] = x; + mV[VRED] = x; mV[VGREEN] = y; - mV[VBLUE] = z; + mV[VBLUE] = z; mV[VALPHA] = a; return (*this); } // deprecated -inline const LLColor4& LLColor4::setVec(const LLColor4 &vec) +inline const LLColor4& LLColor4::setVec(const LLColor4& vec) { - mV[VRED] = vec.mV[VRED]; + mV[VRED] = vec.mV[VRED]; mV[VGREEN] = vec.mV[VGREEN]; - mV[VBLUE] = vec.mV[VBLUE]; + mV[VBLUE] = vec.mV[VBLUE]; mV[VALPHA] = vec.mV[VALPHA]; return (*this); } - // deprecated -inline const LLColor4& LLColor4::setVec(const F32 *vec) +inline const LLColor4& LLColor4::setVec(const F32* vec) { - mV[VRED] = vec[VRED]; + mV[VRED] = vec[VRED]; mV[VGREEN] = vec[VGREEN]; - mV[VBLUE] = vec[VBLUE]; + mV[VBLUE] = vec[VBLUE]; mV[VALPHA] = vec[VALPHA]; return (*this); } -inline const LLColor4& LLColor4::setAlpha(F32 a) +inline const LLColor4& LLColor4::setAlpha(F32 a) { mV[VALPHA] = a; return (*this); @@ -405,155 +400,116 @@ inline const LLColor4& LLColor4::setAlpha(F32 a) // LLColor4 Magnitude and Normalization Functions -inline F32 LLColor4::length(void) const +inline F32 LLColor4::length() const { - return (F32) sqrt(mV[VRED]*mV[VRED] + mV[VGREEN]*mV[VGREEN] + mV[VBLUE]*mV[VBLUE]); + return sqrt(mV[VRED] * mV[VRED] + mV[VGREEN] * mV[VGREEN] + mV[VBLUE] * mV[VBLUE]); } -inline F32 LLColor4::lengthSquared(void) const +inline F32 LLColor4::lengthSquared() const { - return mV[VRED]*mV[VRED] + mV[VGREEN]*mV[VGREEN] + mV[VBLUE]*mV[VBLUE]; + return mV[VRED] * mV[VRED] + mV[VGREEN] * mV[VGREEN] + mV[VBLUE] * mV[VBLUE]; } -inline F32 LLColor4::normalize(void) +inline F32 LLColor4::normalize() { - F32 mag = (F32) sqrt(mV[VRED]*mV[VRED] + mV[VGREEN]*mV[VGREEN] + mV[VBLUE]*mV[VBLUE]); + F32 mag = sqrt(mV[VRED] * mV[VRED] + mV[VGREEN] * mV[VGREEN] + mV[VBLUE] * mV[VBLUE]); F32 oomag; if (mag) { - oomag = 1.f/mag; + oomag = 1.f / mag; mV[VRED] *= oomag; mV[VGREEN] *= oomag; mV[VBLUE] *= oomag; } - return (mag); + return mag; } // deprecated -inline F32 LLColor4::magVec(void) const +inline F32 LLColor4::magVec() const { - return (F32) sqrt(mV[VRED]*mV[VRED] + mV[VGREEN]*mV[VGREEN] + mV[VBLUE]*mV[VBLUE]); + return sqrt(mV[VRED] * mV[VRED] + mV[VGREEN] * mV[VGREEN] + mV[VBLUE] * mV[VBLUE]); } // deprecated -inline F32 LLColor4::magVecSquared(void) const +inline F32 LLColor4::magVecSquared() const { - return mV[VRED]*mV[VRED] + mV[VGREEN]*mV[VGREEN] + mV[VBLUE]*mV[VBLUE]; + return mV[VRED] * mV[VRED] + mV[VGREEN] * mV[VGREEN] + mV[VBLUE] * mV[VBLUE]; } // deprecated -inline F32 LLColor4::normVec(void) +inline F32 LLColor4::normVec() { - F32 mag = (F32) sqrt(mV[VRED]*mV[VRED] + mV[VGREEN]*mV[VGREEN] + mV[VBLUE]*mV[VBLUE]); + F32 mag = sqrt(mV[VRED] * mV[VRED] + mV[VGREEN] * mV[VGREEN] + mV[VBLUE] * mV[VBLUE]); F32 oomag; if (mag) { - oomag = 1.f/mag; + oomag = 1.f / mag; mV[VRED] *= oomag; mV[VGREEN] *= oomag; mV[VBLUE] *= oomag; } - return (mag); + return mag; } // LLColor4 Operators - -inline LLColor4 operator+(const LLColor4 &a, const LLColor4 &b) +inline LLColor4 operator+(const LLColor4& a, const LLColor4& b) { - return LLColor4( - a.mV[VRED] + b.mV[VRED], - a.mV[VGREEN] + b.mV[VGREEN], - a.mV[VBLUE] + b.mV[VBLUE], - a.mV[VALPHA] + b.mV[VALPHA]); + return LLColor4(a.mV[VRED] + b.mV[VRED], a.mV[VGREEN] + b.mV[VGREEN], a.mV[VBLUE] + b.mV[VBLUE], a.mV[VALPHA] + b.mV[VALPHA]); } -inline LLColor4 operator-(const LLColor4 &a, const LLColor4 &b) +inline LLColor4 operator-(const LLColor4& a, const LLColor4& b) { - return LLColor4( - a.mV[VRED] - b.mV[VRED], - a.mV[VGREEN] - b.mV[VGREEN], - a.mV[VBLUE] - b.mV[VBLUE], - a.mV[VALPHA] - b.mV[VALPHA]); + return LLColor4(a.mV[VRED] - b.mV[VRED], a.mV[VGREEN] - b.mV[VGREEN], a.mV[VBLUE] - b.mV[VBLUE], a.mV[VALPHA] - b.mV[VALPHA]); } -inline LLColor4 operator*(const LLColor4 &a, const LLColor4 &b) +inline LLColor4 operator*(const LLColor4& a, const LLColor4& b) { - return LLColor4( - a.mV[VRED] * b.mV[VRED], - a.mV[VGREEN] * b.mV[VGREEN], - a.mV[VBLUE] * b.mV[VBLUE], - a.mV[VALPHA] * b.mV[VALPHA]); + return LLColor4(a.mV[VRED] * b.mV[VRED], a.mV[VGREEN] * b.mV[VGREEN], a.mV[VBLUE] * b.mV[VBLUE], a.mV[VALPHA] * b.mV[VALPHA]); } -inline LLColor4 operator*(const LLColor4 &a, F32 k) +inline LLColor4 operator*(const LLColor4& a, F32 k) { // only affects rgb (not a!) - return LLColor4( - a.mV[VRED] * k, - a.mV[VGREEN] * k, - a.mV[VBLUE] * k, - a.mV[VALPHA]); + return LLColor4(a.mV[VRED] * k, a.mV[VGREEN] * k, a.mV[VBLUE] * k, a.mV[VALPHA]); } -inline LLColor4 operator/(const LLColor4 &a, F32 k) +inline LLColor4 operator/(const LLColor4& a, F32 k) { - return LLColor4( - a.mV[VRED] / k, - a.mV[VGREEN] / k, - a.mV[VBLUE] / k, - a.mV[VALPHA]); + return LLColor4(a.mV[VRED] / k, a.mV[VGREEN] / k, a.mV[VBLUE] / k, a.mV[VALPHA]); } -inline LLColor4 operator*(F32 k, const LLColor4 &a) +inline LLColor4 operator*(F32 k, const LLColor4& a) { // only affects rgb (not a!) - return LLColor4( - a.mV[VRED] * k, - a.mV[VGREEN] * k, - a.mV[VBLUE] * k, - a.mV[VALPHA]); + return LLColor4(a.mV[VRED] * k, a.mV[VGREEN] * k, a.mV[VBLUE] * k, a.mV[VALPHA]); } -inline LLColor4 operator%(F32 k, const LLColor4 &a) +inline LLColor4 operator%(F32 k, const LLColor4& a) { // only affects alpha (not rgb!) - return LLColor4( - a.mV[VRED], - a.mV[VGREEN], - a.mV[VBLUE], - a.mV[VALPHA] * k); + return LLColor4(a.mV[VRED], a.mV[VGREEN], a.mV[VBLUE], a.mV[VALPHA] * k); } -inline LLColor4 operator%(const LLColor4 &a, F32 k) +inline LLColor4 operator%(const LLColor4& a, F32 k) { // only affects alpha (not rgb!) - return LLColor4( - a.mV[VRED], - a.mV[VGREEN], - a.mV[VBLUE], - a.mV[VALPHA] * k); + return LLColor4(a.mV[VRED], a.mV[VGREEN], a.mV[VBLUE], a.mV[VALPHA] * k); } -inline bool operator==(const LLColor4 &a, const LLColor4 &b) +inline bool operator==(const LLColor4& a, const LLColor4& b) { - return ( (a.mV[VRED] == b.mV[VRED]) - &&(a.mV[VGREEN] == b.mV[VGREEN]) - &&(a.mV[VBLUE] == b.mV[VBLUE]) - &&(a.mV[VALPHA] == b.mV[VALPHA])); + return ((a.mV[VRED] == b.mV[VRED]) && (a.mV[VGREEN] == b.mV[VGREEN]) && (a.mV[VBLUE] == b.mV[VBLUE]) && (a.mV[VALPHA] == b.mV[VALPHA])); } -inline bool operator!=(const LLColor4 &a, const LLColor4 &b) +inline bool operator!=(const LLColor4& a, const LLColor4& b) { - return ( (a.mV[VRED] != b.mV[VRED]) - ||(a.mV[VGREEN] != b.mV[VGREEN]) - ||(a.mV[VBLUE] != b.mV[VBLUE]) - ||(a.mV[VALPHA] != b.mV[VALPHA])); + return ((a.mV[VRED] != b.mV[VRED]) || (a.mV[VGREEN] != b.mV[VGREEN]) || (a.mV[VBLUE] != b.mV[VBLUE]) || (a.mV[VALPHA] != b.mV[VALPHA])); } -inline const LLColor4& operator+=(LLColor4 &a, const LLColor4 &b) +inline const LLColor4& operator+=(LLColor4& a, const LLColor4& b) { a.mV[VRED] += b.mV[VRED]; a.mV[VGREEN] += b.mV[VGREEN]; @@ -562,7 +518,7 @@ inline const LLColor4& operator+=(LLColor4 &a, const LLColor4 &b) return a; } -inline const LLColor4& operator-=(LLColor4 &a, const LLColor4 &b) +inline const LLColor4& operator-=(LLColor4& a, const LLColor4& b) { a.mV[VRED] -= b.mV[VRED]; a.mV[VGREEN] -= b.mV[VGREEN]; @@ -571,7 +527,7 @@ inline const LLColor4& operator-=(LLColor4 &a, const LLColor4 &b) return a; } -inline const LLColor4& operator*=(LLColor4 &a, F32 k) +inline const LLColor4& operator*=(LLColor4& a, F32 k) { // only affects rgb (not a!) a.mV[VRED] *= k; @@ -580,121 +536,120 @@ inline const LLColor4& operator*=(LLColor4 &a, F32 k) return a; } -inline const LLColor4& operator *=(LLColor4 &a, const LLColor4 &b) +inline const LLColor4& operator*=(LLColor4& a, const LLColor4& b) { a.mV[VRED] *= b.mV[VRED]; a.mV[VGREEN] *= b.mV[VGREEN]; a.mV[VBLUE] *= b.mV[VBLUE]; -// a.mV[VALPHA] *= b.mV[VALPHA]; + // a.mV[VALPHA] *= b.mV[VALPHA]; return a; } -inline const LLColor4& operator%=(LLColor4 &a, F32 k) +inline const LLColor4& operator%=(LLColor4& a, F32 k) { // only affects alpha (not rgb!) a.mV[VALPHA] *= k; return a; } - // Non-member functions -inline F32 distVec(const LLColor4 &a, const LLColor4 &b) +inline F32 distVec(const LLColor4& a, const LLColor4& b) { LLColor4 vec = a - b; - return (vec.length()); + return vec.length(); } -inline F32 distVec_squared(const LLColor4 &a, const LLColor4 &b) +inline F32 distVec_squared(const LLColor4& a, const LLColor4& b) { LLColor4 vec = a - b; - return (vec.lengthSquared()); + return vec.lengthSquared(); } -inline LLColor4 lerp(const LLColor4 &a, const LLColor4 &b, F32 u) +inline LLColor4 lerp(const LLColor4& a, const LLColor4& b, F32 u) { - return LLColor4( - a.mV[VRED] + (b.mV[VRED] - a.mV[VRED]) * u, - a.mV[VGREEN] + (b.mV[VGREEN] - a.mV[VGREEN]) * u, - a.mV[VBLUE] + (b.mV[VBLUE] - a.mV[VBLUE]) * u, - a.mV[VALPHA] + (b.mV[VALPHA] - a.mV[VALPHA]) * u); + return LLColor4(a.mV[VRED] + (b.mV[VRED] - a.mV[VRED]) * u, + a.mV[VGREEN] + (b.mV[VGREEN] - a.mV[VGREEN]) * u, + a.mV[VBLUE] + (b.mV[VBLUE] - a.mV[VBLUE]) * u, + a.mV[VALPHA] + (b.mV[VALPHA] - a.mV[VALPHA]) * u); } inline bool LLColor4::operator<(const LLColor4& rhs) const { - if (mV[0] != rhs.mV[0]) + if (mV[VRED] != rhs.mV[VRED]) { - return mV[0] < rhs.mV[0]; + return mV[VRED] < rhs.mV[VRED]; } - if (mV[1] != rhs.mV[1]) + if (mV[VGREEN] != rhs.mV[VGREEN]) { - return mV[1] < rhs.mV[1]; + return mV[VGREEN] < rhs.mV[VGREEN]; } - if (mV[2] != rhs.mV[2]) + if (mV[VBLUE] != rhs.mV[VBLUE]) { - return mV[2] < rhs.mV[2]; + return mV[VBLUE] < rhs.mV[VBLUE]; } - return mV[3] < rhs.mV[3]; + return mV[VALPHA] < rhs.mV[VALPHA]; } void LLColor4::clamp() { // Clamp the color... - if (mV[0] < 0.f) + if (mV[VRED] < 0.f) { - mV[0] = 0.f; + mV[VRED] = 0.f; } - else if (mV[0] > 1.f) + else if (mV[VRED] > 1.f) { - mV[0] = 1.f; + mV[VRED] = 1.f; } - if (mV[1] < 0.f) + if (mV[VGREEN] < 0.f) { - mV[1] = 0.f; + mV[VGREEN] = 0.f; } - else if (mV[1] > 1.f) + else if (mV[VGREEN] > 1.f) { - mV[1] = 1.f; + mV[VGREEN] = 1.f; } - if (mV[2] < 0.f) + if (mV[VBLUE] < 0.f) { - mV[2] = 0.f; + mV[VBLUE] = 0.f; } - else if (mV[2] > 1.f) + else if (mV[VBLUE] > 1.f) { - mV[2] = 1.f; + mV[VBLUE] = 1.f; } - if (mV[3] < 0.f) + if (mV[VALPHA] < 0.f) { - mV[3] = 0.f; + mV[VALPHA] = 0.f; } - else if (mV[3] > 1.f) + else if (mV[VALPHA] > 1.f) { - mV[3] = 1.f; + mV[VALPHA] = 1.f; } } // Return the given linear space color value in gamma corrected (sRGB) space -inline const LLColor4 srgbColor4(const LLColor4 &a) { +inline const LLColor4 srgbColor4(const LLColor4& a) +{ LLColor4 srgbColor; - srgbColor.mV[0] = linearTosRGB(a.mV[0]); - srgbColor.mV[1] = linearTosRGB(a.mV[1]); - srgbColor.mV[2] = linearTosRGB(a.mV[2]); - srgbColor.mV[3] = a.mV[3]; + srgbColor.mV[VRED] = linearTosRGB(a.mV[VRED]); + srgbColor.mV[VGREEN] = linearTosRGB(a.mV[VGREEN]); + srgbColor.mV[VBLUE] = linearTosRGB(a.mV[VBLUE]); + srgbColor.mV[VALPHA] = a.mV[VALPHA]; return srgbColor; } // Return the given gamma corrected (sRGB) color in linear space -inline const LLColor4 linearColor4(const LLColor4 &a) +inline const LLColor4 linearColor4(const LLColor4& a) { LLColor4 linearColor; - linearColor.mV[0] = sRGBtoLinear(a.mV[0]); - linearColor.mV[1] = sRGBtoLinear(a.mV[1]); - linearColor.mV[2] = sRGBtoLinear(a.mV[2]); - linearColor.mV[3] = a.mV[3]; + linearColor.mV[VRED] = sRGBtoLinear(a.mV[VRED]); + linearColor.mV[VGREEN] = sRGBtoLinear(a.mV[VGREEN]); + linearColor.mV[VBLUE] = sRGBtoLinear(a.mV[VBLUE]); + linearColor.mV[VALPHA] = a.mV[VALPHA]; return linearColor; } @@ -720,4 +675,3 @@ void LLColor4::write(std::vector& v) const } #endif - diff --git a/indra/llmath/v4coloru.cpp b/indra/llmath/v4coloru.cpp index acf349245a..46314bae26 100644 --- a/indra/llmath/v4coloru.cpp +++ b/indra/llmath/v4coloru.cpp @@ -26,62 +26,19 @@ #include "linden_common.h" -//#include "v3coloru.h" #include "v4coloru.h" -#include "v4color.h" -//#include "vmath.h" #include "llmath.h" // LLColor4U +// clang-format off LLColor4U LLColor4U::white(255, 255, 255, 255); LLColor4U LLColor4U::black( 0, 0, 0, 255); LLColor4U LLColor4U::red (255, 0, 0, 255); LLColor4U LLColor4U::green( 0, 255, 0, 255); LLColor4U LLColor4U::blue ( 0, 0, 255, 255); +// clang-format on -// conversion -/* inlined to fix gcc compile link error -LLColor4U::operator LLColor4() -{ - return(LLColor4((F32)mV[VRED]/255.f,(F32)mV[VGREEN]/255.f,(F32)mV[VBLUE]/255.f,(F32)mV[VALPHA]/255.f)); -} -*/ - -// Constructors - - -/* -LLColor4U::LLColor4U(const LLColor3 &vec) -{ - mV[VRED] = vec.mV[VRED]; - mV[VGREEN] = vec.mV[VGREEN]; - mV[VBLUE] = vec.mV[VBLUE]; - mV[VALPHA] = 255; -} -*/ - - -// Clear and Assignment Functions - - - -// LLColor4U Operators - -/* -LLColor4U LLColor4U::operator=(const LLColor3 &a) -{ - mV[VRED] = a.mV[VRED]; - mV[VGREEN] = a.mV[VGREEN]; - mV[VBLUE] = a.mV[VBLUE]; - -// converting from an rgb sets a=1 (opaque) - mV[VALPHA] = 255; - return (*this); -} -*/ - - -std::ostream& operator<<(std::ostream& s, const LLColor4U &a) +std::ostream& operator<<(std::ostream& s, const LLColor4U& a) { s << "{ " << (S32)a.mV[VRED] << ", " << (S32)a.mV[VGREEN] << ", " << (S32)a.mV[VBLUE] << ", " << (S32)a.mV[VALPHA] << " }"; return s; @@ -90,31 +47,31 @@ std::ostream& operator<<(std::ostream& s, const LLColor4U &a) // static bool LLColor4U::parseColor4U(const std::string& buf, LLColor4U* value) { - if( buf.empty() || value == nullptr) + if (buf.empty() || value == nullptr) { return false; } - U32 v[4]; - S32 count = sscanf( buf.c_str(), "%u, %u, %u, %u", v + 0, v + 1, v + 2, v + 3 ); - if (1 == count ) + U32 v[4]{}; + S32 count = sscanf(buf.c_str(), "%u, %u, %u, %u", v + 0, v + 1, v + 2, v + 3); + if (1 == count) { // try this format - count = sscanf( buf.c_str(), "%u %u %u %u", v + 0, v + 1, v + 2, v + 3 ); + count = sscanf(buf.c_str(), "%u %u %u %u", v + 0, v + 1, v + 2, v + 3); } - if( 4 != count ) + if (4 != count) { return false; } - for( S32 i = 0; i < 4; i++ ) + for (S32 i = 0; i < 4; i++) { - if( v[i] > U8_MAX ) + if (v[i] > U8_MAX) { return false; } } - value->set( U8(v[0]), U8(v[1]), U8(v[2]), U8(v[3]) ); + value->set(U8(v[0]), U8(v[1]), U8(v[2]), U8(v[3])); return true; } diff --git a/indra/llmath/v4coloru.h b/indra/llmath/v4coloru.h index 29128a08a7..e1a0206461 100644 --- a/indra/llmath/v4coloru.h +++ b/indra/llmath/v4coloru.h @@ -28,104 +28,93 @@ #define LL_V4COLORU_H #include "llerror.h" -//#include "vmath.h" #include "llmath.h" -//#include "v4color.h" #include "v3color.h" #include "v4color.h" -//class LLColor3U; class LLColor4; // LLColor4U = | red green blue alpha | -static const U32 LENGTHOFCOLOR4U = 4; - +static constexpr U32 LENGTHOFCOLOR4U = 4; class LLColor4U { public: - U8 mV[LENGTHOFCOLOR4U]; - LLColor4U(); // Initializes LLColor4U to (0, 0, 0, 1) - LLColor4U(U8 r, U8 g, U8 b); // Initializes LLColor4U to (r, g, b, 1) - LLColor4U(U8 r, U8 g, U8 b, U8 a); // Initializes LLColor4U to (r. g, b, a) - LLColor4U(const U8 *vec); // Initializes LLColor4U to (vec[0]. vec[1], vec[2], 1) - explicit LLColor4U(const LLSD& sd) - { - setValue(sd); - } + LLColor4U(); // Initializes LLColor4U to (0, 0, 0, 1) + LLColor4U(U8 r, U8 g, U8 b); // Initializes LLColor4U to (r, g, b, 1) + LLColor4U(U8 r, U8 g, U8 b, U8 a); // Initializes LLColor4U to (r. g, b, a) + LLColor4U(const U8* vec); // Initializes LLColor4U to (vec[0]. vec[1], vec[2], 1) + explicit LLColor4U(const LLSD& sd) { setValue(sd); } void setValue(const LLSD& sd) { - mV[0] = sd[0].asInteger(); - mV[1] = sd[1].asInteger(); - mV[2] = sd[2].asInteger(); - mV[3] = sd[3].asInteger(); + mV[VRED] = sd[0].asInteger(); + mV[VGREEN] = sd[1].asInteger(); + mV[VBLUE] = sd[2].asInteger(); + mV[VALPHA] = sd[3].asInteger(); } LLSD getValue() const { LLSD ret; - ret[0] = mV[0]; - ret[1] = mV[1]; - ret[2] = mV[2]; - ret[3] = mV[3]; + ret[0] = mV[VRED]; + ret[1] = mV[VGREEN]; + ret[2] = mV[VBLUE]; + ret[3] = mV[VALPHA]; return ret; } - const LLColor4U& setToBlack(); // zero LLColor4U to (0, 0, 0, 1) - const LLColor4U& setToWhite(); // zero LLColor4U to (0, 0, 0, 1) + const LLColor4U& setToBlack(); // zero LLColor4U to (0, 0, 0, 1) + const LLColor4U& setToWhite(); // zero LLColor4U to (0, 0, 0, 1) - const LLColor4U& set(U8 r, U8 g, U8 b, U8 a);// Sets LLColor4U to (r, g, b, a) - const LLColor4U& set(U8 r, U8 g, U8 b); // Sets LLColor4U to (r, g, b) (no change in a) - const LLColor4U& set(const LLColor4U &vec); // Sets LLColor4U to vec - const LLColor4U& set(const U8 *vec); // Sets LLColor4U to vec + const LLColor4U& set(U8 r, U8 g, U8 b, U8 a); // Sets LLColor4U to (r, g, b, a) + const LLColor4U& set(U8 r, U8 g, U8 b); // Sets LLColor4U to (r, g, b) (no change in a) + const LLColor4U& set(const LLColor4U& vec); // Sets LLColor4U to vec + const LLColor4U& set(const U8* vec); // Sets LLColor4U to vec - const LLColor4U& setVec(U8 r, U8 g, U8 b, U8 a); // deprecated -- use set() - const LLColor4U& setVec(U8 r, U8 g, U8 b); // deprecated -- use set() - const LLColor4U& setVec(const LLColor4U &vec); // deprecated -- use set() - const LLColor4U& setVec(const U8 *vec); // deprecated -- use set() + const LLColor4U& setVec(U8 r, U8 g, U8 b, U8 a); // deprecated -- use set() + const LLColor4U& setVec(U8 r, U8 g, U8 b); // deprecated -- use set() + const LLColor4U& setVec(const LLColor4U& vec); // deprecated -- use set() + const LLColor4U& setVec(const U8* vec); // deprecated -- use set() - const LLColor4U& setAlpha(U8 a); + const LLColor4U& setAlpha(U8 a); - F32 magVec() const; // deprecated -- use length() - F32 magVecSquared() const; // deprecated -- use lengthSquared() + F32 magVec() const; // deprecated -- use length() + F32 magVecSquared() const; // deprecated -- use lengthSquared() - F32 length() const; // Returns magnitude squared of LLColor4U - F32 lengthSquared() const; // Returns magnitude squared of LLColor4U + F32 length() const; // Returns magnitude squared of LLColor4U + F32 lengthSquared() const; // Returns magnitude squared of LLColor4U - friend std::ostream& operator<<(std::ostream& s, const LLColor4U &a); // Print a - friend LLColor4U operator+(const LLColor4U &a, const LLColor4U &b); // Return vector a + b - friend LLColor4U operator-(const LLColor4U &a, const LLColor4U &b); // Return vector a minus b - friend LLColor4U operator*(const LLColor4U &a, const LLColor4U &b); // Return a * b - friend bool operator==(const LLColor4U &a, const LLColor4U &b); // Return a == b - friend bool operator!=(const LLColor4U &a, const LLColor4U &b); // Return a != b + friend std::ostream& operator<<(std::ostream& s, const LLColor4U& a); // Print a + friend LLColor4U operator+(const LLColor4U& a, const LLColor4U& b); // Return vector a + b + friend LLColor4U operator-(const LLColor4U& a, const LLColor4U& b); // Return vector a minus b + friend LLColor4U operator*(const LLColor4U& a, const LLColor4U& b); // Return a * b + friend bool operator==(const LLColor4U& a, const LLColor4U& b); // Return a == b + friend bool operator!=(const LLColor4U& a, const LLColor4U& b); // Return a != b - friend const LLColor4U& operator+=(LLColor4U &a, const LLColor4U &b); // Return vector a + b - friend const LLColor4U& operator-=(LLColor4U &a, const LLColor4U &b); // Return vector a minus b - friend const LLColor4U& operator*=(LLColor4U &a, U8 k); // Return rgb times scaler k (no alpha change) - friend const LLColor4U& operator%=(LLColor4U &a, U8 k); // Return alpha times scaler k (no rgb change) + friend const LLColor4U& operator+=(LLColor4U& a, const LLColor4U& b); // Return vector a + b + friend const LLColor4U& operator-=(LLColor4U& a, const LLColor4U& b); // Return vector a minus b + friend const LLColor4U& operator*=(LLColor4U& a, U8 k); // Return rgb times scaler k (no alpha change) + friend const LLColor4U& operator%=(LLColor4U& a, U8 k); // Return alpha times scaler k (no rgb change) - LLColor4U addClampMax(const LLColor4U &color); // Add and clamp the max + LLColor4U addClampMax(const LLColor4U& color); // Add and clamp the max - LLColor4U multAll(const F32 k); // Multiply ALL channels by scalar k + LLColor4U multAll(const F32 k); // Multiply ALL channels by scalar k - inline void setVecScaleClamp(const LLColor3 &color); - inline void setVecScaleClamp(const LLColor4 &color); + inline void setVecScaleClamp(const LLColor3& color); + inline void setVecScaleClamp(const LLColor4& color); static bool parseColor4U(const std::string& buf, LLColor4U* value); // conversion - operator LLColor4() const - { - return LLColor4(*this); - } + operator LLColor4() const { return LLColor4(*this); } - U32 asRGBA() const; - void fromRGBA( U32 aVal ); + U32 asRGBA() const; + void fromRGBA(U32 aVal); static LLColor4U white; static LLColor4U black; @@ -134,104 +123,95 @@ public: static LLColor4U blue; }; - // Non-member functions -F32 distVec(const LLColor4U &a, const LLColor4U &b); // Returns distance between a and b -F32 distVec_squared(const LLColor4U &a, const LLColor4U &b); // Returns distance squared between a and b - +F32 distVec(const LLColor4U& a, const LLColor4U& b); // Returns distance between a and b +F32 distVec_squared(const LLColor4U& a, const LLColor4U& b); // Returns distance squared between a and b inline LLColor4U::LLColor4U() { - mV[VRED] = 0; + mV[VRED] = 0; mV[VGREEN] = 0; - mV[VBLUE] = 0; + mV[VBLUE] = 0; mV[VALPHA] = 255; } inline LLColor4U::LLColor4U(U8 r, U8 g, U8 b) { - mV[VRED] = r; + mV[VRED] = r; mV[VGREEN] = g; - mV[VBLUE] = b; + mV[VBLUE] = b; mV[VALPHA] = 255; } inline LLColor4U::LLColor4U(U8 r, U8 g, U8 b, U8 a) { - mV[VRED] = r; + mV[VRED] = r; mV[VGREEN] = g; - mV[VBLUE] = b; + mV[VBLUE] = b; mV[VALPHA] = a; } -inline LLColor4U::LLColor4U(const U8 *vec) +inline LLColor4U::LLColor4U(const U8* vec) { - mV[VRED] = vec[VRED]; + mV[VRED] = vec[VRED]; mV[VGREEN] = vec[VGREEN]; - mV[VBLUE] = vec[VBLUE]; + mV[VBLUE] = vec[VBLUE]; mV[VALPHA] = vec[VALPHA]; } -/* -inline LLColor4U::operator LLColor4() -{ - return(LLColor4((F32)mV[VRED]/255.f,(F32)mV[VGREEN]/255.f,(F32)mV[VBLUE]/255.f,(F32)mV[VALPHA]/255.f)); -} -*/ - inline const LLColor4U& LLColor4U::setToBlack(void) { - mV[VRED] = 0; + mV[VRED] = 0; mV[VGREEN] = 0; - mV[VBLUE] = 0; + mV[VBLUE] = 0; mV[VALPHA] = 255; return (*this); } inline const LLColor4U& LLColor4U::setToWhite(void) { - mV[VRED] = 255; + mV[VRED] = 255; mV[VGREEN] = 255; - mV[VBLUE] = 255; + mV[VBLUE] = 255; mV[VALPHA] = 255; return (*this); } inline const LLColor4U& LLColor4U::set(const U8 x, const U8 y, const U8 z) { - mV[VRED] = x; + mV[VRED] = x; mV[VGREEN] = y; - mV[VBLUE] = z; + mV[VBLUE] = z; -// no change to alpha! -// mV[VALPHA] = 255; + // no change to alpha! + // mV[VALPHA] = 255; return (*this); } inline const LLColor4U& LLColor4U::set(const U8 r, const U8 g, const U8 b, U8 a) { - mV[0] = r; - mV[1] = g; - mV[2] = b; - mV[3] = a; + mV[VRED] = r; + mV[VGREEN] = g; + mV[VBLUE] = b; + mV[VALPHA] = a; return (*this); } -inline const LLColor4U& LLColor4U::set(const LLColor4U &vec) +inline const LLColor4U& LLColor4U::set(const LLColor4U& vec) { - mV[VRED] = vec.mV[VRED]; + mV[VRED] = vec.mV[VRED]; mV[VGREEN] = vec.mV[VGREEN]; - mV[VBLUE] = vec.mV[VBLUE]; + mV[VBLUE] = vec.mV[VBLUE]; mV[VALPHA] = vec.mV[VALPHA]; return (*this); } -inline const LLColor4U& LLColor4U::set(const U8 *vec) +inline const LLColor4U& LLColor4U::set(const U8* vec) { - mV[VRED] = vec[VRED]; + mV[VRED] = vec[VRED]; mV[VGREEN] = vec[VGREEN]; - mV[VBLUE] = vec[VBLUE]; + mV[VBLUE] = vec[VBLUE]; mV[VALPHA] = vec[VALPHA]; return (*this); } @@ -239,12 +219,12 @@ inline const LLColor4U& LLColor4U::set(const U8 *vec) // deprecated inline const LLColor4U& LLColor4U::setVec(const U8 x, const U8 y, const U8 z) { - mV[VRED] = x; + mV[VRED] = x; mV[VGREEN] = y; - mV[VBLUE] = z; + mV[VBLUE] = z; -// no change to alpha! -// mV[VALPHA] = 255; + // no change to alpha! + // mV[VALPHA] = 255; return (*this); } @@ -252,29 +232,29 @@ inline const LLColor4U& LLColor4U::setVec(const U8 x, const U8 y, const U8 z) // deprecated inline const LLColor4U& LLColor4U::setVec(const U8 r, const U8 g, const U8 b, U8 a) { - mV[0] = r; - mV[1] = g; - mV[2] = b; - mV[3] = a; + mV[VRED] = r; + mV[VGREEN] = g; + mV[VBLUE] = b; + mV[VALPHA] = a; return (*this); } // deprecated -inline const LLColor4U& LLColor4U::setVec(const LLColor4U &vec) +inline const LLColor4U& LLColor4U::setVec(const LLColor4U& vec) { - mV[VRED] = vec.mV[VRED]; + mV[VRED] = vec.mV[VRED]; mV[VGREEN] = vec.mV[VGREEN]; - mV[VBLUE] = vec.mV[VBLUE]; + mV[VBLUE] = vec.mV[VBLUE]; mV[VALPHA] = vec.mV[VALPHA]; return (*this); } // deprecated -inline const LLColor4U& LLColor4U::setVec(const U8 *vec) +inline const LLColor4U& LLColor4U::setVec(const U8* vec) { - mV[VRED] = vec[VRED]; + mV[VRED] = vec[VRED]; mV[VGREEN] = vec[VGREEN]; - mV[VBLUE] = vec[VBLUE]; + mV[VBLUE] = vec[VBLUE]; mV[VALPHA] = vec[VALPHA]; return (*this); } @@ -287,131 +267,68 @@ inline const LLColor4U& LLColor4U::setAlpha(U8 a) // LLColor4U Magnitude and Normalization Functions -inline F32 LLColor4U::length(void) const +inline F32 LLColor4U::length() const { - return (F32) sqrt( ((F32)mV[VRED]) * mV[VRED] + ((F32)mV[VGREEN]) * mV[VGREEN] + ((F32)mV[VBLUE]) * mV[VBLUE] ); + return sqrt(((F32)mV[VRED]) * mV[VRED] + ((F32)mV[VGREEN]) * mV[VGREEN] + ((F32)mV[VBLUE]) * mV[VBLUE]); } -inline F32 LLColor4U::lengthSquared(void) const +inline F32 LLColor4U::lengthSquared() const { return ((F32)mV[VRED]) * mV[VRED] + ((F32)mV[VGREEN]) * mV[VGREEN] + ((F32)mV[VBLUE]) * mV[VBLUE]; } // deprecated -inline F32 LLColor4U::magVec(void) const +inline F32 LLColor4U::magVec() const { - return (F32) sqrt( ((F32)mV[VRED]) * mV[VRED] + ((F32)mV[VGREEN]) * mV[VGREEN] + ((F32)mV[VBLUE]) * mV[VBLUE] ); + return sqrt(((F32)mV[VRED]) * mV[VRED] + ((F32)mV[VGREEN]) * mV[VGREEN] + ((F32)mV[VBLUE]) * mV[VBLUE]); } // deprecated -inline F32 LLColor4U::magVecSquared(void) const +inline F32 LLColor4U::magVecSquared() const { return ((F32)mV[VRED]) * mV[VRED] + ((F32)mV[VGREEN]) * mV[VGREEN] + ((F32)mV[VBLUE]) * mV[VBLUE]; } -inline LLColor4U operator+(const LLColor4U &a, const LLColor4U &b) +inline LLColor4U operator+(const LLColor4U& a, const LLColor4U& b) { - return LLColor4U( - a.mV[VRED] + b.mV[VRED], - a.mV[VGREEN] + b.mV[VGREEN], - a.mV[VBLUE] + b.mV[VBLUE], - a.mV[VALPHA] + b.mV[VALPHA]); + return LLColor4U(a.mV[VRED] + b.mV[VRED], a.mV[VGREEN] + b.mV[VGREEN], a.mV[VBLUE] + b.mV[VBLUE], a.mV[VALPHA] + b.mV[VALPHA]); } -inline LLColor4U operator-(const LLColor4U &a, const LLColor4U &b) +inline LLColor4U operator-(const LLColor4U& a, const LLColor4U& b) { - return LLColor4U( - a.mV[VRED] - b.mV[VRED], - a.mV[VGREEN] - b.mV[VGREEN], - a.mV[VBLUE] - b.mV[VBLUE], - a.mV[VALPHA] - b.mV[VALPHA]); + return LLColor4U(a.mV[VRED] - b.mV[VRED], a.mV[VGREEN] - b.mV[VGREEN], a.mV[VBLUE] - b.mV[VBLUE], a.mV[VALPHA] - b.mV[VALPHA]); } -inline LLColor4U operator*(const LLColor4U &a, const LLColor4U &b) +inline LLColor4U operator*(const LLColor4U& a, const LLColor4U& b) { - return LLColor4U( - a.mV[VRED] * b.mV[VRED], - a.mV[VGREEN] * b.mV[VGREEN], - a.mV[VBLUE] * b.mV[VBLUE], - a.mV[VALPHA] * b.mV[VALPHA]); + return LLColor4U(a.mV[VRED] * b.mV[VRED], a.mV[VGREEN] * b.mV[VGREEN], a.mV[VBLUE] * b.mV[VBLUE], a.mV[VALPHA] * b.mV[VALPHA]); } -inline LLColor4U LLColor4U::addClampMax(const LLColor4U &color) +inline LLColor4U LLColor4U::addClampMax(const LLColor4U& color) { return LLColor4U(llmin((S32)mV[VRED] + color.mV[VRED], 255), - llmin((S32)mV[VGREEN] + color.mV[VGREEN], 255), - llmin((S32)mV[VBLUE] + color.mV[VBLUE], 255), - llmin((S32)mV[VALPHA] + color.mV[VALPHA], 255)); + llmin((S32)mV[VGREEN] + color.mV[VGREEN], 255), + llmin((S32)mV[VBLUE] + color.mV[VBLUE], 255), + llmin((S32)mV[VALPHA] + color.mV[VALPHA], 255)); } inline LLColor4U LLColor4U::multAll(const F32 k) { // Round to nearest - return LLColor4U( - (U8)ll_round(mV[VRED] * k), - (U8)ll_round(mV[VGREEN] * k), - (U8)ll_round(mV[VBLUE] * k), - (U8)ll_round(mV[VALPHA] * k)); -} -/* -inline LLColor4U operator*(const LLColor4U &a, U8 k) -{ - // only affects rgb (not a!) - return LLColor4U( - a.mV[VRED] * k, - a.mV[VGREEN] * k, - a.mV[VBLUE] * k, - a.mV[VALPHA]); + return LLColor4U((U8)ll_round(mV[VRED] * k), (U8)ll_round(mV[VGREEN] * k), (U8)ll_round(mV[VBLUE] * k), (U8)ll_round(mV[VALPHA] * k)); } -inline LLColor4U operator*(U8 k, const LLColor4U &a) +inline bool operator==(const LLColor4U& a, const LLColor4U& b) { - // only affects rgb (not a!) - return LLColor4U( - a.mV[VRED] * k, - a.mV[VGREEN] * k, - a.mV[VBLUE] * k, - a.mV[VALPHA]); + return ((a.mV[VRED] == b.mV[VRED]) && (a.mV[VGREEN] == b.mV[VGREEN]) && (a.mV[VBLUE] == b.mV[VBLUE]) && (a.mV[VALPHA] == b.mV[VALPHA])); } -inline LLColor4U operator%(U8 k, const LLColor4U &a) +inline bool operator!=(const LLColor4U& a, const LLColor4U& b) { - // only affects alpha (not rgb!) - return LLColor4U( - a.mV[VRED], - a.mV[VGREEN], - a.mV[VBLUE], - a.mV[VALPHA] * k ); + return ((a.mV[VRED] != b.mV[VRED]) || (a.mV[VGREEN] != b.mV[VGREEN]) || (a.mV[VBLUE] != b.mV[VBLUE]) || (a.mV[VALPHA] != b.mV[VALPHA])); } -inline LLColor4U operator%(const LLColor4U &a, U8 k) -{ - // only affects alpha (not rgb!) - return LLColor4U( - a.mV[VRED], - a.mV[VGREEN], - a.mV[VBLUE], - a.mV[VALPHA] * k ); -} -*/ - -inline bool operator==(const LLColor4U &a, const LLColor4U &b) -{ - return ( (a.mV[VRED] == b.mV[VRED]) - &&(a.mV[VGREEN] == b.mV[VGREEN]) - &&(a.mV[VBLUE] == b.mV[VBLUE]) - &&(a.mV[VALPHA] == b.mV[VALPHA])); -} - -inline bool operator!=(const LLColor4U &a, const LLColor4U &b) -{ - return ( (a.mV[VRED] != b.mV[VRED]) - ||(a.mV[VGREEN] != b.mV[VGREEN]) - ||(a.mV[VBLUE] != b.mV[VBLUE]) - ||(a.mV[VALPHA] != b.mV[VALPHA])); -} - -inline const LLColor4U& operator+=(LLColor4U &a, const LLColor4U &b) +inline const LLColor4U& operator+=(LLColor4U& a, const LLColor4U& b) { a.mV[VRED] += b.mV[VRED]; a.mV[VGREEN] += b.mV[VGREEN]; @@ -420,7 +337,7 @@ inline const LLColor4U& operator+=(LLColor4U &a, const LLColor4U &b) return a; } -inline const LLColor4U& operator-=(LLColor4U &a, const LLColor4U &b) +inline const LLColor4U& operator-=(LLColor4U& a, const LLColor4U& b) { a.mV[VRED] -= b.mV[VRED]; a.mV[VGREEN] -= b.mV[VGREEN]; @@ -429,7 +346,7 @@ inline const LLColor4U& operator-=(LLColor4U &a, const LLColor4U &b) return a; } -inline const LLColor4U& operator*=(LLColor4U &a, U8 k) +inline const LLColor4U& operator*=(LLColor4U& a, U8 k) { // only affects rgb (not a!) a.mV[VRED] *= k; @@ -438,20 +355,20 @@ inline const LLColor4U& operator*=(LLColor4U &a, U8 k) return a; } -inline const LLColor4U& operator%=(LLColor4U &a, U8 k) +inline const LLColor4U& operator%=(LLColor4U& a, U8 k) { // only affects alpha (not rgb!) a.mV[VALPHA] *= k; return a; } -inline F32 distVec(const LLColor4U &a, const LLColor4U &b) +inline F32 distVec(const LLColor4U& a, const LLColor4U& b) { LLColor4U vec = a - b; return (vec.length()); } -inline F32 distVec_squared(const LLColor4U &a, const LLColor4U &b) +inline F32 distVec_squared(const LLColor4U& a, const LLColor4U& b) { LLColor4U vec = a - b; return (vec.lengthSquared()); @@ -460,13 +377,13 @@ inline F32 distVec_squared(const LLColor4U &a, const LLColor4U &b) void LLColor4U::setVecScaleClamp(const LLColor4& color) { F32 color_scale_factor = 255.f; - F32 max_color = llmax(color.mV[0], color.mV[1], color.mV[2]); + F32 max_color = llmax(color.mV[VRED], color.mV[VGREEN], color.mV[VBLUE]); if (max_color > 1.f) { color_scale_factor /= max_color; } - const S32 MAX_COLOR = 255; - S32 r = ll_round(color.mV[0] * color_scale_factor); + constexpr S32 MAX_COLOR = 255; + S32 r = ll_round(color.mV[VRED] * color_scale_factor); if (r > MAX_COLOR) { r = MAX_COLOR; @@ -475,9 +392,9 @@ void LLColor4U::setVecScaleClamp(const LLColor4& color) { r = 0; } - mV[0] = r; + mV[VRED] = r; - S32 g = ll_round(color.mV[1] * color_scale_factor); + S32 g = ll_round(color.mV[VGREEN] * color_scale_factor); if (g > MAX_COLOR) { g = MAX_COLOR; @@ -486,9 +403,9 @@ void LLColor4U::setVecScaleClamp(const LLColor4& color) { g = 0; } - mV[1] = g; + mV[VGREEN] = g; - S32 b = ll_round(color.mV[2] * color_scale_factor); + S32 b = ll_round(color.mV[VBLUE] * color_scale_factor); if (b > MAX_COLOR) { b = MAX_COLOR; @@ -497,10 +414,10 @@ void LLColor4U::setVecScaleClamp(const LLColor4& color) { b = 0; } - mV[2] = b; + mV[VBLUE] = b; // Alpha shouldn't be scaled, just clamped... - S32 a = ll_round(color.mV[3] * MAX_COLOR); + S32 a = ll_round(color.mV[VALPHA] * MAX_COLOR); if (a > MAX_COLOR) { a = MAX_COLOR; @@ -509,44 +426,42 @@ void LLColor4U::setVecScaleClamp(const LLColor4& color) { a = 0; } - mV[3] = a; + mV[VALPHA] = a; } void LLColor4U::setVecScaleClamp(const LLColor3& color) { F32 color_scale_factor = 255.f; - F32 max_color = llmax(color.mV[0], color.mV[1], color.mV[2]); + F32 max_color = llmax(color.mV[VRED], color.mV[VGREEN], color.mV[VBLUE]); if (max_color > 1.f) { color_scale_factor /= max_color; } const S32 MAX_COLOR = 255; - S32 r = ll_round(color.mV[0] * color_scale_factor); + S32 r = ll_round(color.mV[VRED] * color_scale_factor); if (r > MAX_COLOR) { r = MAX_COLOR; } - else - if (r < 0) + else if (r < 0) { r = 0; } - mV[0] = r; + mV[VRED] = r; - S32 g = ll_round(color.mV[1] * color_scale_factor); + S32 g = ll_round(color.mV[VGREEN] * color_scale_factor); if (g > MAX_COLOR) { g = MAX_COLOR; } - else - if (g < 0) + else if (g < 0) { g = 0; } - mV[1] = g; + mV[VGREEN] = g; - S32 b = ll_round(color.mV[2] * color_scale_factor); + S32 b = ll_round(color.mV[VBLUE] * color_scale_factor); if (b > MAX_COLOR) { b = MAX_COLOR; @@ -555,31 +470,29 @@ void LLColor4U::setVecScaleClamp(const LLColor3& color) { b = 0; } - mV[2] = b; + mV[VBLUE] = b; - mV[3] = 255; + mV[VALPHA] = 255; } inline U32 LLColor4U::asRGBA() const { // Little endian: values are swapped in memory. The original code access the array like a U32, so we need to swap here - return (mV[3] << 24) | (mV[2] << 16) | (mV[1] << 8) | mV[0]; + return (mV[VALPHA] << 24) | (mV[VBLUE] << 16) | (mV[VGREEN] << 8) | mV[VRED]; } -inline void LLColor4U::fromRGBA( U32 aVal ) +inline void LLColor4U::fromRGBA(U32 aVal) { // Little endian: values are swapped in memory. The original code access the array like a U32, so we need to swap here - mV[ 0 ] = aVal & 0xFF; + mV[0] = aVal & 0xFF; aVal >>= 8; - mV[ 1 ] = aVal & 0xFF; + mV[1] = aVal & 0xFF; aVal >>= 8; - mV[ 2 ] = aVal & 0xFF; + mV[2] = aVal & 0xFF; aVal >>= 8; - mV[ 3 ] = aVal & 0xFF; + mV[3] = aVal & 0xFF; } - #endif - diff --git a/indra/llmath/xform.h b/indra/llmath/xform.h index 7434301670..fa45fffeae 100644 --- a/indra/llmath/xform.h +++ b/indra/llmath/xform.h @@ -115,7 +115,7 @@ public: void clearChanged(U32 bits) { mChanged &= ~bits; } void setScaleChildOffset(bool scale) { mScaleChildOffset = scale; } - bool getScaleChildOffset() { return mScaleChildOffset; } + bool getScaleChildOffset() const { return mScaleChildOffset; } LLXform* getParent() const { return mParent; } LLXform* getRoot() const; -- cgit v1.2.3 From bd252f8151f777508efb15cfe2c69c9a103d98be Mon Sep 17 00:00:00 2001 From: Ansariel Hiller Date: Sat, 5 Oct 2024 07:07:01 +0200 Subject: Follow up for 39eb250 (#2815) --- indra/llmath/CMakeLists.txt | 2 -- indra/llmath/llcoordframe.cpp | 1 - indra/llmath/llquaternion.cpp | 1 - indra/llmath/llvolume.h | 1 - indra/llmath/m3math.cpp | 1 - indra/llmath/m4math.cpp | 1 - indra/llmath/raytrace.cpp | 1 - indra/llmath/v3dmath.cpp | 1 - indra/llmath/v3math.cpp | 1 - indra/llmath/v4math.cpp | 1 - indra/llmessage/patch_code.cpp | 1 - indra/llmessage/patch_dct.cpp | 1 - indra/llmessage/patch_idct.cpp | 1 - indra/newview/llsky.h | 1 - indra/newview/llsprite.h | 2 -- indra/newview/llsurface.h | 1 - indra/newview/llviewerregion.cpp | 1 - 17 files changed, 19 deletions(-) diff --git a/indra/llmath/CMakeLists.txt b/indra/llmath/CMakeLists.txt index eb29df245a..801d3fe63e 100644 --- a/indra/llmath/CMakeLists.txt +++ b/indra/llmath/CMakeLists.txt @@ -45,8 +45,6 @@ set(llmath_SOURCE_FILES set(llmath_HEADER_FILES CMakeLists.txt - camera.h - coordframe.h llbbox.h llbboxlocal.h llcalc.h diff --git a/indra/llmath/llcoordframe.cpp b/indra/llmath/llcoordframe.cpp index 4d6276b2cd..15c9f6ff3f 100644 --- a/indra/llmath/llcoordframe.cpp +++ b/indra/llmath/llcoordframe.cpp @@ -26,7 +26,6 @@ #include "linden_common.h" -//#include "vmath.h" #include "v3math.h" #include "m3math.h" #include "v4math.h" diff --git a/indra/llmath/llquaternion.cpp b/indra/llmath/llquaternion.cpp index aefb82b2f0..1ab3a73d79 100644 --- a/indra/llmath/llquaternion.cpp +++ b/indra/llmath/llquaternion.cpp @@ -30,7 +30,6 @@ #include "llquaternion.h" -//#include "vmath.h" #include "v3math.h" #include "v3dmath.h" #include "v4math.h" diff --git a/indra/llmath/llvolume.h b/indra/llmath/llvolume.h index 27c5fc5a49..3496928f7b 100644 --- a/indra/llmath/llvolume.h +++ b/indra/llmath/llvolume.h @@ -45,7 +45,6 @@ class LLVolumeOctree; #include "lluuid.h" #include "v4color.h" -//#include "vmath.h" #include "v2math.h" #include "v3math.h" #include "v3dmath.h" diff --git a/indra/llmath/m3math.cpp b/indra/llmath/m3math.cpp index 472d340af5..3c2097f947 100644 --- a/indra/llmath/m3math.cpp +++ b/indra/llmath/m3math.cpp @@ -26,7 +26,6 @@ #include "linden_common.h" -//#include "vmath.h" #include "v3math.h" #include "v3dmath.h" #include "v4math.h" diff --git a/indra/llmath/m4math.cpp b/indra/llmath/m4math.cpp index c46ee587cb..a9853fe7e9 100644 --- a/indra/llmath/m4math.cpp +++ b/indra/llmath/m4math.cpp @@ -26,7 +26,6 @@ #include "linden_common.h" -//#include "vmath.h" #include "v3math.h" #include "v4math.h" #include "m4math.h" diff --git a/indra/llmath/raytrace.cpp b/indra/llmath/raytrace.cpp index 893bf1fc70..c0b5f48f2d 100644 --- a/indra/llmath/raytrace.cpp +++ b/indra/llmath/raytrace.cpp @@ -27,7 +27,6 @@ #include "linden_common.h" #include "math.h" -//#include "vmath.h" #include "v3math.h" #include "llquaternion.h" #include "m3math.h" diff --git a/indra/llmath/v3dmath.cpp b/indra/llmath/v3dmath.cpp index bd6d838186..a2d1b03622 100644 --- a/indra/llmath/v3dmath.cpp +++ b/indra/llmath/v3dmath.cpp @@ -30,7 +30,6 @@ #include "v3dmath.h" -//#include "vmath.h" #include "v4math.h" #include "m4math.h" #include "m3math.h" diff --git a/indra/llmath/v3math.cpp b/indra/llmath/v3math.cpp index 941a122528..bd96417190 100644 --- a/indra/llmath/v3math.cpp +++ b/indra/llmath/v3math.cpp @@ -28,7 +28,6 @@ #include "v3math.h" -//#include "vmath.h" #include "v2math.h" #include "v4math.h" #include "m4math.h" diff --git a/indra/llmath/v4math.cpp b/indra/llmath/v4math.cpp index e87bfc0013..cd475380d6 100644 --- a/indra/llmath/v4math.cpp +++ b/indra/llmath/v4math.cpp @@ -26,7 +26,6 @@ #include "linden_common.h" -//#include "vmath.h" #include "v3math.h" #include "v4math.h" #include "m4math.h" diff --git a/indra/llmessage/patch_code.cpp b/indra/llmessage/patch_code.cpp index 489b6ce6a6..9f9f4c852a 100644 --- a/indra/llmessage/patch_code.cpp +++ b/indra/llmessage/patch_code.cpp @@ -27,7 +27,6 @@ #include "linden_common.h" #include "llmath.h" -//#include "vmath.h" #include "v3math.h" #include "patch_dct.h" #include "patch_code.h" diff --git a/indra/llmessage/patch_dct.cpp b/indra/llmessage/patch_dct.cpp index 728fe84537..e74e5fd459 100644 --- a/indra/llmessage/patch_dct.cpp +++ b/indra/llmessage/patch_dct.cpp @@ -27,7 +27,6 @@ #include "linden_common.h" #include "llmath.h" -//#include "vmath.h" #include "v3math.h" #include "patch_dct.h" diff --git a/indra/llmessage/patch_idct.cpp b/indra/llmessage/patch_idct.cpp index 5483cf98c0..4bcc439917 100644 --- a/indra/llmessage/patch_idct.cpp +++ b/indra/llmessage/patch_idct.cpp @@ -27,7 +27,6 @@ #include "linden_common.h" #include "llmath.h" -//#include "vmath.h" #include "v3math.h" #include "patch_dct.h" diff --git a/indra/newview/llsky.h b/indra/newview/llsky.h index 32599dcee2..d06f181357 100644 --- a/indra/newview/llsky.h +++ b/indra/newview/llsky.h @@ -28,7 +28,6 @@ #define LL_LLSKY_H #include "llmath.h" -//#include "vmath.h" #include "v3math.h" #include "v4math.h" #include "v4color.h" diff --git a/indra/newview/llsprite.h b/indra/newview/llsprite.h index 44439bd30c..d6e8e37ec9 100644 --- a/indra/newview/llsprite.h +++ b/indra/newview/llsprite.h @@ -27,8 +27,6 @@ #ifndef LL_LLSPRITE_H #define LL_LLSPRITE_H -////#include "vmath.h" -//#include "llmath.h" #include "v3math.h" #include "v4math.h" #include "v4color.h" diff --git a/indra/newview/llsurface.h b/indra/newview/llsurface.h index 10a104730b..fc72ab7db7 100644 --- a/indra/newview/llsurface.h +++ b/indra/newview/llsurface.h @@ -27,7 +27,6 @@ #ifndef LL_LLSURFACE_H #define LL_LLSURFACE_H -//#include "vmath.h" #include "v3math.h" #include "v3dmath.h" #include "v4math.h" diff --git a/indra/newview/llviewerregion.cpp b/indra/newview/llviewerregion.cpp index 03d5563c65..88e5d900cb 100755 --- a/indra/newview/llviewerregion.cpp +++ b/indra/newview/llviewerregion.cpp @@ -38,7 +38,6 @@ #include "llregionhandle.h" #include "llsurface.h" #include "message.h" -//#include "vmath.h" #include "v3math.h" #include "v4math.h" -- cgit v1.2.3 From f109f25a1ffbad8baa3177a8c7d861f1dabed387 Mon Sep 17 00:00:00 2001 From: Ansariel Date: Sat, 5 Oct 2024 15:25:48 +0200 Subject: Fix issues with game control preferences: * Add missing names for controls to make content localizable * Fix labels for controls that are obviously wrong/make no sense --- .../xui/en/game_control_table_camera_rows.xml | 12 ++++++------ .../default/xui/en/game_control_table_rows.xml | 8 ++++---- .../xui/en/panel_preferences_game_control.xml | 21 ++++++++++++++------- 3 files changed, 24 insertions(+), 17 deletions(-) diff --git a/indra/newview/skins/default/xui/en/game_control_table_camera_rows.xml b/indra/newview/skins/default/xui/en/game_control_table_camera_rows.xml index 6074b72dc8..ef3d7ee4f7 100644 --- a/indra/newview/skins/default/xui/en/game_control_table_camera_rows.xml +++ b/indra/newview/skins/default/xui/en/game_control_table_camera_rows.xml @@ -22,7 +22,7 @@ halign="left" name="action" tool_tip="Camera move forward/backward" - value="Advance Forward" /> + value="Forward/Backward" /> + value="Pan Left/Right" /> + value="Rise Up/Down" /> + value="Pitch Up/Down" /> + value="Yaw CCW/CW" /> + value="Zoom In/Out" /> x + // + // triangle 1: 0,1,2 + // triangle 2: 1,3,2 + (*(vertices++)).set(0.0f, 0.0f, 0.0f); + (*(vertices++)).set(1.0f, 0.0f, 0.0f); + (*(vertices++)).set(0.0f, 1.0f, 0.0f); + (*(vertices++)).set(1.0f, 1.0f, 0.0f); + *(indices++) = 0; + *(indices++) = 1; + *(indices++) = 2; + *(indices++) = 1; + *(indices++) = 3; + *(indices++) = 2; + buf->unmapBuffer(); + } + return *buf; +} + +}; + +// static +LLTerrainPaintQueue LLTerrainPaintMap::convertPaintQueueRGBAToRGB(LLViewerTexture& tex, LLTerrainPaintQueue& queue_in) +{ +#ifdef SHOW_ASSERT + check_tex(tex); +#endif + llassert(queue_in.getComponents() == LLTerrainPaint::RGBA); + + // TODO: Avoid allocating a scratch render buffer and use mAuxillaryRT instead + // TODO: even if it means performing extra render operations to apply the paints, in rare cases where the paints can't all fit within an area that can be represented by the buffer + LLRenderTarget scratch_target; + const S32 max_dim = llmax(tex.getWidth(), tex.getHeight()); + scratch_target.allocate(max_dim, max_dim, GL_RGB, false, LLTexUnit::eTextureType::TT_TEXTURE, + LLTexUnit::eTextureMipGeneration::TMG_NONE); + if (!scratch_target.isComplete()) + { + llassert(false); + LL_WARNS() << "Failed to allocate render target" << LL_ENDL; + return false; + } + gGL.getTexUnit(0)->disable(); + stop_glerror(); + + scratch_target.bindTarget(); + glClearColor(0, 0, 0, 0); + scratch_target.clear(); + const F32 target_half_width = (F32)scratch_target.getWidth() / 2.0f; + const F32 target_half_height = (F32)scratch_target.getHeight() / 2.0f; + + LLVertexBuffer* buf = &get_paint_triangle_buffer(); + + // Update projection matrix and viewport + // *NOTE: gl_state_for_2d also sets the modelview matrix. This will be overridden later. + { + stop_glerror(); + gGL.matrixMode(LLRender::MM_PROJECTION); + gGL.pushMatrix(); + gGL.loadIdentity(); + gGL.ortho(-target_half_width, target_half_width, -target_half_height, target_half_height, 0.25f, 1.0f); + stop_glerror(); + const LLRect texture_rect(0, scratch_target.getHeight(), scratch_target.getWidth(), 0); + glViewport(texture_rect.mLeft, texture_rect.mBottom, texture_rect.getWidth(), texture_rect.getHeight()); + } + + // View matrix + // Coordinates should be in pixels. 1.0f = 1 pixel on the framebuffer. + // Camera is centered in the middle of the framebuffer. + glh::matrix4f view((GLfloat *) OGL_TO_CFR_ROTATION); + { + LLViewerCamera camera; + const LLVector3 camera_origin(target_half_width, target_half_height, 0.5f); + const LLVector3 camera_look_down(target_half_width, target_half_height, 0.0f); + camera.lookAt(camera_origin, camera_look_down, LLVector3::y_axis); + camera.setAspect(F32(scratch_target.getHeight()) / F32(scratch_target.getWidth())); + GLfloat ogl_matrix[16]; + camera.getOpenGLTransform(ogl_matrix); + view *= glh::matrix4f(ogl_matrix); + } + + LLGLDisable stencil(GL_STENCIL_TEST); + LLGLDisable scissor(GL_SCISSOR_TEST); + LLGLEnable cull_face(GL_CULL_FACE); + LLGLDepthTest depth_test(GL_FALSE, GL_FALSE, GL_ALWAYS); + LLGLEnable blend(GL_BLEND); + gGL.setSceneBlendType(LLRender::BT_ALPHA); + + LLGLSLShader& shader = gTerrainStampProgram; + shader.bind(); + + // First, apply the paint map as the background + { + glh::matrix4f model; + { + model.set_scale(glh::vec3f((F32)tex.getWidth(), (F32)tex.getHeight(), 1.0f)); + model.set_translate(glh::vec3f(0.0f, 0.0f, 0.0f)); + } + glh::matrix4f modelview = view * model; + gGL.matrixMode(LLRender::MM_MODELVIEW); + gGL.loadMatrix(modelview.m); + + shader.bindTexture(LLShaderMgr::DIFFUSE_MAP, &tex); + // We care about the whole paintmap, which is already a power of two. + // Hence, TERRAIN_STAMP_SCALE = (1.0,1.0) + shader.uniform2f(LLShaderMgr::TERRAIN_STAMP_SCALE, 1.0f, 1.0f); + buf->setBuffer(); + buf->draw(LLRender::TRIANGLES, buf->getIndicesSize(), 0); + } + + LLTerrainPaintQueue queue_out(LLTerrainPaint::RGB); + + // Incrementally apply each RGBA paint to the render target, then extract + // the result back into memory as an RGB paint. + // Put each result in queue_out. + const std::vector& queue_in_list = queue_in.get(); + for (size_t i = 0; i < queue_in_list.size(); ++i) + { + // It is currently the responsibility of the paint queue to convert + // incoming bits to the right bit depth for paint operations (this + // could change in the future). + queue_in.convertBitDepths(i, 8); + const LLTerrainPaint::ptr_t& paint_in = queue_in_list[i]; + + // Modelview matrix for the current paint + // View matrix is already computed. Just need the model matrix. + // Orthographic projection matrix is already updated + glh::matrix4f model; + { + model.set_scale(glh::vec3f(paint_in->mWidthX, paint_in->mWidthY, 1.0f)); + model.set_translate(glh::vec3f(paint_in->mStartX, paint_in->mStartY, 0.0f)); + } + glh::matrix4f modelview = view * model; + gGL.matrixMode(LLRender::MM_MODELVIEW); + gGL.loadMatrix(modelview.m); + + // Generate temporary stamp texture from paint contents. + // Our stamp image needs to be a power of two. + // Because the paint data may not cover a whole power-of-two region, + // allocate a bigger 2x2 image if needed, but set the image data later + // for a subset of the image. + // Pixel data outside this subset is left undefined. We will use + // TERRAIN_STAMP_SCALE in the stamp shader to define the subset of the + // image we care about. + const U32 width_rounded = 1 << U32(ceil(log2(F32(paint_in->mWidthX)))); + const U32 height_rounded = 1 << U32(ceil(log2(F32(paint_in->mWidthY)))); + LLPointer stamp_image; + { + // Create image object (dimensions not yet initialized in GL) + U32 stamp_tex_name; + LLImageGL::generateTextures(1, &stamp_tex_name); + const U32 components = paint_in->mComponents; + constexpr LLGLenum target = GL_TEXTURE_2D; + const LLGLenum internal_format = paint_in->mComponents == 4 ? GL_RGBA8 : GL_RGB8; + const LLGLenum format = paint_in->mComponents == 4 ? GL_RGBA : GL_RGB; + constexpr LLGLenum type = GL_UNSIGNED_BYTE; + stamp_image = new LLImageGL(stamp_tex_name, components, target, internal_format, format, type, LLTexUnit::TAM_WRAP); + // Nearest-neighbor filtering to reduce surprises + stamp_image->setFilteringOption(LLTexUnit::TFO_POINT); + + // Initialize the image dimensions in GL + constexpr U8* undefined_data_for_now = nullptr; + gGL.getTexUnit(0)->bind(stamp_image, false, true); + glTexImage2D(GL_TEXTURE_2D, 0, internal_format, width_rounded, height_rounded, 0, format, type, undefined_data_for_now); + gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE); + stamp_image->setSize(width_rounded, height_rounded, components); + stamp_image->setDiscardLevel(0); + + // Manually set a subset of the image in GL + const U8* data = paint_in->mData.data(); + const S32 data_width = paint_in->mWidthX; + const S32 data_height = paint_in->mWidthY; + constexpr S32 origin = 0; + // width = data_width; height = data_height. i.e.: Copy the full + // contents of data into the image. + stamp_image->setSubImage(data, data_width, data_height, origin, origin, /*width=*/data_width, /*height=*/data_height); + } + + // Apply ("stamp") the paint to the render target + { + shader.bindTextureImageGL(LLShaderMgr::DIFFUSE_MAP, stamp_image); + const F32 width_fraction = F32(paint_in->mWidthX) / F32(width_rounded); + const F32 height_fraction = F32(paint_in->mWidthY) / F32(height_rounded); + shader.uniform2f(LLShaderMgr::TERRAIN_STAMP_SCALE, width_fraction, height_fraction); + buf->setBuffer(); + buf->draw(LLRender::TRIANGLES, buf->getIndicesSize(), 0); + } + + // Extract the result back into memory as an RGB paint + LLTerrainPaint::ptr_t paint_out = std::make_shared(); + { + paint_out->mStartX = paint_in->mStartX; + paint_out->mStartY = paint_in->mStartY; + paint_out->mWidthX = paint_in->mWidthX; + paint_out->mWidthY = paint_in->mWidthY; + paint_out->mBitDepth = 8; // Will be reduced to 5 bits later + paint_out->mComponents = LLTerrainPaint::RGB; + paint_out->mData.resize(paint_out->mComponents * paint_out->mWidthX * paint_out->mWidthY); + constexpr GLint miplevel = 0; + const S32 x_offset = paint_out->mStartX; + const S32 y_offset = paint_out->mStartY; + const S32 width = llmin(paint_out->mWidthX, tex.getWidth() - x_offset); + const S32 height = llmin(paint_out->mWidthY, tex.getHeight() - y_offset); + constexpr GLenum pixformat = GL_RGB; + constexpr GLenum pixtype = GL_UNSIGNED_BYTE; + llassert(paint_out->mData.size() <= std::numeric_limits::max()); + const GLsizei buf_size = (GLsizei)paint_out->mData.size(); + U8* pixels = paint_out->mData.data(); + glReadPixels(x_offset, y_offset, width, height, pixformat, pixtype, pixels); + } + + // Enqueue the result to the new paint queue, with bit depths per color + // channel reduced from 8 to 5, and reduced from RGBA (paintmap + // sub-rectangle update with alpha mask) to RGB (paintmap sub-rectangle + // update without alpha mask). This format is suitable for sending + // over the network. + // *TODO: At some point, queue_out will pass through a network + // round-trip which will reduce the bit depth, making the + // pre-conversion step not necessary. + queue_out.enqueue(paint_out); + queue_out.convertBitDepths(queue_out.size()-1, 5); + } + + queue_in.clear(); + + scratch_target.flush(); + + LLGLSLShader::unbind(); + + gGL.matrixMode(LLRender::MM_PROJECTION); + gGL.popMatrix(); + + return queue_out; +} + +LLTerrainPaintQueue::LLTerrainPaintQueue(U8 components) +: mComponents(components) +{ + llassert(mComponents == LLTerrainPaint::RGB || mComponents == LLTerrainPaint::RGBA); +} + +LLTerrainPaintQueue::LLTerrainPaintQueue(const LLTerrainPaintQueue& other) +{ + *this = other; + llassert(mComponents == LLTerrainPaint::RGB || mComponents == LLTerrainPaint::RGBA); +} + +LLTerrainPaintQueue& LLTerrainPaintQueue::operator=(const LLTerrainPaintQueue& other) +{ + mComponents = other.mComponents; + mList = other.mList; + return *this; +} + +bool LLTerrainPaintQueue::enqueue(LLTerrainPaint::ptr_t& paint, bool dry_run) { llassert(paint); if (!paint) { return false; } @@ -353,7 +642,7 @@ bool LLTerrainPaintQueue::enqueue(LLTerrainPaint::ptr_t& paint) // The internal paint map image is currently 8 bits, so that's the maximum // allowed bit depth. llassert(paint->mBitDepth > 0 && paint->mBitDepth <= 8); - llassert(paint->mData.size() == (LLTerrainPaint::COMPONENTS * paint->mWidthX * paint->mWidthY)); + llassert(paint->mData.size() == (mComponents * paint->mWidthX * paint->mWidthY)); llassert(paint->mWidthX > 0); llassert(paint->mWidthY > 0); #ifdef SHOW_ASSERT @@ -364,10 +653,29 @@ bool LLTerrainPaintQueue::enqueue(LLTerrainPaint::ptr_t& paint) llassert(paint->mStartX < max_texture_width); llassert(paint->mStartY < max_texture_width); - mList.push_back(paint); + if (!dry_run) { mList.push_back(paint); } return true; } +bool LLTerrainPaintQueue::enqueue(LLTerrainPaintQueue& paint_queue) +{ + constexpr bool dry_run = true; + for (LLTerrainPaint::ptr_t& paint : paint_queue.mList) + { + if (!enqueue(paint), dry_run) { return false; } + } + for (LLTerrainPaint::ptr_t& paint : paint_queue.mList) + { + enqueue(paint); + } + return true; +} + +size_t LLTerrainPaintQueue::size() const +{ + return mList.size(); +} + bool LLTerrainPaintQueue::empty() const { return mList.empty(); diff --git a/indra/newview/llterrainpaintmap.h b/indra/newview/llterrainpaintmap.h index 9189f12cbd..b4d706b107 100644 --- a/indra/newview/llterrainpaintmap.h +++ b/indra/newview/llterrainpaintmap.h @@ -26,6 +26,8 @@ #pragma once +#include "llviewerprecompiledheaders.h" + class LLViewerRegion; class LLViewerTexture; class LLTerrainPaintQueue; @@ -41,14 +43,15 @@ public: // Returns true if successful static bool bakeHeightNoiseIntoPBRPaintMapRGB(const LLViewerRegion& region, LLViewerTexture& tex); - static void applyPaintQueue(LLViewerTexture& tex, LLTerrainPaintQueue& queue); + static void applyPaintQueueRGB(LLViewerTexture& tex, LLTerrainPaintQueue& queue); + static LLTerrainPaintQueue convertPaintQueueRGBAToRGB(LLViewerTexture& tex, LLTerrainPaintQueue& queue_in); }; // Enqueued paint operations, in texture coordinates. -// data is always RGB, with each U8 storing one color in the provided bit depth. -class LLTerrainPaint +// mData is always RGB or RGBA (determined by mComponents), with each U8 +// storing one color with a max value of (1 >> mBitDepth) - 1 +struct LLTerrainPaint { -public: using ptr_t = std::shared_ptr; U16 mStartX; @@ -56,25 +59,38 @@ public: U16 mWidthX; U16 mWidthY; U8 mBitDepth; - static const U8 COMPONENTS = 3; + U8 mComponents; + const static U8 RGB = 3; + const static U8 RGBA = 4; std::vector mData; }; class LLTerrainPaintQueue { public: - bool enqueue(LLTerrainPaint::ptr_t& paint); + // components determines what type of LLTerrainPaint is allowed. Must be 3 (RGB) or 4 (RGBA) + LLTerrainPaintQueue(U8 components); + LLTerrainPaintQueue(const LLTerrainPaintQueue& other); + LLTerrainPaintQueue& operator=(const LLTerrainPaintQueue& other); + + bool enqueue(LLTerrainPaint::ptr_t& paint, bool dry_run = false); + bool enqueue(LLTerrainPaintQueue& paint_queue); + size_t size() const; bool empty() const; void clear(); const std::vector& get() const { return mList; } + U8 getComponents() const { return mComponents; } // Convert mBitDepth for the LLTerrainPaint in the queue at index + // If mBitDepth is already equal to target_bit_depth, no conversion takes + // place. // It is currently the responsibility of the paint queue to convert // incoming bits to the right bit depth for the paintmap (this could // change in the future). void convertBitDepths(size_t index, U8 target_bit_depth); private: + U8 mComponents; std::vector mList; }; diff --git a/indra/newview/llviewermenu.cpp b/indra/newview/llviewermenu.cpp index fa6e8870b3..c4bb5eaa2e 100644 --- a/indra/newview/llviewermenu.cpp +++ b/indra/newview/llviewermenu.cpp @@ -1435,6 +1435,7 @@ class LLAdvancedTerrainCreateLocalPaintMap : public view_listener_t const U32 max_resolution = gSavedSettings.getU32("RenderMaxTextureResolution"); dim = llclamp(dim, 16, max_resolution); dim = 1 << U32(std::ceil(std::log2(dim))); + // TODO: Could probably get away with not using image raw here, now that we aren't writing bits via the CPU (see load_exr for example) LLPointer image_raw = new LLImageRaw(dim,dim,3); LLPointer tex = LLViewerTextureManager::getLocalTexture(image_raw.get(), true); const bool success = LLTerrainPaintMap::bakeHeightNoiseIntoPBRPaintMapRGB(*region, *tex); @@ -1460,7 +1461,7 @@ class LLAdvancedTerrainEditLocalPaintMap : public view_listener_t return false; } - LLTerrainPaintQueue& paint_queue = gLocalTerrainMaterials.getPaintQueue(); + LLTerrainPaintQueue& paint_request_queue = gLocalTerrainMaterials.getPaintRequestQueue(); // Enqueue a paint // Overrides an entire region patch with the material in the last slot @@ -1477,17 +1478,31 @@ class LLAdvancedTerrainEditLocalPaintMap : public view_listener_t paint->mBitDepth = bit_depth; constexpr U8 max_value = (1 << bit_depth) - 1; const size_t pixel_count = width * width; - paint->mData.resize(LLTerrainPaint::COMPONENTS * pixel_count); - for (size_t pixel = 0; pixel < pixel_count; ++pixel) + const U8 components = LLTerrainPaint::RGBA; + paint->mComponents = components; + paint->mData.resize(components * pixel_count); + for (size_t h = 0; h < paint->mWidthY; ++h) { - paint->mData[(LLTerrainPaint::COMPONENTS*pixel) + LLTerrainPaint::COMPONENTS - 1] = max_value; + for (size_t w = 0; w < paint->mWidthX; ++w) + { + const size_t pixel = (h * paint->mWidthX) + w; + // Solid blue color + paint->mData[(components*pixel) + components - 2] = max_value; // blue + // Alpha gradient from 0.0 to 1.0 along w + const U8 alpha = U8(F32(max_value) * F32(w+1) / F32(paint->mWidthX)); + paint->mData[(components*pixel) + components - 1] = alpha; // alpha + } } - paint_queue.enqueue(paint); + paint_request_queue.enqueue(paint); - // Apply the paint queue ad-hoc right here for now. - // *TODO: Eventually the paint queue should be applied at a predictable - // time in the viewer frame loop. - LLTerrainPaintMap::applyPaintQueue(*tex, paint_queue); + // Apply the paint queues ad-hoc right here for now. + // *TODO: Eventually the paint queue(s) should be applied at a + // predictable time in the viewer frame loop. + // TODO: In hindsight... maybe we *should* bind the paintmap to the render buffer. That makes a lot more sense, and we wouldn't have to reduce its resolution by settling for the bake buffer. If we do that, make a comment above convertPaintQueueRGBAToRGB that the texture is modified! + LLTerrainPaintQueue paint_send_queue = LLTerrainPaintMap::convertPaintQueueRGBAToRGB(*tex, paint_request_queue); + LLTerrainPaintQueue& paint_map_queue = gLocalTerrainMaterials.getPaintMapQueue(); + paint_map_queue.enqueue(paint_send_queue); + LLTerrainPaintMap::applyPaintQueueRGB(*tex, paint_map_queue); return true; } diff --git a/indra/newview/llviewershadermgr.cpp b/indra/newview/llviewershadermgr.cpp index a8fe221d98..8e7af28d41 100644 --- a/indra/newview/llviewershadermgr.cpp +++ b/indra/newview/llviewershadermgr.cpp @@ -100,6 +100,7 @@ LLGLSLShader gReflectionProbeDisplayProgram; LLGLSLShader gCopyProgram; LLGLSLShader gCopyDepthProgram; LLGLSLShader gPBRTerrainBakeProgram; +LLGLSLShader gTerrainStampProgram; //object shaders LLGLSLShader gObjectPreviewProgram; @@ -3170,6 +3171,24 @@ bool LLViewerShaderMgr::loadShadersInterface() } } + if (success) + { + LLGLSLShader* shader = &gTerrainStampProgram; + U32 bit_depth = gSavedSettings.getU32("TerrainPaintBitDepth"); + // LLTerrainPaintMap currently uses an RGB8 texture internally + bit_depth = llclamp(bit_depth, 1, 8); + shader->mName = llformat("Terrain Stamp Shader RGB%o", bit_depth); + + shader->mShaderFiles.clear(); + shader->mShaderFiles.push_back(make_pair("interface/terrainStampV.glsl", GL_VERTEX_SHADER)); + shader->mShaderFiles.push_back(make_pair("interface/terrainStampF.glsl", GL_FRAGMENT_SHADER)); + shader->mShaderLevel = mShaderLevel[SHADER_INTERFACE]; + const U32 value_range = (1 << bit_depth) - 1; + shader->addPermutation("TERRAIN_PAINT_PRECISION", llformat("%d", value_range)); + success = success && shader->createShader(); + llassert(success); + } + if (success) { gAlphaMaskProgram.mName = "Alpha Mask Shader"; diff --git a/indra/newview/llviewershadermgr.h b/indra/newview/llviewershadermgr.h index b08796025a..e654967c46 100644 --- a/indra/newview/llviewershadermgr.h +++ b/indra/newview/llviewershadermgr.h @@ -175,6 +175,7 @@ extern LLGLSLShader gReflectionProbeDisplayProgram; extern LLGLSLShader gCopyProgram; extern LLGLSLShader gCopyDepthProgram; extern LLGLSLShader gPBRTerrainBakeProgram; +extern LLGLSLShader gTerrainStampProgram; //output tex0[tc0] - tex1[tc1] extern LLGLSLShader gTwoTextureCompareProgram; diff --git a/indra/newview/llvlcomposition.cpp b/indra/newview/llvlcomposition.cpp index d87658ba89..ca76d93cd7 100644 --- a/indra/newview/llvlcomposition.cpp +++ b/indra/newview/llvlcomposition.cpp @@ -317,18 +317,14 @@ LLViewerTexture* LLTerrainMaterials::getPaintMap() return mPaintMap.get(); } -LLTerrainPaintQueue& LLTerrainMaterials::getPaintQueue() -{ - return mPaintQueue; -} - void LLTerrainMaterials::setPaintMap(LLViewerTexture* paint_map) { llassert(!paint_map || mPaintType == TERRAIN_PAINT_TYPE_PBR_PAINTMAP); const bool changed = paint_map != mPaintMap; mPaintMap = paint_map; // The paint map has changed, so edits are no longer valid - mPaintQueue.clear(); + mPaintRequestQueue.clear(); + mPaintMapQueue.clear(); } // Boost the texture loading priority diff --git a/indra/newview/llvlcomposition.h b/indra/newview/llvlcomposition.h index 972a46d8db..3f1124a8ac 100644 --- a/indra/newview/llvlcomposition.h +++ b/indra/newview/llvlcomposition.h @@ -90,8 +90,14 @@ public: void setPaintType(U32 paint_type) { mPaintType = paint_type; } LLViewerTexture* getPaintMap(); void setPaintMap(LLViewerTexture* paint_map); - // Paint queue for current paint map - LLTerrainPaintQueue& getPaintQueue(); + // Queue of client-triggered paint operations that need to be converted + // into a form that can be sent to the server. + // Paints in this queue are in RGBA format. + LLTerrainPaintQueue& getPaintRequestQueue() { return mPaintRequestQueue; } + // Paint queue for current paint map - this queue gets applied directly to + // the paint map. Paints within are assumed to have already been sent to + // the server. Paints in this queue are in RGB format. + LLTerrainPaintQueue& getPaintMapQueue() { return mPaintMapQueue; } protected: void unboost(); @@ -110,7 +116,8 @@ protected: U32 mPaintType = TERRAIN_PAINT_TYPE_HEIGHTMAP_WITH_NOISE; LLPointer mPaintMap; - LLTerrainPaintQueue mPaintQueue; + LLTerrainPaintQueue mPaintRequestQueue{U8(4)}; + LLTerrainPaintQueue mPaintMapQueue{U8(3)}; }; // Local materials to override all regions -- cgit v1.2.3 From 642ace3c75d08cc55370374f5a091f0c8ff41a4c Mon Sep 17 00:00:00 2001 From: Cosmic Linden Date: Fri, 16 Aug 2024 17:27:57 -0700 Subject: secondlife/viewer#1883: (WIP) (not yet working) Brush queue --- indra/newview/llterrainpaintmap.cpp | 349 ++++++++++++++++++++++++++++++++---- indra/newview/llterrainpaintmap.h | 90 ++++++++-- indra/newview/llviewermenu.cpp | 40 ++++- indra/newview/llvlcomposition.h | 5 + 4 files changed, 434 insertions(+), 50 deletions(-) diff --git a/indra/newview/llterrainpaintmap.cpp b/indra/newview/llterrainpaintmap.cpp index 7dc09a4748..6a57605325 100644 --- a/indra/newview/llterrainpaintmap.cpp +++ b/indra/newview/llterrainpaintmap.cpp @@ -575,12 +575,16 @@ LLTerrainPaintQueue LLTerrainPaintMap::convertPaintQueueRGBAToRGB(LLViewerTextur paint_out->mWidthY = paint_in->mWidthY; paint_out->mBitDepth = 8; // Will be reduced to 5 bits later paint_out->mComponents = LLTerrainPaint::RGB; +#ifdef SHOW_ASSERT + paint_out->assert_confined_to(tex); +#endif + paint_out->confine_to(tex); paint_out->mData.resize(paint_out->mComponents * paint_out->mWidthX * paint_out->mWidthY); constexpr GLint miplevel = 0; const S32 x_offset = paint_out->mStartX; const S32 y_offset = paint_out->mStartY; - const S32 width = llmin(paint_out->mWidthX, tex.getWidth() - x_offset); - const S32 height = llmin(paint_out->mWidthY, tex.getHeight() - y_offset); + const S32 width = paint_out->mWidthX; + const S32 height = paint_out->mWidthY; constexpr GLenum pixformat = GL_RGB; constexpr GLenum pixtype = GL_UNSIGNED_BYTE; llassert(paint_out->mData.size() <= std::numeric_limits::max()); @@ -613,22 +617,294 @@ LLTerrainPaintQueue LLTerrainPaintMap::convertPaintQueueRGBAToRGB(LLViewerTextur return queue_out; } +// static +LLTerrainPaintQueue LLTerrainPaintMap::convertBrushQueueToPaintRGB(const LLViewerRegion& region, LLViewerTexture& tex, LLTerrainBrushQueue& queue_in) +{ +#ifdef SHOW_ASSERT + check_tex(tex); +#endif + + // TODO: Avoid allocating a scratch render buffer and use mAuxillaryRT instead + // TODO: even if it means performing extra render operations to apply the brushes, in rare cases where the paints can't all fit within an area that can be represented by the buffer + LLRenderTarget scratch_target; + const S32 max_dim = llmax(tex.getWidth(), tex.getHeight()); + scratch_target.allocate(max_dim, max_dim, GL_RGB, false, LLTexUnit::eTextureType::TT_TEXTURE, + LLTexUnit::eTextureMipGeneration::TMG_NONE); + if (!scratch_target.isComplete()) + { + llassert(false); + LL_WARNS() << "Failed to allocate render target" << LL_ENDL; + return false; + } + gGL.getTexUnit(0)->disable(); + stop_glerror(); + + scratch_target.bindTarget(); + glClearColor(0, 0, 0, 0); + scratch_target.clear(); + const F32 target_half_width = (F32)scratch_target.getWidth() / 2.0f; + const F32 target_half_height = (F32)scratch_target.getHeight() / 2.0f; + + LLVertexBuffer* buf = &get_paint_triangle_buffer(); + + // Update projection matrix and viewport + // *NOTE: gl_state_for_2d also sets the modelview matrix. This will be overridden later. + { + stop_glerror(); + gGL.matrixMode(LLRender::MM_PROJECTION); + gGL.pushMatrix(); + gGL.loadIdentity(); + gGL.ortho(-target_half_width, target_half_width, -target_half_height, target_half_height, 0.25f, 1.0f); + stop_glerror(); + const LLRect texture_rect(0, scratch_target.getHeight(), scratch_target.getWidth(), 0); + glViewport(texture_rect.mLeft, texture_rect.mBottom, texture_rect.getWidth(), texture_rect.getHeight()); + } + + // View matrix + // Coordinates should be in pixels. 1.0f = 1 pixel on the framebuffer. + // Camera is centered in the middle of the framebuffer. + glh::matrix4f view((GLfloat *) OGL_TO_CFR_ROTATION); + { + LLViewerCamera camera; + const LLVector3 camera_origin(target_half_width, target_half_height, 0.5f); + const LLVector3 camera_look_down(target_half_width, target_half_height, 0.0f); + camera.lookAt(camera_origin, camera_look_down, LLVector3::y_axis); + camera.setAspect(F32(scratch_target.getHeight()) / F32(scratch_target.getWidth())); + GLfloat ogl_matrix[16]; + camera.getOpenGLTransform(ogl_matrix); + view *= glh::matrix4f(ogl_matrix); + } + + LLGLDisable stencil(GL_STENCIL_TEST); + LLGLDisable scissor(GL_SCISSOR_TEST); + LLGLEnable cull_face(GL_CULL_FACE); + LLGLDepthTest depth_test(GL_FALSE, GL_FALSE, GL_ALWAYS); + LLGLEnable blend(GL_BLEND); + gGL.setSceneBlendType(LLRender::BT_ALPHA); + + LLGLSLShader& shader = gTerrainStampProgram; + shader.bind(); + + // First, apply the paint map as the background + { + glh::matrix4f model; + { + model.set_scale(glh::vec3f((F32)tex.getWidth(), (F32)tex.getHeight(), 1.0f)); + model.set_translate(glh::vec3f(0.0f, 0.0f, 0.0f)); + } + glh::matrix4f modelview = view * model; + gGL.matrixMode(LLRender::MM_MODELVIEW); + gGL.loadMatrix(modelview.m); + + shader.bindTexture(LLShaderMgr::DIFFUSE_MAP, &tex); + // We care about the whole paintmap, which is already a power of two. + // Hence, TERRAIN_STAMP_SCALE = (1.0,1.0) + shader.uniform2f(LLShaderMgr::TERRAIN_STAMP_SCALE, 1.0f, 1.0f); + buf->setBuffer(); + buf->draw(LLRender::TRIANGLES, buf->getIndicesSize(), 0); + } + + LLTerrainPaintQueue queue_out(LLTerrainPaint::RGB); + + // Incrementally apply each brush stroke to the render target, then extract + // the result back into memory as an RGB paint. + // Put each result in queue_out. + const std::vector& brush_list = queue_in.get(); + for (size_t i = 0; i < brush_list.size(); ++i) + { + const LLTerrainBrush::ptr_t& brush_in = brush_list[i]; + + // Modelview matrix for the current brush + // View matrix is already computed. Just need the model matrix. + // Orthographic projection matrix is already updated + // *NOTE: Brush path information is in region space. It will need to be + // converted to paintmap pixel space before it makes sense. + F32 brush_width_x; + F32 brush_width_y; + F32 brush_start_x; + F32 brush_start_y; + { + F32 min_x = brush_in->mPath[0].mV[VX]; + F32 max_x = min_x; + F32 min_y = brush_in->mPath[0].mV[VY]; + F32 max_y = min_y; + for (size_t i = 1; i < brush_in->mPath.size(); ++i) + { + const F32 x = brush_in->mPath[i].mV[VX]; + const F32 y = brush_in->mPath[i].mV[VY]; + min_x = llmin(min_x, x); + max_x = llmax(max_x, x); + min_y = llmin(min_y, y); + max_y = llmax(max_y, y); + } + brush_width_x = brush_in->mBrushSize + (max_x - min_x); + brush_width_y = brush_in->mBrushSize + (max_y - min_y); + brush_start_x = min_x - (brush_in->mBrushSize / 2.0f); + brush_start_y = min_y - (brush_in->mBrushSize / 2.0f); + // Convert brush path information to paintmap pixel space from region + // space. + brush_width_x *= tex.getWidth() / region.getWidth(); + brush_width_y *= tex.getHeight() / region.getWidth(); + brush_start_x *= tex.getWidth() / region.getWidth(); + brush_start_y *= tex.getHeight() / region.getWidth(); + } + glh::matrix4f model; + { + model.set_scale(glh::vec3f(brush_width_x, brush_width_y, 1.0f)); + model.set_translate(glh::vec3f(brush_start_x, brush_start_y, 0.0f)); + } + glh::matrix4f modelview = view * model; + gGL.matrixMode(LLRender::MM_MODELVIEW); + gGL.loadMatrix(modelview.m); + + // Apply the "brush" to the render target + { + // TODO: Use different shader for this - currently this is using the stamp shader. The white image is just a placeholder for now + shader.bindTexture(LLShaderMgr::DIFFUSE_MAP, LLViewerFetchedTexture::sWhiteImagep); + shader.uniform2f(LLShaderMgr::TERRAIN_STAMP_SCALE, 1.0f, 1.0f); + buf->setBuffer(); + buf->draw(LLRender::TRIANGLES, buf->getIndicesSize(), 0); + } + + // Extract the result back into memory as an RGB paint + LLTerrainPaint::ptr_t paint_out = std::make_shared(); + { + paint_out->mStartX = U16(floor(brush_start_x)); + paint_out->mStartY = U16(floor(brush_start_y)); + const F32 dX = brush_start_x - F32(paint_out->mStartX); + const F32 dY = brush_start_y - F32(paint_out->mStartY); + paint_out->mWidthX = U16(ceil(brush_width_x + dX)); + paint_out->mWidthY = U16(ceil(brush_width_y + dY)); + paint_out->mBitDepth = 8; // Will be reduced to 5 bits later + paint_out->mComponents = LLTerrainPaint::RGB; + // The brush strokes are expected to sometimes partially venture + // outside of the paintmap bounds. + paint_out->confine_to(tex); + paint_out->mData.resize(paint_out->mComponents * paint_out->mWidthX * paint_out->mWidthY); + constexpr GLint miplevel = 0; + const S32 x_offset = paint_out->mStartX; + const S32 y_offset = paint_out->mStartY; + const S32 width = paint_out->mWidthX; + const S32 height = paint_out->mWidthY; + constexpr GLenum pixformat = GL_RGB; + constexpr GLenum pixtype = GL_UNSIGNED_BYTE; + llassert(paint_out->mData.size() <= std::numeric_limits::max()); + const GLsizei buf_size = (GLsizei)paint_out->mData.size(); + U8* pixels = paint_out->mData.data(); + glReadPixels(x_offset, y_offset, width, height, pixformat, pixtype, pixels); + } + + // Enqueue the result to the new paint queue, with bit depths per color + // channel reduced from 8 to 5, and reduced from RGBA (paintmap + // sub-rectangle update with alpha mask) to RGB (paintmap sub-rectangle + // update without alpha mask). This format is suitable for sending + // over the network. + // *TODO: At some point, queue_out will pass through a network + // round-trip which will reduce the bit depth, making the + // pre-conversion step not necessary. + queue_out.enqueue(paint_out); + queue_out.convertBitDepths(queue_out.size()-1, 5); + } + + queue_in.clear(); + + scratch_target.flush(); + + LLGLSLShader::unbind(); + + gGL.matrixMode(LLRender::MM_PROJECTION); + gGL.popMatrix(); + + return queue_out; +} + +template +LLTerrainQueue::LLTerrainQueue(LLTerrainQueue& other) +{ + *this = other; +} + +template +LLTerrainQueue& LLTerrainQueue::operator=(LLTerrainQueue& other) +{ + mList = other.mList; + return *this; +} + +template +bool LLTerrainQueue::enqueue(std::shared_ptr& t, bool dry_run) +{ + if (!dry_run) { mList.push_back(t); } + return true; +} + +template +bool LLTerrainQueue::enqueue(std::vector>& list) +{ + constexpr bool dry_run = true; + for (T::ptr_t& t : list) + { + if (!enqueue(t), dry_run) { return false; } + } + for (T::ptr_t& t : list) + { + enqueue(t); + } + return true; +} + +template +size_t LLTerrainQueue::size() const +{ + return mList.size(); +} + +template +bool LLTerrainQueue::empty() const +{ + return mList.empty(); +} + +template +void LLTerrainQueue::clear() +{ + mList.clear(); +} + +void LLTerrainPaint::assert_confined_to(const LLTexture& tex) const +{ + llassert(mStartX >= 0 && mStartX < tex.getWidth()); + llassert(mStartY >= 0 && mStartY < tex.getHeight()); + llassert(mWidthX <= tex.getWidth() - mStartX); + llassert(mWidthY <= tex.getHeight() - mStartY); +} + +void LLTerrainPaint::confine_to(const LLTexture& tex) +{ + mStartX = llmax(mStartX, 0); + mStartY = llmax(mStartY, 0); + mWidthX = llmin(mWidthX, tex.getWidth() - mStartX); + mWidthY = llmin(mWidthY, tex.getHeight() - mStartY); + assert_confined_to(tex); +} + LLTerrainPaintQueue::LLTerrainPaintQueue(U8 components) : mComponents(components) { llassert(mComponents == LLTerrainPaint::RGB || mComponents == LLTerrainPaint::RGBA); } -LLTerrainPaintQueue::LLTerrainPaintQueue(const LLTerrainPaintQueue& other) +LLTerrainPaintQueue::LLTerrainPaintQueue(LLTerrainPaintQueue& other) +: LLTerrainQueue(other) +, mComponents(other.mComponents) { - *this = other; llassert(mComponents == LLTerrainPaint::RGB || mComponents == LLTerrainPaint::RGBA); } -LLTerrainPaintQueue& LLTerrainPaintQueue::operator=(const LLTerrainPaintQueue& other) +LLTerrainPaintQueue& LLTerrainPaintQueue::operator=(LLTerrainPaintQueue& other) { + LLTerrainQueue::operator=(other); mComponents = other.mComponents; - mList = other.mList; return *this; } @@ -653,37 +929,12 @@ bool LLTerrainPaintQueue::enqueue(LLTerrainPaint::ptr_t& paint, bool dry_run) llassert(paint->mStartX < max_texture_width); llassert(paint->mStartY < max_texture_width); - if (!dry_run) { mList.push_back(paint); } - return true; + return LLTerrainQueue::enqueue(paint, dry_run); } -bool LLTerrainPaintQueue::enqueue(LLTerrainPaintQueue& paint_queue) +bool LLTerrainPaintQueue::enqueue(LLTerrainPaintQueue& queue) { - constexpr bool dry_run = true; - for (LLTerrainPaint::ptr_t& paint : paint_queue.mList) - { - if (!enqueue(paint), dry_run) { return false; } - } - for (LLTerrainPaint::ptr_t& paint : paint_queue.mList) - { - enqueue(paint); - } - return true; -} - -size_t LLTerrainPaintQueue::size() const -{ - return mList.size(); -} - -bool LLTerrainPaintQueue::empty() const -{ - return mList.empty(); -} - -void LLTerrainPaintQueue::clear() -{ - mList.clear(); + return LLTerrainQueue::enqueue(queue.mList); } void LLTerrainPaintQueue::convertBitDepths(size_t index, U8 target_bit_depth) @@ -705,3 +956,33 @@ void LLTerrainPaintQueue::convertBitDepths(size_t index, U8 target_bit_depth) paint->mBitDepth = target_bit_depth; } + +LLTerrainBrushQueue::LLTerrainBrushQueue() +: LLTerrainQueue() +{ +} + +LLTerrainBrushQueue::LLTerrainBrushQueue(LLTerrainBrushQueue& other) +: LLTerrainQueue(other) +{ +} + +LLTerrainBrushQueue& LLTerrainBrushQueue::operator=(LLTerrainBrushQueue& other) +{ + LLTerrainQueue::operator=(other); + return *this; +} + +bool LLTerrainBrushQueue::enqueue(LLTerrainBrush::ptr_t& brush, bool dry_run) +{ + llassert(brush->mBrushSize > 0); + llassert(!brush->mPath.empty()); + llassert(brush->mPathOffset < brush->mPath.size()); + llassert(brush->mPathOffset < 2); // Harmless, but doesn't do anything useful, so might be a sign of implementation error + return LLTerrainQueue::enqueue(brush, dry_run); +} + +bool LLTerrainBrushQueue::enqueue(LLTerrainBrushQueue& queue) +{ + return LLTerrainQueue::enqueue(queue.mList); +} diff --git a/indra/newview/llterrainpaintmap.h b/indra/newview/llterrainpaintmap.h index b4d706b107..cffdad80a2 100644 --- a/indra/newview/llterrainpaintmap.h +++ b/indra/newview/llterrainpaintmap.h @@ -28,10 +28,14 @@ #include "llviewerprecompiledheaders.h" +class LLTexture; class LLViewerRegion; class LLViewerTexture; class LLTerrainPaintQueue; +class LLTerrainBrushQueue; +// TODO: Terrain painting across regions. Assuming painting is confined to one +// region for now. class LLTerrainPaintMap { public: @@ -43,8 +47,34 @@ public: // Returns true if successful static bool bakeHeightNoiseIntoPBRPaintMapRGB(const LLViewerRegion& region, LLViewerTexture& tex); + // This operation clears the queue + // TODO: Decide if clearing the queue is needed - seems inconsistent static void applyPaintQueueRGB(LLViewerTexture& tex, LLTerrainPaintQueue& queue); + static LLTerrainPaintQueue convertPaintQueueRGBAToRGB(LLViewerTexture& tex, LLTerrainPaintQueue& queue_in); + // TODO: Implement (it's similar to convertPaintQueueRGBAToRGB but different shader + need to calculate the dimensions + need a different vertex buffer for each brush stroke) + static LLTerrainPaintQueue convertBrushQueueToPaintRGB(const LLViewerRegion& region, LLViewerTexture& tex, LLTerrainBrushQueue& queue_in); +}; + +template +class LLTerrainQueue +{ +public: + LLTerrainQueue() = default; + LLTerrainQueue(LLTerrainQueue& other); + LLTerrainQueue& operator=(LLTerrainQueue& other); + + bool enqueue(std::shared_ptr& t, bool dry_run = false); + size_t size() const; + bool empty() const; + void clear(); + + const std::vector>& get() const { return mList; } + +protected: + bool enqueue(std::vector>& list); + + std::vector> mList; }; // Enqueued paint operations, in texture coordinates. @@ -63,23 +93,27 @@ struct LLTerrainPaint const static U8 RGB = 3; const static U8 RGBA = 4; std::vector mData; + + // Asserts that this paint's start/width fit within the bounds of the + // provided texture dimensions. + void assert_confined_to(const LLTexture& tex) const; + // Confines this paint's start/width so it fits within the bounds of the + // provided texture dimensions. + // Does not allocate mData. + void confine_to(const LLTexture& tex); }; -class LLTerrainPaintQueue +class LLTerrainPaintQueue : public LLTerrainQueue { public: + LLTerrainPaintQueue() = delete; // components determines what type of LLTerrainPaint is allowed. Must be 3 (RGB) or 4 (RGBA) LLTerrainPaintQueue(U8 components); - LLTerrainPaintQueue(const LLTerrainPaintQueue& other); - LLTerrainPaintQueue& operator=(const LLTerrainPaintQueue& other); + LLTerrainPaintQueue(LLTerrainPaintQueue& other); + LLTerrainPaintQueue& operator=(LLTerrainPaintQueue& other); bool enqueue(LLTerrainPaint::ptr_t& paint, bool dry_run = false); - bool enqueue(LLTerrainPaintQueue& paint_queue); - size_t size() const; - bool empty() const; - void clear(); - - const std::vector& get() const { return mList; } + bool enqueue(LLTerrainPaintQueue& queue); U8 getComponents() const { return mComponents; } // Convert mBitDepth for the LLTerrainPaint in the queue at index @@ -92,5 +126,41 @@ public: private: U8 mComponents; - std::vector mList; +}; + +struct LLTerrainBrush +{ + using ptr_t = std::shared_ptr; + + // Width of the brush in region space. The brush is a square texture with + // alpha. + F32 mBrushSize; + // Brush path points in region space, excluding the vertical axis, which + // does not contribute to the paint map. + std::vector mPath; + // Offset of the brush path to actually start drawing at. An offset of 0 + // indicates that a brush stroke has just started (i.e. the user just + // pressed down the mouse button). An offset greater than 0 indicates the + // continuation of a brush stroke. Skipped entries in mPath are not drawn + // directly, but are used for stroke orientation and path interpolation. + // TODO: For the initial implementation, mPathOffset will be 0 and mPath + // will be of length of at most 1, leading to discontinuous paint paths. + // Then, mPathOffset may be 1 or 0, 1 indicating the continuation of a + // stroke with linear interpolation. It is unlikely that we will implement + // anything more sophisticated than that for now. + U8 mPathOffset; + // Indicates if this is the end of the brush stroke. Can occur if the mouse + // button is lifted, or if the mouse temporarily stops while held down. + bool mPathEnd; +}; + +class LLTerrainBrushQueue : public LLTerrainQueue +{ +public: + LLTerrainBrushQueue(); + LLTerrainBrushQueue(LLTerrainBrushQueue& other); + LLTerrainBrushQueue& operator=(LLTerrainBrushQueue& other); + + bool enqueue(LLTerrainBrush::ptr_t& brush, bool dry_run = false); + bool enqueue(LLTerrainBrushQueue& queue); }; diff --git a/indra/newview/llviewermenu.cpp b/indra/newview/llviewermenu.cpp index c4bb5eaa2e..dd6b6d1ca1 100644 --- a/indra/newview/llviewermenu.cpp +++ b/indra/newview/llviewermenu.cpp @@ -1461,17 +1461,38 @@ class LLAdvancedTerrainEditLocalPaintMap : public view_listener_t return false; } + LLTerrainBrushQueue& brush_queue = gLocalTerrainMaterials.getBrushQueue(); LLTerrainPaintQueue& paint_request_queue = gLocalTerrainMaterials.getPaintRequestQueue(); + const LLViewerRegion* region = gAgent.getRegion(); + if (!region) + { + LL_WARNS() << "No current region for calculating paint operations" << LL_ENDL; + return false; + } + // TODO: Create the brush + // Just a dab for now + LLTerrainBrush::ptr_t brush = std::make_shared(); + brush->mBrushSize = 16.0f; + brush->mPath.emplace_back(17.0f, 17.0f); + brush->mPathOffset = 0; + brush->mPathEnd = true; + brush_queue.enqueue(brush); + LLTerrainPaintQueue brush_as_paint_queue = LLTerrainPaintMap::convertBrushQueueToPaintRGB(*region, *tex, brush_queue); + //paint_send_queue.enqueue(brush_as_paint_queue); // TODO: What was this line for? (it might also be a leftover line from an unfinished edit) + + // TODO: Keep this around for later testing (i.e. when reducing framebuffer size and the offsets that requires) +#if 0 // Enqueue a paint - // Overrides an entire region patch with the material in the last slot + // Modifies a subsection of the region paintmap with the material in + // the last slot. // It is currently the responsibility of the paint queue to convert // incoming bits to the right bit depth for the paintmap (this could // change in the future). LLTerrainPaint::ptr_t paint = std::make_shared(); - const U16 width = U16(tex->getWidth() / 16); - paint->mStartX = width - 1; - paint->mStartY = width - 1; + const U16 width = 33; + paint->mStartX = 1; + paint->mStartY = 1; paint->mWidthX = width; paint->mWidthY = width; constexpr U8 bit_depth = 5; @@ -1488,12 +1509,19 @@ class LLAdvancedTerrainEditLocalPaintMap : public view_listener_t const size_t pixel = (h * paint->mWidthX) + w; // Solid blue color paint->mData[(components*pixel) + components - 2] = max_value; // blue - // Alpha gradient from 0.0 to 1.0 along w - const U8 alpha = U8(F32(max_value) * F32(w+1) / F32(paint->mWidthX)); + //// Alpha grid: 1.0 if odd for either dimension, 0.0 otherwise + //const U8 alpha = U8(F32(max_value) * F32(bool(w % 2) || bool(h % 2))); + //paint->mData[(components*pixel) + components - 1] = alpha; // alpha + // Alpha "frame" + const bool edge = w == 0 || h == 0 || w == (paint->mWidthX - 1) || h == (paint->mWidthY - 1); + const bool near_edge_frill = ((w == 1 || w == (paint->mWidthX - 2)) && (h % 2 == 0)) || + ((h == 1 || h == (paint->mWidthY - 2)) && (w % 2 == 0)); + const U8 alpha = U8(F32(max_value) * F32(edge || near_edge_frill)); paint->mData[(components*pixel) + components - 1] = alpha; // alpha } } paint_request_queue.enqueue(paint); +#endif // Apply the paint queues ad-hoc right here for now. // *TODO: Eventually the paint queue(s) should be applied at a diff --git a/indra/newview/llvlcomposition.h b/indra/newview/llvlcomposition.h index 3f1124a8ac..9a5d74adda 100644 --- a/indra/newview/llvlcomposition.h +++ b/indra/newview/llvlcomposition.h @@ -90,6 +90,10 @@ public: void setPaintType(U32 paint_type) { mPaintType = paint_type; } LLViewerTexture* getPaintMap(); void setPaintMap(LLViewerTexture* paint_map); + // Queue of client-triggered brush operations that need to be converted + // into a form that can be sent to the server. + // TODO: Consider getting rid of mPaintRequestQueue, as it's not really needed (brushes go directly to RGB queue) + LLTerrainBrushQueue& getBrushQueue() { return mBrushQueue; } // Queue of client-triggered paint operations that need to be converted // into a form that can be sent to the server. // Paints in this queue are in RGBA format. @@ -116,6 +120,7 @@ protected: U32 mPaintType = TERRAIN_PAINT_TYPE_HEIGHTMAP_WITH_NOISE; LLPointer mPaintMap; + LLTerrainBrushQueue mBrushQueue; LLTerrainPaintQueue mPaintRequestQueue{U8(4)}; LLTerrainPaintQueue mPaintMapQueue{U8(3)}; }; -- cgit v1.2.3 From 81cc4fa7d61e838413b912a4ed2d957cc65bcb46 Mon Sep 17 00:00:00 2001 From: Cosmic Linden Date: Mon, 7 Oct 2024 14:30:29 -0700 Subject: secondlife/viewer#1883: (WIP/Alpha/WOMM) Fix some compiler and runtime errors --- indra/newview/llterrainpaintmap.cpp | 52 ++++++++++++++++++------------------- indra/newview/llviewercontrol.cpp | 1 + indra/newview/llviewermenu.cpp | 8 ++++-- indra/newview/llviewershadermgr.cpp | 38 +++++++++++++++++---------- 4 files changed, 57 insertions(+), 42 deletions(-) diff --git a/indra/newview/llterrainpaintmap.cpp b/indra/newview/llterrainpaintmap.cpp index 6a57605325..d36e4ea657 100644 --- a/indra/newview/llterrainpaintmap.cpp +++ b/indra/newview/llterrainpaintmap.cpp @@ -447,7 +447,7 @@ LLTerrainPaintQueue LLTerrainPaintMap::convertPaintQueueRGBAToRGB(LLViewerTextur // View matrix // Coordinates should be in pixels. 1.0f = 1 pixel on the framebuffer. // Camera is centered in the middle of the framebuffer. - glh::matrix4f view((GLfloat *) OGL_TO_CFR_ROTATION); + glm::mat4 view(glm::make_mat4((GLfloat*) OGL_TO_CFR_ROTATION)); { LLViewerCamera camera; const LLVector3 camera_origin(target_half_width, target_half_height, 0.5f); @@ -456,7 +456,7 @@ LLTerrainPaintQueue LLTerrainPaintMap::convertPaintQueueRGBAToRGB(LLViewerTextur camera.setAspect(F32(scratch_target.getHeight()) / F32(scratch_target.getWidth())); GLfloat ogl_matrix[16]; camera.getOpenGLTransform(ogl_matrix); - view *= glh::matrix4f(ogl_matrix); + view *= glm::make_mat4(ogl_matrix); } LLGLDisable stencil(GL_STENCIL_TEST); @@ -471,14 +471,14 @@ LLTerrainPaintQueue LLTerrainPaintMap::convertPaintQueueRGBAToRGB(LLViewerTextur // First, apply the paint map as the background { - glh::matrix4f model; + glm::mat4 model; { - model.set_scale(glh::vec3f((F32)tex.getWidth(), (F32)tex.getHeight(), 1.0f)); - model.set_translate(glh::vec3f(0.0f, 0.0f, 0.0f)); + model = glm::scale(model, glm::vec3((F32)tex.getWidth(), (F32)tex.getHeight(), 1.0f)); + model = glm::translate(model, glm::vec3(0.0f, 0.0f, 0.0f)); } - glh::matrix4f modelview = view * model; + glm::mat4 modelview = view * model; gGL.matrixMode(LLRender::MM_MODELVIEW); - gGL.loadMatrix(modelview.m); + gGL.loadMatrix(glm::value_ptr(modelview)); shader.bindTexture(LLShaderMgr::DIFFUSE_MAP, &tex); // We care about the whole paintmap, which is already a power of two. @@ -505,14 +505,14 @@ LLTerrainPaintQueue LLTerrainPaintMap::convertPaintQueueRGBAToRGB(LLViewerTextur // Modelview matrix for the current paint // View matrix is already computed. Just need the model matrix. // Orthographic projection matrix is already updated - glh::matrix4f model; + glm::mat4 model; { - model.set_scale(glh::vec3f(paint_in->mWidthX, paint_in->mWidthY, 1.0f)); - model.set_translate(glh::vec3f(paint_in->mStartX, paint_in->mStartY, 0.0f)); + model = glm::scale(model, glm::vec3(paint_in->mWidthX, paint_in->mWidthY, 1.0f)); + model = glm::translate(model, glm::vec3(paint_in->mStartX, paint_in->mStartY, 0.0f)); } - glh::matrix4f modelview = view * model; + glm::mat4 modelview = view * model; gGL.matrixMode(LLRender::MM_MODELVIEW); - gGL.loadMatrix(modelview.m); + gGL.loadMatrix(glm::value_ptr(modelview)); // Generate temporary stamp texture from paint contents. // Our stamp image needs to be a power of two. @@ -663,7 +663,7 @@ LLTerrainPaintQueue LLTerrainPaintMap::convertBrushQueueToPaintRGB(const LLViewe // View matrix // Coordinates should be in pixels. 1.0f = 1 pixel on the framebuffer. // Camera is centered in the middle of the framebuffer. - glh::matrix4f view((GLfloat *) OGL_TO_CFR_ROTATION); + glm::mat4 view(glm::make_mat4((GLfloat*)OGL_TO_CFR_ROTATION)); { LLViewerCamera camera; const LLVector3 camera_origin(target_half_width, target_half_height, 0.5f); @@ -672,7 +672,7 @@ LLTerrainPaintQueue LLTerrainPaintMap::convertBrushQueueToPaintRGB(const LLViewe camera.setAspect(F32(scratch_target.getHeight()) / F32(scratch_target.getWidth())); GLfloat ogl_matrix[16]; camera.getOpenGLTransform(ogl_matrix); - view *= glh::matrix4f(ogl_matrix); + view *= glm::make_mat4(ogl_matrix); } LLGLDisable stencil(GL_STENCIL_TEST); @@ -687,14 +687,14 @@ LLTerrainPaintQueue LLTerrainPaintMap::convertBrushQueueToPaintRGB(const LLViewe // First, apply the paint map as the background { - glh::matrix4f model; + glm::mat4 model; { - model.set_scale(glh::vec3f((F32)tex.getWidth(), (F32)tex.getHeight(), 1.0f)); - model.set_translate(glh::vec3f(0.0f, 0.0f, 0.0f)); + model = glm::scale(model, glm::vec3((F32)tex.getWidth(), (F32)tex.getHeight(), 1.0f)); + model = glm::translate(model, glm::vec3(0.0f, 0.0f, 0.0f)); } - glh::matrix4f modelview = view * model; + glm::mat4 modelview = view * model; gGL.matrixMode(LLRender::MM_MODELVIEW); - gGL.loadMatrix(modelview.m); + gGL.loadMatrix(glm::value_ptr(modelview)); shader.bindTexture(LLShaderMgr::DIFFUSE_MAP, &tex); // We care about the whole paintmap, which is already a power of two. @@ -748,14 +748,14 @@ LLTerrainPaintQueue LLTerrainPaintMap::convertBrushQueueToPaintRGB(const LLViewe brush_start_x *= tex.getWidth() / region.getWidth(); brush_start_y *= tex.getHeight() / region.getWidth(); } - glh::matrix4f model; + glm::mat4 model; { - model.set_scale(glh::vec3f(brush_width_x, brush_width_y, 1.0f)); - model.set_translate(glh::vec3f(brush_start_x, brush_start_y, 0.0f)); + model = glm::scale(model, glm::vec3(brush_width_x, brush_width_y, 1.0f)); + model = glm::translate(model, glm::vec3(brush_start_x, brush_start_y, 0.0f)); } - glh::matrix4f modelview = view * model; + glm::mat4 modelview = view * model; gGL.matrixMode(LLRender::MM_MODELVIEW); - gGL.loadMatrix(modelview.m); + gGL.loadMatrix(glm::value_ptr(modelview)); // Apply the "brush" to the render target { @@ -842,11 +842,11 @@ template bool LLTerrainQueue::enqueue(std::vector>& list) { constexpr bool dry_run = true; - for (T::ptr_t& t : list) + for (auto& t : list) { if (!enqueue(t), dry_run) { return false; } } - for (T::ptr_t& t : list) + for (auto& t : list) { enqueue(t); } diff --git a/indra/newview/llviewercontrol.cpp b/indra/newview/llviewercontrol.cpp index f8a315d4d8..184c0e7d8b 100644 --- a/indra/newview/llviewercontrol.cpp +++ b/indra/newview/llviewercontrol.cpp @@ -911,6 +911,7 @@ void settings_setup_listeners() setting_setup_signal_listener(gSavedSettings, "AutoTuneImpostorByDistEnabled", handleUserImpostorByDistEnabledChanged); setting_setup_signal_listener(gSavedSettings, "TuningFPSStrategy", handleFPSTuningStrategyChanged); { + setting_setup_signal_listener(gSavedSettings, "LocalTerrainPaintEnabled", handleSetShaderChanged); setting_setup_signal_listener(gSavedSettings, "LocalTerrainPaintEnabled", handleLocalTerrainChanged); const char* transform_suffixes[] = { "ScaleU", diff --git a/indra/newview/llviewermenu.cpp b/indra/newview/llviewermenu.cpp index dd6b6d1ca1..b39bf4fdbd 100644 --- a/indra/newview/llviewermenu.cpp +++ b/indra/newview/llviewermenu.cpp @@ -1430,6 +1430,12 @@ class LLAdvancedTerrainCreateLocalPaintMap : public view_listener_t return false; } + // This calls gLocalTerrainMaterials.setPaintType + // It also ensures the terrain bake shader is compiled (handleSetShaderChanged), so call this first + // *TODO: Fix compile errors in shader so it can be used for all platforms. Then we can unhide the shader from behind this setting and remove the hook to handleSetShaderChanged. This advanced setting is intended to be used as a local setting for testing terrain, not a feature flag, but it is currently used like a feature flag as a temporary hack. + // *TODO: Ideally we would call setPaintType *after* the paint map is well-defined. The terrain draw pool should be able to handle an undefined paint map in the meantime. + gSavedSettings.setBOOL("LocalTerrainPaintEnabled", true); + U16 dim = (U16)gSavedSettings.getU32("TerrainPaintResolution"); // Ensure a reasonable image size of power two const U32 max_resolution = gSavedSettings.getU32("RenderMaxTextureResolution"); @@ -1439,8 +1445,6 @@ class LLAdvancedTerrainCreateLocalPaintMap : public view_listener_t LLPointer image_raw = new LLImageRaw(dim,dim,3); LLPointer tex = LLViewerTextureManager::getLocalTexture(image_raw.get(), true); const bool success = LLTerrainPaintMap::bakeHeightNoiseIntoPBRPaintMapRGB(*region, *tex); - // This calls gLocalTerrainMaterials.setPaintType - gSavedSettings.setBOOL("LocalTerrainPaintEnabled", true); // If baking the paintmap failed, set the paintmap to nullptr. This // causes LLDrawPoolTerrain to use a blank paintmap instead. if (!success) { tex = nullptr; } diff --git a/indra/newview/llviewershadermgr.cpp b/indra/newview/llviewershadermgr.cpp index 8e7af28d41..a6d397c039 100644 --- a/indra/newview/llviewershadermgr.cpp +++ b/indra/newview/llviewershadermgr.cpp @@ -3171,22 +3171,32 @@ bool LLViewerShaderMgr::loadShadersInterface() } } - if (success) + if (gSavedSettings.getBOOL("LocalTerrainPaintEnabled")) { - LLGLSLShader* shader = &gTerrainStampProgram; - U32 bit_depth = gSavedSettings.getU32("TerrainPaintBitDepth"); - // LLTerrainPaintMap currently uses an RGB8 texture internally - bit_depth = llclamp(bit_depth, 1, 8); - shader->mName = llformat("Terrain Stamp Shader RGB%o", bit_depth); + if (success) + { + LLGLSLShader* shader = &gTerrainStampProgram; + U32 bit_depth = gSavedSettings.getU32("TerrainPaintBitDepth"); + // LLTerrainPaintMap currently uses an RGB8 texture internally + bit_depth = llclamp(bit_depth, 1, 8); + shader->mName = llformat("Terrain Stamp Shader RGB%o", bit_depth); - shader->mShaderFiles.clear(); - shader->mShaderFiles.push_back(make_pair("interface/terrainStampV.glsl", GL_VERTEX_SHADER)); - shader->mShaderFiles.push_back(make_pair("interface/terrainStampF.glsl", GL_FRAGMENT_SHADER)); - shader->mShaderLevel = mShaderLevel[SHADER_INTERFACE]; - const U32 value_range = (1 << bit_depth) - 1; - shader->addPermutation("TERRAIN_PAINT_PRECISION", llformat("%d", value_range)); - success = success && shader->createShader(); - llassert(success); + shader->mShaderFiles.clear(); + shader->mShaderFiles.push_back(make_pair("interface/terrainStampV.glsl", GL_VERTEX_SHADER)); + shader->mShaderFiles.push_back(make_pair("interface/terrainStampF.glsl", GL_FRAGMENT_SHADER)); + shader->mShaderLevel = mShaderLevel[SHADER_INTERFACE]; + const U32 value_range = (1 << bit_depth) - 1; + shader->addPermutation("TERRAIN_PAINT_PRECISION", llformat("%d", value_range)); + success = success && shader->createShader(); + //llassert(success); + if (!success) + { + LL_WARNS() << "Failed to create shader '" << shader->mName << "', disabling!" << LL_ENDL; + gSavedSettings.setBOOL("RenderCanUseTerrainBakeShaders", false); + // continue as if this shader never happened + success = true; + } + } } if (success) -- cgit v1.2.3 From 303bf1977b802f5a091bec66b65dfb6c2f29b761 Mon Sep 17 00:00:00 2001 From: Cosmic Linden Date: Mon, 7 Oct 2024 14:50:27 -0700 Subject: secondlife/viewer#1883: Fix terrain paintmap bit depth hardcoded in places and increase default bit depth to 8 --- indra/newview/app_settings/settings.xml | 2 +- indra/newview/llterrainpaintmap.cpp | 10 ++++++---- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/indra/newview/app_settings/settings.xml b/indra/newview/app_settings/settings.xml index babb8424dc..ebf1968007 100644 --- a/indra/newview/app_settings/settings.xml +++ b/indra/newview/app_settings/settings.xml @@ -15367,7 +15367,7 @@ Type U32 Value - 5 + 8 TerrainPaintResolution diff --git a/indra/newview/llterrainpaintmap.cpp b/indra/newview/llterrainpaintmap.cpp index d36e4ea657..ec40f299a4 100644 --- a/indra/newview/llterrainpaintmap.cpp +++ b/indra/newview/llterrainpaintmap.cpp @@ -573,7 +573,7 @@ LLTerrainPaintQueue LLTerrainPaintMap::convertPaintQueueRGBAToRGB(LLViewerTextur paint_out->mStartY = paint_in->mStartY; paint_out->mWidthX = paint_in->mWidthX; paint_out->mWidthY = paint_in->mWidthY; - paint_out->mBitDepth = 8; // Will be reduced to 5 bits later + paint_out->mBitDepth = 8; // Will be reduced to TerrainPaintBitDepth bits later paint_out->mComponents = LLTerrainPaint::RGB; #ifdef SHOW_ASSERT paint_out->assert_confined_to(tex); @@ -602,7 +602,8 @@ LLTerrainPaintQueue LLTerrainPaintMap::convertPaintQueueRGBAToRGB(LLViewerTextur // round-trip which will reduce the bit depth, making the // pre-conversion step not necessary. queue_out.enqueue(paint_out); - queue_out.convertBitDepths(queue_out.size()-1, 5); + LLCachedControl bit_depth(gSavedSettings, "TerrainPaintBitDepth"); + queue_out.convertBitDepths(queue_out.size()-1, bit_depth); } queue_in.clear(); @@ -775,7 +776,7 @@ LLTerrainPaintQueue LLTerrainPaintMap::convertBrushQueueToPaintRGB(const LLViewe const F32 dY = brush_start_y - F32(paint_out->mStartY); paint_out->mWidthX = U16(ceil(brush_width_x + dX)); paint_out->mWidthY = U16(ceil(brush_width_y + dY)); - paint_out->mBitDepth = 8; // Will be reduced to 5 bits later + paint_out->mBitDepth = 8; // Will be reduced to TerrainPaintBitDepth bits later paint_out->mComponents = LLTerrainPaint::RGB; // The brush strokes are expected to sometimes partially venture // outside of the paintmap bounds. @@ -803,7 +804,8 @@ LLTerrainPaintQueue LLTerrainPaintMap::convertBrushQueueToPaintRGB(const LLViewe // round-trip which will reduce the bit depth, making the // pre-conversion step not necessary. queue_out.enqueue(paint_out); - queue_out.convertBitDepths(queue_out.size()-1, 5); + LLCachedControl bit_depth(gSavedSettings, "TerrainPaintBitDepth"); + queue_out.convertBitDepths(queue_out.size()-1, bit_depth); } queue_in.clear(); -- cgit v1.2.3 From d505eb4fce23e573c5155d1a9ba2857f549ca281 Mon Sep 17 00:00:00 2001 From: Cosmic Linden Date: Mon, 7 Oct 2024 15:57:50 -0700 Subject: secondlife/viewer#1883: Fix logic error caught by clang --- indra/newview/llterrainpaintmap.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/indra/newview/llterrainpaintmap.cpp b/indra/newview/llterrainpaintmap.cpp index ec40f299a4..09caba8faa 100644 --- a/indra/newview/llterrainpaintmap.cpp +++ b/indra/newview/llterrainpaintmap.cpp @@ -846,7 +846,7 @@ bool LLTerrainQueue::enqueue(std::vector>& list) constexpr bool dry_run = true; for (auto& t : list) { - if (!enqueue(t), dry_run) { return false; } + if (!enqueue(t, dry_run)) { return false; } } for (auto& t : list) { -- cgit v1.2.3 From f88401c97a892ea4a6fce13ee15dad1bb976d992 Mon Sep 17 00:00:00 2001 From: Cosmic Linden Date: Fri, 11 Oct 2024 09:54:47 -0700 Subject: secondlife/viewer#1883: Review feedback --- indra/newview/llterrainpaintmap.cpp | 18 ++++++------------ indra/newview/llterrainpaintmap.h | 6 +++--- indra/newview/llvlcomposition.h | 2 -- 3 files changed, 9 insertions(+), 17 deletions(-) diff --git a/indra/newview/llterrainpaintmap.cpp b/indra/newview/llterrainpaintmap.cpp index 09caba8faa..52376e3bf0 100644 --- a/indra/newview/llterrainpaintmap.cpp +++ b/indra/newview/llterrainpaintmap.cpp @@ -56,15 +56,15 @@ void check_tex(const LLViewerTexture& tex) llassert(tex.getPrimaryFormat() == GL_RGB); llassert(tex.getGLTexture()); } +#else +#define check_tex(tex) #endif } // namespace // static bool LLTerrainPaintMap::bakeHeightNoiseIntoPBRPaintMapRGB(const LLViewerRegion& region, LLViewerTexture& tex) { -#ifdef SHOW_ASSERT check_tex(tex); -#endif const LLSurface& surface = region.getLand(); const U32 patch_count = surface.getPatchesPerEdge(); @@ -309,9 +309,7 @@ void LLTerrainPaintMap::applyPaintQueueRGB(LLViewerTexture& tex, LLTerrainPaintQ { if (queue.empty()) { return; } -#ifdef SHOW_ASSERT check_tex(tex); -#endif gGL.getTexUnit(0)->bind(tex.getGLTexture(), false, true); @@ -362,7 +360,7 @@ namespace // plane. // *NOTE: Because we know the vertex XY coordinates go from 0 to 1 // pre-transform, UVs can be calculated from the vertices -LLVertexBuffer& get_paint_triangle_buffer() +LLVertexBuffer* get_paint_triangle_buffer() { static LLPointer buf = new LLVertexBuffer(LLVertexBuffer::MAP_VERTEX); static bool initialized = false; @@ -395,7 +393,7 @@ LLVertexBuffer& get_paint_triangle_buffer() *(indices++) = 2; buf->unmapBuffer(); } - return *buf; + return buf.get(); } }; @@ -403,9 +401,7 @@ LLVertexBuffer& get_paint_triangle_buffer() // static LLTerrainPaintQueue LLTerrainPaintMap::convertPaintQueueRGBAToRGB(LLViewerTexture& tex, LLTerrainPaintQueue& queue_in) { -#ifdef SHOW_ASSERT check_tex(tex); -#endif llassert(queue_in.getComponents() == LLTerrainPaint::RGBA); // TODO: Avoid allocating a scratch render buffer and use mAuxillaryRT instead @@ -429,7 +425,7 @@ LLTerrainPaintQueue LLTerrainPaintMap::convertPaintQueueRGBAToRGB(LLViewerTextur const F32 target_half_width = (F32)scratch_target.getWidth() / 2.0f; const F32 target_half_height = (F32)scratch_target.getHeight() / 2.0f; - LLVertexBuffer* buf = &get_paint_triangle_buffer(); + LLVertexBuffer* buf = get_paint_triangle_buffer(); // Update projection matrix and viewport // *NOTE: gl_state_for_2d also sets the modelview matrix. This will be overridden later. @@ -621,9 +617,7 @@ LLTerrainPaintQueue LLTerrainPaintMap::convertPaintQueueRGBAToRGB(LLViewerTextur // static LLTerrainPaintQueue LLTerrainPaintMap::convertBrushQueueToPaintRGB(const LLViewerRegion& region, LLViewerTexture& tex, LLTerrainBrushQueue& queue_in) { -#ifdef SHOW_ASSERT check_tex(tex); -#endif // TODO: Avoid allocating a scratch render buffer and use mAuxillaryRT instead // TODO: even if it means performing extra render operations to apply the brushes, in rare cases where the paints can't all fit within an area that can be represented by the buffer @@ -646,7 +640,7 @@ LLTerrainPaintQueue LLTerrainPaintMap::convertBrushQueueToPaintRGB(const LLViewe const F32 target_half_width = (F32)scratch_target.getWidth() / 2.0f; const F32 target_half_height = (F32)scratch_target.getHeight() / 2.0f; - LLVertexBuffer* buf = &get_paint_triangle_buffer(); + LLVertexBuffer* buf = get_paint_triangle_buffer(); // Update projection matrix and viewport // *NOTE: gl_state_for_2d also sets the modelview matrix. This will be overridden later. diff --git a/indra/newview/llterrainpaintmap.h b/indra/newview/llterrainpaintmap.h index cffdad80a2..6c321dc9c6 100644 --- a/indra/newview/llterrainpaintmap.h +++ b/indra/newview/llterrainpaintmap.h @@ -26,7 +26,7 @@ #pragma once -#include "llviewerprecompiledheaders.h" +#include class LLTexture; class LLViewerRegion; @@ -90,8 +90,8 @@ struct LLTerrainPaint U16 mWidthY; U8 mBitDepth; U8 mComponents; - const static U8 RGB = 3; - const static U8 RGBA = 4; + static constexpr U8 RGB = 3; + static constexpr U8 RGBA = 4; std::vector mData; // Asserts that this paint's start/width fit within the bounds of the diff --git a/indra/newview/llvlcomposition.h b/indra/newview/llvlcomposition.h index 9a5d74adda..21fd484375 100644 --- a/indra/newview/llvlcomposition.h +++ b/indra/newview/llvlcomposition.h @@ -27,8 +27,6 @@ #ifndef LL_LLVLCOMPOSITION_H #define LL_LLVLCOMPOSITION_H -#include - #include "llviewerlayer.h" #include "llviewershadermgr.h" #include "llviewertexture.h" -- cgit v1.2.3 From 18797f911e6510044a444104531a28b1f1aa5f2d Mon Sep 17 00:00:00 2001 From: Andrey Kleshchev Date: Thu, 10 Oct 2024 23:42:05 +0300 Subject: viewer#2172 AM/PM selector --- indra/newview/app_settings/settings.xml | 4 +- indra/newview/llchathistory.cpp | 1 + indra/newview/llconversationlog.cpp | 20 ++++++--- indra/newview/llfloaterimsessiontab.cpp | 15 ++++++- indra/newview/llfloaterpreference.cpp | 11 +++++ indra/newview/llfloaterpreference.h | 2 + indra/newview/lllogchat.cpp | 48 +++++++++++----------- indra/newview/llpanelenvironment.cpp | 23 ++++++++--- indra/newview/llpanelteleporthistory.cpp | 15 ++++++- indra/newview/llsidepaneliteminfo.cpp | 3 +- indra/newview/llworldmap.cpp | 18 ++++++-- .../skins/default/xui/da/sidepanel_item_info.xml | 5 ++- .../skins/default/xui/de/sidepanel_item_info.xml | 3 ++ .../xui/en/floater_inventory_item_properties.xml | 6 ++- .../newview/skins/default/xui/en/notifications.xml | 2 +- .../default/xui/en/panel_preferences_general.xml | 31 ++++++++++++++ 16 files changed, 160 insertions(+), 47 deletions(-) diff --git a/indra/newview/app_settings/settings.xml b/indra/newview/app_settings/settings.xml index babb8424dc..1d54b6bb7e 100644 --- a/indra/newview/app_settings/settings.xml +++ b/indra/newview/app_settings/settings.xml @@ -13066,9 +13066,9 @@ Use24HourClock Comment - 12 vs 24. At the moment only for region restart schedule floater + 12 vs 24. At the moment coverage is partial Persist - 0 + 1 Type Boolean Value diff --git a/indra/newview/llchathistory.cpp b/indra/newview/llchathistory.cpp index 3e02820feb..bb5ee558ca 100644 --- a/indra/newview/llchathistory.cpp +++ b/indra/newview/llchathistory.cpp @@ -426,6 +426,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") + "] [" diff --git a/indra/newview/llconversationlog.cpp b/indra/newview/llconversationlog.cpp index ed563cbec9..ed16a4b135 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/llfloaterimsessiontab.cpp b/indra/newview/llfloaterimsessiontab.cpp index 6a96dc0c69..0855a628fb 100644 --- a/indra/newview/llfloaterimsessiontab.cpp +++ b/indra/newview/llfloaterimsessiontab.cpp @@ -629,8 +629,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/llfloaterpreference.cpp b/indra/newview/llfloaterpreference.cpp index 0c96c93d31..d7ac8f0552 100644 --- a/indra/newview/llfloaterpreference.cpp +++ b/indra/newview/llfloaterpreference.cpp @@ -473,6 +473,8 @@ bool LLFloaterPreference::postBuild() getChild("log_path_string")->setEnabled(false); // make it read-only but selectable getChild("language_combobox")->setCommitCallback(boost::bind(&LLFloaterPreference::onLanguageChange, this)); + mTimeFormatCombobox = getChild("time_format_combobox"); + mTimeFormatCombobox->setCommitCallback(boost::bind(&LLFloaterPreference::onTimeFormatChange, this)); getChild("FriendIMOptions")->setCommitCallback(boost::bind(&LLFloaterPreference::onNotificationsChange, this,"FriendIMOptions")); getChild("NonFriendIMOptions")->setCommitCallback(boost::bind(&LLFloaterPreference::onNotificationsChange, this,"NonFriendIMOptions")); @@ -1108,6 +1110,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(OptionName)->getSelectedItemLabel(); @@ -1331,6 +1340,8 @@ void LLFloaterPreference::refresh() advanced->refresh(); } updateClickActionViews(); + + mTimeFormatCombobox->selectByValue(gSavedSettings.getBOOL("Use24HourClock") ? "1" : "0"); } void LLFloaterPreference::onCommitWindowedMode() diff --git a/indra/newview/llfloaterpreference.h b/indra/newview/llfloaterpreference.h index e06e758e3a..d5bd43e0ab 100644 --- a/indra/newview/llfloaterpreference.h +++ b/indra/newview/llfloaterpreference.h @@ -124,6 +124,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); @@ -242,6 +243,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/lllogchat.cpp b/indra/newview/lllogchat.cpp index bf49f33049..51f30dd86b 100644 --- a/indra/newview/lllogchat.cpp +++ b/indra/newview/lllogchat.cpp @@ -78,8 +78,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 @@ -150,6 +150,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); @@ -166,20 +170,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") @@ -308,16 +304,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/llpanelenvironment.cpp b/indra/newview/llpanelenvironment.cpp index be61c44b7c..d3df88b65e 100644 --- a/indra/newview/llpanelenvironment.cpp +++ b/indra/newview/llpanelenvironment.cpp @@ -48,6 +48,7 @@ #include "llappviewer.h" #include "llcallbacklist.h" +#include "llviewercontrol.h" #include "llviewerparcelmgr.h" #include "llinventorymodel.h" @@ -939,19 +940,29 @@ void LLPanelEnvironmentInfo::udpateApparentTimeOfDay() S32Hours hourofday(secondofday); S32Seconds secondofhour(secondofday - hourofday); S32Minutes minutesofhour(secondofhour); + static bool use_24h = gSavedSettings.getBOOL("Use24HourClock"); bool am_pm(hourofday.value() >= 12); - if (hourofday.value() < 1) - hourofday = S32Hours(12); - if (hourofday.value() > 12) - hourofday -= S32Hours(12); + if (!use_24h) + { + if (hourofday.value() < 1) + hourofday = S32Hours(12); + if (hourofday.value() > 12) + hourofday -= S32Hours(12); + } std::string lblminute(((minutesofhour.value() < 10) ? "0" : "") + LLSD(minutesofhour.value()).asString()); - mLabelApparentTime->setTextArg("[HH]", LLSD(hourofday.value()).asString()); mLabelApparentTime->setTextArg("[MM]", lblminute); - mLabelApparentTime->setTextArg("[AP]", std::string(am_pm ? "PM" : "AM")); + if (use_24h) + { + mLabelApparentTime->setTextArg("[AP]", std::string()); + } + else + { + mLabelApparentTime->setTextArg("[AP]", std::string(am_pm ? "PM" : "AM")); + } mLabelApparentTime->setTextArg("[PRC]", LLSD((S32)(100 * perc)).asString()); } diff --git a/indra/newview/llpanelteleporthistory.cpp b/indra/newview/llpanelteleporthistory.cpp index ebfdafdfe2..8097e0ac0b 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/llsidepaneliteminfo.cpp b/indra/newview/llsidepaneliteminfo.cpp index fccf745a74..68c5f3fb56 100644 --- a/indra/newview/llsidepaneliteminfo.cpp +++ b/indra/newview/llsidepaneliteminfo.cpp @@ -483,7 +483,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("acquiredDate24") : getString("acquiredDateAMPM"); LLSD substitution; substitution["datetime"] = (S32) time_utc; LLStringUtil::format (timeStr, substitution); diff --git a/indra/newview/llworldmap.cpp b/indra/newview/llworldmap.cpp index 7962c28e6d..5951d6a93a 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) + { + std::string timeStr = "[" + LLTrans::getString("TimeHour") + "]:[" + + LLTrans::getString("TimeMin") + "]"; + } + else + { + std::string 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/skins/default/xui/da/sidepanel_item_info.xml b/indra/newview/skins/default/xui/da/sidepanel_item_info.xml index d52845160b..3e4f6946ca 100644 --- a/indra/newview/skins/default/xui/da/sidepanel_item_info.xml +++ b/indra/newview/skins/default/xui/da/sidepanel_item_info.xml @@ -12,9 +12,12 @@ Ejer kan: - + [wkday,datetime,local] [mth,datetime,local] [day,datetime,local] [hour,datetime,local]:[min,datetime,local]:[second,datetime,local] [year,datetime,local] + + [wkday,datetime,local] [mth,datetime,local] [day,datetime,local] [hour,datetime,local]:[min,datetime,local]:[second,datetime,local] [ampm,datetime,local] [year,datetime,local] + (Beholdning) 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..6f0028d282 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 @@ [wkday,datetime,local] [mth,datetime,local] [day,datetime,local] [hour,datetime,local]:[min,datetime,local]:[second,datetime,local] [year,datetime,local] + + [wkday,datetime,local] [mth,datetime,local] [day,datetime,local] [hour,datetime,local]:[min,datetime,local]:[second,datetime,local] [ampm,datetime,local] [year,datetime,local] + (Inventar) diff --git a/indra/newview/skins/default/xui/en/floater_inventory_item_properties.xml b/indra/newview/skins/default/xui/en/floater_inventory_item_properties.xml index 6c3214a76d..e6fa3c2172 100644 --- a/indra/newview/skins/default/xui/en/floater_inventory_item_properties.xml +++ b/indra/newview/skins/default/xui/en/floater_inventory_item_properties.xml @@ -25,9 +25,13 @@ Owner can: + name="acquiredDate24"> [wkday,datetime,local] [mth,datetime,local] [day,datetime,local] [hour,datetime,local]:[min,datetime,local]:[second,datetime,local] [year,datetime,local] + + [wkday,datetime,local] [mth,datetime,local] [day,datetime,local] [hour,datetime,local]:[min,datetime,local]:[second,datetime,local] [ampm,datetime,local] [year,datetime,local] + -Changing language will take effect after you restart [APP_NAME]. +Changing language or time format will take effect after you restart [APP_NAME]. + + Time Format: + + + + + Date: Fri, 11 Oct 2024 03:56:52 +0300 Subject: viewer#2172 Fix Unit Test --- indra/newview/tests/llworldmap_test.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/indra/newview/tests/llworldmap_test.cpp b/indra/newview/tests/llworldmap_test.cpp index d5bf189d82..99c98f63ed 100644 --- a/indra/newview/tests/llworldmap_test.cpp +++ b/indra/newview/tests/llworldmap_test.cpp @@ -28,6 +28,7 @@ // Dependencies #include "linden_common.h" #include "llapr.h" +#include "llcontrol.h" // LLControlGroup #include "llsingleton.h" #include "lltrans.h" #include "lluistring.h" @@ -71,6 +72,8 @@ void LLUIString::updateResult() const { } void LLUIString::setArg(const std::string& , const std::string& ) { } void LLUIString::assign(const std::string& ) { } +LLControlGroup gSavedSettings("Global"); // saved at end of session + // End Stubbing // ------------------------------------------------------------------------------------------- @@ -131,6 +134,7 @@ namespace tut // Constructor and destructor of the test wrapper worldmap_test() { + gSavedSettings.declareBOOL("Use24HourClock", true, "", LLControlVariable::PERSIST_NO); mWorld = LLWorldMap::getInstance(); } ~worldmap_test() -- cgit v1.2.3 From bed3b57c52574ec593293c7397dd0da18e801fb4 Mon Sep 17 00:00:00 2001 From: Andrey Kleshchev Date: Fri, 11 Oct 2024 18:50:14 +0300 Subject: viewer#2172 AM/PM selector #2 --- indra/newview/llfloaterinspect.cpp | 3 ++- indra/newview/llfloaterland.cpp | 3 ++- indra/newview/llnotificationlistitem.cpp | 21 +++++++++++++++++---- indra/newview/llpanellandmarkinfo.cpp | 4 +++- indra/newview/llsidepaneliteminfo.cpp | 2 +- indra/newview/lltoastgroupnotifypanel.cpp | 19 +++++++++++++++---- .../skins/default/xui/da/sidepanel_item_info.xml | 2 +- .../skins/default/xui/de/sidepanel_item_info.xml | 2 +- .../skins/default/xui/en/floater_about_land.xml | 3 +++ .../skins/default/xui/en/floater_inspect.xml | 4 ++++ .../xui/en/floater_inventory_item_properties.xml | 2 +- .../skins/default/xui/en/panel_landmark_info.xml | 4 ++++ .../skins/default/xui/en/panel_place_profile.xml | 4 ++++ .../skins/default/xui/en/sidepanel_item_info.xml | 4 ++++ 14 files changed, 62 insertions(+), 15 deletions(-) diff --git a/indra/newview/llfloaterinspect.cpp b/indra/newview/llfloaterinspect.cpp index 0f1eb0cef0..5dea46843c 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/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 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/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/llsidepaneliteminfo.cpp b/indra/newview/llsidepaneliteminfo.cpp index 68c5f3fb56..385e4314a9 100644 --- a/indra/newview/llsidepaneliteminfo.cpp +++ b/indra/newview/llsidepaneliteminfo.cpp @@ -484,7 +484,7 @@ void LLSidepanelItemInfo::refreshFromItem(LLViewerInventoryItem* item) else { static bool use_24h = gSavedSettings.getBOOL("Use24HourClock"); - std::string timeStr = use_24h ? getString("acquiredDate24") : getString("acquiredDateAMPM"); + 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/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/skins/default/xui/da/sidepanel_item_info.xml b/indra/newview/skins/default/xui/da/sidepanel_item_info.xml index 3e4f6946ca..6a2acfedf7 100644 --- a/indra/newview/skins/default/xui/da/sidepanel_item_info.xml +++ b/indra/newview/skins/default/xui/da/sidepanel_item_info.xml @@ -12,7 +12,7 @@ Ejer kan: - + [wkday,datetime,local] [mth,datetime,local] [day,datetime,local] [hour,datetime,local]:[min,datetime,local]:[second,datetime,local] [year,datetime,local] 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 6f0028d282..d5ff203e89 100644 --- a/indra/newview/skins/default/xui/de/sidepanel_item_info.xml +++ b/indra/newview/skins/default/xui/de/sidepanel_item_info.xml @@ -21,7 +21,7 @@ [wkday,datetime,local] [mth,datetime,local] [day,datetime,local] [hour,datetime,local]:[min,datetime,local]:[second,datetime,local] [year,datetime,local] - + [wkday,datetime,local] [mth,datetime,local] [day,datetime,local] [hour,datetime,local]:[min,datetime,local]:[second,datetime,local] [ampm,datetime,local] [year,datetime,local] 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. + + [wkday,datetime,slt] [mth,datetime,slt] [day,datetime,slt] [hour12,datetime,slt]:[min,datetime,slt]:[second,datetime,slt] [ampm,datetime,slt] [year,datetime,slt] + [wkday,datetime,slt] [mth,datetime,slt] [day,datetime,slt] [hour,datetime,slt]:[min,datetime,slt]:[second,datetime,slt] [year,datetime,slt] 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 @@ -11,6 +11,10 @@ save_rect="true" title="INSPECT OBJECTS" width="400"> + + [wkday,datetime,slt] [mth,datetime,slt] [day,datetime,slt] [hour12,datetime,slt]:[min,datetime,slt]:[second,datetime,slt] [ampm,datetime,slt] [year,datetime,slt] + [wkday,datetime,slt] [mth,datetime,slt] [day,datetime,slt] [hour,datetime,slt]:[min,datetime,slt]:[second,datetime,slt] [year,datetime,slt] diff --git a/indra/newview/skins/default/xui/en/floater_inventory_item_properties.xml b/indra/newview/skins/default/xui/en/floater_inventory_item_properties.xml index e6fa3c2172..cc7942abea 100644 --- a/indra/newview/skins/default/xui/en/floater_inventory_item_properties.xml +++ b/indra/newview/skins/default/xui/en/floater_inventory_item_properties.xml @@ -25,7 +25,7 @@ Owner can: + name="acquiredDate"> [wkday,datetime,local] [mth,datetime,local] [day,datetime,local] [hour,datetime,local]:[min,datetime,local]:[second,datetime,local] [year,datetime,local] Information about this location is unavailable due to access restrictions. Please check your permissions with the parcel owner. + + [wkday,datetime,slt] [mth,datetime,slt] [day,datetime,slt] [hour12,datetime,slt]:[min,datetime,slt]:[second,datetime,slt] [ampm,datetime,slt] [year,datetime,slt] + [wkday,datetime,slt] [mth,datetime,slt] [day,datetime,slt] [hour,datetime,slt]:[min,datetime,slt]:[second,datetime,slt] [year,datetime,slt] 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 @@ -88,6 +88,10 @@ name="server_forbidden_text"> Information about this location is unavailable due to access restrictions. Please check your permissions with the parcel owner. + + [wkday,datetime,local] [mth,datetime,local] [day,datetime,local] [hour12,datetime,local]:[min,datetime,local]:[second,datetime,local] [ampm,datetime,local] [year,datetime,local] + [wkday,datetime,local] [mth,datetime,local] [day,datetime,local] [hour,datetime,local]:[min,datetime,local]:[second,datetime,local] [year,datetime,local] 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 @@ -31,6 +31,10 @@ name="owner_can"> Owner can: + + [wkday,datetime,slt] [mth,datetime,slt] [day,datetime,slt] [hour12,datetime,slt]:[min,datetime,slt]:[second,datetime,slt] [ampm,datetime,slt] [year,datetime,slt] + [wkday,datetime,slt] [mth,datetime,slt] [day,datetime,slt] [hour,datetime,slt]:[min,datetime,slt]:[second,datetime,slt] [year,datetime,slt] -- cgit v1.2.3 From a6a3a1771b81e551e2d7fcd68e9df0cec092b27b Mon Sep 17 00:00:00 2001 From: Rye Date: Fri, 11 Oct 2024 06:35:10 -0700 Subject: Rework GHA matrix config to fix linux build --- .github/workflows/build.yaml | 23 +++++++++++------------ indra/cmake/UI.cmake | 1 - indra/newview/rlvhelper.cpp | 4 ++-- 3 files changed, 13 insertions(+), 15 deletions(-) diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index fda6545e83..b35b98a641 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -13,7 +13,7 @@ jobs: runs-on: ubuntu-latest outputs: release_run: ${{ steps.setvar.outputs.release_run }} - configurations: ${{ steps.setvar.outputs.configurations }} + config: ${{ steps.setvar.outputs.config }} bugsplat_db: ${{ steps.setvar.outputs.bugsplat_db }} env: # Build with a tag like "Second_Life#abcdef0" to generate a release page @@ -36,10 +36,10 @@ jobs: if [[ "$FROM_FORK" == "true" ]]; then # PR from fork; don't build with Bugsplat, proprietary libs - echo 'configurations=["ReleaseOS"]' >> $GITHUB_OUTPUT + echo 'config=ReleaseOS' >> $GITHUB_OUTPUT echo "bugsplat_db=" >> $GITHUB_OUTPUT else - echo 'configurations=["Release"]' >> $GITHUB_OUTPUT + echo 'config=Release' >> $GITHUB_OUTPUT echo "bugsplat_db=SecondLife_Viewer_2018" >> $GITHUB_OUTPUT fi build: @@ -48,7 +48,6 @@ jobs: strategy: matrix: runner: [windows-large, macos-14-xlarge] - configuration: ${{ fromJSON(needs.setup.outputs.configurations) }} experimental: [false] include: - runner: linux-large @@ -60,11 +59,11 @@ jobs: viewer_branch: ${{ steps.which-branch.outputs.branch }} relnotes: ${{ steps.which-branch.outputs.relnotes }} imagename: ${{ steps.build.outputs.imagename }} - configuration: ${{ matrix.configuration }} + config: ${{ needs.setup.outputs.config }} env: AUTOBUILD_ADDRSIZE: 64 AUTOBUILD_BUILD_ID: ${{ github.run_id }} - AUTOBUILD_CONFIGURATION: ${{ matrix.configuration }} + AUTOBUILD_CONFIGURATION: ${{ needs.setup.outputs.config }} # authorizes fetching private constituent packages AUTOBUILD_GITHUB_TOKEN: ${{ secrets.SHARED_AUTOBUILD_GITHUB_TOKEN }} AUTOBUILD_INSTALLABLE_CACHE: ${{ github.workspace }}/.autobuild-installables @@ -91,7 +90,7 @@ jobs: master_message_template_checkout: ${{ github.workspace }}/.master-message-template # Only set variants to the one configuration: don't let build.sh loop # over variants, let GitHub distribute variants over multiple hosts. - variants: ${{ matrix.configuration }} + variants: ${{ needs.setup.outputs.config }} steps: - name: Checkout code uses: actions/checkout@v4 @@ -124,9 +123,9 @@ jobs: uses: actions/cache@v4 with: path: .autobuild-installables - key: ${{ runner.os }}-64-${{ matrix.configuration }}-${{ hashFiles('autobuild.xml') }} + key: ${{ runner.os }}-64-${{ needs.setup.outputs.config }}-${{ hashFiles('autobuild.xml') }} restore-keys: | - ${{ runner.os }}-64-${{ matrix.configuration }}- + ${{ runner.os }}-64-${{ needs.setup.outputs.config }}- ${{ runner.os }}-64- - name: Install Linux dependencies @@ -321,7 +320,7 @@ jobs: - name: Upload physics package uses: actions/upload-artifact@v4 # should only be set for viewer-private - if: matrix.configuration == 'Release' && steps.build.outputs.physicstpv + if: needs.setup.outputs.config == 'Release' && steps.build.outputs.physicstpv with: name: "${{ steps.build.outputs.artifact }}-physics" # emitted by build.sh, zero or one lines @@ -395,7 +394,7 @@ jobs: BUGSPLAT_USER: ${{ secrets.BUGSPLAT_USER }} BUGSPLAT_PASS: ${{ secrets.BUGSPLAT_PASS }} needs: build - if: needs.build.outputs.configuration == 'Release' + if: needs.build.outputs.config == 'Release' runs-on: ubuntu-latest steps: - name: Download viewer exe @@ -430,7 +429,7 @@ jobs: BUGSPLAT_USER: ${{ secrets.BUGSPLAT_USER }} BUGSPLAT_PASS: ${{ secrets.BUGSPLAT_PASS }} needs: build - if: needs.build.outputs.configuration == 'Release' + if: needs.build.outputs.config == 'Release' runs-on: ubuntu-latest steps: - name: Download Mac Symbols diff --git a/indra/cmake/UI.cmake b/indra/cmake/UI.cmake index 23bd8018dd..595f394af4 100644 --- a/indra/cmake/UI.cmake +++ b/indra/cmake/UI.cmake @@ -6,7 +6,6 @@ include(GLIB) add_library( ll::uilibraries INTERFACE IMPORTED ) if (LINUX) - use_prebuilt_binary(fltk) target_compile_definitions(ll::uilibraries INTERFACE LL_X11=1 ) if( USE_CONAN ) diff --git a/indra/newview/rlvhelper.cpp b/indra/newview/rlvhelper.cpp index 7cb1473c8c..1dac297bf1 100644 --- a/indra/newview/rlvhelper.cpp +++ b/indra/newview/rlvhelper.cpp @@ -95,12 +95,12 @@ void BehaviourDictionary::addEntry(const BehaviourInfo* entry_p) } // Sanity check for duplicate entries -#ifndef LL_RELEASE_FOR_DOWNLOAD +#if LL_RELEASE_WITH_DEBUG_INFO || LL_DEBUG std::for_each(mBhvrInfoList.begin(), mBhvrInfoList.end(), [&entry_p](const BehaviourInfo* bhvr_info_p) { RLV_ASSERT_DBG((bhvr_info_p->getBehaviour() != entry_p->getBehaviour()) || ((bhvr_info_p->getParamTypeMask() & entry_p->getParamTypeMask()) == 0)); }); -#endif // LL_RELEASE_FOR_DOWNLOAD +#endif // LL_RELEASE_WITH_DEBUG_INFO || LL_DEBUG mBhvrInfoList.push_back(entry_p); } -- cgit v1.2.3 From 90ff4b416d12814035492e519c08c36d925bbab5 Mon Sep 17 00:00:00 2001 From: Alexander Gavriliuk Date: Fri, 11 Oct 2024 21:12:10 +0200 Subject: #2408 The long covenant with emojis (no double requesting) --- indra/newview/llfloaterregioninfo.cpp | 177 +++++++++++++++------------------- indra/newview/llfloaterregioninfo.h | 32 +++--- indra/newview/llviewerregion.cpp | 4 +- 3 files changed, 98 insertions(+), 115 deletions(-) diff --git a/indra/newview/llfloaterregioninfo.cpp b/indra/newview/llfloaterregioninfo.cpp index 165fb5ef88..df4acfac5e 100644 --- a/indra/newview/llfloaterregioninfo.cpp +++ b/indra/newview/llfloaterregioninfo.cpp @@ -148,40 +148,6 @@ public: static LLSD getIDs( sparam_t::const_iterator it, sparam_t::const_iterator end, S32 count ); }; - -/* -void unpack_request_params( - LLMessageSystem* msg, - LLDispatcher::sparam_t& strings, - LLDispatcher::iparam_t& integers) -{ - char str_buf[MAX_STRING]; - S32 str_count = msg->getNumberOfBlocksFast(_PREHASH_StringData); - S32 i; - for (i = 0; i < str_count; ++i) - { - // we treat the SParam as binary data (since it might be an - // LLUUID in compressed form which may have embedded \0's,) - str_buf[0] = '\0'; - S32 data_size = msg->getSizeFast(_PREHASH_StringData, i, _PREHASH_SParam); - if (data_size >= 0) - { - msg->getBinaryDataFast(_PREHASH_StringData, _PREHASH_SParam, - str_buf, data_size, i, MAX_STRING - 1); - strings.push_back(std::string(str_buf, data_size)); - } - } - - U32 int_buf; - S32 int_count = msg->getNumberOfBlocksFast(_PREHASH_IntegerData); - for (i = 0; i < int_count; ++i) - { - msg->getU32("IntegerData", "IParam", int_buf, i); - integers.push_back(int_buf); - } -} -*/ - class LLPanelRegionEnvironment : public LLPanelEnvironmentInfo { public: @@ -326,8 +292,8 @@ void LLFloaterRegionInfo::onOpen(const LLSD& key) disableTabCtrls(); return; } - refreshFromRegion(gAgent.getRegion()); - requestRegionInfo(); + refreshFromRegion(gAgent.getRegion(), ERefreshFromRegionPhase::BeforeRequestRegionInfo); + requestRegionInfo(true); if (!mGodLevelChangeSlot.connected()) { @@ -347,12 +313,14 @@ void LLFloaterRegionInfo::onRegionChanged() { if (getVisible()) //otherwise onOpen will do request { - requestRegionInfo(); + requestRegionInfo(false); } } -void LLFloaterRegionInfo::requestRegionInfo() +void LLFloaterRegionInfo::requestRegionInfo(bool is_opening) { + mIsRegionInfoRequestedFromOpening = is_opening; + LLTabContainer* tab = findChild("region_panels"); if (tab) { @@ -523,8 +491,7 @@ void LLFloaterRegionInfo::processRegionInfo(LLMessageSystem* msg) panel->getChild("agent_limit_spin")->setMaxValue((F32)hard_agent_limit); - LLPanelRegionGeneralInfo* panel_general = LLFloaterRegionInfo::getPanelGeneral(); - if (panel) + if (LLPanelRegionGeneralInfo* panel_general = LLFloaterRegionInfo::getPanelGeneral()) { panel_general->setObjBonusFactor(object_bonus_factor); } @@ -561,21 +528,25 @@ void LLFloaterRegionInfo::processRegionInfo(LLMessageSystem* msg) { // Note: region info also causes LLRegionInfoModel::instance().update(msg); -> requestRegion(); -> changed message // we need to know env version here and in update(msg) to know when to request and when not to, when to filter 'changed' - floater->refreshFromRegion(gAgent.getRegion()); + ERefreshFromRegionPhase phase = floater->mIsRegionInfoRequestedFromOpening ? + ERefreshFromRegionPhase::AfterRequestRegionInfo : + ERefreshFromRegionPhase::NotFromFloaterOpening; + floater->refreshFromRegion(gAgent.getRegion(), phase); } // else will rerequest on onOpen either way } // static -void LLFloaterRegionInfo::sRefreshFromRegion(LLViewerRegion* region) +void LLFloaterRegionInfo::refreshFromRegion(LLViewerRegion* region) { - if (region != gAgent.getRegion()) { return; } - - LLFloaterRegionInfo* floater = LLFloaterReg::getTypedInstance("region_info"); - if (!floater) { return; } + if (region != gAgent.getRegion()) + return; - if (floater->getVisible() && region == gAgent.getRegion()) + if (LLFloaterRegionInfo* floater = LLFloaterReg::getTypedInstance("region_info")) { - floater->refreshFromRegion(region); + if (floater->getVisible() && region == gAgent.getRegion()) + { + floater->refreshFromRegion(region, ERefreshFromRegionPhase::NotFromFloaterOpening); + } } } @@ -682,7 +653,7 @@ void LLFloaterRegionInfo::onTabSelected(const LLSD& param) active_panel->onOpen(LLSD()); } -void LLFloaterRegionInfo::refreshFromRegion(LLViewerRegion* region) +void LLFloaterRegionInfo::refreshFromRegion(LLViewerRegion* region, ERefreshFromRegionPhase phase) { if (!region) { @@ -692,7 +663,7 @@ void LLFloaterRegionInfo::refreshFromRegion(LLViewerRegion* region) // call refresh from region on all panels for (const auto& infoPanel : mInfoPanels) { - infoPanel->refreshFromRegion(region); + infoPanel->refreshFromRegion(region, phase); } mEnvironmentPanel->refreshFromRegion(region); } @@ -725,7 +696,7 @@ void LLFloaterRegionInfo::onGodLevelChange(U8 god_level) LLFloaterRegionInfo* floater = LLFloaterReg::getTypedInstance("region_info"); if (floater && floater->getVisible()) { - refreshFromRegion(gAgent.getRegion()); + refreshFromRegion(gAgent.getRegion(), ERefreshFromRegionPhase::NotFromFloaterOpening); } } @@ -795,7 +766,7 @@ void LLPanelRegionInfo::updateChild(LLUICtrl* child_ctr) } // virtual -bool LLPanelRegionInfo::refreshFromRegion(LLViewerRegion* region) +bool LLPanelRegionInfo::refreshFromRegion(LLViewerRegion* region, ERefreshFromRegionPhase phase) { if (region) mHost = region->getHost(); return true; @@ -823,12 +794,10 @@ void LLPanelRegionInfo::sendEstateOwnerMessage( } else { - strings_t::const_iterator it = strings.begin(); - strings_t::const_iterator end = strings.end(); - for(; it != end; ++it) + for (const std::string& string : strings) { msg->nextBlock("ParamList"); - msg->addString("Parameter", *it); + msg->addString("Parameter", string); } } msg->sendReliable(mHost); @@ -887,7 +856,7 @@ void LLPanelRegionInfo::onClickManageRestartSchedule() ///////////////////////////////////////////////////////////////////////////// // LLPanelRegionGeneralInfo // -bool LLPanelRegionGeneralInfo::refreshFromRegion(LLViewerRegion* region) +bool LLPanelRegionGeneralInfo::refreshFromRegion(LLViewerRegion* region, ERefreshFromRegionPhase phase) { bool allow_modify = gAgent.isGodlike() || (region && region->canManageEstate()); setCtrlsEnabled(allow_modify); @@ -904,7 +873,7 @@ bool LLPanelRegionGeneralInfo::refreshFromRegion(LLViewerRegion* region) // Data gets filled in by processRegionInfo - return LLPanelRegionInfo::refreshFromRegion(region); + return LLPanelRegionInfo::refreshFromRegion(region, phase); } bool LLPanelRegionGeneralInfo::postBuild() @@ -1173,7 +1142,7 @@ bool LLPanelRegionDebugInfo::postBuild() } // virtual -bool LLPanelRegionDebugInfo::refreshFromRegion(LLViewerRegion* region) +bool LLPanelRegionDebugInfo::refreshFromRegion(LLViewerRegion* region, ERefreshFromRegionPhase phase) { bool allow_modify = gAgent.isGodlike() || (region && region->canManageEstate()); setCtrlsEnabled(allow_modify); @@ -1191,7 +1160,7 @@ bool LLPanelRegionDebugInfo::refreshFromRegion(LLViewerRegion* region) getChildView("cancel_restart_btn")->setEnabled(allow_modify); getChildView("region_debug_console_btn")->setEnabled(allow_modify); - return LLPanelRegionInfo::refreshFromRegion(region); + return LLPanelRegionInfo::refreshFromRegion(region, phase); } // virtual @@ -1233,7 +1202,7 @@ void LLPanelRegionDebugInfo::callbackAvatarID(const uuid_vec_t& ids, const std:: if (ids.empty() || names.empty()) return; mTargetAvatar = ids[0]; getChild("target_avatar_name")->setValue(LLSD(names[0].getCompleteName())); - refreshFromRegion( gAgent.getRegion() ); + refreshFromRegion(gAgent.getRegion(), ERefreshFromRegionPhase::NotFromFloaterOpening); } // static @@ -1675,7 +1644,7 @@ void LLPanelRegionTerrainInfo::updateForMaterialType() } // virtual -bool LLPanelRegionTerrainInfo::refreshFromRegion(LLViewerRegion* region) +bool LLPanelRegionTerrainInfo::refreshFromRegion(LLViewerRegion* region, ERefreshFromRegionPhase phase) { bool owner_or_god = gAgent.isGodlike() || (region && (region->getOwner() == gAgent.getID())); @@ -1819,7 +1788,7 @@ bool LLPanelRegionTerrainInfo::refreshFromRegion(LLViewerRegion* region) getChildView("upload_raw_btn")->setEnabled(owner_or_god); getChildView("bake_terrain_btn")->setEnabled(owner_or_god); - return LLPanelRegionInfo::refreshFromRegion(region); + return LLPanelRegionInfo::refreshFromRegion(region, phase); } @@ -2289,25 +2258,27 @@ void LLPanelEstateInfo::updateControls(LLViewerRegion* region) refresh(); } -bool LLPanelEstateInfo::refreshFromRegion(LLViewerRegion* region) +bool LLPanelEstateInfo::refreshFromRegion(LLViewerRegion* region, ERefreshFromRegionPhase phase) { updateControls(region); // let the parent class handle the general data collection. - bool rv = LLPanelRegionInfo::refreshFromRegion(region); - - // We want estate info. To make sure it works across region - // boundaries and multiple packets, we add a serial number to the - // integers and track against that on update. - strings_t strings; - //integers_t integers; - //LLFloaterRegionInfo::incrementSerial(); - LLFloaterRegionInfo::nextInvoice(); - LLUUID invoice(LLFloaterRegionInfo::getLastInvoice()); - //integers.push_back(LLFloaterRegionInfo::());::getPanelEstate(); + bool rv = LLPanelRegionInfo::refreshFromRegion(region, phase); + if (phase != ERefreshFromRegionPhase::BeforeRequestRegionInfo) + { + // We want estate info. To make sure it works across region + // boundaries and multiple packets, we add a serial number to the + // integers and track against that on update. + strings_t strings; + //integers_t integers; + //LLFloaterRegionInfo::incrementSerial(); + LLFloaterRegionInfo::nextInvoice(); + LLUUID invoice(LLFloaterRegionInfo::getLastInvoice()); + //integers.push_back(LLFloaterRegionInfo::());::getPanelEstate(); - sendEstateOwnerMessage(gMessageSystem, "getinfo", invoice, strings); + sendEstateOwnerMessage(gMessageSystem, "getinfo", invoice, strings); + } refresh(); @@ -2528,7 +2499,7 @@ LLPanelEstateCovenant::LLPanelEstateCovenant() } // virtual -bool LLPanelEstateCovenant::refreshFromRegion(LLViewerRegion* region) +bool LLPanelEstateCovenant::refreshFromRegion(LLViewerRegion* region, ERefreshFromRegionPhase phase) { LLTextBox* region_name = getChild("region_name_text"); if (region_name) @@ -2574,13 +2545,17 @@ bool LLPanelEstateCovenant::refreshFromRegion(LLViewerRegion* region) getChild("reset_covenant")->setEnabled(gAgent.isGodlike() || (region && region->canManageEstate())); // let the parent class handle the general data collection. - bool rv = LLPanelRegionInfo::refreshFromRegion(region); - LLMessageSystem *msg = gMessageSystem; - msg->newMessage("EstateCovenantRequest"); - msg->nextBlockFast(_PREHASH_AgentData); - msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID()); - msg->addUUIDFast(_PREHASH_SessionID,gAgent.getSessionID()); - msg->sendReliable(region->getHost()); + bool rv = LLPanelRegionInfo::refreshFromRegion(region, phase); + + if (phase != ERefreshFromRegionPhase::AfterRequestRegionInfo) + { + gMessageSystem->newMessage("EstateCovenantRequest"); + gMessageSystem->nextBlockFast(_PREHASH_AgentData); + gMessageSystem->addUUIDFast(_PREHASH_AgentID, gAgent.getID()); + gMessageSystem->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID()); + gMessageSystem->sendReliable(region->getHost()); + } + return rv; } @@ -3128,7 +3103,7 @@ std::string LLPanelRegionExperiences::regionCapabilityQuery(LLViewerRegion* regi return region->getCapability(cap); } -bool LLPanelRegionExperiences::refreshFromRegion(LLViewerRegion* region) +bool LLPanelRegionExperiences::refreshFromRegion(LLViewerRegion* region, ERefreshFromRegionPhase phase) { bool allow_modify = gAgent.isGodlike() || (region && region->canManageEstate()); @@ -3151,10 +3126,13 @@ bool LLPanelRegionExperiences::refreshFromRegion(LLViewerRegion* region) mTrusted->loading(); mTrusted->setReadonly(!allow_modify); - LLExperienceCache::instance().getRegionExperiences(boost::bind(&LLPanelRegionExperiences::regionCapabilityQuery, region, _1), - boost::bind(&LLPanelRegionExperiences::infoCallback, getDerivedHandle(), _1)); + if (phase != ERefreshFromRegionPhase::AfterRequestRegionInfo) + { + LLExperienceCache::instance().getRegionExperiences(boost::bind(&LLPanelRegionExperiences::regionCapabilityQuery, region, _1), + boost::bind(&LLPanelRegionExperiences::infoCallback, getDerivedHandle(), _1)); + } - return LLPanelRegionInfo::refreshFromRegion(region); + return LLPanelRegionInfo::refreshFromRegion(region, phase); } LLSD LLPanelRegionExperiences::addIds(LLPanelExperienceListEditor* panel) @@ -4181,35 +4159,32 @@ void LLPanelEstateAccess::copyListToClipboard(std::string list_name) { LLPanelEstateAccess* panel = LLFloaterRegionInfo::getPanelAccess(); if (!panel) return; - LLNameListCtrl* name_list = panel->getChild(list_name); - if (!name_list) return; + LLNameListCtrl* name_list = panel->getChild(list_name); std::vector list_vector = name_list->getAllData(); - if (list_vector.size() == 0) return; + if (list_vector.empty()) + return; LLSD::String list_to_copy; - for (std::vector::const_iterator iter = list_vector.begin(); - iter != list_vector.end(); - iter++) + for (LLScrollListItem* item : list_vector) { - LLScrollListItem *item = (*iter); if (item) { + if (!list_to_copy.empty()) + { + list_to_copy += "\n"; + } list_to_copy += item->getColumn(0)->getValue().asString(); } - if (std::next(iter) != list_vector.end()) - { - list_to_copy += "\n"; - } } LLClipboard::instance().copyToClipboard(utf8str_to_wstring(list_to_copy), 0, static_cast(list_to_copy.length())); } -bool LLPanelEstateAccess::refreshFromRegion(LLViewerRegion* region) +bool LLPanelEstateAccess::refreshFromRegion(LLViewerRegion* region, ERefreshFromRegionPhase phase) { updateLists(); - return LLPanelRegionInfo::refreshFromRegion(region); + return LLPanelRegionInfo::refreshFromRegion(region, phase); } //========================================================================= diff --git a/indra/newview/llfloaterregioninfo.h b/indra/newview/llfloaterregioninfo.h index 65c1291728..119f7c98f3 100644 --- a/indra/newview/llfloaterregioninfo.h +++ b/indra/newview/llfloaterregioninfo.h @@ -71,6 +71,13 @@ class LLPanelRegionEnvironment; class LLEventTimer; +enum class ERefreshFromRegionPhase +{ + NotFromFloaterOpening, + BeforeRequestRegionInfo, + AfterRequestRegionInfo +}; + class LLFloaterRegionInfo : public LLFloater { friend class LLFloaterReg; @@ -85,7 +92,7 @@ public: // get and process region info if necessary. static void processRegionInfo(LLMessageSystem* msg); - static void sRefreshFromRegion(LLViewerRegion* region); + static void refreshFromRegion(LLViewerRegion* region); static const LLUUID& getLastInvoice() { return sRequestInvoice; } static void nextInvoice() { sRequestInvoice.generate(); } @@ -104,7 +111,7 @@ public: void refresh() override; void onRegionChanged(); - void requestRegionInfo(); + void requestRegionInfo(bool is_opening); void enableTopButtons(); void disableTopButtons(); @@ -116,7 +123,7 @@ private: protected: void onTabSelected(const LLSD& param); void disableTabCtrls(); - void refreshFromRegion(LLViewerRegion* region); + void refreshFromRegion(LLViewerRegion* region, ERefreshFromRegionPhase phase); void onGodLevelChange(U8 god_level); // member data @@ -124,6 +131,7 @@ protected: typedef std::vector info_panels_t; info_panels_t mInfoPanels; LLPanelRegionEnvironment *mEnvironmentPanel; + bool mIsRegionInfoRequestedFromOpening { false }; //static S32 sRequestSerial; // serial # of last EstateOwnerRequest static LLUUID sRequestInvoice; @@ -144,7 +152,7 @@ public: void onChangeAnything(); static void onChangeText(LLLineEditor* caller, void* user_data); - virtual bool refreshFromRegion(LLViewerRegion* region); + virtual bool refreshFromRegion(LLViewerRegion* region, ERefreshFromRegionPhase phase); virtual bool estateUpdate(LLMessageSystem* msg) { return true; } bool postBuild() override; @@ -190,7 +198,7 @@ public: : LLPanelRegionInfo() {} ~LLPanelRegionGeneralInfo() {} - bool refreshFromRegion(LLViewerRegion* region) override; + bool refreshFromRegion(LLViewerRegion* region, ERefreshFromRegionPhase phase) override; bool postBuild() override; @@ -222,7 +230,7 @@ public: bool postBuild() override; - bool refreshFromRegion(LLViewerRegion* region) override; + bool refreshFromRegion(LLViewerRegion* region, ERefreshFromRegionPhase phase) override; protected: bool sendUpdate() override; @@ -254,8 +262,8 @@ public: bool postBuild() override; - bool refreshFromRegion(LLViewerRegion* region) override; // refresh local settings from region update from simulator - void setEnvControls(bool available); // Whether environment settings are available for this region + bool refreshFromRegion(LLViewerRegion* region, ERefreshFromRegionPhase phase) override; // refresh local settings from region update from simulator + void setEnvControls(bool available); // Whether environment settings are available for this region bool validateTextureSizes(); bool validateMaterials(); @@ -327,7 +335,7 @@ public: static void updateEstateName(const std::string& name); static void updateEstateOwnerName(const std::string& name); - bool refreshFromRegion(LLViewerRegion* region) override; + bool refreshFromRegion(LLViewerRegion* region, ERefreshFromRegionPhase phase) override; bool estateUpdate(LLMessageSystem* msg) override; bool postBuild() override; @@ -364,7 +372,7 @@ public: bool postBuild() override; void updateChild(LLUICtrl* child_ctrl) override; - bool refreshFromRegion(LLViewerRegion* region) override; + bool refreshFromRegion(LLViewerRegion* region, ERefreshFromRegionPhase phase) override; bool estateUpdate(LLMessageSystem* msg) override; // LLView overrides @@ -430,7 +438,7 @@ public: static void sendEstateExperienceDelta(U32 flags, const LLUUID& agent_id); static void infoCallback(LLHandle handle, const LLSD& content); - bool refreshFromRegion(LLViewerRegion* region) override; + bool refreshFromRegion(LLViewerRegion* region, ERefreshFromRegionPhase phase) override; void sendPurchaseRequest()const; void processResponse( const LLSD& content ); @@ -470,7 +478,7 @@ public: void setPendingUpdate(bool pending) { mPendingUpdate = pending; } bool getPendingUpdate() { return mPendingUpdate; } - bool refreshFromRegion(LLViewerRegion* region) override; + bool refreshFromRegion(LLViewerRegion* region, ERefreshFromRegionPhase phase) override; private: void onClickAddAllowedAgent(); diff --git a/indra/newview/llviewerregion.cpp b/indra/newview/llviewerregion.cpp index 88e5d900cb..739821d2f5 100755 --- a/indra/newview/llviewerregion.cpp +++ b/indra/newview/llviewerregion.cpp @@ -3155,7 +3155,7 @@ void LLViewerRegion::unpackRegionHandshake() std::string cap = getCapability("ModifyRegion"); // needed for queueQuery if (cap.empty()) { - LLFloaterRegionInfo::sRefreshFromRegion(this); + LLFloaterRegionInfo::refreshFromRegion(this); } else { @@ -3167,7 +3167,7 @@ void LLViewerRegion::unpackRegionHandshake() LLVLComposition* compp = region->getComposition(); if (!compp) { return; } compp->apply(composition_changes); - LLFloaterRegionInfo::sRefreshFromRegion(region); + LLFloaterRegionInfo::refreshFromRegion(region); }); } } -- cgit v1.2.3 From e8d33a8b5b06b70b6354672b6d426e82a8aed578 Mon Sep 17 00:00:00 2001 From: Andrey Lihatskiy Date: Sat, 12 Oct 2024 01:49:27 +0300 Subject: Revert "Add toggles to avatar dropdown for hear sound or voice from avatar. (#2518, #2519)" This reverts commit 6af471482d6801530915c1c9ae4bdf788af52eae. --- indra/newview/llagent.cpp | 14 -------------- indra/newview/llagent.h | 7 ------- indra/newview/llviewermenu.cpp | 2 -- indra/newview/skins/default/xui/en/menu_viewer.xml | 19 ------------------- 4 files changed, 42 deletions(-) diff --git a/indra/newview/llagent.cpp b/indra/newview/llagent.cpp index ca35608175..3d4f5e1054 100644 --- a/indra/newview/llagent.cpp +++ b/indra/newview/llagent.cpp @@ -355,20 +355,6 @@ bool LLAgent::isMicrophoneOn(const LLSD& sdname) return LLVoiceClient::getInstance()->getUserPTTState(); } -//static -void LLAgent::toggleHearMediaSoundFromAvatar() -{ - const S32 mediaSoundsEarLocation = gSavedSettings.getS32("MediaSoundsEarLocation"); - gSavedSettings.setS32("MediaSoundsEarLocation", !mediaSoundsEarLocation); -} - -//static -void LLAgent::toggleHearVoiceFromAvatar() -{ - const S32 voiceEarLocation = gSavedSettings.getS32("VoiceEarLocation"); - gSavedSettings.setS32("VoiceEarLocation", !voiceEarLocation); -} - // ************************************************************ // Enabled this definition to compile a 'hacked' viewer that // locally believes the end user has godlike powers. diff --git a/indra/newview/llagent.h b/indra/newview/llagent.h index 448ee575f5..489131d974 100644 --- a/indra/newview/llagent.h +++ b/indra/newview/llagent.h @@ -376,13 +376,6 @@ public: private: bool mVoiceConnected; - //-------------------------------------------------------------------- - // Sound - //-------------------------------------------------------------------- -public: - static void toggleHearMediaSoundFromAvatar(); - static void toggleHearVoiceFromAvatar(); - //-------------------------------------------------------------------- // Chat //-------------------------------------------------------------------- diff --git a/indra/newview/llviewermenu.cpp b/indra/newview/llviewermenu.cpp index 10506e0e74..120a3094cd 100644 --- a/indra/newview/llviewermenu.cpp +++ b/indra/newview/llviewermenu.cpp @@ -9627,8 +9627,6 @@ void initialize_menus() registrar.add("Agent.ToggleMicrophone", boost::bind(&LLAgent::toggleMicrophone, _2), cb_info::UNTRUSTED_BLOCK); enable.add("Agent.IsMicrophoneOn", boost::bind(&LLAgent::isMicrophoneOn, _2)); enable.add("Agent.IsActionAllowed", boost::bind(&LLAgent::isActionAllowed, _2)); - registrar.add("Agent.ToggleHearMediaSoundFromAvatar", boost::bind(&LLAgent::toggleHearMediaSoundFromAvatar), cb_info::UNTRUSTED_BLOCK); - registrar.add("Agent.ToggleHearVoiceFromAvatar", boost::bind(&LLAgent::toggleHearVoiceFromAvatar), cb_info::UNTRUSTED_BLOCK); // File menu init_menu_file(); diff --git a/indra/newview/skins/default/xui/en/menu_viewer.xml b/indra/newview/skins/default/xui/en/menu_viewer.xml index e1d33e5bc3..1c645d8d70 100644 --- a/indra/newview/skins/default/xui/en/menu_viewer.xml +++ b/indra/newview/skins/default/xui/en/menu_viewer.xml @@ -561,25 +561,6 @@ parameter="conversation" /> - - - - - - - - - - Date: Sun, 13 Oct 2024 08:09:51 +0200 Subject: Remove traces of FLTK (#2834) --- indra/llwindow/llwindowsdl.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/indra/llwindow/llwindowsdl.cpp b/indra/llwindow/llwindowsdl.cpp index f87a00c34b..9d736a9970 100644 --- a/indra/llwindow/llwindowsdl.cpp +++ b/indra/llwindow/llwindowsdl.cpp @@ -1369,7 +1369,7 @@ void LLWindowSDL::processMiscNativeEvents() void LLWindowSDL::gatherInput(bool app_has_focus) { - SDL_Event event; + SDL_Event event; // Handle all outstanding SDL events while (SDL_PollEvent(&event)) -- cgit v1.2.3 From 2252f0fc932e9cd3533c72b96bdb032b7ef89da1 Mon Sep 17 00:00:00 2001 From: Ansariel Hiller Date: Sun, 13 Oct 2024 11:01:38 +0200 Subject: Fix time format copy&paste error (#2844) --- .../newview/skins/default/xui/en/floater_inventory_item_properties.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/indra/newview/skins/default/xui/en/floater_inventory_item_properties.xml b/indra/newview/skins/default/xui/en/floater_inventory_item_properties.xml index cc7942abea..1aa216d6b4 100644 --- a/indra/newview/skins/default/xui/en/floater_inventory_item_properties.xml +++ b/indra/newview/skins/default/xui/en/floater_inventory_item_properties.xml @@ -30,7 +30,7 @@ - [wkday,datetime,local] [mth,datetime,local] [day,datetime,local] [hour,datetime,local]:[min,datetime,local]:[second,datetime,local] [ampm,datetime,local] [year,datetime,local] + [wkday,datetime,local] [mth,datetime,local] [day,datetime,local] [hour12,datetime,local]:[min,datetime,local]:[second,datetime,local] [ampm,datetime,local] [year,datetime,local] Date: Mon, 14 Oct 2024 13:17:23 +0300 Subject: #1921 Add setting to hide source info from output in Lua Debug Console --- indra/newview/app_settings/settings.xml | 11 +++++++++++ indra/newview/llfloaterluadebug.cpp | 4 +++- indra/newview/llluamanager.cpp | 9 +++++---- indra/newview/skins/default/xui/en/floater_lua_debug.xml | 11 +++++++++++ 4 files changed, 30 insertions(+), 5 deletions(-) diff --git a/indra/newview/app_settings/settings.xml b/indra/newview/app_settings/settings.xml index d58ff17aaa..b11ba8955a 100644 --- a/indra/newview/app_settings/settings.xml +++ b/indra/newview/app_settings/settings.xml @@ -4122,6 +4122,17 @@ Value + LuaDebugShowSource + + Comment + Show source info in Lua Debug Console output + Persist + 1 + Type + Boolean + Value + 0 + GridStatusRSS Comment diff --git a/indra/newview/llfloaterluadebug.cpp b/indra/newview/llfloaterluadebug.cpp index 06877df816..f7f3b8d588 100644 --- a/indra/newview/llfloaterluadebug.cpp +++ b/indra/newview/llfloaterluadebug.cpp @@ -58,7 +58,9 @@ bool LLFloaterLUADebug::postBuild() .listen("LLFloaterLUADebug", [mResultOutput=mResultOutput](const LLSD& data) { - mResultOutput->pasteTextWithLinebreaks(data.asString()); + LLCachedControl show_source_info(gSavedSettings, "LuaDebugShowSource", false); + std::string source_info = show_source_info ? data["source_info"].asString() : ""; + mResultOutput->pasteTextWithLinebreaks(data["level"].asString() + source_info + data["msg"].asString()); mResultOutput->addLineBreakChar(true); return false; }); diff --git a/indra/newview/llluamanager.cpp b/indra/newview/llluamanager.cpp index 7fe5c1ece0..4a65276384 100644 --- a/indra/newview/llluamanager.cpp +++ b/indra/newview/llluamanager.cpp @@ -72,9 +72,10 @@ std::string lua_print_msg(lua_State* L, std::string_view level) lluau_checkstack(L, 2); luaL_where(L, 1); // start with the 'where' info at the top of the stack - std::ostringstream out; - out << lua_tostring(L, -1); + std::string source_info{ lua_tostring(L, -1) }; lua_pop(L, 1); + + std::ostringstream out; const char* sep = ""; // 'where' info ends with ": " // now iterate over arbitrary args, calling Lua tostring() on each and // concatenating with separators @@ -101,10 +102,10 @@ std::string lua_print_msg(lua_State* L, std::string_view level) // capture message string std::string msg{ out.str() }; // put message out there for any interested party (*koff* LLFloaterLUADebug *koff*) - LLEventPumps::instance().obtain("lua output").post(stringize(level, ": ", msg)); + LLEventPumps::instance().obtain("lua output").post(llsd::map("msg", msg, "level", stringize(level, ": "), "source_info", source_info)); llcoro::suspend(); - return msg; + return source_info + msg; } lua_function(print_debug, "print_debug(args...): DEBUG level logging") diff --git a/indra/newview/skins/default/xui/en/floater_lua_debug.xml b/indra/newview/skins/default/xui/en/floater_lua_debug.xml index 15027f1647..5efe1c958a 100644 --- a/indra/newview/skins/default/xui/en/floater_lua_debug.xml +++ b/indra/newview/skins/default/xui/en/floater_lua_debug.xml @@ -25,6 +25,17 @@ width="100"> LUA string: + Date: Mon, 14 Oct 2024 18:06:04 +0200 Subject: Use correct German date format for German localization (#2845) --- indra/newview/skins/default/xui/de/floater_about_land.xml | 3 ++- indra/newview/skins/default/xui/de/floater_inspect.xml | 5 ++++- indra/newview/skins/default/xui/de/panel_classified_info.xml | 2 +- indra/newview/skins/default/xui/de/panel_landmark_info.xml | 5 ++++- indra/newview/skins/default/xui/de/panel_place_profile.xml | 5 ++++- indra/newview/skins/default/xui/de/panel_profile_classified.xml | 2 +- indra/newview/skins/default/xui/de/panel_profile_secondlife.xml | 6 ++++++ indra/newview/skins/default/xui/de/panel_status_bar.xml | 2 +- indra/newview/skins/default/xui/de/sidepanel_item_info.xml | 8 ++++---- indra/newview/skins/default/xui/de/strings.xml | 8 ++++---- 10 files changed, 31 insertions(+), 15 deletions(-) diff --git a/indra/newview/skins/default/xui/de/floater_about_land.xml b/indra/newview/skins/default/xui/de/floater_about_land.xml index bb9ab26ef5..9c93ff38ad 100644 --- a/indra/newview/skins/default/xui/de/floater_about_land.xml +++ b/indra/newview/skins/default/xui/de/floater_about_land.xml @@ -25,7 +25,8 @@ (keiner) (Wird verkauft) Keine Parzelle ausgewählt. - [wkday,datetime,local] [mth,datetime,local] [day,datetime,local] [hour,datetime,local]:[min,datetime,local]:[second,datetime,local] [year,datetime,local] + [wkday,datetime,local]. [day,datetime,local]. [mth,datetime,local]. [year,datetime,local] [hour12,datetime,local]:[min,datetime,local]:[second,datetime,local] [ampm,datetime,slt] + [wkday,datetime,local]. [day,datetime,local]. [mth,datetime,local]. [year,datetime,local] [hour,datetime,local]:[min,datetime,local]:[second,datetime,local] Name: Beschreibung: Typ: diff --git a/indra/newview/skins/default/xui/de/floater_inspect.xml b/indra/newview/skins/default/xui/de/floater_inspect.xml index da97ceb2d8..a193e1123e 100644 --- a/indra/newview/skins/default/xui/de/floater_inspect.xml +++ b/indra/newview/skins/default/xui/de/floater_inspect.xml @@ -1,7 +1,10 @@ + + [wkday,datetime,local]. [day,datetime,local]. [mth,datetime,local]. [year,datetime,local] [hour12,datetime,local]:[min,datetime,local]:[second,datetime,local] [ampm,datetime,slt] + - [wkday,datetime,local] [mth,datetime,local] [day,datetime,local] [hour,datetime,local]:[min,datetime,local]:[second,datetime,local] [year,datetime,local] + [wkday,datetime,local]. [day,datetime,local]. [mth,datetime,local]. [year,datetime,local] [hour,datetime,local]:[min,datetime,local]:[second,datetime,local] diff --git a/indra/newview/skins/default/xui/de/panel_classified_info.xml b/indra/newview/skins/default/xui/de/panel_classified_info.xml index 007e9d69f0..1b8caf5f78 100644 --- a/indra/newview/skins/default/xui/de/panel_classified_info.xml +++ b/indra/newview/skins/default/xui/de/panel_classified_info.xml @@ -13,7 +13,7 @@ [TELEPORT] teleportieren, [MAP] Karte, [PROFILE] Profil - [mthnum,datetime,slt]/[day,datetime,slt]/[year,datetime,slt] + [day,datetime,slt].[mthnum,datetime,slt].[year,datetime,slt] Aktiviert diff --git a/indra/newview/skins/default/xui/de/panel_landmark_info.xml b/indra/newview/skins/default/xui/de/panel_landmark_info.xml index 10cf34c170..8726d5e645 100644 --- a/indra/newview/skins/default/xui/de/panel_landmark_info.xml +++ b/indra/newview/skins/default/xui/de/panel_landmark_info.xml @@ -15,8 +15,11 @@ Die Informationen über diesen Standort sind zugriffsbeschränkt. Bitte wenden Sie sich bezüglich Ihrer Berechtigungen an den Eigentümer der Parzelle. + + [wkday,datetime,local]. [day,datetime,local]. [mth,datetime,local]. [year,datetime,local] [hour12,datetime,local]:[min,datetime,local]:[second,datetime,local] [ampm,datetime,local] + - [wkday,datetime,local] [mth,datetime,local] [day,datetime,local] [hour,datetime,local]:[min,datetime,local]:[second,datetime,local] [year,datetime,local] + [wkday,datetime,local]. [day,datetime,local]. [mth,datetime,local]. [year,datetime,local] [hour,datetime,local]:[min,datetime,local]:[second,datetime,local]