summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorErik Kundiman <erik@megapahit.org>2026-03-30 17:10:44 +0800
committerErik Kundiman <erik@megapahit.org>2026-03-30 17:10:44 +0800
commit14239ba6f6d4a73038a270de5d8f295fb59498bd (patch)
treeb64cb90623d9cf4709fc2bec5a51b4c99a421a4a
parenteac10375e294cd24c21c09497e838e70dcf4d74a (diff)
parentf148dbcc8a9566f1f717fdeb00813ad57427bee7 (diff)
Merge tag 'Second_Life_Release#f148dbcc-26.2' into 2026.02
-rw-r--r--indra/llcommon/llapp.h1
-rw-r--r--indra/llcommon/llwatchdog.cpp70
-rw-r--r--indra/llcommon/llwatchdog.h25
-rw-r--r--indra/llprimitive/llgltfmaterial.cpp65
-rw-r--r--indra/newview/llappviewer.cpp143
-rw-r--r--indra/newview/llappviewer.h3
-rw-r--r--indra/newview/llappviewerwin32.cpp50
-rw-r--r--indra/newview/llappviewerwin32.h1
-rw-r--r--indra/newview/llfilepicker_mac.mm4
-rw-r--r--indra/newview/llvoiceclient.cpp10
-rw-r--r--indra/newview/llworldmapmessage.cpp1
-rw-r--r--indra/newview/skins/default/xui/en/strings.xml3
12 files changed, 314 insertions, 62 deletions
diff --git a/indra/llcommon/llapp.h b/indra/llcommon/llapp.h
index ce09c566a9..fef7dc80b3 100644
--- a/indra/llcommon/llapp.h
+++ b/indra/llcommon/llapp.h
@@ -285,6 +285,7 @@ public:
#ifdef LL_WINDOWS
virtual bool reportCrashToBugsplat(void* pExcepInfo /*EXCEPTION_POINTERS*/) { return false; }
+ virtual bool reportCustomToBugsplat(const std::string& desription) { return false; }
#endif
public:
diff --git a/indra/llcommon/llwatchdog.cpp b/indra/llcommon/llwatchdog.cpp
index 1bc1283d0b..1622aeb180 100644
--- a/indra/llcommon/llwatchdog.cpp
+++ b/indra/llcommon/llwatchdog.cpp
@@ -87,7 +87,7 @@ void LLWatchdogEntry::start()
void LLWatchdogEntry::stop()
{
// this can happen very late in the shutdown sequence
- if (!LLWatchdog::wasDeleted())
+ if (LLWatchdog::instanceExists())
{
LLWatchdog::getInstance()->remove(this);
}
@@ -173,6 +173,17 @@ void LLWatchdog::add(LLWatchdogEntry* e)
{
lockThread();
mSuspects.insert(e);
+
+ if (!mFrozeList.empty())
+ {
+ mFrozeList.erase(e);
+ if (mFrozeList.empty())
+ {
+ // Clear error marker file if there is no frozen threads,
+ // viewer is responsive again.
+ mClearMarkerFnc();
+ }
+ }
unlockThread();
}
@@ -183,7 +194,12 @@ void LLWatchdog::remove(LLWatchdogEntry* e)
unlockThread();
}
-void LLWatchdog::init(func_t set_error_state_callback)
+void LLWatchdog::init(
+ create_marker_func_t error_state_callback,
+ clear_marker_func_t clear_marker_callback,
+ report_func_t report_callback,
+ notify_func_t notify_callback,
+ bool crash_on_freeze)
{
if (!mSuspectsAccessMutex && !mTimer)
{
@@ -196,7 +212,11 @@ void LLWatchdog::init(func_t set_error_state_callback)
// start needs to use the mSuspectsAccessMutex
mTimer->start();
}
- mCreateMarkerFnc = set_error_state_callback;
+ mCreateMarkerFnc = error_state_callback;
+ mClearMarkerFnc = clear_marker_callback;
+ mCrashReportFnc = report_callback;
+ mNotifyFnc = notify_callback;
+ mCrashOnFreeze = crash_on_freeze;
}
void LLWatchdog::cleanup()
@@ -251,21 +271,45 @@ void LLWatchdog::run()
mTimer->stop();
}
- // Sets error marker file
- mCreateMarkerFnc();
- // Todo1: Warn user?
- // Todo2: We probably want to report even if 5 seconds passed, just not error 'yet'.
std::string last_state = (*result)->getLastState();
- if (last_state.empty())
+ std::string description = "Watchdog timer for thread " + (*result)->getThreadName() + " expired";
+ if (!last_state.empty())
{
- LL_ERRS() << "Watchdog timer for thread " << (*result)->getThreadName()
- << " expired; assuming viewer is hung and crashing" << LL_ENDL;
+ description += " with state: " + last_state;
+ }
+ description += "; assuming viewer is hung and crashing";
+
+ if (!mCrashOnFreeze)
+ {
+ // Sets watchdog marker file
+ mCreateMarkerFnc(false);
+ // If it's mainloop and it somehow recovers, it will re-add itself
+ mSuspects.erase(*result);
+ mFrozeList.insert(*result);
+ LL_WARNS() << description << LL_ENDL;
}
else
{
- LL_ERRS() << "Watchdog timer for thread " << (*result)->getThreadName()
- << " expired with state: " << last_state
- << "; assuming viewer is hung and crashing" << LL_ENDL;
+
+ if (!mCrashReportFnc(description))
+ {
+ // Sets error marker file
+ mCreateMarkerFnc(true);
+ // If false is returned, then we failed to report the issue to bugsplat,
+ // instead, Notify user, then crash viewer.
+ // Todo: ask user if viewer should quit or wait?
+ mNotifyFnc();
+ LL_ERRS() << description << LL_ENDL;
+ }
+ else
+ {
+ // Sets watchdog marker file
+ mCreateMarkerFnc(false);
+ // Already reported, don't report again.
+ // If it's mainloop and it somehow recovers, it will re-add itself
+ mSuspects.erase(result);
+ mFrozeList.insert(*result);
+ }
}
}
}
diff --git a/indra/llcommon/llwatchdog.h b/indra/llcommon/llwatchdog.h
index fded881bb8..f138fbccb0 100644
--- a/indra/llcommon/llwatchdog.h
+++ b/indra/llcommon/llwatchdog.h
@@ -83,18 +83,26 @@ private:
};
class LLWatchdogTimerThread; // Defined in the cpp
-class LLWatchdog : public LLSingleton<LLWatchdog>
+class LLWatchdog : public LLSimpleton<LLWatchdog>
{
- LLSINGLETON(LLWatchdog);
+public:
+ LLWatchdog();
~LLWatchdog();
-public:
// Add an entry to the watchdog.
void add(LLWatchdogEntry* e);
void remove(LLWatchdogEntry* e);
- typedef std::function<void()> func_t;
- void init(func_t set_error_state_callback);
+ typedef std::function<void(bool)> create_marker_func_t;
+ typedef std::function<void()> clear_marker_func_t;
+ typedef std::function<bool(std::string&)> report_func_t;
+ typedef std::function<void()> notify_func_t;
+ void init(
+ create_marker_func_t error_state_callback,
+ clear_marker_func_t clear_marker_callback,
+ report_func_t report_callback,
+ notify_func_t notify_callback,
+ bool crash_on_freeze);
void run();
void cleanup();
@@ -105,14 +113,19 @@ private:
typedef std::set<LLWatchdogEntry*> SuspectsRegistry;
SuspectsRegistry mSuspects;
+ SuspectsRegistry mFrozeList;
LLMutex* mSuspectsAccessMutex;
LLWatchdogTimerThread* mTimer;
U64 mLastClockCount;
+ bool mCrashOnFreeze;
// At the moment watchdog expects app to set markers in mCreateMarkerFnc,
// but technically can be used to set any error states or do some cleanup
// or show warnings.
- func_t mCreateMarkerFnc;
+ create_marker_func_t mCreateMarkerFnc;
+ clear_marker_func_t mClearMarkerFnc;
+ report_func_t mCrashReportFnc;
+ notify_func_t mNotifyFnc;
};
#endif // LL_LLTHREADWATCHDOG_H
diff --git a/indra/llprimitive/llgltfmaterial.cpp b/indra/llprimitive/llgltfmaterial.cpp
index 1c47001272..764ab222ad 100644
--- a/indra/llprimitive/llgltfmaterial.cpp
+++ b/indra/llprimitive/llgltfmaterial.cpp
@@ -937,6 +937,15 @@ void LLGLTFMaterial::updateTextureTracking()
// Case 4.
// Input: scale 1.0,1.0; Offset horizontal 0.5, Offset vertical 0.0 Rotation 0.349066;
// Expected output: scale 1.0,1.0; Offset horizontal 0.701, Offset vertical -0.141 Rotation -0.349066;
+// Case 5.
+// Input: scale 10.0,15.0; Offset horizontal 0.0, Offset vertical 0.0 Rotation -1.57079637
+// Expected output: scale 15.0,10.0; Offset horizontal 7.5, Offset vertical -4.0 Rotation 1.57079637;
+// Case 6.
+// Input: scale 10.0,15.0; Offset horizontal 0.0, Offset vertical 0.0 Rotation 0
+// Expected output: scale 10.0,15.0; Offset horizontal 0.5, Offset vertical .0 Rotation 0;
+// Case 7.
+// Input: scale 10.0,15.0; Offset horizontal 0.0, Offset vertical 0.0 Rotation -0.785398163
+// Expected output: scale 12.74,12.74; Offset horizontal 0.5, Offset vertical .0 Rotation 0.785398163;
//
// Legacy offsets are right to left and top to bottom.
// PBR offsets are right to left and bottom to top.
@@ -953,16 +962,32 @@ void LLGLTFMaterial::convertTextureTransformToPBR(
LLVector2& pbr_offset,
F32& pbr_rotation)
{
- pbr_scale.set(tex_scale_s, tex_scale_t);
+ // Legacy is counter-clockwise, PBR is clockwise
pbr_rotation = -tex_rotation;
// Center of the tile
const F32 center_s = 0.5f;
const F32 center_t = 0.5f;
+ // Calculate the rotated scale
+ F32 cos_rot = cosf(tex_rotation);
+ F32 sin_rot = sinf(tex_rotation);
+ F32 cos_sq = cos_rot * cos_rot;
+ F32 sin_sq = sin_rot * sin_rot;
+
+ // GLTF scale doesn't match legacy scaling when rotation is applied.
+ // Legacy applies scale then rotation, which allows for planar aligment
+ // withoutn deformations, but gltf rotates first, so when scale gets
+ // aplied image gets deformed by rotation.
+ // It appears to be imposible to properly match legacy scale, so this
+ // is an approximation that at least matches at 0, 90, 180, 270 degree
+ // rotations, and is close enough at angles like 45.
+ pbr_scale.mV[VX] = tex_scale_s * cos_sq + tex_scale_t * sin_sq;
+ pbr_scale.mV[VY] = tex_scale_s * sin_sq + tex_scale_t * cos_sq;
+
// Center adjustment for scale
- F32 center_adjust_s = 0.5f * (1.0f - tex_scale_s);
- F32 center_adjust_t = 0.5f * (1.0f - tex_scale_t);
+ F32 center_adjust_s = 0.5f * (1.0f - pbr_scale.mV[VX]);
+ F32 center_adjust_t = 0.5f * (1.0f - pbr_scale.mV[VY]);
// 2. Offset from center
F32 pos_s = center_adjust_s - center_s;
@@ -990,17 +1015,43 @@ void LLGLTFMaterial::convertPBRTransformToTexture(
F32& tex_offset_t,
F32& tex_rotation)
{
- tex_scale_s = pbr_scale.mV[0];
- tex_scale_t = pbr_scale.mV[1];
tex_rotation = -pbr_rotation;
+ // Reverse the scale transformation
+ // From: pbr_s = tex_s * cos² + tex_t * sin²
+ // pbr_t = tex_s * sin² + tex_t * cos²
+ // Solve for tex_s and tex_t
+ F32 cos_rot = cosf(tex_rotation);
+ F32 sin_rot = sinf(tex_rotation);
+ F32 cos_sq = cos_rot * cos_rot;
+ F32 sin_sq = sin_rot * sin_rot;
+
+ F32 denom = cos_sq * cos_sq - sin_sq * sin_sq;
+
+ if (fabsf(denom) < 0.0001f) // Near 45 degrees (cos²≈sin²≈0.5)
+ {
+ // At 45°: both scales contribute equally
+ // pbr_s = pbr_t = (tex_s + tex_t) / 2
+ // So: tex_s + tex_t = 2 * pbr_avg
+ // Use the average and assume symmetric scaling
+ tex_scale_s = tex_scale_t = (pbr_scale.mV[VX] + pbr_scale.mV[VY]) / 2.f;
+ }
+ else
+ {
+ // Solve the 2x2 system:
+ // pbr_s * cos² - pbr_t * sin² = tex_s * (cos⁴ - sin⁴)
+ // pbr_t * cos² - pbr_s * sin² = tex_t * (cos⁴ - sin⁴)
+ tex_scale_s = (pbr_scale.mV[VX] * cos_sq - pbr_scale.mV[VY] * sin_sq) / denom;
+ tex_scale_t = (pbr_scale.mV[VY] * cos_sq - pbr_scale.mV[VX] * sin_sq) / denom;
+ }
+
// Center of the tile
const F32 center_s = 0.5f;
const F32 center_t = 0.5f;
// Center adjustment for scale
- F32 center_adjust_s = 0.5f * (1.0f - tex_scale_s);
- F32 center_adjust_t = 0.5f * (1.0f - tex_scale_t);
+ F32 center_adjust_s = 0.5f * (1.0f - pbr_scale.mV[VX]);
+ F32 center_adjust_t = 0.5f * (1.0f - pbr_scale.mV[VY]);
// 2. Offset from center
F32 pos_s = center_adjust_s - center_s;
diff --git a/indra/newview/llappviewer.cpp b/indra/newview/llappviewer.cpp
index 7653ffe424..b64b38c44f 100644
--- a/indra/newview/llappviewer.cpp
+++ b/indra/newview/llappviewer.cpp
@@ -390,6 +390,7 @@ const std::string MARKER_FILE_NAME("SecondLife.exec_marker");
const std::string START_MARKER_FILE_NAME("SecondLife.start_marker");
const std::string ERROR_MARKER_FILE_NAME("SecondLife.error_marker");
const std::string LOGOUT_MARKER_FILE_NAME("SecondLife.logout_marker");
+const std::string WATCHDOG_MARKER_FILE_NAME("SecondLife.watchdog_marker");
static std::string gLaunchFileOnQuit;
// Used on Win32 for other apps to identify our window (eg, win_setup)
@@ -984,6 +985,7 @@ bool LLAppViewer::init()
// Initialize event recorder
LLViewerEventRecorder::createInstance();
+ LLWatchdog::createInstance();
//
// Initialize the window
@@ -3262,20 +3264,60 @@ bool LLAppViewer::initWindow()
<< " (setting = " << watchdog_enabled_setting << ")"
<< LL_ENDL;
- if (use_watchdog)
+ // Watchdog reports to statistics via marker files, that is
+ // pointless without ability to write (!mSecondInstance) those files.
+ // If use_watchdog is set, watchdog also reports to bugspat.
+ if (use_watchdog || !mSecondInstance)
{
- LLWatchdog::getInstance()->init([]()
- {
- LLAppViewer* app = LLAppViewer::instance();
- if (app->logoutRequestSent())
+ LLWatchdog::getInstance()->init(
+ [](bool final_marker)
{
- app->createErrorMarker(LAST_EXEC_LOGOUT_FROZE);
- }
- else
+ LLAppViewer* app = LLAppViewer::instance();
+ // Without watchdog everything will be counted as
+ // either 'unknown' (no crash marker) or based of present crash marker
+ if (final_marker)
+ {
+ // watchdog is going to crash viewer, so crate a 'crash' marker
+ if (app->logoutRequestSent())
+ {
+ app->createErrorMarker(LAST_EXEC_LOGOUT_FROZE);
+ }
+ else
+ {
+ app->createErrorMarker(LAST_EXEC_FROZE);
+ }
+ }
+ else
+ {
+ // not going to crash, just create a 'watchdog' marker
+ app->createWatchdogMarker();
+ }
+ },
+ []()
{
- app->createErrorMarker(LAST_EXEC_FROZE);
- }
- });
+ LLAppViewer* app = LLAppViewer::instance();
+ // in case process recovered from freeze, remove watchdog marker.
+ app->removeWatchdogMarker();
+ },
+ [](std::string &desc)
+ {
+#if LL_WINDOWS && LL_BUGSPLAT
+ LLAppViewer* app = LLAppViewer::instance();
+ app->writeDebugInfo();
+ return app->reportCustomToBugsplat(desc);
+#else
+ return false;
+#endif
+ },
+ []()
+ {
+ LLAppViewer* app = LLAppViewer::instance();
+ app->sendLogoutRequest();
+ // Might be better to ask user if user wants to terminate the app or wait.
+ OSMessageBox(LLTrans::getString("MBFreezeDetected"), LLTrans::getString("MBFatalError"), OSMB_OK);
+ },
+ use_watchdog);
+
}
LLNotificationsUI::LLNotificationManager::getInstance();
@@ -4059,13 +4101,8 @@ void LLAppViewer::processMarkerFiles()
{
// the file existed, is ours, and matched our version, so we can report on what it says
LL_INFOS("MarkerFile") << "Exec marker '"<< mMarkerFileName << "' found; last exec crashed or froze" << LL_ENDL;
-#if LL_WINDOWS && LL_BUGSPLAT
- // bugsplat will set correct state in bugsplatSendLog
- // Might be more accurate to rename this one into 'unknown'
+ // App terminated unexpectedly or froze, we don't know the cause yet.
gLastExecEvent = LAST_EXEC_UNKNOWN;
-#else
- gLastExecEvent = LAST_EXEC_OTHER_CRASH;
-#endif // LL_WINDOWS
}
else
@@ -4118,23 +4155,29 @@ void LLAppViewer::processMarkerFiles()
}
LLAPRFile::remove(logout_marker_file);
}
- // and last refine based on whether or not a marker created during a non-llerr crash is found
+ // Refine based on whether or not a marker created during
+ // a crash is found or if wathdog caught a freeze.
+ // Bugsplat will set correct state in bugsplatSendLog.
std::string error_marker_file = gDirUtilp->getExpandedFilename(LL_PATH_LOGS, ERROR_MARKER_FILE_NAME);
+ std::string watchdog_marker_file = gDirUtilp->getExpandedFilename(LL_PATH_LOGS, WATCHDOG_MARKER_FILE_NAME);
if(LLAPRFile::isExist(error_marker_file, NULL, LL_APR_RB))
{
S32 marker_code = getMarkerErrorCode(error_marker_file);
if (marker_code >= 0)
{
- if (gLastExecEvent == LAST_EXEC_LOGOUT_FROZE)
- {
- gLastExecEvent = LAST_EXEC_LOGOUT_CRASH;
- LL_INFOS("MarkerFile") << "Error marker '"<< error_marker_file << "' crashed, setting LastExecEvent to LOGOUT_CRASH" << LL_ENDL;
- }
- else if (marker_code > 0 && marker_code < (S32)LAST_EXEC_COUNT)
+ if (marker_code > 0 && marker_code < (S32)LAST_EXEC_COUNT)
{
+ // If we have a code, it takes precendence
gLastExecEvent = (eLastExecEvent)marker_code;
LL_INFOS("MarkerFile") << "Error marker '"<< error_marker_file << "' crashed, setting LastExecEvent to " << gLastExecEvent << LL_ENDL;
}
+ // if we have the marker, even without a code, it's a crash.
+ else if (gLastExecEvent == LAST_EXEC_LOGOUT_UNKNOWN
+ || gLastExecEvent == LAST_EXEC_LOGOUT_FROZE)
+ {
+ gLastExecEvent = LAST_EXEC_LOGOUT_CRASH;
+ LL_INFOS("MarkerFile") << "Error marker '" << error_marker_file << "' crashed, setting LastExecEvent to LOGOUT_CRASH" << LL_ENDL;
+ }
else
{
gLastExecEvent = LAST_EXEC_OTHER_CRASH;
@@ -4146,6 +4189,33 @@ void LLAppViewer::processMarkerFiles()
LL_INFOS("MarkerFile") << "Error marker '"<< error_marker_file << "' marker found, but versions did not match" << LL_ENDL;
}
LLAPRFile::remove(error_marker_file);
+ if (LLAPRFile::isExist(watchdog_marker_file, NULL, LL_APR_RB))
+ {
+ // If viewer crashed after a freeze was detected,
+ // crash still takes precendence. Just clear watchdog.
+ removeWatchdogMarker();
+ }
+ }
+ else
+ {
+ // so only check watchdog marker if there is no error marker.
+ if (LLAPRFile::isExist(watchdog_marker_file, NULL, LL_APR_RB))
+ {
+ if (LAST_EXEC_UNKNOWN == gLastExecEvent
+ || LAST_EXEC_LOGOUT_UNKNOWN == gLastExecEvent)
+ {
+ // watchdog marker gets created if we detect a freeze,
+ // so if viwer did not stop gracefully, and we know it wasn't a crash,
+ // we have no other info, check watchdog.
+ if (markerIsSameVersion(watchdog_marker_file))
+ {
+ gLastExecEvent = LAST_EXEC_UNKNOWN == gLastExecEvent ? LAST_EXEC_FROZE : LAST_EXEC_LOGOUT_FROZE;
+ LL_INFOS("MarkerFile") << "Watchdog marker '" << watchdog_marker_file << "' found, setting LastExecEvent to FROZE"
+ << LL_ENDL;
+ }
+ }
+ removeWatchdogMarker();
+ }
}
#if LL_DARWIN
@@ -4190,6 +4260,7 @@ void LLAppViewer::removeMarkerFiles()
{
LL_WARNS("MarkerFile") << "logout marker '"<<mLogoutMarkerFileName<<"' not open"<< LL_ENDL;
}
+ removeWatchdogMarker();
}
else
{
@@ -5577,6 +5648,30 @@ bool LLAppViewer::errorMarkerExists() const
return LLAPRFile::isExist(error_marker_file, NULL, LL_APR_RB);
}
+void LLAppViewer::createWatchdogMarker() const
+{
+ if (!mSecondInstance)
+ {
+ std::string error_marker = gDirUtilp->getExpandedFilename(LL_PATH_LOGS, WATCHDOG_MARKER_FILE_NAME);
+
+ LLAPRFile file;
+ file.open(error_marker, LL_APR_WB);
+ if (file.getFileHandle())
+ {
+ recordMarkerVersion(file);
+ file.close();
+ }
+ }
+}
+void LLAppViewer::removeWatchdogMarker() const
+{
+ if (!mSecondInstance)
+ {
+ std::string error_marker_file = gDirUtilp->getExpandedFilename(LL_PATH_LOGS, WATCHDOG_MARKER_FILE_NAME);
+ LLFile::remove(error_marker_file);
+ }
+}
+
void LLAppViewer::outOfMemorySoftQuit()
{
if (!mQuitRequested)
diff --git a/indra/newview/llappviewer.h b/indra/newview/llappviewer.h
index e1119419af..71033b6d3f 100644
--- a/indra/newview/llappviewer.h
+++ b/indra/newview/llappviewer.h
@@ -257,6 +257,9 @@ public:
void createErrorMarker(eLastExecEvent error_code) const;
bool errorMarkerExists() const;
+ void createWatchdogMarker() const;
+ void removeWatchdogMarker() const;
+
// Attempt a 'soft' quit with disconnect and saving of settings/cache.
// Intended to be thread safe.
// Good chance of viewer crashing either way, but better than alternatives.
diff --git a/indra/newview/llappviewerwin32.cpp b/indra/newview/llappviewerwin32.cpp
index 5288dce69c..c6ecc43198 100644
--- a/indra/newview/llappviewerwin32.cpp
+++ b/indra/newview/llappviewerwin32.cpp
@@ -118,6 +118,7 @@ namespace
// MiniDmpSender pointer. As things stand, though, we must define an
// actual function and store the pointer statically.
static MiniDmpSender *sBugSplatSender = nullptr;
+ static std::string sBugsplatDesriptionField;
bool bugsplatSendLog(UINT nCode, LPVOID lpVal1, LPVOID lpVal2)
{
@@ -154,8 +155,21 @@ namespace
WCSTR(gDirUtilp->getExpandedFilename(LL_PATH_PER_SL_ACCOUNT, "settings_per_account.xml")));
}
- // LL_ERRS message, when there is one
- sBugSplatSender->setDefaultUserDescription(WCSTR(LLError::getFatalMessage()));
+ if (!sBugsplatDesriptionField.empty())
+ {
+ // Can be set by watchdog or other code that detects a problem
+ // and wants to add some context to the crash report.
+ // Will be visible in the BugSplat web UI.
+ sBugSplatSender->setDefaultUserDescription(WCSTR(LLError::getFatalMessage()));
+ // This type of crash is not nessesarily a crash, or final.
+ // Prepare for the next one.
+ sBugsplatDesriptionField.clear();
+ }
+ else
+ {
+ // LL_ERRS message, when there is one
+ sBugSplatSender->setDefaultUserDescription(WCSTR(LLError::getFatalMessage()));
+ }
sBugSplatSender->setAttribute(WCSTR(L"OS"), WCSTR(LLOSInfo::instance().getOSStringSimple())); // In case we ever stop using email for this
sBugSplatSender->setAttribute(WCSTR(L"AppState"), WCSTR(LLStartUp::getStartupStateString()));
@@ -849,6 +863,38 @@ bool LLAppViewerWin32::reportCrashToBugsplat(void* pExcepInfo)
return false;
}
+#if defined(LL_BUGSPLAT)
+static int reportCustomToBugsplatFilter(EXCEPTION_POINTERS* pExcepInfo)
+{
+ if (sBugSplatSender)
+ {
+ sBugSplatSender->createReport(pExcepInfo);
+ }
+ return EXCEPTION_EXECUTE_HANDLER;
+}
+#endif
+
+bool LLAppViewerWin32::reportCustomToBugsplat(const std::string &description)
+{
+#if defined(LL_BUGSPLAT)
+ if (sBugSplatSender)
+ {
+ sBugsplatDesriptionField = description;
+
+ __try
+ {
+ // Generate a custom exception code
+ RaiseException(0xE0000001, 0, 0, NULL);
+ }
+ __except (reportCustomToBugsplatFilter(GetExceptionInformation()))
+ {
+ }
+ return true;
+ }
+#endif // LL_BUGSPLAT
+ return false;
+}
+
bool LLAppViewerWin32::initWindow()
{
// This is a workaround/hotfix for a change in Windows 11 24H2 (and possibly later)
diff --git a/indra/newview/llappviewerwin32.h b/indra/newview/llappviewerwin32.h
index 3fad53ec72..5abcca1ce9 100644
--- a/indra/newview/llappviewerwin32.h
+++ b/indra/newview/llappviewerwin32.h
@@ -44,6 +44,7 @@ public:
bool cleanup() override;
bool reportCrashToBugsplat(void* pExcepInfo) override;
+ bool reportCustomToBugsplat(const std::string& desription) override;
protected:
bool initWindow() override; // Override to initialize the viewer's window.
diff --git a/indra/newview/llfilepicker_mac.mm b/indra/newview/llfilepicker_mac.mm
index 7262d18483..99e93bafbf 100644
--- a/indra/newview/llfilepicker_mac.mm
+++ b/indra/newview/llfilepicker_mac.mm
@@ -245,8 +245,8 @@ void doSaveDialogModeless(const std::string* file,
NSURL *last_url = [[NSUserDefaults standardUserDefaults] URLForKey:@"NSNavLastRootDirectory"];
if(!last_url)
{
- NSURL *documents_url = [[NSFileManager defaultManager] URLsForDirectory:NSDocumentDirectory inDomains:NSUserDomainMask].firstObject;
- [panel setDirectoryURL:documents_url];
+ NSURL *downloads_url = [[NSFileManager defaultManager] URLsForDirectory:NSDownloadsDirectory inDomains:NSUserDomainMask].firstObject;
+ [panel setDirectoryURL:downloads_url];
}
[panel beginWithCompletionHandler:^(NSModalResponse result)
diff --git a/indra/newview/llvoiceclient.cpp b/indra/newview/llvoiceclient.cpp
index 91d5451bff..4fd0a8e2b8 100644
--- a/indra/newview/llvoiceclient.cpp
+++ b/indra/newview/llvoiceclient.cpp
@@ -749,6 +749,9 @@ void LLVoiceClient::setUserPTTState(bool ptt)
{
if (ptt)
{
+ // Nearby chat is muted by moderator, don't toggle PTT
+ if (!mUserPTTState && LLNearbyVoiceModeration::getInstance()->showNotificationIfNeeded())
+ return;
LLUIUsage::instance().logCommand("Agent.EnableMicrophone");
}
mUserPTTState = ptt;
@@ -793,13 +796,6 @@ bool LLVoiceClient::getPTTIsToggle()
void LLVoiceClient::inputUserControlState(bool down)
{
- if (down && !getUserPTTState())
- {
- // Nearby chat is muted by moderator, don't toggle PTT
- if (LLNearbyVoiceModeration::getInstance()->showNotificationIfNeeded())
- return;
- }
-
if(mPTTIsToggle)
{
if(down) // toggle open-mic state on 'down'
diff --git a/indra/newview/llworldmapmessage.cpp b/indra/newview/llworldmapmessage.cpp
index a5433133ab..3264f8ae8b 100644
--- a/indra/newview/llworldmapmessage.cpp
+++ b/indra/newview/llworldmapmessage.cpp
@@ -34,7 +34,6 @@
#include "llfloaterworldmap.h"
constexpr U32 LAYER_FLAG = 2;
-constexpr S32 MAP_SIM_RETURN_NULL_SIMS = 0x00010000;
//---------------------------------------------------------------------------
// World Map Message Handling
diff --git a/indra/newview/skins/default/xui/en/strings.xml b/indra/newview/skins/default/xui/en/strings.xml
index 82dcbf2d30..63369431b1 100644
--- a/indra/newview/skins/default/xui/en/strings.xml
+++ b/indra/newview/skins/default/xui/en/strings.xml
@@ -3008,6 +3008,9 @@ If this message persists, restart your computer.
[APP_NAME] appears to have frozen or crashed on the previous run.
Would you like to send a crash report?
</string>
+ <string name="MBFreezeDetected">
+ [APP_NAME] appears to have frozen. If this issue occurs regularly, please contact support at https://support.secondlife.com.
+ </string>
<string name="MBAlert">Notification</string>
<string name="MBNoDirectX">
[APP_NAME] is unable to detect DirectX 9.0b or greater.