# Conflicts:
#	indra/llui/lltextbase.cpp
#	indra/newview/llappviewerwin32.cpp
#	indra/newview/lleventpoll.cpp
master
Ansariel 2025-09-22 23:11:55 +02:00
commit 5e5a3f4102
38 changed files with 477 additions and 118 deletions

View File

@ -97,7 +97,7 @@ jobs:
uses: actions/checkout@v5
with:
repository: secondlife/build-variables
ref: universal
ref: master
path: .build-variables
- name: Checkout master-message-template
@ -309,7 +309,7 @@ jobs:
steps:
- name: Sign and package Windows viewer
if: env.AZURE_KEY_VAULT_URI && env.AZURE_CERT_NAME && env.AZURE_CLIENT_ID && env.AZURE_CLIENT_SECRET && env.AZURE_TENANT_ID
uses: secondlife/viewer-build-util/sign-pkg-windows@v2
uses: secondlife/viewer-build-util/sign-pkg-windows@v2.0.4
with:
vault_uri: "${{ env.AZURE_KEY_VAULT_URI }}"
cert_name: "${{ env.AZURE_CERT_NAME }}"

View File

@ -188,6 +188,17 @@ public:
<< childout.peek(0, peeklen) << "..." << LL_ENDL;
}
// Handle any remaining stderr data (partial lines) the same way as we do
// for stdout: log it.
LLProcess::ReadPipe& childerr(mChild->getReadPipe(LLProcess::STDERR));
if (childerr.size())
{
LLProcess::ReadPipe::size_type
peeklen((std::min)(LLProcess::ReadPipe::size_type(50), childerr.size()));
LL_WARNS("LLLeap") << "Final stderr " << childerr.size() << " bytes: "
<< childerr.peek(0, peeklen) << "..." << LL_ENDL;
}
// Kill this instance. MUST BE LAST before return!
delete this;
return false;

View File

@ -557,11 +557,6 @@ public:
}
if (!opj_setup_encoder(encoder, &parameters, image))
{
return false;
}
U32 width_tiles = (rawImageIn.getWidth() >> 6);
U32 height_tiles = (rawImageIn.getHeight() >> 6);
@ -575,6 +570,19 @@ public:
height_tiles = 1;
}
if (width_tiles == 1 || height_tiles == 1)
{
// Images with either dimension less than 32 need less number of resolutions otherwise they error
int min_dim = rawImageIn.getWidth() < rawImageIn.getHeight() ? rawImageIn.getWidth() : rawImageIn.getHeight();
int max_res = 1 + (int)floor(log2(min_dim));
parameters.numresolution = max_res;
}
if (!opj_setup_encoder(encoder, &parameters, image))
{
return false;
}
U32 tile_count = width_tiles * height_tiles;
U32 data_size_guess = tile_count * TILE_SIZE;

View File

@ -1604,6 +1604,7 @@ bool LLFolderView::handleRightMouseDown( S32 x, S32 y, MASK mask )
&& ( count > 0 && (hasVisibleChildren()) ))) && // show menu only if selected items are visible
!hide_folder_menu)
{
LL_INFOS("Inventory") << "Opening inventory menu from path: " << getPathname() << LL_ENDL;
if (mCallbackRegistrar)
{
mCallbackRegistrar->pushScope();

View File

@ -207,6 +207,7 @@ LLTextBase::LLTextBase(const LLTextBase::Params &p)
mURLClickSignal(NULL),
mIsFriendSignal(NULL),
mIsObjectBlockedSignal(NULL),
mIsObjectReachableSignal(NULL),
mMaxTextByteLength( p.max_text_length ),
mFont(p.font),
mFontShadow(p.font_shadow),
@ -320,6 +321,7 @@ LLTextBase::~LLTextBase()
delete mURLClickSignal;
delete mIsFriendSignal;
delete mIsObjectBlockedSignal;
delete mIsObjectReachableSignal;
}
void LLTextBase::initFromParams(const LLTextBase::Params& p)
@ -2510,6 +2512,15 @@ void LLTextBase::createUrlContextMenu(S32 x, S32 y, const std::string &in_url)
}
}
if (mIsObjectReachableSignal)
{
bool is_reachable = *(*mIsObjectReachableSignal)(LLUUID(LLUrlAction::getObjectId(url)));
if (LLView* zoom_btn = menu->getChild<LLView>("zoom_in"))
{
zoom_btn->setEnabled(is_reachable);
}
}
// <FS:Zi> hide the moderation tools in the context menu unless we are in a group IM floater
LLFloater* parent_floater = getParentByType<LLFloater>();
if (!parent_floater || parent_floater->getName() != "panel_im")
@ -3740,6 +3751,15 @@ boost::signals2::connection LLTextBase::setIsObjectBlockedCallback(const is_bloc
return mIsObjectBlockedSignal->connect(cb);
}
boost::signals2::connection LLTextBase::setIsObjectReachableCallback(const is_obj_reachable_signal_t::slot_type& cb)
{
if (!mIsObjectReachableSignal)
{
mIsObjectReachableSignal = new is_obj_reachable_signal_t();
}
return mIsObjectReachableSignal->connect(cb);
}
//
// LLTextSegment
//

View File

@ -346,6 +346,7 @@ public:
typedef boost::signals2::signal<bool (const LLUUID& user_id)> is_friend_signal_t;
typedef boost::signals2::signal<bool (const LLUUID& blocked_id, const std::string from)> is_blocked_signal_t;
typedef boost::signals2::signal<bool (const LLUUID& obj_id)> is_obj_reachable_signal_t;
struct LineSpacingParams : public LLInitParam::ChoiceBlock<LineSpacingParams>
{
@ -572,6 +573,7 @@ public:
boost::signals2::connection setURLClickedCallback(const commit_signal_t::slot_type& cb);
boost::signals2::connection setIsFriendCallback(const is_friend_signal_t::slot_type& cb);
boost::signals2::connection setIsObjectBlockedCallback(const is_blocked_signal_t::slot_type& cb);
boost::signals2::connection setIsObjectReachableCallback(const is_obj_reachable_signal_t::slot_type& cb);
void setWordWrap(bool wrap);
LLScrollContainer* getScrollContainer() const { return mScroller; }
@ -841,6 +843,7 @@ protected:
// Used to check if user with given ID is avatar's friend
is_friend_signal_t* mIsFriendSignal;
is_blocked_signal_t* mIsObjectBlockedSignal;
is_obj_reachable_signal_t* mIsObjectReachableSignal;
LLUIString mLabel; // text label that is visible when no user text provided
// <FS:Ansariel> Optional icon position

View File

@ -350,6 +350,16 @@ void LLWebRTCImpl::init()
void LLWebRTCImpl::terminate()
{
mWorkerThread->BlockingCall(
[this]()
{
if (mDeviceModule)
{
mDeviceModule->ForceStopRecording();
mDeviceModule->StopPlayout();
}
});
for (auto &connection : mPeerConnections)
{
connection->terminate();
@ -368,8 +378,6 @@ void LLWebRTCImpl::terminate()
{
if (mDeviceModule)
{
mDeviceModule->StopRecording();
mDeviceModule->StopPlayout();
mDeviceModule->Terminate();
}
mDeviceModule = nullptr;
@ -442,11 +450,7 @@ void LLWebRTCImpl::unsetDevicesObserver(LLWebRTCDevicesObserver *observer)
void LLWebRTCImpl::workerDeployDevices()
{
int16_t recordingDevice = RECORD_DEVICE_DEFAULT;
#if WEBRTC_WIN
int16_t recording_device_start = 0;
#else
int16_t recording_device_start = 1;
#endif
if (mRecordingDevice != "Default")
{
@ -455,6 +459,12 @@ void LLWebRTCImpl::workerDeployDevices()
if (mRecordingDeviceList[i].mID == mRecordingDevice)
{
recordingDevice = i;
#if !WEBRTC_WIN
// linux and mac devices range from 1 to the end of the list, with the index 0 being the
// 'default' device. Windows has a special 'default' device and other devices are indexed
// from 0
recordingDevice++;
#endif
break;
}
}
@ -479,11 +489,7 @@ void LLWebRTCImpl::workerDeployDevices()
mDeviceModule->InitRecording();
int16_t playoutDevice = PLAYOUT_DEVICE_DEFAULT;
#if WEBRTC_WIN
int16_t playout_device_start = 0;
#else
int16_t playout_device_start = 1;
#endif
if (mPlayoutDevice != "Default")
{
for (int16_t i = playout_device_start; i < mPlayoutDeviceList.size(); i++)
@ -491,6 +497,12 @@ void LLWebRTCImpl::workerDeployDevices()
if (mPlayoutDeviceList[i].mID == mPlayoutDevice)
{
playoutDevice = i;
#if !WEBRTC_WIN
// linux and mac devices range from 1 to the end of the list, with the index 0 being the
// 'default' device. Windows has a special 'default' device and other devices are indexed
// from 0
playoutDevice++;
#endif
break;
}
}
@ -546,20 +558,14 @@ void LLWebRTCImpl::workerDeployDevices()
void LLWebRTCImpl::setCaptureDevice(const std::string &id)
{
if (mRecordingDevice != id)
{
mRecordingDevice = id;
deployDevices();
}
mRecordingDevice = id;
deployDevices();
}
void LLWebRTCImpl::setRenderDevice(const std::string &id)
{
if (mPlayoutDevice != id)
{
mPlayoutDevice = id;
deployDevices();
}
mPlayoutDevice = id;
deployDevices();
}
// updateDevices needs to happen on the worker thread.
@ -609,7 +615,7 @@ void LLWebRTCImpl::updateDevices()
void LLWebRTCImpl::OnDevicesUpdated()
{
deployDevices();
updateDevices();
}

View File

@ -239,8 +239,10 @@ public:
return 0;
}
int32_t StopRecording() override {
if (tuning_) return 0; // if we're tuning, disregard the StopRecording we get from disabling the streams
return inner_->StopRecording();
// ignore stop recording as webrtc.lib will send one when streams shut down,
// even if there are other streams in place. Start/Stop recording are entirely
// controlled by the app
return 0;
}
int32_t ForceStartRecording() { return inner_->StartRecording(); }
int32_t ForceStopRecording() { return inner_->StopRecording(); }

View File

@ -68,7 +68,13 @@ void LLWindowCallbacks::handleMouseLeave(LLWindow *window)
return;
}
bool LLWindowCallbacks::handleCloseRequest(LLWindow *window)
bool LLWindowCallbacks::handleCloseRequest(LLWindow *window, bool from_user)
{
//allow the window to close
return true;
}
bool LLWindowCallbacks::handleSessionExit(LLWindow* window)
{
//allow the window to close
return true;

View File

@ -42,7 +42,8 @@ public:
virtual bool handleMouseUp(LLWindow *window, LLCoordGL pos, MASK mask);
virtual void handleMouseLeave(LLWindow *window);
// return true to allow window to close, which will then cause handleQuit to be called
virtual bool handleCloseRequest(LLWindow *window);
virtual bool handleCloseRequest(LLWindow *window, bool from_user);
virtual bool handleSessionExit(LLWindow* window);
// window is about to be destroyed, clean up your business
virtual void handleQuit(LLWindow *window);
virtual bool handleRightMouseDown(LLWindow *window, LLCoordGL pos, MASK mask);

View File

@ -618,7 +618,7 @@ void callQuitHandler()
{
if (gWindowImplementation && gWindowImplementation->getCallbacks())
{
if(gWindowImplementation->getCallbacks()->handleCloseRequest(gWindowImplementation))
if(gWindowImplementation->getCallbacks()->handleCloseRequest(gWindowImplementation, true))
{
gWindowImplementation->getCallbacks()->handleQuit(gWindowImplementation);
}

View File

@ -2170,7 +2170,7 @@ void LLWindowSDL::gatherInput()
{
// *FIX: More informative dialog?
LL_INFOS() << "Could not recreate context after resize! Quitting..." << LL_ENDL;
if(mCallbacks->handleCloseRequest(this))
if(mCallbacks->handleCloseRequest(this, false))
{
// Get the app to initiate cleanup.
mCallbacks->handleQuit(this);
@ -2220,7 +2220,7 @@ void LLWindowSDL::gatherInput()
break;
case SDL_QUIT:
if(mCallbacks->handleCloseRequest(this))
if(mCallbacks->handleCloseRequest(this, true))
{
// Get the app to initiate cleanup.
mCallbacks->handleQuit(this);

View File

@ -2556,10 +2556,13 @@ LRESULT CALLBACK LLWindowWin32::mainWindowProc(HWND h_wnd, UINT u_msg, WPARAM w_
case WM_CLOSE:
{
LL_PROFILE_ZONE_NAMED_CATEGORY_WIN32("mwp - WM_CLOSE");
// todo: WM_CLOSE can be caused by user and by task manager,
// distinguish these cases.
// For now assume it is always user.
window_imp->post([=]()
{
// Will the app allow the window to close?
if (window_imp->mCallbacks->handleCloseRequest(window_imp))
if (window_imp->mCallbacks->handleCloseRequest(window_imp, true))
{
// Get the app to initiate cleanup.
window_imp->mCallbacks->handleQuit(window_imp);
@ -2577,6 +2580,50 @@ LRESULT CALLBACK LLWindowWin32::mainWindowProc(HWND h_wnd, UINT u_msg, WPARAM w_
}
return 0;
}
case WM_QUERYENDSESSION:
{
// Generally means that OS is going to shut down or user is going to log off.
// Can use ShutdownBlockReasonCreate here.
LL_INFOS("Window") << "Received WM_QUERYENDSESSION with wParam: " << (U32)w_param << " lParam: " << (U32)l_param << LL_ENDL;
return TRUE; // 1 = ok to end session. 0 no longer works by itself, use ShutdownBlockReasonCreate
}
case WM_ENDSESSION:
{
// OS session is shutting down, initiate cleanup.
// Comes after WM_QUERYENDSESSION
LL_PROFILE_ZONE_NAMED_CATEGORY_WIN32("mwp - WM_ENDSESSION");
LL_INFOS("Window") << "Received WM_ENDSESSION with wParam: " << (U32)w_param << " lParam: " << (U32)l_param << LL_ENDL;
unsigned int end_session_flags = (U32)w_param;
if (end_session_flags == 0)
{
// session is not actually ending
return 0;
}
if ((end_session_flags & ENDSESSION_CLOSEAPP)
|| (end_session_flags & ENDSESSION_CRITICAL)
|| (end_session_flags & ENDSESSION_LOGOFF))
{
window_imp->post([=]()
{
// Check if app needs cleanup or can be closed immediately.
if (window_imp->mCallbacks->handleSessionExit(window_imp))
{
// Get the app to initiate cleanup.
window_imp->mCallbacks->handleQuit(window_imp);
// The app is responsible for calling destroyWindow when done with GL
}
});
// Give app a second to finish up. That's not enough for a clean exit,
// but better than nothing.
// Todo: sync this better, some kind of waitForResult? Can't wait forever,
// but can potentially use ShutdownBlockReasonCreate for a bigger delay.
ms_sleep(1000);
}
// Don't need to post quit or destroy window,
// if session is ending OS is going to take care of it.
return 0;
}
case WM_COMMAND:
{
LL_PROFILE_ZONE_NAMED_CATEGORY_WIN32("mwp - WM_COMMAND");

View File

@ -12680,7 +12680,7 @@ Change of this parameter will affect the layout of buttons in notification toast
<key>RenderQualityPerformance</key>
<map>
<key>Comment</key>
<string>Which graphics settings you've chosen</string>
<string>Which graphics settings you've chosen. Don't use this setting to change quality directly from debug settings.</string>
<key>Persist</key>
<integer>1</integer>
<key>Type</key>
@ -12688,7 +12688,18 @@ Change of this parameter will affect the layout of buttons in notification toast
<key>Value</key>
<integer>1</integer>
</map>
<key>DebugQualityPerformance</key>
<map>
<key>Comment</key>
<string>Allows to change performance quality directly from debug settings.</string>
<key>Persist</key>
<integer>1</integer>
<key>Type</key>
<string>U32</string>
<key>Value</key>
<integer>1</integer>
</map>
<key>RenderReflectionDetail</key>
<map>
<key>Comment</key>

View File

@ -238,7 +238,8 @@ public:
LLUUID obj_id = mObjectData["object_id"];
if (obj_id.notNull())
{
return nullptr != gObjectList.findObject(mAvatarID);
LLViewerObject* object = gObjectList.findObject(obj_id);
return object && object->isReachable();
}
return false;
}
@ -1280,6 +1281,11 @@ FSChatHistory::FSChatHistory(const FSChatHistory::Params& p)
mUseColor = true;
setIsObjectBlockedCallback(boost::bind(&LLMuteList::isMuted, LLMuteList::getInstance(), _1, _2, 0));
setIsObjectReachableCallback([](const LLUUID& obj_id)
{
LLViewerObject* object = gObjectList.findObject(obj_id);
return object && object->isReachable();
});
}
LLSD FSChatHistory::getValue() const

View File

@ -158,6 +158,11 @@ bool Buffer::prep(Asset& asset)
{
std::string dir = gDirUtilp->getDirName(asset.mFilename);
std::string bin_file = dir + gDirUtilp->getDirDelimiter() + mUri;
if (!gDirUtilp->fileExists(bin_file))
{
// Characters might be escaped in the URI
bin_file = dir + gDirUtilp->getDirDelimiter() + LLURI::unescape(mUri);
}
llifstream file(bin_file.c_str(), std::ios::binary);
if (!file.is_open())

View File

@ -652,6 +652,14 @@ std::string LLGLTFLoader::processTexture(S32 texture_index, const std::string& t
filename = filename.substr(pos + 1);
}
std::string dir = gDirUtilp->getDirName(mFilename);
std::string full_path = dir + gDirUtilp->getDirDelimiter() + filename;
if (!gDirUtilp->fileExists(full_path) && filename.find("data:") == std::string::npos)
{
// Uri might be escaped
filename = LLURI::unescape(filename);
}
LL_INFOS("GLTF_IMPORT") << "Found texture: " << filename << " for material: " << material_name << LL_ENDL;
LLSD args;

View File

@ -1427,19 +1427,10 @@ bool LLAppViewer::init()
LLStringOps::setupMonthShortNames(LLTrans::getString("dateTimeMonthShortNames"));
LLStringOps::setupDayFormat(LLTrans::getString("dateTimeDayFormat"));
// <FS:Ansariel> Always override AM/PM because otherwise AM/PM indicator might be empty strings when calling using
// [ampm,datetime,...] formatting depending on locale
// LLStringOps::sAM = LLTrans::getString("dateTimeAM");
// LLStringOps::sPM = LLTrans::getString("dateTimePM");
// </FS:Ansariel>
LLStringOps::sAM = LLTrans::getString("dateTimeAM");
LLStringOps::sPM = LLTrans::getString("dateTimePM");
}
// <FS:Ansariel> Always override AM/PM because otherwise AM/PM indicator might be empty strings when calling using [ampm,datetime,...]
// formatting depending on locale
LLStringOps::sAM = LLTrans::getString("dateTimeAM");
LLStringOps::sPM = LLTrans::getString("dateTimePM");
// </FS:Ansariel>
LLAgentLanguage::init();
/// Tell the Coprocedure manager how to discover and store the pool sizes
@ -1487,6 +1478,7 @@ bool LLAppViewer::init()
LLViewerCamera::createInstance();
LL::GLTFSceneManager::createInstance();
gSavedSettings.setU32("DebugQualityPerformance", gSavedSettings.getU32("RenderQualityPerformance"));
#if LL_WINDOWS
if (!mSecondInstance)
@ -4738,8 +4730,15 @@ void LLAppViewer::processMarkerFiles()
else if (marker_is_same_version)
{
// the file existed, is ours, and matched our version, so we can report on what it says
LL_INFOS("MarkerFile") << "Exec marker '"<< mMarkerFileName << "' found; last exec crashed" << LL_ENDL;
LL_INFOS("MarkerFile") << "Exec marker '"<< mMarkerFileName << "' found; last exec crashed or froze" << LL_ENDL;
#if LL_WINDOWS && LL_BUGSPLAT
// bugsplat will set correct state in bugsplatSendLog
// Might be more accurate to rename this one into 'unknown'
gLastExecEvent = LAST_EXEC_FROZE;
#else
gLastExecEvent = LAST_EXEC_OTHER_CRASH;
#endif // LL_WINDOWS
}
else
{
@ -4999,7 +4998,7 @@ void LLAppViewer::earlyExit(const std::string& name, const LLSD& substitutions)
// case where we need the viewer to exit without any need for notifications
void LLAppViewer::earlyExitNoNotify()
{
LL_WARNS() << "app_early_exit with no notification: " << LL_ENDL;
LL_WARNS() << "app_early_exit with no notification." << LL_ENDL;
gDoDisconnect = true;
finish_early_exit( LLSD(), LLSD() );
}
@ -6330,6 +6329,12 @@ void LLAppViewer::createErrorMarker(eLastExecEvent error_code) const
}
}
bool LLAppViewer::errorMarkerExists() const
{
std::string error_marker_file = gDirUtilp->getExpandedFilename(LL_PATH_LOGS, ERROR_MARKER_FILE_NAME);
return LLAPRFile::isExist(error_marker_file, NULL, LL_APR_RB);
}
void LLAppViewer::outOfMemorySoftQuit()
{
if (!mQuitRequested)

View File

@ -149,6 +149,12 @@ public:
std::string getWindowTitle() const; // The window display name.
void forceDisconnect(const std::string& msg); // Force disconnection, with a message to the user.
// sendSimpleLogoutRequest does not create a marker file.
// Meant for lost network case, and for forced shutdowns,
// to at least attempt to remove the ghost from the world.
void sendSimpleLogoutRequest();
void badNetworkHandler(); // Cause a crash state due to bad network packet.
bool hasSavedFinalSnapshot() { return mSavedFinalSnapshot; }
@ -246,6 +252,7 @@ public:
// Writes an error code into the error_marker file for use on next startup.
void createErrorMarker(eLastExecEvent error_code) const;
bool errorMarkerExists() const;
// Attempt a 'soft' quit with disconnect and saving of settings/cache.
// Intended to be thread safe.
@ -314,10 +321,6 @@ private:
void sendLogoutRequest();
void disconnectViewer();
// Does not create a marker file. For lost network case,
// to at least attempt to remove the ghost from the world.
void sendSimpleLogoutRequest();
// *FIX: the app viewer class should be some sort of singleton, no?
// Perhaps its child class is the singleton and this should be an abstract base.
static LLAppViewer* sInstance;

View File

@ -233,10 +233,20 @@ namespace
BugSplatAttributes::instance().setAttribute("Location", std::string(fullLocation));
// </FS:Beq>
}
// <FS:Beq> Improve bugsplat reporting with attributes
LLAppViewer::instance()->writeDebugInfo();
sBugSplatSender->sendAdditionalFile(WCSTR(BugSplatAttributes::getCrashContextFileName())); // <FS:Beq/> Add the new attributes file
// </FS:Beq>
LLAppViewer* app = LLAppViewer::instance();
if (!app->isSecondInstance() && !app->errorMarkerExists())
{
// If marker doesn't exist, create a marker with 'other' code for next launch
// otherwise don't override existing file
// Any unmarked crashes will be considered as freezes
app->createErrorMarker(LAST_EXEC_OTHER_CRASH);
}
} // MDSCB_EXCEPTIONCODE
return false;

View File

@ -229,7 +229,8 @@ public:
LLUUID obj_id = mObjectData["object_id"];
if (obj_id.notNull())
{
return nullptr != gObjectList.findObject(mAvatarID);
LLViewerObject* object = gObjectList.findObject(obj_id);
return object && object->isReachable();
}
return false;
}
@ -1178,7 +1179,11 @@ LLChatHistory::LLChatHistory(const LLChatHistory::Params& p)
mEditor = LLUICtrlFactory::create<LLTextEditor>(editor_params, this);
mEditor->setIsFriendCallback(LLAvatarActions::isFriend);
mEditor->setIsObjectBlockedCallback(boost::bind(&LLMuteList::isMuted, LLMuteList::getInstance(), _1, _2, 0));
mEditor->setIsObjectReachableCallback([](const LLUUID& obj_id)
{
LLViewerObject* object = gObjectList.findObject(obj_id);
return object && object->isReachable();
});
}
LLSD LLChatHistory::getValue() const

View File

@ -57,13 +57,6 @@ namespace Details
void stop();
private:
// We will wait RETRY_SECONDS + (errorCount * RETRY_SECONDS_INC) before retrying after an error.
// This means we attempt to recover relatively quickly but back off giving more time to recover
// until we finally give up after MAX_EVENT_POLL_HTTP_ERRORS attempts.
static const F32 EVENT_POLL_ERROR_RETRY_SECONDS;
static const F32 EVENT_POLL_ERROR_RETRY_SECONDS_INC;
static const S32 MAX_EVENT_POLL_HTTP_ERRORS;
void eventPollCoro(std::string url);
void handleMessage(const LLSD &content);
@ -71,7 +64,6 @@ namespace Details
bool mDone;
LLCore::HttpRequest::ptr_t mHttpRequest;
LLCore::HttpRequest::policy_t mHttpPolicy;
LLCore::HttpOptions::ptr_t mHttpOptions; // <FS:Ansariel> Restore pre-coro behavior (60s timeout, no retries)
std::string mSenderIp;
int mCounter;
LLCoreHttpUtil::HttpCoroutineAdapter::wptr_t mAdapter;
@ -80,9 +72,13 @@ namespace Details
};
const F32 LLEventPollImpl::EVENT_POLL_ERROR_RETRY_SECONDS = 15.f; // ~ half of a normal timeout.
const F32 LLEventPollImpl::EVENT_POLL_ERROR_RETRY_SECONDS_INC = 5.f; // ~ half of a normal timeout.
const S32 LLEventPollImpl::MAX_EVENT_POLL_HTTP_ERRORS = 10; // ~5 minutes, by the above rules.
// We will wait RETRY_SECONDS + (errorCount * RETRY_SECONDS_INC) before retrying after an error.
// This means we attempt to recover relatively quickly but back off giving more time to recover
// until we finally give up after MAX_EVENT_POLL_HTTP_ERRORS attempts.
constexpr F32 EVENT_POLL_ERROR_RETRY_SECONDS = 15.f; // ~ half of a normal timeout.
constexpr F32 EVENT_POLL_ERROR_RETRY_SECONDS_INC = 5.f; // ~ half of a normal timeout.
constexpr S32 MAX_EVENT_POLL_HTTP_ERRORS = 10; // ~5 minutes, by the above rules.
constexpr F64 MIN_SECONDS_PASSED = 10.0; // Minimum time we expect the server to hold the request.
int LLEventPollImpl::sNextCounter = 1;
@ -91,7 +87,6 @@ namespace Details
mDone(false),
mHttpRequest(),
mHttpPolicy(LLCore::HttpRequest::DEFAULT_POLICY_ID),
mHttpOptions(), // <FS:Ansariel> Restore pre-coro behavior (60s timeout, no retries)
mSenderIp(),
mCounter(sNextCounter++)
@ -100,16 +95,6 @@ namespace Details
mHttpRequest = LLCore::HttpRequest::ptr_t(new LLCore::HttpRequest);
mHttpPolicy = app_core_http.getPolicy(LLAppCoreHttp::AP_LONG_POLL);
// <FS:Ansariel> Restore pre-coro behavior (60s timeout, no retries)
mHttpOptions = LLCore::HttpOptions::ptr_t(new LLCore::HttpOptions);
#ifdef OPENSIM
if (LLGridManager::instance().isInOpenSim())
{
mHttpOptions->setRetries(0);
mHttpOptions->setTransferTimeout(60);
}
#endif
// </FS:Ansariel>
mSenderIp = sender.getIPandPort();
}
@ -122,13 +107,11 @@ namespace Details
{
message["sender"] = mSenderIp;
// <FS:ND> Guard against messages with no "body"
// message["body"] = content["body"];
if( content.has( "body" ) )
if (content.has("body"))
message["body"] = content["body"];
else
LL_WARNS() << "Malformed content? " << ll_pretty_print_sd( content ) << LL_ENDL;
LL_WARNS() << "Malformed content? " << ll_pretty_print_sd(content) << LL_ENDL;
// <FS:ND>
}
catch (std::bad_alloc&)
@ -174,11 +157,25 @@ namespace Details
LLSD acknowledge;
int errorCount = 0;
int counter = mCounter; // saved on the stack for logging.
LLTimer message_time;
LL_DEBUGS("LLEventPollImpl") << " <" << counter << "> entering coroutine." << LL_ENDL;
mAdapter = httpAdapter;
// This is a loop with its own waitToRetry implementation,
// so disable retries.
LLCore::HttpOptions::ptr_t httpOpts(new LLCore::HttpOptions);
httpOpts->setRetries(0);
// <FS:Ansariel> Restore pre-coro behavior (60s timeout, no retries)
#ifdef OPENSIM
if (LLGridManager::instance().isInOpenSim())
{
httpOpts->setTransferTimeout(60);
}
#endif
// </FS:Ansariel>
LL::WorkQueue::ptr_t main_queue = nullptr;
// HACK -- grab the mainloop workqueue to move execution of the handler
@ -195,14 +192,13 @@ namespace Details
request["ack"] = acknowledge;
request["done"] = mDone;
message_time.reset();
// LL_DEBUGS("LLEventPollImpl::eventPollCoro") << "<" << counter << "> request = "
// << LLSDXMLStreamer(request) << LL_ENDL;
LL_DEBUGS("LLEventPollImpl") << " <" << counter << "> posting and yielding." << LL_ENDL;
// <FS:Ansariel> Restore pre-coro behavior (60s timeout, no retries)
//LLSD result = httpAdapter->postAndSuspend(mHttpRequest, url, request);
LLSD result = httpAdapter->postAndSuspend(mHttpRequest, url, request, mHttpOptions);
// </FS:Ansariel>
LLSD result = httpAdapter->postAndSuspend(mHttpRequest, url, request, httpOpts);
// LL_DEBUGS("LLEventPollImpl::eventPollCoro") << "<" << counter << "> result = "
// << LLSDXMLStreamer(result) << LL_ENDL;
@ -220,11 +216,30 @@ namespace Details
if (!status)
{
if (status == LLCore::HttpStatus(LLCore::HttpStatus::EXT_CURL_EASY, CURLE_OPERATION_TIMEDOUT))
{ // A standard timeout response we get this when there are no events.
LL_DEBUGS("LLEventPollImpl") << "All is very quiet on target server. It may have gone idle?" << LL_ENDL;
errorCount = 0;
continue;
if (status == LLCore::HttpStatus(LLCore::HttpStatus::EXT_CURL_EASY, CURLE_OPERATION_TIMEDOUT) // A standard timeout, no events.
|| status == LLCore::HttpStatus(HTTP_BAD_GATEWAY) // An expected 'No events' case.
|| status == LLCore::HttpStatus(HTTP_INTERNAL_ERROR)
|| status == LLCore::HttpStatus(HTTP_SERVICE_UNAVAILABLE)
|| status == LLCore::HttpStatus(HTTP_GATEWAY_TIME_OUT))
{
if (message_time.getElapsedSeconds() < MIN_SECONDS_PASSED)
{
// Server is supposed to hold request for 20 to 30 seconds.
// If it didn't hold the request at least for 10s, treat as an error.
LL_WARNS("LLEventPollImpl") << "Response arrived too early, status: " << status.toTerseString()
<< ", time passed: " << message_time.getElapsedSeconds() << LL_ENDL;
}
else
{
// Timeout, expected and means 'no events'. Request is to be re-issued immediately.
// Current definition of a timeout is any of :
// - libcurl easy 28 status code
// - Linden 499 special http status code
// - RFC - standard 502 - 504 http status codes
LL_DEBUGS("LLEventPollImpl") << "No events, from: " << mSenderIp <<" status: " << (S32)status.getStatus() << LL_ENDL;
errorCount = 0;
continue;
}
}
// <FS:Ansariel> Restore pre-coro behavior (60s timeout, no retries)
#ifdef OPENSIM
@ -243,13 +258,13 @@ namespace Details
// some cases the server gets ahead of the viewer and will
// return a 404 error (Not Found) before the cancel event
// comes back in the queue
LL_WARNS("LLEventPollImpl") << "Canceling coroutine" << LL_ENDL;
LL_WARNS("LLEventPollImpl") << "<" << counter << "> Canceling coroutine, status: " << status.toTerseString() << LL_ENDL;
break;
}
else if (!status.isHttpStatus())
{
/// Some LLCore or LIBCurl error was returned. This is unlikely to be recoverable
LL_WARNS("LLEventPollImpl") << "Critical error from poll request returned from libraries. Canceling coroutine." << LL_ENDL;
LL_WARNS("LLEventPollImpl") << "<" << counter << "> Critical error from poll request returned from libraries. Canceling coroutine." << LL_ENDL;
break;
}
LL_WARNS("LLEventPollImpl") << "<" << counter << "> Error result from LLCoreHttpUtil::HttpCoroHandler. Code "
@ -292,6 +307,10 @@ namespace Details
LL_WARNS("LLEventPollImpl") << "< " << counter << "> Forcing disconnect due to stalled main region event poll." << LL_ENDL;
LLAppViewer::instance()->forceDisconnect(LLTrans::getString("AgentLostConnection"));
}
else
{
LL_WARNS("LLEventPollImpl") << "< " << counter << "> Stopping event poll for " << mSenderIp << " due to failures." << LL_ENDL;
}
break;
}
}

View File

@ -40,7 +40,30 @@ namespace Details
class LLEventPoll
///< implements the viewer side of server-to-viewer pushed events.
///< Implements the viewer side of server-to-viewer pushed events.
///
/// This class implements the sole consumer of the EventQueueGet capability
/// and delivers data, including llsd-encoded llmessage payloads, from
/// simulator to viewer.
///
/// https://wiki.secondlife.com/wiki/EventQueueGet
/// The wiki page is neither complete nor entirely correct. Request timeouts
/// became the de facto method of returning an empty event set to the viewer.
/// But the timeout behavior was never defined. It was simply whatever
/// behavior a given grid implementation implemented.
///
/// In SL's case, the path may include reverse proxies, http caches, http and
/// socks proxies, transparent hijacking, and other horrors. A pitfall for
/// implementors.
///
/// Current definition of a timeout is any of :
/// - libcurl easy 28 status code
/// - Linden 499 special http status code
/// - RFC - standard 502 - 504 http status codes
/// If requests are failing too quickly with the above errors, they are treated
/// as actual errors and not an empty payload. These will count towards a final
/// error declaration and can lead to disconnection from a simulator or the
/// entire grid.
{
public:
LLEventPoll(const std::string& pollURL, const LLHost& sender);

View File

@ -2421,6 +2421,7 @@ void LLFloaterPreference::onChangeQuality(const LLSD& data)
}
mLastQualityLevel = level;
LLFeatureManager::getInstance()->setGraphicsLevel(level, true);
gSavedSettings.setU32("DebugQualityPerformance", level);
refreshEnabledGraphics();
refresh();
}

View File

@ -240,13 +240,38 @@ void LLPanelVoiceDeviceSettings::refresh()
if(mCtrlInputDevices)
{
mCtrlInputDevices->removeall();
mCtrlInputDevices->add(getLocalizedDeviceName(mInputDevice), mInputDevice, ADD_BOTTOM);
auto it = mLocalizedDeviceNames.find(mInputDevice);
if (it != mLocalizedDeviceNames.end())
{
mCtrlInputDevices->add(getLocalizedDeviceName(mInputDevice), mInputDevice, ADD_BOTTOM);
}
else
{
// Display name generaly doesn't match value.
// Value is an id so it's not nessesary readable,
// might not even be valid (disconnected usb).
// Until we get the data, don't change the device,
// otherwise box might override the control.
// But show a readable placeholder.
// Combo is disabled so it's safe to show
// a placeholder.
mCtrlInputDevices->add(getString("device_not_loaded"), mInputDevice, ADD_BOTTOM);
}
mCtrlInputDevices->setValue(mInputDevice);
}
if(mCtrlOutputDevices)
{
mCtrlOutputDevices->removeall();
mCtrlOutputDevices->add(getLocalizedDeviceName(mOutputDevice), mOutputDevice, ADD_BOTTOM);
auto it = mLocalizedDeviceNames.find(mOutputDevice);
if (it != mLocalizedDeviceNames.end())
{
mCtrlOutputDevices->add(getLocalizedDeviceName(mOutputDevice), mOutputDevice, ADD_BOTTOM);
}
else
{
// Don't change the device, only the label
mCtrlOutputDevices->add(getString("device_not_loaded"), mOutputDevice, ADD_BOTTOM);
}
mCtrlOutputDevices->setValue(mOutputDevice);
}
}

View File

@ -152,7 +152,11 @@ void LLFloaterSettingsPicker::onClose(bool app_quitting)
owner->setFocus(true);
}
mSettingItemID.setNull();
mInventoryPanel->getRootFolder()->clearSelection();
mInventoryPanel->clearSelection();
if (mInventoryPanel->getRootFolder())
{
mInventoryPanel->getRootFolder()->clearSelection();
}
}
void LLFloaterSettingsPicker::setValue(const LLSD& value)

View File

@ -696,7 +696,26 @@ bool idle_startup()
system = osString.substr (begIdx, endIdx - begIdx);
system += "Locale";
LLStringUtil::setLocale (LLTrans::getString(system));
std::string locale = LLTrans::getString(system);
if (locale != LLStringUtil::getLocale()) // is there a reason to do this on repeat?
{
LLStringUtil::setLocale(locale);
// Not all locales have AMPM, test it
if (LLStringOps::sAM.empty()) // Might already be overriden from LLAppViewer::init()
{
LLDate datetime(0.0);
std::string val = datetime.toHTTPDateString("%p");
if (val.empty())
{
LL_DEBUGS("InitInfo") << "Current locale \"" << locale << "\" "
<< "doesn't support AM/PM time format" << LL_ENDL;
// fallback to declarations in strings.xml
LLStringOps::sAM = LLTrans::getString("dateTimeAM");
LLStringOps::sPM = LLTrans::getString("dateTimePM");
}
}
}
//note: Removing this line will cause incorrect button size in the login screen. -- bao.
gTextureList.updateImages(0.01f) ;

View File

@ -1081,6 +1081,8 @@ void LLFloaterTexturePicker::onBtnSetToDefault(void* userdata)
{
self->setImageID( self->getDefaultImageAssetID() );
self->setTentative(false);
// Deselect in case inventory has a selected item with the same id
self->mInventoryPanel->getRootFolder()->clearSelection();
}
self->commitIfImmediateSet();
}
@ -1092,6 +1094,8 @@ void LLFloaterTexturePicker::onBtnBlank(void* userdata)
self->setCanApply(true, true);
self->setImageID( self->getBlankImageAssetID() );
self->setTentative(false);
// Deselect in case inventory has a selected item with the same id
self->mInventoryPanel->getRootFolder()->clearSelection();
self->commitIfImmediateSet();
}
@ -1113,6 +1117,8 @@ void LLFloaterTexturePicker::onBtnNone(void* userdata)
self->setCanApply(true, true);
self->setImageID( LLUUID::null );
self->setTentative(false);
// Deselect in case inventory has a selected item with null id
self->mInventoryPanel->getRootFolder()->clearSelection();
self->commitIfImmediateSet();
}

View File

@ -74,6 +74,8 @@
#include "llrender.h"
#include "llnavigationbar.h"
#include "llnotificationsutil.h"
#include "llfloaterpreference.h"
#include "llfloaterreg.h"
#include "llfloatertools.h"
#include "llpaneloutfitsinventory.h"
// <FS:Ansariel> [FS Login Panel]
@ -229,6 +231,21 @@ static bool handleDebugAvatarJointsChanged(const LLSD& newvalue)
return true;
}
static bool handleDebugQualityPerformanceChanged(const LLSD& newvalue)
{
// control was set directly or after adjusting Preference setting, no need to update
if (gSavedSettings.getU32("RenderQualityPerformance") != gSavedSettings.getU32("DebugQualityPerformance"))
{
LLFloaterPreference* instance = LLFloaterReg::getTypedInstance<LLFloaterPreference>("preferences");
if (instance)
{
gSavedSettings.setU32("RenderQualityPerformance", newvalue.asInteger());
instance->onChangeQuality(newvalue);
}
}
return true;
}
static bool handleAvatarHoverOffsetChanged(const LLSD& newvalue)
{
if (isAgentAvatarValid())
@ -1420,6 +1437,7 @@ void settings_setup_listeners()
setting_setup_signal_listener(gSavedSettings, "SpellCheckDictionary", handleSpellCheckChanged);
setting_setup_signal_listener(gSavedSettings, "LoginLocation", handleLoginLocationChanged);
setting_setup_signal_listener(gSavedSettings, "DebugAvatarJoints", handleDebugAvatarJointsChanged);
setting_setup_signal_listener(gSavedSettings, "DebugQualityPerformance", handleDebugQualityPerformanceChanged);
setting_setup_signal_listener(gSavedSettings, "TargetFPS", handleTargetFPSChanged);
setting_setup_signal_listener(gSavedSettings, "AutoTuneFPS", handleAutoTuneFPSChanged);

View File

@ -8096,7 +8096,7 @@ bool handle_zoom_to_object(const LLUUID& object_id, const std::optional<LLVector
LLViewerObject* object = gObjectList.findObject(object_id);
if (object)
if (object && object->isReachable())
{
gAgentCamera.setFocusOnAvatar(false, ANIMATE);

View File

@ -8026,6 +8026,51 @@ void LLViewerObject::clearTEWaterExclusion(const U8 te)
}
}
bool LLViewerObject::isReachable()
{
LLViewerRegion* agent_region = gAgent.getRegion();
LLViewerRegion* object_region = getRegion();
if (!agent_region || !object_region)
{
return false;
}
if (agent_region == object_region)
{
return true;
}
std::unordered_set<LLViewerRegion*> visited;
std::queue<LLViewerRegion*> pending;
visited.insert(agent_region);
pending.push(agent_region);
while (!pending.empty())
{
LLViewerRegion* current = pending.front();
pending.pop();
std::vector<LLViewerRegion*> neighbors;
current->getNeighboringRegions(neighbors);
for (LLViewerRegion* neighbor : neighbors)
{
if (!neighbor) continue;
if (neighbor == object_region)
{
return true;
}
// region's neighbors were not checked
if (visited.insert(neighbor).second)
{
pending.push(neighbor);
}
}
}
return false;
}
class ObjectPhysicsProperties : public LLHTTPNode
{
public:

View File

@ -266,6 +266,9 @@ public:
// Accessor functions
LLViewerRegion* getRegion() const { return mRegionp; }
// Check if object is reachable from agent region by traversing loaded neighboring regions
bool isReachable();
bool isSelected() const { return mUserSelected; }
// Check whole linkset
bool isAnySelected() const;

View File

@ -1568,18 +1568,43 @@ void LLViewerWindow::handleMouseLeave(LLWindow *window)
LLToolTipMgr::instance().blockToolTips();
}
bool LLViewerWindow::handleCloseRequest(LLWindow *window)
bool LLViewerWindow::handleCloseRequest(LLWindow *window, bool from_user)
{
if (!LLApp::isExiting() && !LLApp::isStopped())
{
// User has indicated they want to close, but we may need to ask
// about modified documents.
LLAppViewer::instance()->userQuit();
// Don't quit immediately
if (from_user)
{
// User has indicated they want to close, but we may need to ask
// about modified documents.
LLAppViewer::instance()->userQuit();
// Don't quit immediately
}
else
{
// OS is asking us to quit, assume we have time and start cleanup
LLAppViewer::instance()->requestQuit();
}
}
return false;
}
bool LLViewerWindow::handleSessionExit(LLWindow* window)
{
if (!LLApp::isExiting() && !LLApp::isStopped())
{
// Viewer received WM_ENDSESSION and app will be killed soon if it doesn't respond
LLAppViewer* app = LLAppViewer::instance();
app->sendSimpleLogoutRequest();
app->earlyExitNoNotify();
// Not viewer's fault, remove marker files so
// that statistics won't consider this to be a crash
app->removeMarkerFiles();
return false;
}
return true;
}
void LLViewerWindow::handleQuit(LLWindow *window)
{
if (gNonInteractive)

View File

@ -199,7 +199,8 @@ public:
/*virtual*/ bool handleUnicodeChar(llwchar uni_char, MASK mask); // NOT going to handle extended
/*virtual*/ bool handleMouseDown(LLWindow *window, LLCoordGL pos, MASK mask);
/*virtual*/ bool handleMouseUp(LLWindow *window, LLCoordGL pos, MASK mask);
/*virtual*/ bool handleCloseRequest(LLWindow *window);
/*virtual*/ bool handleCloseRequest(LLWindow *window, bool from_user);
/*virtual*/ bool handleSessionExit(LLWindow* window);
/*virtual*/ void handleQuit(LLWindow *window);
/*virtual*/ bool handleRightMouseDown(LLWindow *window, LLCoordGL pos, MASK mask);
/*virtual*/ bool handleRightMouseUp(LLWindow *window, LLCoordGL pos, MASK mask);

View File

@ -288,6 +288,8 @@ void LLWebRTCVoiceClient::terminate()
return;
}
LL_INFOS("Voice") << "Terminating WebRTC" << LL_ENDL;
mVoiceEnabled = false;
llwebrtc::terminate();

View File

@ -12115,21 +12115,24 @@ public:
}
};
// Called from LLViewHighlightTransparent when "Highlight Transparent" is toggled
void LLPipeline::rebuildDrawInfo()
{
for (LLWorld::region_list_t::const_iterator iter = LLWorld::getInstance()->getRegionList().begin();
iter != LLWorld::getInstance()->getRegionList().end(); ++iter)
const U32 types_to_traverse[] =
{
LLViewerRegion* region = *iter;
LLViewerRegion::PARTITION_VOLUME,
LLViewerRegion::PARTITION_BRIDGE,
LLViewerRegion::PARTITION_AVATAR
};
LLOctreeDirty dirty;
LLSpatialPartition* part = region->getSpatialPartition(LLViewerRegion::PARTITION_VOLUME);
dirty.traverse(part->mOctree);
part = region->getSpatialPartition(LLViewerRegion::PARTITION_BRIDGE);
dirty.traverse(part->mOctree);
LLOctreeDirty dirty;
for (LLViewerRegion* region : LLWorld::getInstance()->getRegionList())
{
for (U32 type : types_to_traverse)
{
LLSpatialPartition* part = region->getSpatialPartition(type);
dirty.traverse(part->mOctree);
}
}
}

View File

@ -9,6 +9,9 @@
<string name="name_default_system_device">
Standardsystemgerät
</string>
<string name="device_not_loaded">
Gerät nicht geladen
</string>
<text name="Input">
Eingabe
</text>

View File

@ -24,6 +24,10 @@
name="name_default_system_device">
Default System Device
</string>
<string
name="device_not_loaded">
Device not loaded
</string>
<icon
follows="left|top"
height="18"