summaryrefslogtreecommitdiff
path: root/indra
diff options
context:
space:
mode:
Diffstat (limited to 'indra')
-rw-r--r--indra/cmake/00-Common.cmake5
-rw-r--r--indra/linux_crash_logger/linux_crash_logger.cpp14
-rw-r--r--indra/linux_crash_logger/llcrashloggerlinux.cpp2
-rw-r--r--indra/llcommon/indra_constants.h2
-rw-r--r--indra/llcommon/llsys.cpp710
-rw-r--r--indra/llcommon/llsys.h22
-rw-r--r--indra/llcommon/llversionviewer.h2
-rw-r--r--indra/llcrashlogger/llcrashlogger.cpp95
-rw-r--r--indra/llcrashlogger/llcrashlogger.h11
-rw-r--r--indra/llmath/CMakeLists.txt4
-rw-r--r--indra/llmath/llcalc.cpp145
-rw-r--r--indra/llmath/llcalc.h83
-rw-r--r--indra/llmath/llcalcparser.cpp46
-rw-r--r--indra/llmath/llcalcparser.h174
-rw-r--r--indra/llui/lllineeditor.cpp33
-rw-r--r--indra/llui/lllineeditor.h3
-rw-r--r--indra/llui/llspinctrl.cpp23
-rw-r--r--indra/mac_crash_logger/CrashReporter.nib/objects.xib2
-rw-r--r--indra/mac_crash_logger/llcrashloggermac.cpp5
-rw-r--r--indra/mac_crash_logger/mac_crash_logger.cpp15
-rw-r--r--indra/newview/app_settings/cmd_line.xml13
-rw-r--r--indra/newview/app_settings/settings_files.xml5
-rwxr-xr-x[-rw-r--r--]indra/newview/llagent.cpp5
-rw-r--r--indra/newview/llappviewer.cpp89
-rw-r--r--indra/newview/llappviewerlinux.cpp63
-rw-r--r--indra/newview/llappviewerwin32.cpp6
-rw-r--r--indra/newview/llassetuploadresponders.cpp2
-rw-r--r--indra/newview/llassetuploadresponders.h1
-rwxr-xr-xindra/newview/llfloaterpreference.cpp5
-rw-r--r--indra/newview/llpanelface.cpp21
-rw-r--r--indra/newview/llpanelobject.cpp51
-rw-r--r--indra/newview/llpanelwearing.cpp24
-rw-r--r--indra/newview/llpanelwearing.h2
-rw-r--r--indra/newview/lltexlayer.cpp87
-rw-r--r--indra/newview/lltexlayer.h6
-rw-r--r--indra/newview/llviewercontrol.cpp4
-rw-r--r--indra/newview/skins/default/xui/en/menu_wearing_gear.xml7
-rw-r--r--indra/newview/skins/default/xui/en/panel_preferences_setup.xml2
-rw-r--r--indra/win_crash_logger/llcrashloggerwindows.cpp7
-rw-r--r--indra/win_crash_logger/llcrashloggerwindows.h1
-rw-r--r--indra/win_crash_logger/win_crash_logger.cpp35
41 files changed, 1540 insertions, 292 deletions
diff --git a/indra/cmake/00-Common.cmake b/indra/cmake/00-Common.cmake
index 2c974fb4ff..0266239454 100644
--- a/indra/cmake/00-Common.cmake
+++ b/indra/cmake/00-Common.cmake
@@ -171,7 +171,10 @@ if (LINUX)
add_definitions(-fvisibility=hidden)
# don't catch SIGCHLD in our base application class for the viewer - some of our 3rd party libs may need their *own* SIGCHLD handler to work. Sigh! The viewer doesn't need to catch SIGCHLD anyway.
add_definitions(-DLL_IGNORE_SIGCHLD)
- add_definitions(-march=pentium4 -mfpmath=sse)
+ if (WORD_SIZE EQUAL 32)
+ add_definitions(-march=pentium4)
+ endif (WORD_SIZE EQUAL 32)
+ add_definitions(-mfpmath=sse)
#add_definitions(-ftree-vectorize) # THIS CRASHES GCC 3.1-3.2
if (NOT STANDALONE)
# this stops us requiring a really recent glibc at runtime
diff --git a/indra/linux_crash_logger/linux_crash_logger.cpp b/indra/linux_crash_logger/linux_crash_logger.cpp
index 8beae555fb..99d0ad7e14 100644
--- a/indra/linux_crash_logger/linux_crash_logger.cpp
+++ b/indra/linux_crash_logger/linux_crash_logger.cpp
@@ -24,16 +24,24 @@
* $/LicenseInfo$
*/
+#include "linden_common.h"
#include "llcrashloggerlinux.h"
int main(int argc, char **argv)
{
+ llinfos << "Starting crash reporter." << llendl;
+
LLCrashLoggerLinux app;
app.parseCommandOptions(argc, argv);
- app.init();
+
+ if (! app.init())
+ {
+ llwarns << "Unable to initialize application." << llendl;
+ return 1;
+ }
+
app.mainLoop();
app.cleanup();
+ llinfos << "Crash reporter finished normally." << llendl;
return 0;
}
-
-
diff --git a/indra/linux_crash_logger/llcrashloggerlinux.cpp b/indra/linux_crash_logger/llcrashloggerlinux.cpp
index 7449c6426f..7316717193 100644
--- a/indra/linux_crash_logger/llcrashloggerlinux.cpp
+++ b/indra/linux_crash_logger/llcrashloggerlinux.cpp
@@ -30,8 +30,6 @@
#include "linden_common.h"
-#include "boost/tokenizer.hpp"
-
#include "indra_constants.h" // CRASH_BEHAVIOR_ASK, CRASH_SETTING_NAME
#include "llerror.h"
#include "llfile.h"
diff --git a/indra/llcommon/indra_constants.h b/indra/llcommon/indra_constants.h
index d0f287657e..0745696ef3 100644
--- a/indra/llcommon/indra_constants.h
+++ b/indra/llcommon/indra_constants.h
@@ -387,8 +387,6 @@ const S32 MAP_SIM_RETURN_NULL_SIMS = 0x00010000;
const S32 MAP_SIM_PRELUDE = 0x00020000;
// Crash reporter behavior
-const char* const CRASH_SETTINGS_FILE = "settings_crash_behavior.xml";
-const char* const CRASH_BEHAVIOR_SETTING = "CrashSubmitBehavior";
const S32 CRASH_BEHAVIOR_ASK = 0;
const S32 CRASH_BEHAVIOR_ALWAYS_SEND = 1;
const S32 CRASH_BEHAVIOR_NEVER_SEND = 2;
diff --git a/indra/llcommon/llsys.cpp b/indra/llcommon/llsys.cpp
index e8616a9be6..99e61433c6 100644
--- a/indra/llcommon/llsys.cpp
+++ b/indra/llcommon/llsys.cpp
@@ -1,6 +1,6 @@
/**
* @file llsys.cpp
- * @brief Impelementation of the basic system query functions.
+ * @brief Implementation of the basic system query functions.
*
* $LicenseInfo:firstyear=2002&license=viewerlgpl$
* Second Life Viewer Source Code
@@ -24,6 +24,10 @@
* $/LicenseInfo$
*/
+#if LL_WINDOWS
+#pragma warning (disable : 4355) // 'this' used in initializer list: yes, intentionally
+#endif
+
#include "linden_common.h"
#include "llsys.h"
@@ -36,22 +40,43 @@
#endif
#include "llprocessor.h"
+#include "llerrorcontrol.h"
+#include "llevents.h"
+#include "lltimer.h"
+#include "llsdserialize.h"
+#include "llsdutil.h"
+#include <boost/bind.hpp>
+#include <boost/circular_buffer.hpp>
+#include <boost/regex.hpp>
+#include <boost/foreach.hpp>
+#include <boost/lexical_cast.hpp>
+#include <boost/range.hpp>
+#include <boost/utility/enable_if.hpp>
+#include <boost/type_traits/is_integral.hpp>
+#include <boost/type_traits/is_float.hpp>
+
+using namespace llsd;
#if LL_WINDOWS
# define WIN32_LEAN_AND_MEAN
# include <winsock2.h>
# include <windows.h>
+# include <psapi.h> // GetPerformanceInfo() et al.
#elif LL_DARWIN
# include <errno.h>
# include <sys/sysctl.h>
# include <sys/utsname.h>
# include <stdint.h>
# include <Carbon/Carbon.h>
+# include <sys/wait.h>
+# include <string.h>
+# include <stdexcept>
#elif LL_LINUX
# include <errno.h>
# include <sys/utsname.h>
# include <unistd.h>
# include <sys/sysinfo.h>
+# include <stdexcept>
const char MEMINFO_FILE[] = "/proc/meminfo";
#elif LL_SOLARIS
# include <stdio.h>
@@ -70,6 +95,15 @@ extern int errno;
static const S32 CPUINFO_BUFFER_SIZE = 16383;
LLCPUInfo gSysCPU;
+// Don't log memory info any more often than this. It also serves as our
+// framerate sample size.
+static const F32 MEM_INFO_THROTTLE = 20;
+// Sliding window of samples. We intentionally limit the length of time we
+// remember "the slowest" framerate because framerate is very slow at login.
+// If we only triggered FrameWatcher logging when the session framerate
+// dropped below the login framerate, we'd have very little additional data.
+static const F32 MEM_INFO_WINDOW = 10*60;
+
#if LL_WINDOWS
#ifndef DLLVERSIONINFO
typedef struct _DllVersionInfo
@@ -613,8 +647,78 @@ void LLCPUInfo::stream(std::ostream& s) const
s << "->mCPUString: " << mCPUString << std::endl;
}
+// Helper class for LLMemoryInfo: accumulate stats in the form we store for
+// LLMemoryInfo::getStatsMap().
+class Stats
+{
+public:
+ Stats():
+ mStats(LLSD::emptyMap())
+ {}
+
+ // Store every integer type as LLSD::Integer.
+ template <class T>
+ void add(const LLSD::String& name, const T& value,
+ typename boost::enable_if<boost::is_integral<T> >::type* = 0)
+ {
+ mStats[name] = LLSD::Integer(value);
+ }
+
+ // Store every floating-point type as LLSD::Real.
+ template <class T>
+ void add(const LLSD::String& name, const T& value,
+ typename boost::enable_if<boost::is_float<T> >::type* = 0)
+ {
+ mStats[name] = LLSD::Real(value);
+ }
+
+ // Hope that LLSD::Date values are sufficiently unambiguous.
+ void add(const LLSD::String& name, const LLSD::Date& value)
+ {
+ mStats[name] = value;
+ }
+
+ LLSD get() const { return mStats; }
+
+private:
+ LLSD mStats;
+};
+
+// Wrap boost::regex_match() with a function that doesn't throw.
+template <typename S, typename M, typename R>
+static bool regex_match_no_exc(const S& string, M& match, const R& regex)
+{
+ try
+ {
+ return boost::regex_match(string, match, regex);
+ }
+ catch (const std::runtime_error& e)
+ {
+ LL_WARNS("LLMemoryInfo") << "error matching with '" << regex.str() << "': "
+ << e.what() << ":\n'" << string << "'" << LL_ENDL;
+ return false;
+ }
+}
+
+// Wrap boost::regex_search() with a function that doesn't throw.
+template <typename S, typename M, typename R>
+static bool regex_search_no_exc(const S& string, M& match, const R& regex)
+{
+ try
+ {
+ return boost::regex_search(string, match, regex);
+ }
+ catch (const std::runtime_error& e)
+ {
+ LL_WARNS("LLMemoryInfo") << "error searching with '" << regex.str() << "': "
+ << e.what() << ":\n'" << string << "'" << LL_ENDL;
+ return false;
+ }
+}
+
LLMemoryInfo::LLMemoryInfo()
{
+ refresh();
}
#if LL_WINDOWS
@@ -638,11 +742,7 @@ static U32 LLMemoryAdjustKBResult(U32 inKB)
U32 LLMemoryInfo::getPhysicalMemoryKB() const
{
#if LL_WINDOWS
- MEMORYSTATUSEX state;
- state.dwLength = sizeof(state);
- GlobalMemoryStatusEx(&state);
-
- return LLMemoryAdjustKBResult((U32)(state.ullTotalPhys >> 10));
+ return LLMemoryAdjustKBResult(mStatsMap["Total Physical KB"].asInteger());
#elif LL_DARWIN
// This might work on Linux as well. Someone check...
@@ -690,12 +790,82 @@ U32 LLMemoryInfo::getPhysicalMemoryClamped() const
void LLMemoryInfo::getAvailableMemoryKB(U32& avail_physical_mem_kb, U32& avail_virtual_mem_kb)
{
#if LL_WINDOWS
- MEMORYSTATUSEX state;
- state.dwLength = sizeof(state);
- GlobalMemoryStatusEx(&state);
+ // Sigh, this shouldn't be a static method, then we wouldn't have to
+ // reload this data separately from refresh()
+ LLSD statsMap(loadStatsMap());
+
+ avail_physical_mem_kb = statsMap["Avail Physical KB"].asInteger();
+ avail_virtual_mem_kb = statsMap["Avail Virtual KB"].asInteger();
+
+#elif LL_DARWIN
+ // mStatsMap is derived from vm_stat, look for (e.g.) "kb free":
+ // $ vm_stat
+ // Mach Virtual Memory Statistics: (page size of 4096 bytes)
+ // Pages free: 462078.
+ // Pages active: 142010.
+ // Pages inactive: 220007.
+ // Pages wired down: 159552.
+ // "Translation faults": 220825184.
+ // Pages copy-on-write: 2104153.
+ // Pages zero filled: 167034876.
+ // Pages reactivated: 65153.
+ // Pageins: 2097212.
+ // Pageouts: 41759.
+ // Object cache: 841598 hits of 7629869 lookups (11% hit rate)
+ avail_physical_mem_kb = -1 ;
+ avail_virtual_mem_kb = -1 ;
- avail_physical_mem_kb = (U32)(state.ullAvailPhys/1024) ;
- avail_virtual_mem_kb = (U32)(state.ullAvailVirtual/1024) ;
+#elif LL_LINUX
+ // mStatsMap is derived from MEMINFO_FILE:
+ // $ cat /proc/meminfo
+ // MemTotal: 4108424 kB
+ // MemFree: 1244064 kB
+ // Buffers: 85164 kB
+ // Cached: 1990264 kB
+ // SwapCached: 0 kB
+ // Active: 1176648 kB
+ // Inactive: 1427532 kB
+ // Active(anon): 529152 kB
+ // Inactive(anon): 15924 kB
+ // Active(file): 647496 kB
+ // Inactive(file): 1411608 kB
+ // Unevictable: 16 kB
+ // Mlocked: 16 kB
+ // HighTotal: 3266316 kB
+ // HighFree: 721308 kB
+ // LowTotal: 842108 kB
+ // LowFree: 522756 kB
+ // SwapTotal: 6384632 kB
+ // SwapFree: 6384632 kB
+ // Dirty: 28 kB
+ // Writeback: 0 kB
+ // AnonPages: 528820 kB
+ // Mapped: 89472 kB
+ // Shmem: 16324 kB
+ // Slab: 159624 kB
+ // SReclaimable: 145168 kB
+ // SUnreclaim: 14456 kB
+ // KernelStack: 2560 kB
+ // PageTables: 5560 kB
+ // NFS_Unstable: 0 kB
+ // Bounce: 0 kB
+ // WritebackTmp: 0 kB
+ // CommitLimit: 8438844 kB
+ // Committed_AS: 1271596 kB
+ // VmallocTotal: 122880 kB
+ // VmallocUsed: 65252 kB
+ // VmallocChunk: 52356 kB
+ // HardwareCorrupted: 0 kB
+ // HugePages_Total: 0
+ // HugePages_Free: 0
+ // HugePages_Rsvd: 0
+ // HugePages_Surp: 0
+ // Hugepagesize: 2048 kB
+ // DirectMap4k: 434168 kB
+ // DirectMap2M: 477184 kB
+ // (could also run 'free', but easier to read a file than run a program)
+ avail_physical_mem_kb = -1 ;
+ avail_virtual_mem_kb = -1 ;
#else
//do not know how to collect available memory info for other systems.
@@ -708,56 +878,389 @@ void LLMemoryInfo::getAvailableMemoryKB(U32& avail_physical_mem_kb, U32& avail_v
void LLMemoryInfo::stream(std::ostream& s) const
{
+ // We want these memory stats to be easy to grep from the log, along with
+ // the timestamp. So preface each line with the timestamp and a
+ // distinctive marker. Without that, we'd have to search the log for the
+ // introducer line, then read subsequent lines, etc...
+ std::string pfx(LLError::utcTime() + " <mem> ");
+
+ // Max key length
+ size_t key_width(0);
+ BOOST_FOREACH(const MapEntry& pair, inMap(mStatsMap))
+ {
+ size_t len(pair.first.length());
+ if (len > key_width)
+ {
+ key_width = len;
+ }
+ }
+
+ // Now stream stats
+ BOOST_FOREACH(const MapEntry& pair, inMap(mStatsMap))
+ {
+ s << pfx << std::setw(key_width+1) << (pair.first + ':') << ' ';
+ LLSD value(pair.second);
+ if (value.isInteger())
+ s << std::setw(12) << value.asInteger();
+ else if (value.isReal())
+ s << std::fixed << std::setprecision(1) << value.asReal();
+ else if (value.isDate())
+ value.asDate().toStream(s);
+ else
+ s << value; // just use default LLSD formatting
+ s << std::endl;
+ }
+}
+
+LLSD LLMemoryInfo::getStatsMap() const
+{
+ return mStatsMap;
+}
+
+LLMemoryInfo& LLMemoryInfo::refresh()
+{
+ mStatsMap = loadStatsMap();
+
+ LL_DEBUGS("LLMemoryInfo") << "Populated mStatsMap:\n";
+ LLSDSerialize::toPrettyXML(mStatsMap, LL_CONT);
+ LL_ENDL;
+
+ return *this;
+}
+
+LLSD LLMemoryInfo::loadStatsMap()
+{
+ // This implementation is derived from stream() code (as of 2011-06-29).
+ Stats stats;
+
+ // associate timestamp for analysis over time
+ stats.add("timestamp", LLDate::now());
+
#if LL_WINDOWS
MEMORYSTATUSEX state;
state.dwLength = sizeof(state);
GlobalMemoryStatusEx(&state);
- s << "Percent Memory use: " << (U32)state.dwMemoryLoad << '%' << std::endl;
- s << "Total Physical KB: " << (U32)(state.ullTotalPhys/1024) << std::endl;
- s << "Avail Physical KB: " << (U32)(state.ullAvailPhys/1024) << std::endl;
- s << "Total page KB: " << (U32)(state.ullTotalPageFile/1024) << std::endl;
- s << "Avail page KB: " << (U32)(state.ullAvailPageFile/1024) << std::endl;
- s << "Total Virtual KB: " << (U32)(state.ullTotalVirtual/1024) << std::endl;
- s << "Avail Virtual KB: " << (U32)(state.ullAvailVirtual/1024) << std::endl;
+ stats.add("Percent Memory use", state.dwMemoryLoad);
+ stats.add("Total Physical KB", state.ullTotalPhys/1024);
+ stats.add("Avail Physical KB", state.ullAvailPhys/1024);
+ stats.add("Total page KB", state.ullTotalPageFile/1024);
+ stats.add("Avail page KB", state.ullAvailPageFile/1024);
+ stats.add("Total Virtual KB", state.ullTotalVirtual/1024);
+ stats.add("Avail Virtual KB", state.ullAvailVirtual/1024);
+
+ PERFORMANCE_INFORMATION perf;
+ perf.cb = sizeof(perf);
+ GetPerformanceInfo(&perf, sizeof(perf));
+
+ SIZE_T pagekb(perf.PageSize/1024);
+ stats.add("CommitTotal KB", perf.CommitTotal * pagekb);
+ stats.add("CommitLimit KB", perf.CommitLimit * pagekb);
+ stats.add("CommitPeak KB", perf.CommitPeak * pagekb);
+ stats.add("PhysicalTotal KB", perf.PhysicalTotal * pagekb);
+ stats.add("PhysicalAvail KB", perf.PhysicalAvailable * pagekb);
+ stats.add("SystemCache KB", perf.SystemCache * pagekb);
+ stats.add("KernelTotal KB", perf.KernelTotal * pagekb);
+ stats.add("KernelPaged KB", perf.KernelPaged * pagekb);
+ stats.add("KernelNonpaged KB", perf.KernelNonpaged * pagekb);
+ stats.add("PageSize KB", pagekb);
+ stats.add("HandleCount", perf.HandleCount);
+ stats.add("ProcessCount", perf.ProcessCount);
+ stats.add("ThreadCount", perf.ThreadCount);
+
+ PROCESS_MEMORY_COUNTERS_EX pmem;
+ pmem.cb = sizeof(pmem);
+ // GetProcessMemoryInfo() is documented to accept either
+ // PROCESS_MEMORY_COUNTERS* or PROCESS_MEMORY_COUNTERS_EX*, presumably
+ // using the redundant size info to distinguish. But its prototype
+ // specifically accepts PROCESS_MEMORY_COUNTERS*, and since this is a
+ // classic-C API, PROCESS_MEMORY_COUNTERS_EX isn't a subclass. Cast the
+ // pointer.
+ GetProcessMemoryInfo(GetCurrentProcess(), PPROCESS_MEMORY_COUNTERS(&pmem), sizeof(pmem));
+
+ stats.add("Page Fault Count", pmem.PageFaultCount);
+ stats.add("PeakWorkingSetSize KB", pmem.PeakWorkingSetSize/1024);
+ stats.add("WorkingSetSize KB", pmem.WorkingSetSize/1024);
+ stats.add("QutaPeakPagedPoolUsage KB", pmem.QuotaPeakPagedPoolUsage/1024);
+ stats.add("QuotaPagedPoolUsage KB", pmem.QuotaPagedPoolUsage/1024);
+ stats.add("QuotaPeakNonPagedPoolUsage KB", pmem.QuotaPeakNonPagedPoolUsage/1024);
+ stats.add("QuotaNonPagedPoolUsage KB", pmem.QuotaNonPagedPoolUsage/1024);
+ stats.add("PagefileUsage KB", pmem.PagefileUsage/1024);
+ stats.add("PeakPagefileUsage KB", pmem.PeakPagefileUsage/1024);
+ stats.add("PrivateUsage KB", pmem.PrivateUsage/1024);
+
#elif LL_DARWIN
uint64_t phys = 0;
size_t len = sizeof(phys);
- if(sysctlbyname("hw.memsize", &phys, &len, NULL, 0) == 0)
+ if (sysctlbyname("hw.memsize", &phys, &len, NULL, 0) == 0)
{
- s << "Total Physical KB: " << phys/1024 << std::endl;
+ stats.add("Total Physical KB", phys/1024);
}
else
{
- s << "Unable to collect memory information";
+ LL_WARNS("LLMemoryInfo") << "Unable to collect hw.memsize memory information" << LL_ENDL;
+ }
+
+ FILE* pout = popen("vm_stat 2>&1", "r");
+ if (! pout) // popen() couldn't run vm_stat
+ {
+ // Save errno right away.
+ int popen_errno(errno);
+ LL_WARNS("LLMemoryInfo") << "Unable to collect vm_stat memory information: ";
+ char buffer[256];
+ if (0 == strerror_r(popen_errno, buffer, sizeof(buffer)))
+ {
+ LL_CONT << buffer;
+ }
+ else
+ {
+ LL_CONT << "errno " << popen_errno;
+ }
+ LL_CONT << LL_ENDL;
+ }
+ else // popen() launched vm_stat
+ {
+ // Mach Virtual Memory Statistics: (page size of 4096 bytes)
+ // Pages free: 462078.
+ // Pages active: 142010.
+ // Pages inactive: 220007.
+ // Pages wired down: 159552.
+ // "Translation faults": 220825184.
+ // Pages copy-on-write: 2104153.
+ // Pages zero filled: 167034876.
+ // Pages reactivated: 65153.
+ // Pageins: 2097212.
+ // Pageouts: 41759.
+ // Object cache: 841598 hits of 7629869 lookups (11% hit rate)
+
+ // Intentionally don't pass the boost::no_except flag. These
+ // boost::regex objects are constructed with string literals, so they
+ // should be valid every time. If they become invalid, we WANT an
+ // exception, hopefully even before the dev checks in.
+ boost::regex pagesize_rx("\\(page size of ([0-9]+) bytes\\)");
+ boost::regex stat_rx("(.+): +([0-9]+)\\.");
+ boost::regex cache_rx("Object cache: ([0-9]+) hits of ([0-9]+) lookups "
+ "\\(([0-9]+)% hit rate\\)");
+ boost::cmatch matched;
+ LLSD::Integer pagesizekb(4096/1024);
+
+ // Here 'pout' is vm_stat's stdout. Search it for relevant data.
+ char line[100];
+ line[sizeof(line)-1] = '\0';
+ while (fgets(line, sizeof(line)-1, pout))
+ {
+ size_t linelen(strlen(line));
+ // Truncate any trailing newline
+ if (line[linelen - 1] == '\n')
+ {
+ line[--linelen] = '\0';
+ }
+ LL_DEBUGS("LLMemoryInfo") << line << LL_ENDL;
+ if (regex_search_no_exc(line, matched, pagesize_rx))
+ {
+ // "Mach Virtual Memory Statistics: (page size of 4096 bytes)"
+ std::string pagesize_str(matched[1].first, matched[1].second);
+ try
+ {
+ // Reasonable to assume that pagesize will always be a
+ // multiple of 1Kb?
+ pagesizekb = boost::lexical_cast<LLSD::Integer>(pagesize_str)/1024;
+ }
+ catch (const boost::bad_lexical_cast&)
+ {
+ LL_WARNS("LLMemoryInfo") << "couldn't parse '" << pagesize_str
+ << "' in vm_stat line: " << line << LL_ENDL;
+ continue;
+ }
+ stats.add("page size", pagesizekb);
+ }
+ else if (regex_match_no_exc(line, matched, stat_rx))
+ {
+ // e.g. "Pages free: 462078."
+ // Strip double-quotes off certain statistic names
+ const char *key_begin(matched[1].first), *key_end(matched[1].second);
+ if (key_begin[0] == '"' && key_end[-1] == '"')
+ {
+ ++key_begin;
+ --key_end;
+ }
+ LLSD::String key(key_begin, key_end);
+ LLSD::String value_str(matched[2].first, matched[2].second);
+ LLSD::Integer value(0);
+ try
+ {
+ value = boost::lexical_cast<LLSD::Integer>(value_str);
+ }
+ catch (const boost::bad_lexical_cast&)
+ {
+ LL_WARNS("LLMemoryInfo") << "couldn't parse '" << value_str
+ << "' in vm_stat line: " << line << LL_ENDL;
+ continue;
+ }
+ // Store this statistic.
+ stats.add(key, value);
+ // Is this in units of pages? If so, convert to Kb.
+ static const LLSD::String pages("Pages ");
+ if (key.substr(0, pages.length()) == pages)
+ {
+ // Synthesize a new key with kb in place of Pages
+ LLSD::String kbkey("kb ");
+ kbkey.append(key.substr(pages.length()));
+ stats.add(kbkey, value * pagesizekb);
+ }
+ }
+ else if (regex_match_no_exc(line, matched, cache_rx))
+ {
+ // e.g. "Object cache: 841598 hits of 7629869 lookups (11% hit rate)"
+ static const char* cache_keys[] = { "cache hits", "cache lookups", "cache hit%" };
+ std::vector<LLSD::Integer> cache_values;
+ for (size_t i = 0; i < (sizeof(cache_keys)/sizeof(cache_keys[0])); ++i)
+ {
+ LLSD::String value_str(matched[i+1].first, matched[i+1].second);
+ LLSD::Integer value(0);
+ try
+ {
+ value = boost::lexical_cast<LLSD::Integer>(value_str);
+ }
+ catch (boost::bad_lexical_cast&)
+ {
+ LL_WARNS("LLMemoryInfo") << "couldn't parse '" << value_str
+ << "' in vm_stat line: " << line << LL_ENDL;
+ continue;
+ }
+ stats.add(cache_keys[i], value);
+ }
+ }
+ else
+ {
+ LL_WARNS("LLMemoryInfo") << "unrecognized vm_stat line: " << line << LL_ENDL;
+ }
+ }
+ int status(pclose(pout));
+ if (status == -1) // pclose() couldn't retrieve rc
+ {
+ // Save errno right away.
+ int pclose_errno(errno);
+ // The ECHILD error happens so frequently that unless filtered,
+ // the warning below spams the log file. This is too bad, because
+ // sometimes the logic above fails to produce any output derived
+ // from vm_stat, but we've been unable to observe any specific
+ // error indicating the problem.
+ if (pclose_errno != ECHILD)
+ {
+ LL_WARNS("LLMemoryInfo") << "Unable to obtain vm_stat termination code: ";
+ char buffer[256];
+ if (0 == strerror_r(pclose_errno, buffer, sizeof(buffer)))
+ {
+ LL_CONT << buffer;
+ }
+ else
+ {
+ LL_CONT << "errno " << pclose_errno;
+ }
+ LL_CONT << LL_ENDL;
+ }
+ }
+ else // pclose() retrieved rc; analyze
+ {
+ if (WIFEXITED(status))
+ {
+ int rc(WEXITSTATUS(status));
+ if (rc != 0)
+ {
+ LL_WARNS("LLMemoryInfo") << "vm_stat terminated with rc " << rc << LL_ENDL;
+ }
+ }
+ else if (WIFSIGNALED(status))
+ {
+ LL_WARNS("LLMemoryInfo") << "vm_stat terminated by signal " << WTERMSIG(status)
+ << LL_ENDL;
+ }
+ }
}
+
#elif LL_SOLARIS
- U64 phys = 0;
+ U64 phys = 0;
- phys = (U64)(sysconf(_SC_PHYS_PAGES)) * (U64)(sysconf(_SC_PAGESIZE)/1024);
+ phys = (U64)(sysconf(_SC_PHYS_PAGES)) * (U64)(sysconf(_SC_PAGESIZE)/1024);
- s << "Total Physical KB: " << phys << std::endl;
-#else
- // *NOTE: This works on linux. What will it do on other systems?
- LLFILE* meminfo = LLFile::fopen(MEMINFO_FILE,"rb");
- if(meminfo)
+ stats.add("Total Physical KB", phys);
+
+#elif LL_LINUX
+ std::ifstream meminfo(MEMINFO_FILE);
+ if (meminfo.is_open())
{
- char line[MAX_STRING]; /* Flawfinder: ignore */
- memset(line, 0, MAX_STRING);
- while(fgets(line, MAX_STRING, meminfo))
+ // MemTotal: 4108424 kB
+ // MemFree: 1244064 kB
+ // Buffers: 85164 kB
+ // Cached: 1990264 kB
+ // SwapCached: 0 kB
+ // Active: 1176648 kB
+ // Inactive: 1427532 kB
+ // ...
+ // VmallocTotal: 122880 kB
+ // VmallocUsed: 65252 kB
+ // VmallocChunk: 52356 kB
+ // HardwareCorrupted: 0 kB
+ // HugePages_Total: 0
+ // HugePages_Free: 0
+ // HugePages_Rsvd: 0
+ // HugePages_Surp: 0
+ // Hugepagesize: 2048 kB
+ // DirectMap4k: 434168 kB
+ // DirectMap2M: 477184 kB
+
+ // Intentionally don't pass the boost::no_except flag. This
+ // boost::regex object is constructed with a string literal, so it
+ // should be valid every time. If it becomes invalid, we WANT an
+ // exception, hopefully even before the dev checks in.
+ boost::regex stat_rx("(.+): +([0-9]+)( kB)?");
+ boost::smatch matched;
+
+ std::string line;
+ while (std::getline(meminfo, line))
{
- line[strlen(line)-1] = ' '; /*Flawfinder: ignore*/
- s << line;
+ LL_DEBUGS("LLMemoryInfo") << line << LL_ENDL;
+ if (regex_match_no_exc(line, matched, stat_rx))
+ {
+ // e.g. "MemTotal: 4108424 kB"
+ LLSD::String key(matched[1].first, matched[1].second);
+ LLSD::String value_str(matched[2].first, matched[2].second);
+ LLSD::Integer value(0);
+ try
+ {
+ value = boost::lexical_cast<LLSD::Integer>(value_str);
+ }
+ catch (const boost::bad_lexical_cast&)
+ {
+ LL_WARNS("LLMemoryInfo") << "couldn't parse '" << value_str
+ << "' in " << MEMINFO_FILE << " line: "
+ << line << LL_ENDL;
+ continue;
+ }
+ // Store this statistic.
+ stats.add(key, value);
+ }
+ else
+ {
+ LL_WARNS("LLMemoryInfo") << "unrecognized " << MEMINFO_FILE << " line: "
+ << line << LL_ENDL;
+ }
}
- fclose(meminfo);
}
else
{
- s << "Unable to collect memory information";
+ LL_WARNS("LLMemoryInfo") << "Unable to collect memory information" << LL_ENDL;
}
+
+#else
+ LL_WARNS("LLMemoryInfo") << "Unknown system; unable to collect memory information" << LL_ENDL;
+
#endif
+
+ return stats.get();
}
std::ostream& operator<<(std::ostream& s, const LLOSInfo& info)
@@ -778,6 +1281,143 @@ std::ostream& operator<<(std::ostream& s, const LLMemoryInfo& info)
return s;
}
+class FrameWatcher
+{
+public:
+ FrameWatcher():
+ // Hooking onto the "mainloop" event pump gets us one call per frame.
+ mConnection(LLEventPumps::instance()
+ .obtain("mainloop")
+ .listen("FrameWatcher", boost::bind(&FrameWatcher::tick, this, _1))),
+ // Initializing mSampleStart to an invalid timestamp alerts us to skip
+ // trying to compute framerate on the first call.
+ mSampleStart(-1),
+ // Initializing mSampleEnd to 0 ensures that we treat the first call
+ // as the completion of a sample window.
+ mSampleEnd(0),
+ mFrames(0),
+ // Both MEM_INFO_WINDOW and MEM_INFO_THROTTLE are in seconds. We need
+ // the number of integer MEM_INFO_THROTTLE sample slots that will fit
+ // in MEM_INFO_WINDOW. Round up.
+ mSamples(int((MEM_INFO_WINDOW / MEM_INFO_THROTTLE) + 0.7)),
+ // Initializing to F32_MAX means that the first real frame will become
+ // the slowest ever, which sounds like a good idea.
+ mSlowest(F32_MAX)
+ {}
+
+ bool tick(const LLSD&)
+ {
+ F32 timestamp(mTimer.getElapsedTimeF32());
+
+ // Count this frame in the interval just completed.
+ ++mFrames;
+
+ // Have we finished a sample window yet?
+ if (timestamp < mSampleEnd)
+ {
+ // no, just keep waiting
+ return false;
+ }
+
+ // Set up for next sample window. Capture values for previous frame in
+ // local variables and reset data members.
+ U32 frames(mFrames);
+ F32 sampleStart(mSampleStart);
+ // No frames yet in next window
+ mFrames = 0;
+ // which starts right now
+ mSampleStart = timestamp;
+ // and ends MEM_INFO_THROTTLE seconds in the future
+ mSampleEnd = mSampleStart + MEM_INFO_THROTTLE;
+
+ // On the very first call, that's all we can do, no framerate
+ // computation is possible.
+ if (sampleStart < 0)
+ {
+ return false;
+ }
+
+ // How long did this actually take? As framerate slows, the duration
+ // of the frame we just finished could push us WELL beyond our desired
+ // sample window size.
+ F32 elapsed(timestamp - sampleStart);
+ F32 framerate(frames/elapsed);
+
+ // Remember previous slowest framerate because we're just about to
+ // update it.
+ F32 slowest(mSlowest);
+ // Remember previous number of samples.
+ boost::circular_buffer<F32>::size_type prevSize(mSamples.size());
+
+ // Capture new framerate in our samples buffer. Once the buffer is
+ // full (after MEM_INFO_WINDOW seconds), this will displace the oldest
+ // sample. ("So they all rolled over, and one fell out...")
+ mSamples.push_back(framerate);
+
+ // Calculate the new minimum framerate. I know of no way to update a
+ // rolling minimum without ever rescanning the buffer. But since there
+ // are only a few tens of items in this buffer, rescanning it is
+ // probably cheaper (and certainly easier to reason about) than
+ // attempting to optimize away some of the scans.
+ mSlowest = framerate; // pick an arbitrary entry to start
+ for (boost::circular_buffer<F32>::const_iterator si(mSamples.begin()), send(mSamples.end());
+ si != send; ++si)
+ {
+ if (*si < mSlowest)
+ {
+ mSlowest = *si;
+ }
+ }
+
+ // We're especially interested in memory as framerate drops. Only log
+ // when framerate drops below the slowest framerate we remember.
+ // (Should always be true for the end of the very first sample
+ // window.)
+ if (framerate >= slowest)
+ {
+ return false;
+ }
+ // Congratulations, we've hit a new low. :-P
+
+ LL_INFOS("FrameWatcher") << ' ';
+ if (! prevSize)
+ {
+ LL_CONT << "initial framerate ";
+ }
+ else
+ {
+ LL_CONT << "slowest framerate for last " << int(prevSize * MEM_INFO_THROTTLE)
+ << " seconds ";
+ }
+ LL_CONT << std::fixed << std::setprecision(1) << framerate << '\n'
+ << LLMemoryInfo() << LL_ENDL;
+
+ return false;
+ }
+
+private:
+ // Storing the connection in an LLTempBoundListener ensures it will be
+ // disconnected when we're destroyed.
+ LLTempBoundListener mConnection;
+ // Track elapsed time
+ LLTimer mTimer;
+ // Some of what you see here is in fact redundant with functionality you
+ // can get from LLTimer. Unfortunately the LLTimer API is missing the
+ // feature we need: has at least the stated interval elapsed, and if so,
+ // exactly how long has passed? So we have to do it by hand, sigh.
+ // Time at start, end of sample window
+ F32 mSampleStart, mSampleEnd;
+ // Frames this sample window
+ U32 mFrames;
+ // Sliding window of framerate samples
+ boost::circular_buffer<F32> mSamples;
+ // Slowest framerate in mSamples
+ F32 mSlowest;
+};
+
+// Need an instance of FrameWatcher before it does any good
+static FrameWatcher sFrameWatcher;
+
BOOL gunzip_file(const std::string& srcfile, const std::string& dstfile)
{
std::string tmpfile;
diff --git a/indra/llcommon/llsys.h b/indra/llcommon/llsys.h
index 41a4f25000..739e795d3a 100644
--- a/indra/llcommon/llsys.h
+++ b/indra/llcommon/llsys.h
@@ -36,6 +36,7 @@
// llinfos << info << llendl;
//
+#include "llsd.h"
#include <iosfwd>
#include <string>
@@ -117,6 +118,27 @@ public:
//get the available memory infomation in KiloBytes.
static void getAvailableMemoryKB(U32& avail_physical_mem_kb, U32& avail_virtual_mem_kb);
+
+ // Retrieve a map of memory statistics. The keys of the map are platform-
+ // dependent. The values are in kilobytes to try to avoid integer overflow.
+ LLSD getStatsMap() const;
+
+ // Re-fetch memory data (as reported by stream() and getStatsMap()) from the
+ // system. Normally this is fetched at construction time. Return (*this)
+ // to permit usage of the form:
+ // @code
+ // LLMemoryInfo info;
+ // ...
+ // info.refresh().getStatsMap();
+ // @endcode
+ LLMemoryInfo& refresh();
+
+private:
+ // set mStatsMap
+ static LLSD loadStatsMap();
+
+ // Memory stats for getStatsMap().
+ LLSD mStatsMap;
};
diff --git a/indra/llcommon/llversionviewer.h b/indra/llcommon/llversionviewer.h
index 3dcaca4f16..0018b8e844 100644
--- a/indra/llcommon/llversionviewer.h
+++ b/indra/llcommon/llversionviewer.h
@@ -29,7 +29,7 @@
const S32 LL_VERSION_MAJOR = 2;
const S32 LL_VERSION_MINOR = 8;
-const S32 LL_VERSION_PATCH = 0;
+const S32 LL_VERSION_PATCH = 1;
const S32 LL_VERSION_BUILD = 0;
const char * const LL_CHANNEL = "Second Life Developer";
diff --git a/indra/llcrashlogger/llcrashlogger.cpp b/indra/llcrashlogger/llcrashlogger.cpp
index 68e45f36e4..93f3c910bd 100644
--- a/indra/llcrashlogger/llcrashlogger.cpp
+++ b/indra/llcrashlogger/llcrashlogger.cpp
@@ -31,10 +31,12 @@
#include "llcrashlogger.h"
#include "linden_common.h"
#include "llstring.h"
-#include "indra_constants.h" // CRASH_BEHAVIOR_ASK, CRASH_SETTING_NAME
+#include "indra_constants.h" // CRASH_BEHAVIOR_...
#include "llerror.h"
+#include "llerrorcontrol.h"
#include "lltimer.h"
#include "lldir.h"
+#include "llfile.h"
#include "llsdserialize.h"
#include "lliopipe.h"
#include "llpumpio.h"
@@ -54,7 +56,7 @@ public:
virtual void error(U32 status, const std::string& reason)
{
- gBreak = true;
+ gBreak = true;
}
virtual void result(const LLSD& content)
@@ -64,21 +66,8 @@ public:
}
};
-bool LLCrashLoggerText::mainLoop()
-{
- std::cout << "Entering main loop" << std::endl;
- sendCrashLogs();
- return true;
-}
-
-void LLCrashLoggerText::updateApplication(const std::string& message)
-{
- LLCrashLogger::updateApplication(message);
- std::cout << message << std::endl;
-}
-
LLCrashLogger::LLCrashLogger() :
- mCrashBehavior(CRASH_BEHAVIOR_ASK),
+ mCrashBehavior(CRASH_BEHAVIOR_ALWAYS_SEND),
mCrashInPreviousExec(false),
mCrashSettings("CrashSettings"),
mSentCrashLogs(false),
@@ -281,26 +270,48 @@ LLSD LLCrashLogger::constructPostData()
return mCrashInfo;
}
+const char* const CRASH_SETTINGS_FILE = "settings_crash_behavior.xml";
+
S32 LLCrashLogger::loadCrashBehaviorSetting()
{
+ // First check user_settings (in the user's home dir)
std::string filename = gDirUtilp->getExpandedFilename(LL_PATH_USER_SETTINGS, CRASH_SETTINGS_FILE);
+ if (! mCrashSettings.loadFromFile(filename))
+ {
+ // Next check app_settings (in the SL program dir)
+ std::string filename = gDirUtilp->getExpandedFilename(LL_PATH_APP_SETTINGS, CRASH_SETTINGS_FILE);
+ mCrashSettings.loadFromFile(filename);
+ }
- mCrashSettings.loadFromFile(filename);
-
- S32 value = mCrashSettings.getS32(CRASH_BEHAVIOR_SETTING);
-
- if (value < CRASH_BEHAVIOR_ASK || CRASH_BEHAVIOR_NEVER_SEND < value) return CRASH_BEHAVIOR_ASK;
+ // If we didn't load any files above, this will return the default
+ S32 value = mCrashSettings.getS32("CrashSubmitBehavior");
- return value;
+ // Whatever value we got, make sure it's valid
+ switch (value)
+ {
+ case CRASH_BEHAVIOR_NEVER_SEND:
+ return CRASH_BEHAVIOR_NEVER_SEND;
+ case CRASH_BEHAVIOR_ALWAYS_SEND:
+ return CRASH_BEHAVIOR_ALWAYS_SEND;
+ }
+
+ return CRASH_BEHAVIOR_ASK;
}
bool LLCrashLogger::saveCrashBehaviorSetting(S32 crash_behavior)
{
- if (crash_behavior != CRASH_BEHAVIOR_ASK && crash_behavior != CRASH_BEHAVIOR_ALWAYS_SEND) return false;
+ switch (crash_behavior)
+ {
+ case CRASH_BEHAVIOR_ASK:
+ case CRASH_BEHAVIOR_NEVER_SEND:
+ case CRASH_BEHAVIOR_ALWAYS_SEND:
+ break;
+ default:
+ return false;
+ }
- mCrashSettings.setS32(CRASH_BEHAVIOR_SETTING, crash_behavior);
+ mCrashSettings.setS32("CrashSubmitBehavior", crash_behavior);
std::string filename = gDirUtilp->getExpandedFilename(LL_PATH_USER_SETTINGS, CRASH_SETTINGS_FILE);
-
mCrashSettings.saveToFile(filename, FALSE);
return true;
@@ -309,14 +320,13 @@ bool LLCrashLogger::saveCrashBehaviorSetting(S32 crash_behavior)
bool LLCrashLogger::runCrashLogPost(std::string host, LLSD data, std::string msg, int retries, int timeout)
{
gBreak = false;
- std::string status_message;
for(int i = 0; i < retries; ++i)
{
- status_message = llformat("%s, try %d...", msg.c_str(), i+1);
+ updateApplication(llformat("%s, try %d...", msg.c_str(), i+1));
LLHTTPClient::post(host, data, new LLCrashLoggerResponder(), timeout);
while(!gBreak)
{
- updateApplication(status_message);
+ updateApplication(); // No new message, just pump the IO
}
if(gSent)
{
@@ -336,7 +346,7 @@ bool LLCrashLogger::sendCrashLogs()
updateApplication("Sending reports...");
std::string dump_path = gDirUtilp->getExpandedFilename(LL_PATH_LOGS,
- "SecondLifeCrashReport");
+ "SecondLifeCrashReport");
std::string report_file = dump_path + ".log";
std::ofstream out_file(report_file.c_str());
@@ -365,6 +375,7 @@ void LLCrashLogger::updateApplication(const std::string& message)
{
gServicePump->pump();
gServicePump->callback();
+ if (!message.empty()) llinfos << message << llendl;
}
bool LLCrashLogger::init()
@@ -374,14 +385,27 @@ bool LLCrashLogger::init()
// We assume that all the logs we're looking for reside on the current drive
gDirUtilp->initAppDirs("SecondLife");
+ LLError::initForApplication(gDirUtilp->getExpandedFilename(LL_PATH_APP_SETTINGS, ""));
+
// Default to the product name "Second Life" (this is overridden by the -name argument)
mProductName = "Second Life";
+
+ // Rename current log file to ".old"
+ std::string old_log_file = gDirUtilp->getExpandedFilename(LL_PATH_LOGS, "crashreport.log.old");
+ std::string log_file = gDirUtilp->getExpandedFilename(LL_PATH_LOGS, "crashreport.log");
+ LLFile::rename(log_file.c_str(), old_log_file.c_str());
+
+ // Set the log file to crashreport.log
+ LLError::logToFile(log_file);
- mCrashSettings.declareS32(CRASH_BEHAVIOR_SETTING, CRASH_BEHAVIOR_ASK, "Controls behavior when viewer crashes "
- "(0 = ask before sending crash report, 1 = always send crash report, 2 = never send crash report)");
+ mCrashSettings.declareS32("CrashSubmitBehavior", CRASH_BEHAVIOR_ALWAYS_SEND,
+ "Controls behavior when viewer crashes "
+ "(0 = ask before sending crash report, "
+ "1 = always send crash report, "
+ "2 = never send crash report)");
- llinfos << "Loading crash behavior setting" << llendl;
- mCrashBehavior = loadCrashBehaviorSetting();
+ // llinfos << "Loading crash behavior setting" << llendl;
+ // mCrashBehavior = loadCrashBehaviorSetting();
// If user doesn't want to send, bail out
if (mCrashBehavior == CRASH_BEHAVIOR_NEVER_SEND)
@@ -394,10 +418,11 @@ bool LLCrashLogger::init()
gServicePump->prime(gAPRPoolp);
LLHTTPClient::setPump(*gServicePump);
- //If we've opened the crash logger, assume we can delete the marker file if it exists
+ //If we've opened the crash logger, assume we can delete the marker file if it exists
if( gDirUtilp )
{
- std::string marker_file = gDirUtilp->getExpandedFilename(LL_PATH_LOGS,"SecondLife.exec_marker");
+ std::string marker_file = gDirUtilp->getExpandedFilename(LL_PATH_LOGS,
+ "SecondLife.exec_marker");
LLAPRFile::remove( marker_file );
}
diff --git a/indra/llcrashlogger/llcrashlogger.h b/indra/llcrashlogger/llcrashlogger.h
index a5daa74247..5d0cb5931c 100644
--- a/indra/llcrashlogger/llcrashlogger.h
+++ b/indra/llcrashlogger/llcrashlogger.h
@@ -66,15 +66,4 @@ protected:
bool mSentCrashLogs;
};
-class LLCrashLoggerText : public LLCrashLogger
-{
-public:
- LLCrashLoggerText(void) {}
- ~LLCrashLoggerText(void) {}
-
- virtual bool mainLoop();
- virtual void updateApplication(const std::string& message = LLStringUtil::null);
-};
-
-
#endif //LLCRASHLOGGER_H
diff --git a/indra/llmath/CMakeLists.txt b/indra/llmath/CMakeLists.txt
index 9dadad7dd3..cd100cdf9f 100644
--- a/indra/llmath/CMakeLists.txt
+++ b/indra/llmath/CMakeLists.txt
@@ -12,6 +12,8 @@ include_directories(
set(llmath_SOURCE_FILES
llbbox.cpp
llbboxlocal.cpp
+ llcalc.cpp
+ llcalcparser.cpp
llcamera.cpp
llcoordframe.cpp
llline.cpp
@@ -46,6 +48,8 @@ set(llmath_HEADER_FILES
coordframe.h
llbbox.h
llbboxlocal.h
+ llcalc.h
+ llcalcparser.h
llcamera.h
llcoord.h
llcoordframe.h
diff --git a/indra/llmath/llcalc.cpp b/indra/llmath/llcalc.cpp
new file mode 100644
index 0000000000..597d0815fb
--- /dev/null
+++ b/indra/llmath/llcalc.cpp
@@ -0,0 +1,145 @@
+/*
+ * LLCalc.cpp
+ * SecondLife
+ *
+ * Created by Aimee Walton on 28/09/2008.
+ * Copyright 2008 Aimee Walton.
+ *
+ */
+
+#include "linden_common.h"
+
+#include "llcalc.h"
+
+#include "llcalcparser.h"
+#include "llmath.h"
+
+
+// Variable names for use in the build floater
+const char* LLCalc::X_POS = "PX";
+const char* LLCalc::Y_POS = "PY";
+const char* LLCalc::Z_POS = "PZ";
+const char* LLCalc::X_SCALE = "SX";
+const char* LLCalc::Y_SCALE = "SY";
+const char* LLCalc::Z_SCALE = "SZ";
+const char* LLCalc::X_ROT = "RX";
+const char* LLCalc::Y_ROT = "RY";
+const char* LLCalc::Z_ROT = "RZ";
+const char* LLCalc::HOLLOW = "HLW";
+const char* LLCalc::CUT_BEGIN = "CB";
+const char* LLCalc::CUT_END = "CE";
+const char* LLCalc::PATH_BEGIN = "PB";
+const char* LLCalc::PATH_END = "PE";
+const char* LLCalc::TWIST_BEGIN = "TB";
+const char* LLCalc::TWIST_END = "TE";
+const char* LLCalc::X_SHEAR = "SHX";
+const char* LLCalc::Y_SHEAR = "SHY";
+const char* LLCalc::X_TAPER = "TPX";
+const char* LLCalc::Y_TAPER = "TPY";
+const char* LLCalc::RADIUS_OFFSET = "ROF";
+const char* LLCalc::REVOLUTIONS = "REV";
+const char* LLCalc::SKEW = "SKW";
+const char* LLCalc::X_HOLE = "HLX";
+const char* LLCalc::Y_HOLE = "HLY";
+const char* LLCalc::TEX_U_SCALE = "TSU";
+const char* LLCalc::TEX_V_SCALE = "TSV";
+const char* LLCalc::TEX_U_OFFSET = "TOU";
+const char* LLCalc::TEX_V_OFFSET = "TOV";
+const char* LLCalc::TEX_ROTATION = "TROT";
+const char* LLCalc::TEX_TRANSPARENCY = "TRNS";
+const char* LLCalc::TEX_GLOW = "GLOW";
+
+
+LLCalc* LLCalc::sInstance = NULL;
+
+LLCalc::LLCalc() : mLastErrorPos(0)
+{
+ // Init table of constants
+ mConstants["PI"] = F_PI;
+ mConstants["TWO_PI"] = F_TWO_PI;
+ mConstants["PI_BY_TWO"] = F_PI_BY_TWO;
+ mConstants["SQRT_TWO_PI"] = F_SQRT_TWO_PI;
+ mConstants["SQRT2"] = F_SQRT2;
+ mConstants["SQRT3"] = F_SQRT3;
+ mConstants["DEG_TO_RAD"] = DEG_TO_RAD;
+ mConstants["RAD_TO_DEG"] = RAD_TO_DEG;
+ mConstants["GRAVITY"] = GRAVITY;
+}
+
+LLCalc::~LLCalc()
+{
+}
+
+//static
+void LLCalc::cleanUp()
+{
+ delete sInstance;
+ sInstance = NULL;
+}
+
+//static
+LLCalc* LLCalc::getInstance()
+{
+ if (!sInstance) sInstance = new LLCalc();
+ return sInstance;
+}
+
+void LLCalc::setVar(const std::string& name, const F32& value)
+{
+ mVariables[name] = value;
+}
+
+void LLCalc::clearVar(const std::string& name)
+{
+ mVariables.erase(name);
+}
+
+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;
+ LLStringUtil::toUpper(expr_upper);
+
+ LLCalcParser calc(result, &mConstants, &mVariables);
+
+ mLastErrorPos = 0;
+ std::string::iterator start = expr_upper.begin();
+ parse_info<std::string::iterator> info;
+
+ try
+ {
+ info = parse(start, expr_upper.end(), calc, space_p);
+ lldebugs << "Math expression: " << expression << " = " << result << llendl;
+ }
+ catch(parser_error<std::string, std::string::iterator> &e)
+ {
+ mLastErrorPos = e.where - expr_upper.begin();
+
+ llinfos << "Calc parser exception: " << e.descriptor << " at " << mLastErrorPos << " in expression: " << expression << llendl;
+ return false;
+ }
+
+ if (!info.full)
+ {
+ mLastErrorPos = info.stop - expr_upper.begin();
+ llinfos << "Unhandled syntax error at " << mLastErrorPos << " in expression: " << expression << llendl;
+ return false;
+ }
+
+ return true;
+}
diff --git a/indra/llmath/llcalc.h b/indra/llmath/llcalc.h
new file mode 100644
index 0000000000..cc31950cb6
--- /dev/null
+++ b/indra/llmath/llcalc.h
@@ -0,0 +1,83 @@
+/*
+ * LLCalc.h
+ * SecondLife
+ *
+ * Created by Aimee Walton on 28/09/2008.
+ * Copyright 2008 Aimee Walton.
+ *
+ */
+
+#ifndef LL_CALC_H
+#define LL_CALC_H
+
+#include <map>
+#include <string>
+
+class LLCalc
+{
+public:
+ LLCalc();
+ ~LLCalc();
+
+ // Variable name constants
+ static const char* X_POS;
+ static const char* Y_POS;
+ static const char* Z_POS;
+ static const char* X_SCALE;
+ static const char* Y_SCALE;
+ static const char* Z_SCALE;
+ static const char* X_ROT;
+ static const char* Y_ROT;
+ static const char* Z_ROT;
+ static const char* HOLLOW;
+ static const char* CUT_BEGIN;
+ static const char* CUT_END;
+ static const char* PATH_BEGIN;
+ static const char* PATH_END;
+ static const char* TWIST_BEGIN;
+ static const char* TWIST_END;
+ static const char* X_SHEAR;
+ static const char* Y_SHEAR;
+ static const char* X_TAPER;
+ static const char* Y_TAPER;
+ static const char* RADIUS_OFFSET;
+ static const char* REVOLUTIONS;
+ static const char* SKEW;
+ static const char* X_HOLE;
+ static const char* Y_HOLE;
+ static const char* TEX_U_SCALE;
+ static const char* TEX_V_SCALE;
+ static const char* TEX_U_OFFSET;
+ static const char* TEX_V_OFFSET;
+ static const char* TEX_ROTATION;
+ static const char* TEX_TRANSPARENCY;
+ static const char* TEX_GLOW;
+
+ 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; }
+
+ static LLCalc* getInstance();
+ static void cleanUp();
+
+ typedef std::map<std::string, F32> calc_map_t;
+
+private:
+ std::string::size_type mLastErrorPos;
+
+ 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;
+};
+
+#endif // LL_CALC_H
diff --git a/indra/llmath/llcalcparser.cpp b/indra/llmath/llcalcparser.cpp
new file mode 100644
index 0000000000..fd55376fa9
--- /dev/null
+++ b/indra/llmath/llcalcparser.cpp
@@ -0,0 +1,46 @@
+/*
+ * LLCalcParser.cpp
+ * SecondLife
+ *
+ * Created by Aimee Walton on 28/09/2008.
+ * Copyright 2008 Aimee Walton.
+ *
+ */
+
+#include "linden_common.h"
+
+#include "llcalcparser.h"
+using namespace boost::spirit::classic;
+
+F32 LLCalcParser::lookup(const std::string::iterator& start, const std::string::iterator& end) const
+{
+ LLCalc::calc_map_t::iterator iter;
+
+ std::string name(start, end);
+
+ if (mConstants)
+ {
+ iter = mConstants->find(name);
+ if (iter != mConstants->end())
+ {
+ return (*iter).second;
+ }
+ }
+ else
+ {
+ // This should never happen!
+ throw_(end, std::string("Missing constants table"));
+ }
+
+ if (mVariables)
+ {
+ iter = mVariables->find(name);
+ if (iter != mVariables->end())
+ {
+ return (*iter).second;
+ }
+ }
+
+ throw_(end, std::string("Unknown symbol " + name));
+ return 0.f;
+}
diff --git a/indra/llmath/llcalcparser.h b/indra/llmath/llcalcparser.h
new file mode 100644
index 0000000000..600e173661
--- /dev/null
+++ b/indra/llmath/llcalcparser.h
@@ -0,0 +1,174 @@
+/*
+ * LLCalcParser.h
+ * SecondLife
+ *
+ * Created by Aimee Walton on 28/09/2008.
+ * Copyright 2008 Aimee Walton.
+ *
+ */
+
+#ifndef LL_CALCPARSER_H
+#define LL_CALCPARSER_H
+
+#include <boost/spirit/include/classic_attribute.hpp>
+#include <boost/spirit/include/classic_core.hpp>
+#include <boost/spirit/include/classic_error_handling.hpp>
+#include <boost/spirit/include/classic_position_iterator.hpp>
+#include <boost/spirit/include/phoenix1_binders.hpp>
+#include <boost/spirit/include/classic_symbols.hpp>
+using namespace boost::spirit::classic;
+
+#include "llcalc.h"
+#include "llmath.h"
+
+struct LLCalcParser : grammar<LLCalcParser>
+{
+ LLCalcParser(F32& result, LLCalc::calc_map_t* constants, LLCalc::calc_map_t* vars) :
+ mResult(result), mConstants(constants), mVariables(vars) {};
+
+ struct value_closure : closure<value_closure, F32>
+ {
+ member1 value;
+ };
+
+ template <typename ScannerT>
+ struct definition
+ {
+ // Rule declarations
+ rule<ScannerT> statement, identifier;
+ rule<ScannerT, value_closure::context_t> expression, term,
+ power,
+ unary_expr,
+ factor,
+ unary_func,
+ binary_func,
+ group;
+
+ // start() should return the starting symbol
+ rule<ScannerT> const& start() const { return statement; }
+
+ definition(LLCalcParser const& self)
+ {
+ using namespace phoenix;
+
+ assertion<std::string> assert_domain("Domain error");
+// assertion<std::string> assert_symbol("Unknown symbol");
+ assertion<std::string> assert_syntax("Syntax error");
+
+ identifier =
+ lexeme_d[(alpha_p | '_') >> *(alnum_p | '_')]
+ ;
+
+ group =
+ '(' >> expression[group.value = arg1] >> assert_syntax(ch_p(')'))
+ ;
+
+ unary_func =
+ ((str_p("SIN") >> '(' >> expression[unary_func.value = bind(&LLCalcParser::_sin)(self,arg1)]) |
+ (str_p("COS") >> '(' >> expression[unary_func.value = bind(&LLCalcParser::_cos)(self,arg1)]) |
+ (str_p("TAN") >> '(' >> expression[unary_func.value = bind(&LLCalcParser::_tan)(self,arg1)]) |
+ (str_p("ASIN") >> '(' >> expression[unary_func.value = bind(&LLCalcParser::_asin)(self,arg1)]) |
+ (str_p("ACOS") >> '(' >> expression[unary_func.value = bind(&LLCalcParser::_acos)(self,arg1)]) |
+ (str_p("ATAN") >> '(' >> expression[unary_func.value = bind(&LLCalcParser::_atan)(self,arg1)]) |
+ (str_p("SQRT") >> '(' >> expression[unary_func.value = bind(&LLCalcParser::_sqrt)(self,arg1)]) |
+ (str_p("LOG") >> '(' >> expression[unary_func.value = bind(&LLCalcParser::_log)(self,arg1)]) |
+ (str_p("EXP") >> '(' >> expression[unary_func.value = bind(&LLCalcParser::_exp)(self,arg1)]) |
+ (str_p("ABS") >> '(' >> expression[unary_func.value = bind(&LLCalcParser::_fabs)(self,arg1)]) |
+ (str_p("FLR") >> '(' >> expression[unary_func.value = bind(&LLCalcParser::_floor)(self,arg1)]) |
+ (str_p("CEIL") >> '(' >> expression[unary_func.value = bind(&LLCalcParser::_ceil)(self,arg1)])
+ ) >> assert_syntax(ch_p(')'))
+ ;
+
+ binary_func =
+ ((str_p("ATAN2") >> '(' >> expression[binary_func.value = arg1] >> ',' >>
+ expression[binary_func.value = bind(&LLCalcParser::_atan2)(self, binary_func.value, arg1)]) |
+ (str_p("MIN") >> '(' >> expression[binary_func.value = arg1] >> ',' >>
+ expression[binary_func.value = bind(&LLCalcParser::_min)(self, binary_func.value, arg1)]) |
+ (str_p("MAX") >> '(' >> expression[binary_func.value = arg1] >> ',' >>
+ expression[binary_func.value = bind(&LLCalcParser::_max)(self, binary_func.value, arg1)])
+ ) >> assert_syntax(ch_p(')'))
+ ;
+
+ // *TODO: Localisation of the decimal point?
+ // Problem, LLLineEditor::postvalidateFloat accepts a comma when appropriate
+ // for the current locale. However to do that here could clash with using
+ // the comma as a separator when passing arguments to functions.
+ factor =
+ (ureal_p[factor.value = arg1] |
+ group[factor.value = arg1] |
+ unary_func[factor.value = arg1] |
+ binary_func[factor.value = arg1] |
+ // Lookup throws an Unknown Symbol error if it is unknown, while this works fine,
+ // would be "neater" to handle symbol lookup from here with an assertive parser.
+// constants_p[factor.value = arg1]|
+ identifier[factor.value = bind(&LLCalcParser::lookup)(self, arg1, arg2)]
+ ) >>
+ // Detect and throw math errors.
+ assert_domain(eps_p(bind(&LLCalcParser::checkNaN)(self, factor.value)))
+ ;
+
+ unary_expr =
+ !ch_p('+') >> factor[unary_expr.value = arg1] |
+ '-' >> factor[unary_expr.value = -arg1]
+ ;
+
+ power =
+ unary_expr[power.value = arg1] >>
+ *('^' >> assert_syntax(unary_expr[power.value = bind(&powf)(power.value, arg1)]))
+ ;
+
+ term =
+ power[term.value = arg1] >>
+ *(('*' >> assert_syntax(power[term.value *= arg1])) |
+ ('/' >> assert_syntax(power[term.value /= arg1])) |
+ ('%' >> assert_syntax(power[term.value = bind(&fmodf)(term.value, arg1)]))
+ )
+ ;
+
+ expression =
+ assert_syntax(term[expression.value = arg1]) >>
+ *(('+' >> assert_syntax(term[expression.value += arg1])) |
+ ('-' >> assert_syntax(term[expression.value -= arg1]))
+ )
+ ;
+
+ statement =
+ !ch_p('=') >> ( expression )[var(self.mResult) = arg1] >> (end_p)
+ ;
+ }
+ };
+
+private:
+ // Member functions for semantic actions
+ F32 lookup(const std::string::iterator&, const std::string::iterator&) const;
+ F32 _min(const F32& a, const F32& b) const { return llmin(a, b); }
+ F32 _max(const F32& a, const F32& b) const { return llmax(a, b); }
+
+ bool checkNaN(const F32& a) const { return !llisnan(a); }
+
+ //FIX* non ambigious function fix making SIN() work for calc -Cryogenic Blitz
+ F32 _sin(const F32& a) const { return sin(DEG_TO_RAD * a); }
+ F32 _cos(const F32& a) const { return cos(DEG_TO_RAD * a); }
+ F32 _tan(const F32& a) const { return tan(DEG_TO_RAD * a); }
+ F32 _asin(const F32& a) const { return asin(a * RAD_TO_DEG); }
+ F32 _acos(const F32& a) const { return acos(a * RAD_TO_DEG); }
+ F32 _atan(const F32& a) const { return atan(a * RAD_TO_DEG); }
+ F32 _sqrt(const F32& a) const { return sqrt(a); }
+ F32 _log(const F32& a) const { return log(a); }
+ F32 _exp(const F32& a) const { return exp(a); }
+ F32 _fabs(const F32& a) const { return fabs(a); }
+ F32 _floor(const F32& a) const { return llfloor(a); }
+ F32 _ceil(const F32& a) const { return llceil(a); }
+
+ F32 _atan2(const F32& a,const F32& b) const { return atan2(a,b); }
+
+
+
+ LLCalc::calc_map_t* mConstants;
+ LLCalc::calc_map_t* mVariables;
+// LLCalc::calc_map_t* mUserVariables;
+
+ F32& mResult;
+};
+
+#endif // LL_CALCPARSER_H
diff --git a/indra/llui/lllineeditor.cpp b/indra/llui/lllineeditor.cpp
index 123997e5e9..06fbc0f234 100644
--- a/indra/llui/lllineeditor.cpp
+++ b/indra/llui/lllineeditor.cpp
@@ -37,6 +37,7 @@
#include "llgl.h"
#include "lltimer.h"
+#include "llcalc.h"
//#include "llclipboard.h"
#include "llcontrol.h"
#include "llbutton.h"
@@ -133,6 +134,7 @@ LLLineEditor::LLLineEditor(const LLLineEditor::Params& p)
mIgnoreTab( p.ignore_tab ),
mDrawAsterixes( p.is_password ),
mSelectAllonFocusReceived( p.select_on_focus ),
+ mSelectAllonCommit( TRUE ),
mPassDelete(FALSE),
mReadOnly(FALSE),
mBgImage( p.background_image ),
@@ -230,7 +232,10 @@ void LLLineEditor::onCommit()
setControlValue(getValue());
LLUICtrl::onCommit();
- selectAll();
+
+ // Selection on commit needs to be turned off when evaluating maths
+ // expressions, to allow indication of the error position
+ if (mSelectAllonCommit) selectAll();
}
// Returns TRUE if user changed value at all
@@ -2072,6 +2077,32 @@ BOOL LLLineEditor::postvalidateFloat(const std::string &str)
return success;
}
+BOOL LLLineEditor::evaluateFloat()
+{
+ bool success;
+ F32 result = 0.f;
+ std::string expr = getText();
+ LLStringUtil::toUpper(expr);
+
+ success = LLCalc::getInstance()->evalString(expr, result);
+
+ if (!success)
+ {
+ // Move the cursor to near the error on failure
+ setCursor(LLCalc::getInstance()->getLastErrorPos());
+ // *TODO: Translated error message indicating the type of error? Select error text?
+ }
+ else
+ {
+ // Replace the expression with the result
+ std::string result_str = llformat("%f",result);
+ setText(result_str);
+ selectAll();
+ }
+
+ return success;
+}
+
void LLLineEditor::onMouseCaptureLost()
{
endSelection();
diff --git a/indra/llui/lllineeditor.h b/indra/llui/lllineeditor.h
index 4b77708590..583bde360a 100644
--- a/indra/llui/lllineeditor.h
+++ b/indra/llui/lllineeditor.h
@@ -221,6 +221,7 @@ public:
void deleteSelection();
void setSelectAllonFocusReceived(BOOL b);
+ void setSelectAllonCommit(BOOL b) { mSelectAllonCommit = b; }
typedef boost::function<void (LLLineEditor* caller, void* user_data)> callback_t;
void setKeystrokeCallback(callback_t callback, void* user_data);
@@ -241,6 +242,7 @@ public:
static BOOL postvalidateFloat(const std::string &str);
bool prevalidateInput(const LLWString& wstr);
+ BOOL evaluateFloat();
// line history support:
void setEnableLineHistory( BOOL enabled ) { mHaveHistory = enabled; } // switches line history on or off
@@ -340,6 +342,7 @@ protected:
BOOL mDrawAsterixes;
BOOL mSelectAllonFocusReceived;
+ BOOL mSelectAllonCommit;
BOOL mPassDelete;
BOOL mReadOnly;
diff --git a/indra/llui/llspinctrl.cpp b/indra/llui/llspinctrl.cpp
index 15a7438ec9..934879cdfd 100644
--- a/indra/llui/llspinctrl.cpp
+++ b/indra/llui/llspinctrl.cpp
@@ -44,7 +44,7 @@
#include "llresmgr.h"
#include "lluictrlfactory.h"
-const U32 MAX_STRING_LENGTH = 32;
+const U32 MAX_STRING_LENGTH = 255;
static LLDefaultChildRegistry::Register<LLSpinCtrl> r2("spinner");
@@ -124,14 +124,7 @@ LLSpinCtrl::LLSpinCtrl(const LLSpinCtrl::Params& p)
params.max_length.bytes(MAX_STRING_LENGTH);
params.commit_callback.function((boost::bind(&LLSpinCtrl::onEditorCommit, this, _2)));
- if( mPrecision>0 )//should accept float numbers
- {
- params.prevalidate_callback(&LLTextValidate::validateFloat);
- }
- else //should accept int numbers
- {
- params.prevalidate_callback(&LLTextValidate::validateInt);
- }
+ //*NOTE: allow entering of any chars for LLCalc, proper input will be evaluated on commit
params.follows.flags(FOLLOWS_LEFT | FOLLOWS_BOTTOM);
mEditor = LLUICtrlFactory::create<LLLineEditor> (params);
@@ -140,6 +133,7 @@ LLSpinCtrl::LLSpinCtrl(const LLSpinCtrl::Params& p)
// than when it doesn't. Instead, if you always have to double click to select all the text,
// it's easier to understand
//mEditor->setSelectAllonFocusReceived(TRUE);
+ mEditor->setSelectAllonCommit(FALSE);
addChild(mEditor);
updateEditor();
@@ -304,9 +298,10 @@ void LLSpinCtrl::onEditorCommit( const LLSD& data )
{
BOOL success = FALSE;
- std::string text = mEditor->getText();
- if( LLLineEditor::postvalidateFloat( text ) )
+ if( mEditor->evaluateFloat() )
{
+ std::string text = mEditor->getText();
+
LLLocale locale(LLLocale::USER_LOCALE);
F32 val = (F32) atof(text.c_str());
@@ -327,7 +322,11 @@ void LLSpinCtrl::onEditorCommit( const LLSD& data )
}
updateEditor();
- if( !success )
+ if( success )
+ {
+ updateEditor();
+ }
+ else
{
reportInvalidData();
}
diff --git a/indra/mac_crash_logger/CrashReporter.nib/objects.xib b/indra/mac_crash_logger/CrashReporter.nib/objects.xib
index 634d1c5321..32647391b6 100644
--- a/indra/mac_crash_logger/CrashReporter.nib/objects.xib
+++ b/indra/mac_crash_logger/CrashReporter.nib/objects.xib
@@ -15,7 +15,7 @@
<string name="bounds">414 390 434 487 </string>
</object>
<object class="IBCarbonStaticText" id="181">
- <string name="title">Second Life appears to have crashed or frozen the last time it ran.&#10;&#10;This crash reporter collects information about your computer&apos;s hardware configuration, operating system, and some Second Life logs, all of which are used for debugging purposes only.&#10;&#10;In the space below, please briefly describe what you were doing or trying to do just prior to the crash. Thank you for your help!&#10;&#10;This report is NOT read by Customer Support. If you have billing or other questions, please go to: http://www.secondlife.com/support/&#10;&#10;If you don&apos;t wish to send Linden Lab a crash report, press Cancel.&#10;</string>
+ <string name="title">Second Life appears to have crashed or frozen the last time it ran.&#10;&#10;This crash reporter collects information about your computer&apos;s hardware configuration, operating system, and some Second Life logs, all of which are used for debugging purposes only.&#10;&#10;In the space below, please briefly describe what you were doing or trying to do just prior to the crash. Thank you for your help!&#10;&#10;This report is NOT read by Customer Support. If you have billing or other questions, please go to: http://www.secondlife.com/support/&#10;&#10;If you don&apos;t wish to send Linden Lab a crash report, press Don&apos;t Send.&#10;</string>
<string name="bounds">20 20 231 487 </string>
</object>
<object class="IBCarbonWindow" id="166">
diff --git a/indra/mac_crash_logger/llcrashloggermac.cpp b/indra/mac_crash_logger/llcrashloggermac.cpp
index bec8cce04e..b555e92b96 100644
--- a/indra/mac_crash_logger/llcrashloggermac.cpp
+++ b/indra/mac_crash_logger/llcrashloggermac.cpp
@@ -29,9 +29,6 @@
#include <Carbon/Carbon.h>
#include <iostream>
-#include <sstream>
-
-#include "boost/tokenizer.hpp"
#include "indra_constants.h" // CRASH_BEHAVIOR_ASK, CRASH_SETTING_NAME
#include "llerror.h"
@@ -247,7 +244,7 @@ bool LLCrashLoggerMac::mainLoop()
void LLCrashLoggerMac::updateApplication(const std::string& message)
{
- LLCrashLogger::updateApplication();
+ LLCrashLogger::updateApplication(message);
}
bool LLCrashLoggerMac::cleanup()
diff --git a/indra/mac_crash_logger/mac_crash_logger.cpp b/indra/mac_crash_logger/mac_crash_logger.cpp
index 20b491c401..6571b35241 100644
--- a/indra/mac_crash_logger/mac_crash_logger.cpp
+++ b/indra/mac_crash_logger/mac_crash_logger.cpp
@@ -25,22 +25,23 @@
*/
#include "linden_common.h"
-
#include "llcrashloggermac.h"
int main(int argc, char **argv)
{
- //time(&gLaunchTime);
-
- llinfos << "Starting Second Life Viewer Crash Reporter" << llendl;
+ llinfos << "Starting crash reporter." << llendl;
LLCrashLoggerMac app;
app.parseCommandOptions(argc, argv);
- if(!app.init())
+
+ if (! app.init())
{
- return 0;
+ llwarns << "Unable to initialize application." << llendl;
+ return 1;
}
+
app.mainLoop();
-
+ app.cleanup();
+ llinfos << "Crash reporter finished normally." << llendl;
return 0;
}
diff --git a/indra/newview/app_settings/cmd_line.xml b/indra/newview/app_settings/cmd_line.xml
index 89e5949fbe..15434f2b8f 100644
--- a/indra/newview/app_settings/cmd_line.xml
+++ b/indra/newview/app_settings/cmd_line.xml
@@ -220,8 +220,7 @@
<map>
<key>desc</key>
<string>Set the detail level.
- 0 - low, 1 - medium, 2 - high, 3 - ultra
- </string>
+0 - low, 1 - medium, 2 - high, 3 - ultra</string>
<key>count</key>
<integer>1</integer>
</map>
@@ -229,10 +228,7 @@
<key>setdefault</key>
<map>
<key>desc</key>
- <string> specify the value of a particular
- configuration variable which can be
- overridden by settings.xml
- </string>
+ <string>specify the value of a particular configuration variable which can be overridden by settings.xml.</string>
<key>count</key>
<integer>2</integer>
<!-- Special case. Mapped to settings procedurally. -->
@@ -241,10 +237,7 @@
<key>set</key>
<map>
<key>desc</key>
- <string> specify the value of a particular
- configuration variable that
- overrides all other settings
- </string>
+ <string>specify the value of a particular configuration variable that overrides all other settings.</string>
<key>count</key>
<integer>2</integer>
<key>compose</key>
diff --git a/indra/newview/app_settings/settings_files.xml b/indra/newview/app_settings/settings_files.xml
index 079a54f957..bfc09286e3 100644
--- a/indra/newview/app_settings/settings_files.xml
+++ b/indra/newview/app_settings/settings_files.xml
@@ -20,7 +20,8 @@
file_name="settings.xml"
file_name_setting="ClientSettingsFile"/>
<file name="CrashSettings"
- file_name="settings_crash_behavior"/>
+ file_name="settings_crash_behavior.xml"
+ file_name_setting="CrashSettingsFile"/>
<file name="Warnings"
file_name="ignorable_dialogs.xml"
file_name_setting="WarningSettingsFile"/>
@@ -61,4 +62,4 @@
file_name="colors.xml"
file_name_setting="SkinningSettingsFile"/>
</group>
-</settings_files> \ No newline at end of file
+</settings_files>
diff --git a/indra/newview/llagent.cpp b/indra/newview/llagent.cpp
index 8954937f69..492cfe7c1b 100644..100755
--- a/indra/newview/llagent.cpp
+++ b/indra/newview/llagent.cpp
@@ -577,7 +577,10 @@ void LLAgent::setFlying(BOOL fly)
// static
void LLAgent::toggleFlying()
{
- LLToolPie::instance().stopClickToWalk();
+ if ( gAgent.mAutoPilot )
+ {
+ LLToolPie::instance().stopClickToWalk();
+ }
BOOL fly = !gAgent.getFlying();
diff --git a/indra/newview/llappviewer.cpp b/indra/newview/llappviewer.cpp
index 1d9519d675..92e0513464 100644
--- a/indra/newview/llappviewer.cpp
+++ b/indra/newview/llappviewer.cpp
@@ -56,6 +56,7 @@
#include "llallocator.h"
#include "llares.h"
#include "llcurl.h"
+#include "llcalc.h"
#include "lltexturestats.h"
#include "lltexturestats.h"
#include "llviewerwindow.h"
@@ -468,18 +469,6 @@ void request_initial_instant_messages()
}
}
-// A settings system callback for CrashSubmitBehavior
-bool handleCrashSubmitBehaviorChanged(const LLSD& newvalue)
-{
- S32 cb = newvalue.asInteger();
- const S32 NEVER_SUBMIT_REPORT = 2;
- if(cb == NEVER_SUBMIT_REPORT)
- {
- LLAppViewer::instance()->destroyMainloopTimeout();
- }
- return true;
-}
-
// Use these strictly for things that are constructed at startup,
// or for things that are performance critical. JC
static void settings_to_globals()
@@ -611,9 +600,6 @@ bool LLAppViewer::sendURLToOtherInstance(const std::string& url)
// Static members.
// The single viewer app.
LLAppViewer* LLAppViewer::sInstance = NULL;
-
-const std::string LLAppViewer::sGlobalSettingsName = "Global";
-
LLTextureCache* LLAppViewer::sTextureCache = NULL;
LLImageDecodeThread* LLAppViewer::sImageDecodeThread = NULL;
LLTextureFetch* LLAppViewer::sTextureFetch = NULL;
@@ -771,16 +757,6 @@ bool LLAppViewer::init()
LL_INFOS("InitInfo") << "J2C Engine is: " << LLImageJ2C::getEngineInfo() << LL_ENDL;
LL_INFOS("InitInfo") << "libcurl version is: " << LLCurl::getVersionString() << LL_ENDL;
- // Get the single value from the crash settings file, if it exists
- std::string crash_settings_filename = gDirUtilp->getExpandedFilename(LL_PATH_USER_SETTINGS, CRASH_SETTINGS_FILE);
- gCrashSettings.loadFromFile(crash_settings_filename);
- if(gSavedSettings.getBOOL("IgnoreAllNotifications"))
- {
- gCrashSettings.setS32(CRASH_BEHAVIOR_SETTING, CRASH_BEHAVIOR_ALWAYS_SEND);
- gCrashSettings.saveToFile(crash_settings_filename, FALSE);
- }
- LL_INFOS("InitInfo") << "Crash settings done." << LL_ENDL ;
-
/////////////////////////////////////////////////
// OS-specific login dialogs
/////////////////////////////////////////////////
@@ -1055,7 +1031,7 @@ bool LLAppViewer::init()
//EXT-7013 - On windows for some locale (Japanese) standard
//datetime formatting functions didn't support some parameters such as "weekday".
//Names for days and months localized in xml are also useful for Polish locale(STORM-107).
- std::string language = LLControlGroup::getInstance(sGlobalSettingsName)->getString("Language");
+ std::string language = gSavedSettings.getString("Language");
if(language == "ja" || language == "pl")
{
LLStringOps::setupWeekDaysNames(LLTrans::getString("dateTimeWeekdaysNames"));
@@ -1543,7 +1519,9 @@ bool LLAppViewer::cleanup()
// Note: this is where gLocalSpeakerMgr and gActiveSpeakerMgr used to be deleted.
LLWorldMap::getInstance()->reset(); // release any images
-
+
+ LLCalc::cleanUp();
+
llinfos << "Global stuff deleted" << llendflush;
if (gAudiop)
@@ -1706,10 +1684,6 @@ bool LLAppViewer::cleanup()
llinfos << "Saved settings" << llendflush;
}
- std::string crash_settings_filename = gDirUtilp->getExpandedFilename(LL_PATH_USER_SETTINGS, CRASH_SETTINGS_FILE);
- // save all settings, even if equals defaults
- gCrashSettings.saveToFile(crash_settings_filename, FALSE);
-
std::string warnings_settings_filename = gDirUtilp->getExpandedFilename(LL_PATH_USER_SETTINGS, getSettingsFilename("Default", "Warnings"));
gWarningSettings.saveToFile(warnings_settings_filename, TRUE);
@@ -1839,7 +1813,6 @@ bool LLAppViewer::cleanup()
gSavedSettings.cleanup();
LLUIColorTable::instance().clear();
- gCrashSettings.cleanup();
LLWatchdog::getInstance()->cleanup();
@@ -1982,7 +1955,6 @@ bool LLAppViewer::loadSettingsFromDirectory(const std::string& location_key,
llerrs << "Invalid settings location list" << llendl;
}
- LLControlGroup* global_settings = LLControlGroup::getInstance(sGlobalSettingsName);
for(LLInitParam::ParamIterator<SettingsGroup>::const_iterator it = mSettingsLocationList->groups.begin(), end_it = mSettingsLocationList->groups.end();
it != end_it;
++it)
@@ -2015,11 +1987,15 @@ bool LLAppViewer::loadSettingsFromDirectory(const std::string& location_key,
std::string full_settings_path;
if (file_it->file_name_setting.isProvided()
- && global_settings->controlExists(file_it->file_name_setting))
+ && gSavedSettings.controlExists(file_it->file_name_setting))
{
// try to find filename stored in file_name_setting control
- full_settings_path = global_settings->getString(file_it->file_name_setting);
- if (!gDirUtilp->fileExists(full_settings_path))
+ full_settings_path = gSavedSettings.getString(file_it->file_name_setting);
+ if (full_settings_path.empty())
+ {
+ continue;
+ }
+ else if (!gDirUtilp->fileExists(full_settings_path))
{
// search in default path
full_settings_path = gDirUtilp->getExpandedFilename((ELLPath)path_index, full_settings_path);
@@ -2165,8 +2141,6 @@ bool LLAppViewer::initConfiguration()
gSavedSettings.setS32("WatchdogEnabled", 0);
#endif
- gCrashSettings.getControl(CRASH_BEHAVIOR_SETTING)->getSignal()->connect(boost::bind(&handleCrashSubmitBehaviorChanged, _2));
-
// These are warnings that appear on the first experience of that condition.
// They are already set in the settings_default.xml file, but still need to be added to LLFirstUse
// for disable/reset ability
@@ -2297,15 +2271,33 @@ bool LLAppViewer::initConfiguration()
{
const std::string& name = *itr;
const std::string& value = *(++itr);
- LLControlVariable* c = LLControlGroup::getInstance(sGlobalSettingsName)->getControl(name);
- if(c)
+ std::string name_part;
+ std::string group_part;
+ LLControlVariable* control = NULL;
+
+ // Name can be further split into ControlGroup.Name, with the default control group being Global
+ size_t pos = name.find('.');
+ if (pos != std::string::npos)
+ {
+ group_part = name.substr(0, pos);
+ name_part = name.substr(pos+1);
+ llinfos << "Setting " << group_part << "." << name_part << " to " << value << llendl;
+ LLControlGroup* g = LLControlGroup::getInstance(group_part);
+ if (g) control = g->getControl(name_part);
+ }
+ else
+ {
+ llinfos << "Setting Global." << name << " to " << value << llendl;
+ control = gSavedSettings.getControl(name);
+ }
+
+ if (control)
{
- c->setValue(value, false);
+ control->setValue(value, false);
}
else
{
- llwarns << "'--set' specified with unknown setting: '"
- << name << "'." << llendl;
+ llwarns << "Failed --set " << name << ": setting name unknown." << llendl;
}
}
}
@@ -2762,7 +2754,8 @@ void LLAppViewer::checkForCrash(void)
// Pop up a freeze or crash warning dialog
//
S32 choice;
- if(gCrashSettings.getS32(CRASH_BEHAVIOR_SETTING) == CRASH_BEHAVIOR_ASK)
+ const S32 cb = gCrashSettings.getS32("CrashSubmitBehavior");
+ if(cb == CRASH_BEHAVIOR_ASK)
{
std::ostringstream msg;
msg << LLTrans::getString("MBFrozenCrashed");
@@ -2771,7 +2764,7 @@ void LLAppViewer::checkForCrash(void)
alert,
OSMB_YESNO);
}
- else if(gCrashSettings.getS32(CRASH_BEHAVIOR_SETTING) == CRASH_BEHAVIOR_NEVER_SEND)
+ else if(cb == CRASH_BEHAVIOR_NEVER_SEND)
{
choice = OSBTN_NO;
}
@@ -2828,10 +2821,9 @@ bool LLAppViewer::initWindow()
LL_INFOS("AppInit") << "gViewerwindow created." << LL_ENDL;
// Need to load feature table before cheking to start watchdog.
- const S32 NEVER_SUBMIT_REPORT = 2;
bool use_watchdog = false;
int watchdog_enabled_setting = gSavedSettings.getS32("WatchdogEnabled");
- if(watchdog_enabled_setting == -1)
+ if (watchdog_enabled_setting == -1)
{
use_watchdog = !LLFeatureManager::getInstance()->isFeatureAvailable("WatchdogDisabled");
}
@@ -2841,8 +2833,7 @@ bool LLAppViewer::initWindow()
use_watchdog = bool(watchdog_enabled_setting);
}
- bool send_reports = gCrashSettings.getS32(CRASH_BEHAVIOR_SETTING) != NEVER_SUBMIT_REPORT;
- if(use_watchdog && send_reports)
+ if (use_watchdog)
{
LLWatchdog::getInstance()->init(watchdog_killer_callback);
}
diff --git a/indra/newview/llappviewerlinux.cpp b/indra/newview/llappviewerlinux.cpp
index 714e0e6163..48d02dfeaa 100644
--- a/indra/newview/llappviewerlinux.cpp
+++ b/indra/newview/llappviewerlinux.cpp
@@ -361,46 +361,35 @@ void LLAppViewerLinux::handleCrashReporting(bool reportFreeze)
}
else
{
- const S32 cb = gCrashSettings.getS32(CRASH_BEHAVIOR_SETTING);
-
- // Always generate the report, have the logger do the asking, and
- // don't wait for the logger before exiting (-> total cleanup).
- if (CRASH_BEHAVIOR_NEVER_SEND != cb)
- {
- // launch the actual crash logger
- const char* ask_dialog = "-dialog";
- if (CRASH_BEHAVIOR_ASK != cb)
- ask_dialog = ""; // omit '-dialog' option
- const char * cmdargv[] =
- {cmd.c_str(),
- ask_dialog,
- "-user",
- (char*)LLGridManager::getInstance()->getGridLabel().c_str(),
- "-name",
- LLAppViewer::instance()->getSecondLifeTitle().c_str(),
- NULL};
- fflush(NULL);
- pid_t pid = fork();
- if (pid == 0)
- { // child
- execv(cmd.c_str(), (char* const*) cmdargv); /* Flawfinder: ignore */
- llwarns << "execv failure when trying to start " << cmd << llendl;
- _exit(1); // avoid atexit()
+ // launch the actual crash logger
+ const char * cmdargv[] =
+ {cmd.c_str(),
+ "-user",
+ (char*)LLGridManager::getInstance()->getGridLabel().c_str(),
+ "-name",
+ LLAppViewer::instance()->getSecondLifeTitle().c_str(),
+ NULL};
+ fflush(NULL);
+ pid_t pid = fork();
+ if (pid == 0)
+ { // child
+ execv(cmd.c_str(), (char* const*) cmdargv); /* Flawfinder: ignore */
+ llwarns << "execv failure when trying to start " << cmd << llendl;
+ _exit(1); // avoid atexit()
+ }
+ else
+ {
+ if (pid > 0)
+ {
+ // DO NOT wait for child proc to die; we want
+ // the logger to outlive us while we quit to
+ // free up the screen/keyboard/etc.
+ ////int childExitStatus;
+ ////waitpid(pid, &childExitStatus, 0);
}
else
{
- if (pid > 0)
- {
- // DO NOT wait for child proc to die; we want
- // the logger to outlive us while we quit to
- // free up the screen/keyboard/etc.
- ////int childExitStatus;
- ////waitpid(pid, &childExitStatus, 0);
- }
- else
- {
- llwarns << "fork failure." << llendl;
- }
+ llwarns << "fork failure." << llendl;
}
}
// Sometimes signals don't seem to quit the viewer. Also, we may
diff --git a/indra/newview/llappviewerwin32.cpp b/indra/newview/llappviewerwin32.cpp
index 445bd208ef..f94c843ad9 100644
--- a/indra/newview/llappviewerwin32.cpp
+++ b/indra/newview/llappviewerwin32.cpp
@@ -518,11 +518,7 @@ void LLAppViewerWin32::handleCrashReporting(bool reportFreeze)
}
else
{
- S32 cb = gCrashSettings.getS32(CRASH_BEHAVIOR_SETTING);
- if(cb != CRASH_BEHAVIOR_NEVER_SEND)
- {
- _spawnl(_P_NOWAIT, exe_path.c_str(), arg_str, NULL);
- }
+ _spawnl(_P_NOWAIT, exe_path.c_str(), arg_str, NULL);
}
}
diff --git a/indra/newview/llassetuploadresponders.cpp b/indra/newview/llassetuploadresponders.cpp
index d7ba4ea470..5b9a449be1 100644
--- a/indra/newview/llassetuploadresponders.cpp
+++ b/indra/newview/llassetuploadresponders.cpp
@@ -449,7 +449,7 @@ void LLSendTexLayerResponder::uploadComplete(const LLSD& content)
std::string result = content["state"];
LLUUID new_id = content["new_asset"];
- llinfos << "result: " << result << "new_id:" << new_id << llendl;
+ llinfos << "result: " << result << " new_id: " << new_id << llendl;
if (result == "complete"
&& mBakedUploadData != NULL)
{ // Invoke
diff --git a/indra/newview/llassetuploadresponders.h b/indra/newview/llassetuploadresponders.h
index 70871b62e2..381b919c4a 100644
--- a/indra/newview/llassetuploadresponders.h
+++ b/indra/newview/llassetuploadresponders.h
@@ -112,6 +112,7 @@ private:
struct LLBakedUploadData;
class LLSendTexLayerResponder : public LLAssetUploadResponder
{
+ LOG_CLASS(LLSendTexLayerResponder);
public:
LLSendTexLayerResponder(const LLSD& post_data,
const LLUUID& vfile_id,
diff --git a/indra/newview/llfloaterpreference.cpp b/indra/newview/llfloaterpreference.cpp
index 7848484ac6..5fd262a720 100755
--- a/indra/newview/llfloaterpreference.cpp
+++ b/indra/newview/llfloaterpreference.cpp
@@ -751,10 +751,7 @@ void LLFloaterPreference::onBtnOK()
closeFloater(false);
LLUIColorTable::instance().saveUserSettings();
- gSavedSettings.saveToFile( gSavedSettings.getString("ClientSettingsFile"), TRUE );
- std::string crash_settings_filename = gDirUtilp->getExpandedFilename(LL_PATH_USER_SETTINGS, CRASH_SETTINGS_FILE);
- // save all settings, even if equals defaults
- gCrashSettings.saveToFile(crash_settings_filename, FALSE);
+ gSavedSettings.saveToFile(gSavedSettings.getString("ClientSettingsFile"), TRUE);
}
else
{
diff --git a/indra/newview/llpanelface.cpp b/indra/newview/llpanelface.cpp
index 07c7f35989..a4f6921f98 100644
--- a/indra/newview/llpanelface.cpp
+++ b/indra/newview/llpanelface.cpp
@@ -30,6 +30,7 @@
#include "llpanelface.h"
// library includes
+#include "llcalc.h"
#include "llerror.h"
#include "llfocusmgr.h"
#include "llrect.h"
@@ -926,6 +927,16 @@ void LLPanelFace::getState()
getChildView("button apply")->setEnabled(enabled);
}
}
+
+ // Set variable values for numeric expressions
+ LLCalc* calcp = LLCalc::getInstance();
+ calcp->setVar(LLCalc::TEX_U_SCALE, childGetValue("TexScaleU").asReal());
+ calcp->setVar(LLCalc::TEX_V_SCALE, childGetValue("TexScaleV").asReal());
+ calcp->setVar(LLCalc::TEX_U_OFFSET, childGetValue("TexOffsetU").asReal());
+ calcp->setVar(LLCalc::TEX_V_OFFSET, childGetValue("TexOffsetV").asReal());
+ calcp->setVar(LLCalc::TEX_ROTATION, childGetValue("TexRot").asReal());
+ calcp->setVar(LLCalc::TEX_TRANSPARENCY, childGetValue("ColorTrans").asReal());
+ calcp->setVar(LLCalc::TEX_GLOW, childGetValue("glow").asReal());
}
else
{
@@ -961,6 +972,16 @@ void LLPanelFace::getState()
//getChildView("has media")->setEnabled(FALSE);
//getChildView("media info set")->setEnabled(FALSE);
+
+ // Set variable values for numeric expressions
+ LLCalc* calcp = LLCalc::getInstance();
+ calcp->clearVar(LLCalc::TEX_U_SCALE);
+ calcp->clearVar(LLCalc::TEX_V_SCALE);
+ calcp->clearVar(LLCalc::TEX_U_OFFSET);
+ calcp->clearVar(LLCalc::TEX_V_OFFSET);
+ calcp->clearVar(LLCalc::TEX_ROTATION);
+ calcp->clearVar(LLCalc::TEX_TRANSPARENCY);
+ calcp->clearVar(LLCalc::TEX_GLOW);
}
}
diff --git a/indra/newview/llpanelobject.cpp b/indra/newview/llpanelobject.cpp
index 52917ff20b..c222bbb191 100644
--- a/indra/newview/llpanelobject.cpp
+++ b/indra/newview/llpanelobject.cpp
@@ -41,6 +41,7 @@
// project includes
#include "llagent.h"
#include "llbutton.h"
+#include "llcalc.h"
#include "llcheckboxctrl.h"
#include "llcolorswatch.h"
#include "llcombobox.h"
@@ -318,6 +319,8 @@ void LLPanelObject::getState( )
}
}
+ LLCalc* calcp = LLCalc::getInstance();
+
LLVOVolume *volobjp = NULL;
if ( objectp && (objectp->getPCode() == LL_PCODE_VOLUME))
{
@@ -334,6 +337,7 @@ void LLPanelObject::getState( )
// Disable all text input fields
clearCtrls();
+ calcp->clearAllVariables();
return;
}
@@ -360,12 +364,18 @@ void LLPanelObject::getState( )
mCtrlPosX->set( vec.mV[VX] );
mCtrlPosY->set( vec.mV[VY] );
mCtrlPosZ->set( vec.mV[VZ] );
+ calcp->setVar(LLCalc::X_POS, vec.mV[VX]);
+ calcp->setVar(LLCalc::Y_POS, vec.mV[VY]);
+ calcp->setVar(LLCalc::Z_POS, vec.mV[VZ]);
}
else
{
mCtrlPosX->clear();
mCtrlPosY->clear();
mCtrlPosZ->clear();
+ calcp->clearVar(LLCalc::X_POS);
+ calcp->clearVar(LLCalc::Y_POS);
+ calcp->clearVar(LLCalc::Z_POS);
}
@@ -380,12 +390,18 @@ void LLPanelObject::getState( )
mCtrlScaleX->set( vec.mV[VX] );
mCtrlScaleY->set( vec.mV[VY] );
mCtrlScaleZ->set( vec.mV[VZ] );
+ calcp->setVar(LLCalc::X_SCALE, vec.mV[VX]);
+ calcp->setVar(LLCalc::Y_SCALE, vec.mV[VY]);
+ calcp->setVar(LLCalc::Z_SCALE, vec.mV[VZ]);
}
else
{
mCtrlScaleX->clear();
mCtrlScaleY->clear();
mCtrlScaleZ->clear();
+ calcp->setVar(LLCalc::X_SCALE, 0.f);
+ calcp->setVar(LLCalc::Y_SCALE, 0.f);
+ calcp->setVar(LLCalc::Z_SCALE, 0.f);
}
mLabelSize->setEnabled( enable_scale );
@@ -405,12 +421,18 @@ void LLPanelObject::getState( )
mCtrlRotX->set( mCurEulerDegrees.mV[VX] );
mCtrlRotY->set( mCurEulerDegrees.mV[VY] );
mCtrlRotZ->set( mCurEulerDegrees.mV[VZ] );
+ calcp->setVar(LLCalc::X_ROT, mCurEulerDegrees.mV[VX]);
+ calcp->setVar(LLCalc::Y_ROT, mCurEulerDegrees.mV[VY]);
+ calcp->setVar(LLCalc::Z_ROT, mCurEulerDegrees.mV[VZ]);
}
else
{
mCtrlRotX->clear();
mCtrlRotY->clear();
mCtrlRotZ->clear();
+ calcp->clearVar(LLCalc::X_ROT);
+ calcp->clearVar(LLCalc::Y_ROT);
+ calcp->clearVar(LLCalc::Z_ROT);
}
mLabelRotation->setEnabled( enable_rotate );
@@ -625,9 +647,9 @@ void LLPanelObject::getState( )
F32 end_t = volume_params.getEndT();
// Hollowness
- F32 hollow = volume_params.getHollow();
- mSpinHollow->set( 100.f * hollow );
-
+ F32 hollow = 100.f * volume_params.getHollow();
+ mSpinHollow->set( hollow );
+ calcp->setVar(LLCalc::HOLLOW, hollow);
// All hollow objects allow a shape to be selected.
if (hollow > 0.f)
{
@@ -679,6 +701,10 @@ void LLPanelObject::getState( )
mSpinCutEnd ->set( cut_end );
mCtrlPathBegin ->set( adv_cut_begin );
mCtrlPathEnd ->set( adv_cut_end );
+ calcp->setVar(LLCalc::CUT_BEGIN, cut_begin);
+ calcp->setVar(LLCalc::CUT_END, cut_end);
+ calcp->setVar(LLCalc::PATH_BEGIN, adv_cut_begin);
+ calcp->setVar(LLCalc::PATH_END, adv_cut_end);
// Twist
F32 twist = volume_params.getTwist();
@@ -697,18 +723,24 @@ void LLPanelObject::getState( )
mSpinTwist ->set( twist );
mSpinTwistBegin ->set( twist_begin );
+ calcp->setVar(LLCalc::TWIST_END, twist);
+ calcp->setVar(LLCalc::TWIST_BEGIN, twist_begin);
// Shear
F32 shear_x = volume_params.getShearX();
F32 shear_y = volume_params.getShearY();
mSpinShearX->set( shear_x );
mSpinShearY->set( shear_y );
+ calcp->setVar(LLCalc::X_SHEAR, shear_x);
+ calcp->setVar(LLCalc::Y_SHEAR, shear_y);
// Taper
F32 taper_x = volume_params.getTaperX();
F32 taper_y = volume_params.getTaperY();
mSpinTaperX->set( taper_x );
mSpinTaperY->set( taper_y );
+ calcp->setVar(LLCalc::X_TAPER, taper_x);
+ calcp->setVar(LLCalc::Y_TAPER, taper_y);
// Radius offset.
F32 radius_offset = volume_params.getRadiusOffset();
@@ -738,10 +770,12 @@ void LLPanelObject::getState( )
}
}
mSpinRadiusOffset->set( radius_offset);
+ calcp->setVar(LLCalc::RADIUS_OFFSET, radius_offset);
// Revolutions
F32 revolutions = volume_params.getRevolutions();
mSpinRevolutions->set( revolutions );
+ calcp->setVar(LLCalc::REVOLUTIONS, revolutions);
// Skew
F32 skew = volume_params.getSkew();
@@ -766,6 +800,7 @@ void LLPanelObject::getState( )
}
}
mSpinSkew->set( skew );
+ calcp->setVar(LLCalc::SKEW, skew);
}
// Compute control visibility, label names, and twist range.
@@ -869,6 +904,8 @@ void LLPanelObject::getState( )
case MI_RING:
mSpinScaleX->set( scale_x );
mSpinScaleY->set( scale_y );
+ calcp->setVar(LLCalc::X_HOLE, scale_x);
+ calcp->setVar(LLCalc::Y_HOLE, scale_y);
mSpinScaleX->setMinValue(OBJECT_MIN_HOLE_SIZE);
mSpinScaleX->setMaxValue(OBJECT_MAX_HOLE_SIZE_X);
mSpinScaleY->setMinValue(OBJECT_MIN_HOLE_SIZE);
@@ -883,6 +920,14 @@ void LLPanelObject::getState( )
mSpinScaleX->setMaxValue(1.f);
mSpinScaleY->setMinValue(-1.f);
mSpinScaleY->setMaxValue(1.f);
+
+ // Torus' Hole Size is Box/Cyl/Prism's Taper
+ calcp->setVar(LLCalc::X_TAPER, 1.f - scale_x);
+ calcp->setVar(LLCalc::Y_TAPER, 1.f - scale_y);
+
+ // Box/Cyl/Prism have no hole size
+ calcp->setVar(LLCalc::X_HOLE, 0.f);
+ calcp->setVar(LLCalc::Y_HOLE, 0.f);
}
break;
}
diff --git a/indra/newview/llpanelwearing.cpp b/indra/newview/llpanelwearing.cpp
index 0645fd8a54..f19b54c1d4 100644
--- a/indra/newview/llpanelwearing.cpp
+++ b/indra/newview/llpanelwearing.cpp
@@ -38,6 +38,8 @@
#include "llsidetray.h"
#include "llviewermenu.h"
#include "llwearableitemslist.h"
+#include "llsdserialize.h"
+#include "llclipboard.h"
// Context menu and Gear menu helper.
static void edit_outfit()
@@ -58,6 +60,7 @@ public:
registrar.add("Gear.Edit", boost::bind(&edit_outfit));
registrar.add("Gear.TakeOff", boost::bind(&LLWearingGearMenu::onTakeOff, this));
+ registrar.add("Gear.Copy", boost::bind(&LLPanelWearing::copyToClipboard, mPanelWearing));
enable_registrar.add("Gear.OnEnable", boost::bind(&LLPanelWearing::isActionEnabled, mPanelWearing, _2));
@@ -280,4 +283,25 @@ void LLPanelWearing::getSelectedItemsUUIDs(uuid_vec_t& selected_uuids) const
mCOFItemsList->getSelectedUUIDs(selected_uuids);
}
+void LLPanelWearing::copyToClipboard()
+{
+ std::string text;
+ std::vector<LLSD> data;
+ mCOFItemsList->getValues(data);
+
+ for(std::vector<LLSD>::const_iterator iter = data.begin(); iter != data.end();)
+ {
+ LLSD uuid = (*iter);
+ LLViewerInventoryItem* item = gInventory.getItem(uuid);
+
+ iter++;
+ if (item != NULL)
+ {
+ // Append a newline to all but the last line
+ text += iter != data.end() ? item->getName() + "\n" : item->getName();
+ }
+ }
+
+ gClipboard.copyFromString(utf8str_to_wstring(text));
+}
// EOF
diff --git a/indra/newview/llpanelwearing.h b/indra/newview/llpanelwearing.h
index 157b2c4c5f..9a212b3cca 100644
--- a/indra/newview/llpanelwearing.h
+++ b/indra/newview/llpanelwearing.h
@@ -60,6 +60,8 @@ public:
/*virtual*/ void getSelectedItemsUUIDs(uuid_vec_t& selected_uuids) const;
+ /*virtual*/ void copyToClipboard();
+
boost::signals2::connection setSelectionChangeCallback(commit_callback_t cb);
bool hasItemSelected();
diff --git a/indra/newview/lltexlayer.cpp b/indra/newview/lltexlayer.cpp
index 500c2a7b86..bd41aa64f0 100644
--- a/indra/newview/lltexlayer.cpp
+++ b/indra/newview/lltexlayer.cpp
@@ -51,6 +51,9 @@
using namespace LLVOAvatarDefines;
+static const S32 BAKE_UPLOAD_ATTEMPTS = 7;
+static const F32 BAKE_UPLOAD_RETRY_DELAY = 2.f; // actual delay grows by power of 2 each attempt
+
class LLTexLayerInfo
{
friend class LLTexLayer;
@@ -93,11 +96,13 @@ private:
//-----------------------------------------------------------------------------
LLBakedUploadData::LLBakedUploadData(const LLVOAvatarSelf* avatar,
LLTexLayerSet* layerset,
- const LLUUID& id) :
+ const LLUUID& id,
+ bool highest_res) :
mAvatar(avatar),
mTexLayerSet(layerset),
mID(id),
- mStartTime(LLFrameTimer::getTotalTime()) // Record starting time
+ mStartTime(LLFrameTimer::getTotalTime()), // Record starting time
+ mIsHighestRes(highest_res)
{
}
@@ -116,6 +121,7 @@ LLTexLayerSetBuffer::LLTexLayerSetBuffer(LLTexLayerSet* const owner,
mUploadPending(FALSE), // Not used for any logic here, just to sync sending of updates
mNeedsUpload(FALSE),
mNumLowresUploads(0),
+ mUploadFailCount(0),
mNeedsUpdate(TRUE),
mNumLowresUpdates(0),
mTexLayerSet(owner)
@@ -204,6 +210,7 @@ void LLTexLayerSetBuffer::cancelUpload()
mNeedsUpload = FALSE;
mUploadPending = FALSE;
mNeedsUploadTimer.pause();
+ mUploadRetryTimer.reset();
}
void LLTexLayerSetBuffer::pushProjection() const
@@ -356,25 +363,38 @@ BOOL LLTexLayerSetBuffer::isReadyToUpload() const
if (!gAgentQueryManager.hasNoPendingQueries()) return FALSE; // Can't upload if there are pending queries.
if (isAgentAvatarValid() && !gAgentAvatarp->isUsingBakedTextures()) return FALSE; // Don't upload if avatar is using composites.
- // If we requested an upload and have the final LOD ready, then upload.
- if (mTexLayerSet->isLocalTextureDataFinal()) return TRUE;
-
- // Upload if we've hit a timeout. Upload is a pretty expensive process so we need to make sure
- // we aren't doing uploads too frequently.
- const U32 texture_timeout = gSavedSettings.getU32("AvatarBakedTextureUploadTimeout");
- if (texture_timeout != 0)
+ BOOL ready = FALSE;
+ if (mTexLayerSet->isLocalTextureDataFinal())
+ {
+ // If we requested an upload and have the final LOD ready, upload (or wait a while if this is a retry)
+ if (mUploadFailCount == 0)
+ {
+ ready = TRUE;
+ }
+ else
+ {
+ ready = mUploadRetryTimer.getElapsedTimeF32() >= BAKE_UPLOAD_RETRY_DELAY * (1 << (mUploadFailCount - 1));
+ }
+ }
+ else
{
- // The timeout period increases exponentially between every lowres upload in order to prevent
- // spamming the server with frequent uploads.
- const U32 texture_timeout_threshold = texture_timeout*(1 << mNumLowresUploads);
+ // Upload if we've hit a timeout. Upload is a pretty expensive process so we need to make sure
+ // we aren't doing uploads too frequently.
+ const U32 texture_timeout = gSavedSettings.getU32("AvatarBakedTextureUploadTimeout");
+ if (texture_timeout != 0)
+ {
+ // The timeout period increases exponentially between every lowres upload in order to prevent
+ // spamming the server with frequent uploads.
+ const U32 texture_timeout_threshold = texture_timeout*(1 << mNumLowresUploads);
- // If we hit our timeout and have textures available at even lower resolution, then upload.
- const BOOL is_upload_textures_timeout = mNeedsUploadTimer.getElapsedTimeF32() >= texture_timeout_threshold;
- const BOOL has_lower_lod = mTexLayerSet->isLocalTextureDataAvailable();
- if (has_lower_lod && is_upload_textures_timeout) return TRUE;
+ // If we hit our timeout and have textures available at even lower resolution, then upload.
+ const BOOL is_upload_textures_timeout = mNeedsUploadTimer.getElapsedTimeF32() >= texture_timeout_threshold;
+ const BOOL has_lower_lod = mTexLayerSet->isLocalTextureDataAvailable();
+ ready = has_lower_lod && is_upload_textures_timeout;
+ }
}
- return FALSE;
+ return ready;
}
BOOL LLTexLayerSetBuffer::isReadyToUpdate() const
@@ -482,17 +502,20 @@ void LLTexLayerSetBuffer::doUpload()
if (valid)
{
+ const bool highest_lod = mTexLayerSet->isLocalTextureDataFinal();
// Baked_upload_data is owned by the responder and deleted after the request completes.
LLBakedUploadData* baked_upload_data = new LLBakedUploadData(gAgentAvatarp,
this->mTexLayerSet,
- asset_id);
+ asset_id,
+ highest_lod);
// upload ID is used to avoid overlaps, e.g. when the user rapidly makes two changes outside of Face Edit.
mUploadID = asset_id;
// Upload the image
const std::string url = gAgent.getRegion()->getCapability("UploadBakedTexture");
if(!url.empty()
- && !LLPipeline::sForceOldBakedUpload) // toggle debug setting UploadBakedTexOld to change between the new caps method and old method
+ && !LLPipeline::sForceOldBakedUpload // toggle debug setting UploadBakedTexOld to change between the new caps method and old method
+ && (mUploadFailCount < (BAKE_UPLOAD_ATTEMPTS - 1))) // Try last ditch attempt via asset store if cap upload is failing.
{
LLSD body = LLSD::emptyMap();
// The responder will call LLTexLayerSetBuffer::onTextureUploadComplete()
@@ -511,7 +534,6 @@ void LLTexLayerSetBuffer::doUpload()
llinfos << "Baked texture upload via Asset Store." << llendl;
}
- const BOOL highest_lod = mTexLayerSet->isLocalTextureDataFinal();
if (highest_lod)
{
// Sending the final LOD for the baked texture. All done, pause
@@ -603,14 +625,15 @@ void LLTexLayerSetBuffer::onTextureUploadComplete(const LLUUID& uuid,
{
LLBakedUploadData* baked_upload_data = (LLBakedUploadData*)userdata;
- if ((result == 0) &&
- isAgentAvatarValid() &&
+ if (isAgentAvatarValid() &&
!gAgentAvatarp->isDead() &&
(baked_upload_data->mAvatar == gAgentAvatarp) && // Sanity check: only the user's avatar should be uploading textures.
(baked_upload_data->mTexLayerSet->hasComposite()))
{
LLTexLayerSetBuffer* layerset_buffer = baked_upload_data->mTexLayerSet->getComposite();
-
+ S32 failures = layerset_buffer->mUploadFailCount;
+ layerset_buffer->mUploadFailCount = 0;
+
if (layerset_buffer->mUploadID.isNull())
{
// The upload got canceled, we should be in the
@@ -626,20 +649,28 @@ void LLTexLayerSetBuffer::onTextureUploadComplete(const LLUUID& uuid,
{
// This is the upload we're currently waiting for.
layerset_buffer->mUploadID.setNull();
+ const std::string name(baked_upload_data->mTexLayerSet->getBodyRegionName());
+ const std::string resolution = baked_upload_data->mIsHighestRes ? " full res " : " low res ";
if (result >= 0)
{
- layerset_buffer->mUploadPending = FALSE;
+ layerset_buffer->mUploadPending = FALSE; // Allows sending of AgentSetAppearance later
LLVOAvatarDefines::ETextureIndex baked_te = gAgentAvatarp->getBakedTE(layerset_buffer->mTexLayerSet);
// Update baked texture info with the new UUID
U64 now = LLFrameTimer::getTotalTime(); // Record starting time
- llinfos << "Baked texture upload took " << (S32)((now - baked_upload_data->mStartTime) / 1000) << " ms" << llendl;
+ llinfos << "Baked" << resolution << "texture upload for " << name << " took " << (S32)((now - baked_upload_data->mStartTime) / 1000) << " ms" << llendl;
gAgentAvatarp->setNewBakedTexture(baked_te, uuid);
}
else
{
- // Avatar appearance is changing, ignore the upload results
- llinfos << "Baked upload failed. Reason: " << result << llendl;
- // *FIX: retry upload after n seconds, asset server could be busy
+ ++failures;
+ S32 max_attempts = baked_upload_data->mIsHighestRes ? BAKE_UPLOAD_ATTEMPTS : 1; // only retry final bakes
+ llwarns << "Baked" << resolution << "texture upload for " << name << " failed (attempt " << failures << "/" << max_attempts << ")" << llendl;
+ if (failures < max_attempts)
+ {
+ layerset_buffer->mUploadFailCount = failures;
+ layerset_buffer->mUploadRetryTimer.start();
+ layerset_buffer->requestUpload();
+ }
}
}
else
diff --git a/indra/newview/lltexlayer.h b/indra/newview/lltexlayer.h
index 2d710d2dce..85dadb213c 100644
--- a/indra/newview/lltexlayer.h
+++ b/indra/newview/lltexlayer.h
@@ -312,6 +312,8 @@ private:
BOOL mUploadPending; // Whether we have received back the new baked textures
LLUUID mUploadID; // The current upload process (null if none).
LLFrameTimer mNeedsUploadTimer; // Tracks time since upload was requested and performed.
+ S32 mUploadFailCount; // Number of consecutive upload failures
+ LLFrameTimer mUploadRetryTimer; // Tracks time since last upload failure.
//--------------------------------------------------------------------
// Updates
@@ -363,12 +365,14 @@ struct LLBakedUploadData
{
LLBakedUploadData(const LLVOAvatarSelf* avatar,
LLTexLayerSet* layerset,
- const LLUUID& id);
+ const LLUUID& id,
+ bool highest_res);
~LLBakedUploadData() {}
const LLUUID mID;
const LLVOAvatarSelf* mAvatar; // note: backlink only; don't LLPointer
LLTexLayerSet* mTexLayerSet;
const U64 mStartTime; // for measuring baked texture upload time
+ const bool mIsHighestRes; // whether this is a "final" bake, or intermediate low res
};
#endif // LL_LLTEXLAYER_H
diff --git a/indra/newview/llviewercontrol.cpp b/indra/newview/llviewercontrol.cpp
index 87ca80260f..b87ca1eaec 100644
--- a/indra/newview/llviewercontrol.cpp
+++ b/indra/newview/llviewercontrol.cpp
@@ -566,7 +566,7 @@ bool toggle_show_object_render_cost(const LLSD& newvalue)
return true;
}
-void toggle_updater_service_active(LLControlVariable* control, const LLSD& new_value)
+void toggle_updater_service_active(const LLSD& new_value)
{
if(new_value.asInteger())
{
@@ -735,7 +735,7 @@ void settings_setup_listeners()
gSavedSettings.getControl("ShowNavbarFavoritesPanel")->getSignal()->connect(boost::bind(&toggle_show_favorites_panel, _2));
gSavedSettings.getControl("ShowMiniLocationPanel")->getSignal()->connect(boost::bind(&toggle_show_mini_location_panel, _2));
gSavedSettings.getControl("ShowObjectRenderingCost")->getSignal()->connect(boost::bind(&toggle_show_object_render_cost, _2));
- gSavedSettings.getControl("UpdaterServiceSetting")->getSignal()->connect(&toggle_updater_service_active);
+ gSavedSettings.getControl("UpdaterServiceSetting")->getSignal()->connect(boost::bind(&toggle_updater_service_active, _2));
gSavedSettings.getControl("ForceShowGrid")->getSignal()->connect(boost::bind(&handleForceShowGrid, _2));
gSavedSettings.getControl("RenderTransparentWater")->getSignal()->connect(boost::bind(&handleRenderTransparentWaterChanged, _2));
}
diff --git a/indra/newview/skins/default/xui/en/menu_wearing_gear.xml b/indra/newview/skins/default/xui/en/menu_wearing_gear.xml
index 0ac2c14253..0e858ccf10 100644
--- a/indra/newview/skins/default/xui/en/menu_wearing_gear.xml
+++ b/indra/newview/skins/default/xui/en/menu_wearing_gear.xml
@@ -20,4 +20,11 @@
function="Gear.OnEnable"
parameter="take_off" />
</menu_item_call>
+ <menu_item_call
+ label="Copy outfit list to clipboard"
+ layout="topleft"
+ name="copy">
+ <on_click
+ function="Gear.Copy" />
+ </menu_item_call>
</toggleable_menu>
diff --git a/indra/newview/skins/default/xui/en/panel_preferences_setup.xml b/indra/newview/skins/default/xui/en/panel_preferences_setup.xml
index 1c22a5c02e..e639f0dc9d 100644
--- a/indra/newview/skins/default/xui/en/panel_preferences_setup.xml
+++ b/indra/newview/skins/default/xui/en/panel_preferences_setup.xml
@@ -268,7 +268,7 @@
height="23"
layout="topleft"
left_delta="50"
- top_pad="5"
+ top_pad="5"
name="updater_service_combobox"
width="300">
<combo_box.item
diff --git a/indra/win_crash_logger/llcrashloggerwindows.cpp b/indra/win_crash_logger/llcrashloggerwindows.cpp
index 51ff754c27..170babbb98 100644
--- a/indra/win_crash_logger/llcrashloggerwindows.cpp
+++ b/indra/win_crash_logger/llcrashloggerwindows.cpp
@@ -296,6 +296,7 @@ void LLCrashLoggerWindows::gatherPlatformSpecificFiles()
bool LLCrashLoggerWindows::mainLoop()
{
+ llinfos << "CrashSubmitBehavior is " << mCrashBehavior << llendl;
// Note: parent hwnd is 0 (the desktop). No dlg proc. See Petzold (5th ed) HexCalc example, Chapter 11, p529
// win_crash_logger.rc has been edited by hand.
@@ -308,6 +309,7 @@ bool LLCrashLoggerWindows::mainLoop()
if (mCrashBehavior == CRASH_BEHAVIOR_ALWAYS_SEND)
{
+ llinfos << "Showing crash report submit progress window." << llendl;
ShowWindow(gHwndProgress, SW_SHOW );
sendCrashLogs();
}
@@ -354,7 +356,7 @@ bool LLCrashLoggerWindows::mainLoop()
void LLCrashLoggerWindows::updateApplication(const std::string& message)
{
- LLCrashLogger::updateApplication();
+ LLCrashLogger::updateApplication(message);
if(!message.empty()) show_progress(message);
update_messages();
}
@@ -370,6 +372,3 @@ bool LLCrashLoggerWindows::cleanup()
PostQuitMessage(0);
return true;
}
-
-
-
diff --git a/indra/win_crash_logger/llcrashloggerwindows.h b/indra/win_crash_logger/llcrashloggerwindows.h
index 24c564457c..5c45a998b3 100644
--- a/indra/win_crash_logger/llcrashloggerwindows.h
+++ b/indra/win_crash_logger/llcrashloggerwindows.h
@@ -41,7 +41,6 @@ public:
virtual void updateApplication(const std::string& message = LLStringUtil::null);
virtual bool cleanup();
virtual void gatherPlatformSpecificFiles();
- //void annotateCallStack();
void setHandle(HINSTANCE hInst) { mhInst = hInst; }
private:
void ProcessDlgItemText(HWND hWnd, int nIDDlgItem);
diff --git a/indra/win_crash_logger/win_crash_logger.cpp b/indra/win_crash_logger/win_crash_logger.cpp
index 5c22053317..8e916ae437 100644
--- a/indra/win_crash_logger/win_crash_logger.cpp
+++ b/indra/win_crash_logger/win_crash_logger.cpp
@@ -24,51 +24,30 @@
* $/LicenseInfo$
*/
-// win_crash_logger.cpp : Defines the entry point for the application.
-//
-
-// Must be first include, precompiled headers.
#include "linden_common.h"
-
#include "stdafx.h"
-
#include <stdlib.h>
-
#include "llcrashloggerwindows.h"
-
-
-//
-// Implementation
-//
-
int APIENTRY WinMain(HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPSTR lpCmdLine,
int nCmdShow)
{
- llinfos << "Starting crash reporter" << llendl;
+ llinfos << "Starting crash reporter." << llendl;
LLCrashLoggerWindows app;
app.setHandle(hInstance);
- bool ok = app.init();
- if(!ok)
+ app.parseCommandOptions(__argc, __argv);
+
+ if (! app.init())
{
llwarns << "Unable to initialize application." << llendl;
return -1;
}
- // Run the application main loop
- if(!LLApp::isQuitting()) app.mainLoop();
-
- if (!app.isError())
- {
- //
- // We don't want to do cleanup here if the error handler got called -
- // the assumption is that the error handler is responsible for doing
- // app cleanup if there was a problem.
- //
- app.cleanup();
- }
+ app.mainLoop();
+ app.cleanup();
+ llinfos << "Crash reporter finished normally." << llendl;
return 0;
}