diff options
| author | Andrey Kleshchev <117672381+akleshchev@users.noreply.github.com> | 2026-01-09 22:14:10 +0200 |
|---|---|---|
| committer | Andrey Kleshchev <117672381+akleshchev@users.noreply.github.com> | 2026-01-09 22:16:49 +0200 |
| commit | 9de30971d327a1c9bda9f8e4f8e01f1b9d49f5fe (patch) | |
| tree | c52fcafd621736ac775383f3ead8abcc3d95317c /indra/newview | |
| parent | c3b07f8f01ae2733b0fb1309af95a513878170c1 (diff) | |
| parent | 31e909870b316b7b4f36396357f70e44c724e608 (diff) | |
Merge release/2026.01 into develop
Diffstat (limited to 'indra/newview')
| -rw-r--r-- | indra/newview/CMakeLists.txt | 2 | ||||
| -rw-r--r-- | indra/newview/VIEWER_VERSION.txt | 2 | ||||
| -rw-r--r-- | indra/newview/app_settings/settings.xml | 2 | ||||
| -rw-r--r-- | indra/newview/llappviewer.cpp | 42 | ||||
| -rw-r--r-- | indra/newview/lleventpoll.cpp | 58 | ||||
| -rw-r--r-- | indra/newview/llfavoritesbar.cpp | 36 | ||||
| -rw-r--r-- | indra/newview/lllandmarklist.cpp | 49 | ||||
| -rw-r--r-- | indra/newview/lllandmarklist.h | 1 | ||||
| -rw-r--r-- | indra/newview/llpanelface.cpp | 32 | ||||
| -rw-r--r-- | indra/newview/llpanelface.h | 2 | ||||
| -rw-r--r-- | indra/newview/llpanelplaceprofile.cpp | 4 | ||||
| -rw-r--r-- | indra/newview/llpanelplaceprofile.h | 2 | ||||
| -rw-r--r-- | indra/newview/llpanelsnapshotinventory.cpp | 6 | ||||
| -rw-r--r-- | indra/newview/llviewerassetstorage.cpp | 71 | ||||
| -rw-r--r-- | indra/newview/llwatchdog.cpp | 277 | ||||
| -rw-r--r-- | indra/newview/llwatchdog.h | 99 | ||||
| -rw-r--r-- | indra/newview/skins/default/xui/en/menu_favorites.xml | 6 | ||||
| -rw-r--r-- | indra/newview/skins/default/xui/en/notifications.xml | 11 | ||||
| -rwxr-xr-x | indra/newview/viewer_manifest.py | 5 |
19 files changed, 268 insertions, 439 deletions
diff --git a/indra/newview/CMakeLists.txt b/indra/newview/CMakeLists.txt index 0c5f3f3fd9..0949a3b59f 100644 --- a/indra/newview/CMakeLists.txt +++ b/indra/newview/CMakeLists.txt @@ -736,7 +736,6 @@ set(viewer_SOURCE_FILES llvovolume.cpp llvowater.cpp llvowlsky.cpp - llwatchdog.cpp llwearableitemslist.cpp llwearablelist.cpp llweb.cpp @@ -1414,7 +1413,6 @@ set(viewer_HEADER_FILES llvovolume.h llvowater.h llvowlsky.h - llwatchdog.h llwearableitemslist.h llwearablelist.h llweb.h diff --git a/indra/newview/VIEWER_VERSION.txt b/indra/newview/VIEWER_VERSION.txt index 4703009f54..2aaedf9944 100644 --- a/indra/newview/VIEWER_VERSION.txt +++ b/indra/newview/VIEWER_VERSION.txt @@ -1 +1 @@ -2026.01.0 +26.1.0 diff --git a/indra/newview/app_settings/settings.xml b/indra/newview/app_settings/settings.xml index 5cbd007d7b..5a1a1187a0 100644 --- a/indra/newview/app_settings/settings.xml +++ b/indra/newview/app_settings/settings.xml @@ -9069,7 +9069,7 @@ <key>Comment</key> <string>Show reflection probes in the transparency debug view</string> <key>Persist</key> - <integer>1</integer> + <integer>0</integer> <key>Type</key> <string>Boolean</string> <key>Value</key> diff --git a/indra/newview/llappviewer.cpp b/indra/newview/llappviewer.cpp index bf00a7fed4..8aabdc5669 100644 --- a/indra/newview/llappviewer.cpp +++ b/indra/newview/llappviewer.cpp @@ -1239,7 +1239,7 @@ bool LLAppViewer::init() /*----------------------------------------------------------------------*/ // nat 2016-06-29 moved the following here from the former mainLoop(). - mMainloopTimeout = new LLWatchdogTimeout(); + mMainloopTimeout = new LLWatchdogTimeout("mainloop"); // Create IO Pump to use for HTTP Requests. gServicePump = new LLPumpIO(gAPRPoolp); @@ -1429,12 +1429,14 @@ bool LLAppViewer::doFrame() { LL_PROFILE_ZONE_NAMED_CATEGORY_APP("df mainloop"); + pingMainloopTimeout("df mainloop"); // canonical per-frame event mainloop.post(newFrame); } { LL_PROFILE_ZONE_NAMED_CATEGORY_APP("df suspend"); + pingMainloopTimeout("df suspend"); // give listeners a chance to run llcoro::suspend(); // if one of our coroutines threw an uncaught exception, rethrow it now @@ -1470,6 +1472,7 @@ bool LLAppViewer::doFrame() { { LL_PROFILE_ZONE_NAMED_CATEGORY_APP("df pauseMainloopTimeout"); + pingMainloopTimeout("df idle"); // So that it will be aware of last state. pauseMainloopTimeout(); // *TODO: Remove. Messages shouldn't be stalling for 20+ seconds! } @@ -1481,7 +1484,7 @@ bool LLAppViewer::doFrame() { LL_PROFILE_ZONE_NAMED_CATEGORY_APP("df resumeMainloopTimeout"); - resumeMainloopTimeout(); + resumeMainloopTimeout("df idle"); } } @@ -1496,7 +1499,7 @@ bool LLAppViewer::doFrame() } disconnectViewer(); - resumeMainloopTimeout(); + resumeMainloopTimeout("df snapshot n disconnect"); } // Render scene. @@ -2301,7 +2304,22 @@ void errorHandler(const std::string& title_string, const std::string& message_st } if (!message_string.empty()) { - OSMessageBox(message_string, title_string.empty() ? LLTrans::getString("MBFatalError") : title_string, OSMB_OK); + if (on_main_thread()) + { + // Prevent watchdog from killing us while dialog is up. + // Can't do pauseMainloopTimeout, since this may be called + // from threads and we are not going to need watchdog now. + LLAppViewer::instance()->pauseMainloopTimeout(); + + // todo: might want to have non-crashing timeout for OOM cases + // and needs a way to pause main loop. + OSMessageBox(message_string, title_string.empty() ? LLTrans::getString("MBFatalError") : title_string, OSMB_OK); + LLAppViewer::instance()->resumeMainloopTimeout(); + } + else + { + OSMessageBox(message_string, title_string.empty() ? LLTrans::getString("MBFatalError") : title_string, OSMB_OK); + } } } @@ -3167,7 +3185,19 @@ bool LLAppViewer::initWindow() if (use_watchdog) { - LLWatchdog::getInstance()->init(); + LLWatchdog::getInstance()->init([]() + { + LLAppViewer* app = LLAppViewer::instance(); + if (app->logoutRequestSent()) + { + app->createErrorMarker(LAST_EXEC_LOGOUT_FROZE); + } + else + { + app->createErrorMarker(LAST_EXEC_FROZE); + } + }); + gViewerWindow->getWindow()->initWatchdog(); } LLNotificationsUI::LLNotificationManager::getInstance(); @@ -5840,7 +5870,7 @@ void LLAppViewer::initMainloopTimeout(std::string_view state) { if (!mMainloopTimeout) { - mMainloopTimeout = new LLWatchdogTimeout(); + mMainloopTimeout = new LLWatchdogTimeout("mainloop"); resumeMainloopTimeout(state); } } diff --git a/indra/newview/lleventpoll.cpp b/indra/newview/lleventpoll.cpp index de3752d879..f1b46f0533 100644 --- a/indra/newview/lleventpoll.cpp +++ b/indra/newview/lleventpoll.cpp @@ -56,6 +56,7 @@ namespace Details private: void eventPollCoro(std::string url); + void handleMessage(const std::string& msg_name, const LLSD& body); void handleMessage(const LLSD &content); bool mDone; @@ -95,21 +96,23 @@ namespace Details mSenderIp = sender.getIPandPort(); } + void LLEventPollImpl::handleMessage(const std::string &msg_name, const LLSD &body) + { + LL_PROFILE_ZONE_SCOPED_CATEGORY_APP; + LLSD message; + message["sender"] = mSenderIp; + message["body"] = body; + + LLMessageSystem::dispatch(msg_name, message); + } + void LLEventPollImpl::handleMessage(const LLSD& content) { LL_PROFILE_ZONE_SCOPED_CATEGORY_APP; std::string msg_name = content["message"].asString(); LLSD message; - try - { - message["sender"] = mSenderIp; - message["body"] = content["body"]; - } - catch (std::bad_alloc&) - { - LLError::LLUserWarningMsg::showOutOfMemory(); - LL_ERRS("LLCoros") << "Bad memory allocation on message: " << msg_name << LL_ENDL; - } + message["sender"] = mSenderIp; + message["body"] = content["body"]; LLMessageSystem::dispatch(msg_name, message); } @@ -194,7 +197,7 @@ namespace Details break; } - LLSD httpResults = result["http_result"]; + LLSD &httpResults = result["http_result"]; LLCore::HttpStatus status = LLCoreHttpUtil::HttpCoroutineAdapter::getStatusFromLLSD(httpResults); if (!status) @@ -299,7 +302,7 @@ namespace Details } acknowledge = result["id"]; - LLSD events = result["events"]; + LLSD &events = result["events"]; if (acknowledge.isUndefined()) { @@ -310,20 +313,37 @@ namespace Details LL_DEBUGS("LLEventPollImpl") << " <" << counter << "> " << events.size() << "events (id " << acknowledge << ")" << LL_ENDL; - LLSD::array_const_iterator i = events.beginArray(); - LLSD::array_const_iterator end = events.endArray(); + LLSD::array_iterator i = events.beginArray(); + LLSD::array_iterator end = events.endArray(); for (; i != end; ++i) { if (i->has("message")) { if (main_queue) - { // shuttle to a sensible spot in the main thread instead + { + // Shuttle copy to a sensible spot in the main thread instead // of wherever this coroutine happens to be executing - const LLSD& msg = *i; - main_queue->post([this, msg]() + + LL::WorkQueue::Work work; + { + // LLSD is too smart for it's own good and may act like a smart + // pointer for the content of (*i), so instead of passing (*i) + // pass a prepared name and move ownership of "body", + // as we are not going to need "body" anywhere else. + std::string msg_name = (*i)["message"].asString(); + + // WARNING: This is a shallow copy! + // If something still retains the data (like in httpAdapter?) this might still + // result in a crash, if it does appear to be the case, make a deep copy or + // convert data to string and pass that string. + const LLSD body = (*i)["body"]; + (*i)["body"].clear(); + work = [this, msg_name, body]() { - handleMessage(msg); - }); + handleMessage(msg_name, body); + }; + } + main_queue->post(work); } else { diff --git a/indra/newview/llfavoritesbar.cpp b/indra/newview/llfavoritesbar.cpp index 377710c170..98b3ca820b 100644 --- a/indra/newview/llfavoritesbar.cpp +++ b/indra/newview/llfavoritesbar.cpp @@ -1395,6 +1395,19 @@ bool LLFavoritesBarCtrl::enableSelected(const LLSD& userdata) { return !LLAgentPicksInfo::getInstance()->isPickLimitReached(); } + else if (param == "copy_slurl" + || param == "show_on_map") + { + LLViewerInventoryItem* item = gInventory.getItem(mSelectedItemID); + if (nullptr == item) + return false; // shouldn't happen as it is selected from existing items + + const LLUUID& asset_id = item->getAssetUUID(); + + // Favorites are supposed to be loaded first, it should be here already + LLLandmark* landmark = gLandmarkList.getAsset(asset_id, NULL /*callback*/); + return nullptr != landmark; + } return false; } @@ -1425,10 +1438,17 @@ void LLFavoritesBarCtrl::doToSelected(const LLSD& userdata) LLVector3d posGlobal; LLLandmarkActions::getLandmarkGlobalPos(mSelectedItemID, posGlobal); + // inventory item and asset exist, otherwise + // enableSelected wouldn't have let it get here, + // only need to check location validity if (!posGlobal.isExactlyZero()) { LLLandmarkActions::getSLURLfromPosGlobal(posGlobal, copy_slurl_to_clipboard_cb); } + else + { + LLNotificationsUtil::add("LandmarkLocationUnknown"); + } } else if (action == "show_on_map") { @@ -1437,10 +1457,20 @@ void LLFavoritesBarCtrl::doToSelected(const LLSD& userdata) LLVector3d posGlobal; LLLandmarkActions::getLandmarkGlobalPos(mSelectedItemID, posGlobal); - if (!posGlobal.isExactlyZero() && worldmap_instance) + if (worldmap_instance) { - worldmap_instance->trackLocation(posGlobal); - LLFloaterReg::showInstance("world_map", "center"); + // inventory item and asset exist, otherwise + // enableSelected wouldn't have let it get here, + // only need to check location validity + if (!posGlobal.isExactlyZero()) + { + worldmap_instance->trackLocation(posGlobal); + LLFloaterReg::showInstance("world_map", "center"); + } + else + { + LLNotificationsUtil::add("LandmarkLocationUnknown"); + } } } else if (action == "create_pick") diff --git a/indra/newview/lllandmarklist.cpp b/indra/newview/lllandmarklist.cpp index 3fa0ab99f3..b25a42a938 100644 --- a/indra/newview/lllandmarklist.cpp +++ b/indra/newview/lllandmarklist.cpp @@ -147,29 +147,66 @@ void LLLandmarkList::processGetAssetReply( else { // failed to parse, shouldn't happen + LL_WARNS("Landmarks") << "Failed to parse landmark " << uuid << LL_ENDL; gLandmarkList.eraseCallbacks(uuid); } } else { // got a good status, but no file, shouldn't happen + LL_WARNS("Landmarks") << "Empty buffer for landmark " << uuid << LL_ENDL; gLandmarkList.eraseCallbacks(uuid); } + + // We got this asset, remove it from retry and bad lists. + gLandmarkList.mRetryList.erase(uuid); + gLandmarkList.mBadList.erase(uuid); } else { - // SJB: No use case for a notification here. Use LL_DEBUGS() instead - if( LL_ERR_ASSET_REQUEST_NOT_IN_DATABASE == status ) + if (LL_ERR_NO_CAP == status) + { + // A problem with asset cap, always allow retrying. + // Todo: should this reschedule? + gLandmarkList.mRequestedList.erase(uuid); + gLandmarkList.eraseCallbacks(uuid); + // If there was a previous request, it likely failed due to an obsolete cap + // so clear the retry marker to allow multiple retries. + gLandmarkList.mRetryList.erase(uuid); + return; + } + if (gLandmarkList.mBadList.find(uuid) != gLandmarkList.mBadList.end()) + { + // Already on the 'bad' list, ignore + gLandmarkList.mRequestedList.erase(uuid); + gLandmarkList.eraseCallbacks(uuid); + return; + } + if (LL_ERR_ASSET_REQUEST_FAILED == status + && gLandmarkList.mRetryList.find(uuid) == gLandmarkList.mRetryList.end()) + { + // There is a number of reasons why an asset request can fail, + // like a cap being obsolete due to user teleporting. + // Let viewer rerequest at least once more. + // Todo: should this reshchedule? + gLandmarkList.mRetryList.emplace(uuid); + gLandmarkList.mRequestedList.erase(uuid); + gLandmarkList.eraseCallbacks(uuid); + return; + } + + if (LL_ERR_ASSET_REQUEST_NOT_IN_DATABASE == status) { - LL_WARNS("Landmarks") << "Missing Landmark" << LL_ENDL; - //LLNotificationsUtil::add("LandmarkMissing"); + LL_WARNS("Landmarks") << "Missing Landmark " << uuid << LL_ENDL; } else { - LL_WARNS("Landmarks") << "Unable to load Landmark" << LL_ENDL; - //LLNotificationsUtil::add("UnableToLoadLandmark"); + LL_WARNS("Landmarks") << "Unable to load Landmark " << uuid + << ". asset status: " << status + << ". Extended status: " << (S64)ext_status << LL_ENDL; } + gLandmarkList.mRetryList.erase(uuid); gLandmarkList.mBadList.insert(uuid); gLandmarkList.mRequestedList.erase(uuid); //mBadList effectively blocks any load, so no point keeping id in requests gLandmarkList.eraseCallbacks(uuid); diff --git a/indra/newview/lllandmarklist.h b/indra/newview/lllandmarklist.h index fb8b5a1960..76b5b97211 100644 --- a/indra/newview/lllandmarklist.h +++ b/indra/newview/lllandmarklist.h @@ -72,6 +72,7 @@ protected: typedef std::set<LLUUID> landmark_uuid_list_t; landmark_uuid_list_t mBadList; + landmark_uuid_list_t mRetryList; typedef std::map<LLUUID,F32> landmark_requested_list_t; landmark_requested_list_t mRequestedList; diff --git a/indra/newview/llpanelface.cpp b/indra/newview/llpanelface.cpp index de8ab95dee..bcb51b22ca 100644 --- a/indra/newview/llpanelface.cpp +++ b/indra/newview/llpanelface.cpp @@ -1201,7 +1201,7 @@ void LLPanelFace::updateUI(bool force_set_values /*false*/) // See if that's been overridden by a material setting for same... // - LLSelectedTEMaterial::getCurrentDiffuseAlphaMode(alpha_mode, identical_alpha_mode, mIsAlpha); + LLSelectedTEMaterial::getCurrentDiffuseAlphaMode(alpha_mode, identical_alpha_mode); // it is invalid to have any alpha mode other than blend if transparency is greater than zero ... // Want masking? Want emissive? Tough! You get BLEND! @@ -1211,6 +1211,12 @@ void LLPanelFace::updateUI(bool force_set_values /*false*/) alpha_mode = mIsAlpha ? alpha_mode : LLMaterial::DIFFUSE_ALPHA_MODE_NONE; mComboAlphaMode->getSelectionInterface()->selectNthItem(alpha_mode); + mComboAlphaMode->setTentative(!identical_alpha_mode); + if (!identical_alpha_mode) + { + std::string multiple = LLTrans::getString("multiple_textures"); + mComboAlphaMode->setLabel(multiple); + } updateAlphaControls(); mExcludeWater &= (LLMaterial::DIFFUSE_ALPHA_MODE_BLEND == alpha_mode); @@ -5484,32 +5490,40 @@ void LLPanelFace::LLSelectedTEMaterial::getMaxNormalRepeats(F32& repeats, bool& identical = LLSelectMgr::getInstance()->getSelection()->getSelectedTEValue( &max_norm_repeats_func, repeats); } -void LLPanelFace::LLSelectedTEMaterial::getCurrentDiffuseAlphaMode(U8& diffuse_alpha_mode, bool& identical, bool diffuse_texture_has_alpha) +void LLPanelFace::LLSelectedTEMaterial::getCurrentDiffuseAlphaMode(U8& diffuse_alpha_mode, bool& identical) { struct LLSelectedTEGetDiffuseAlphaMode : public LLSelectedTEGetFunctor<U8> { - LLSelectedTEGetDiffuseAlphaMode() : _isAlpha(false) {} - LLSelectedTEGetDiffuseAlphaMode(bool diffuse_texture_has_alpha) : _isAlpha(diffuse_texture_has_alpha) {} + LLSelectedTEGetDiffuseAlphaMode() {} virtual ~LLSelectedTEGetDiffuseAlphaMode() {} U8 get(LLViewerObject* object, S32 face) { - U8 diffuse_mode = _isAlpha ? LLMaterial::DIFFUSE_ALPHA_MODE_BLEND : LLMaterial::DIFFUSE_ALPHA_MODE_NONE; - LLTextureEntry* tep = object->getTE(face); if (tep) { LLMaterial* mat = tep->getMaterialParams().get(); if (mat) { - diffuse_mode = mat->getDiffuseAlphaMode(); + return mat->getDiffuseAlphaMode(); + } + } + + bool has_alpha = false; + LLViewerTexture* image = object->getTEImage(face); + if (image) + { + LLGLenum format = image->getPrimaryFormat(); + if (format == GL_RGBA || format == GL_ALPHA) + { + has_alpha = true; } } + U8 diffuse_mode = has_alpha ? LLMaterial::DIFFUSE_ALPHA_MODE_BLEND : LLMaterial::DIFFUSE_ALPHA_MODE_NONE; return diffuse_mode; } - bool _isAlpha; // whether or not the diffuse texture selected contains alpha information - } get_diff_mode(diffuse_texture_has_alpha); + } get_diff_mode; identical = LLSelectMgr::getInstance()->getSelection()->getSelectedTEValue( &get_diff_mode, diffuse_alpha_mode); } diff --git a/indra/newview/llpanelface.h b/indra/newview/llpanelface.h index 63fee6bab8..82790ac54b 100644 --- a/indra/newview/llpanelface.h +++ b/indra/newview/llpanelface.h @@ -652,7 +652,7 @@ public: static void getCurrent(LLMaterialPtr& material_ptr, bool& identical_material); static void getMaxSpecularRepeats(F32& repeats, bool& identical); static void getMaxNormalRepeats(F32& repeats, bool& identical); - static void getCurrentDiffuseAlphaMode(U8& diffuse_alpha_mode, bool& identical, bool diffuse_texture_has_alpha); + static void getCurrentDiffuseAlphaMode(U8& diffuse_alpha_mode, bool& identical); static void selectionNormalScaleAutofit(LLPanelFace* panel_face, F32 repeats_per_meter); static void selectionSpecularScaleAutofit(LLPanelFace* panel_face, F32 repeats_per_meter); diff --git a/indra/newview/llpanelplaceprofile.cpp b/indra/newview/llpanelplaceprofile.cpp index 87f05f2028..c380b6860f 100644 --- a/indra/newview/llpanelplaceprofile.cpp +++ b/indra/newview/llpanelplaceprofile.cpp @@ -517,7 +517,7 @@ void LLPanelPlaceProfile::displaySelectedParcelInfo(LLParcel* parcel, std::string parcel_owner = LLSLURL("agent", parcel->getOwnerID(), "inspect").getSLURLString(); mParcelOwner->setText(parcel_owner); - LLAvatarNameCache::get(region->getOwner(), boost::bind(&LLPanelPlaceInfo::onAvatarNameCache, _1, _2, mRegionOwnerText)); + mAvatarNameCacheConnection = LLAvatarNameCache::get(region->getOwner(), boost::bind(&LLPanelPlaceInfo::onAvatarNameCache, _1, _2, mRegionOwnerText)); mRegionGroupText->setText( getString("none_text")); } @@ -548,7 +548,7 @@ void LLPanelPlaceProfile::displaySelectedParcelInfo(LLParcel* parcel, const LLUUID& auth_buyer_id = parcel->getAuthorizedBuyerID(); if(auth_buyer_id.notNull()) { - LLAvatarNameCache::get(auth_buyer_id, boost::bind(&LLPanelPlaceInfo::onAvatarNameCache, _1, _2, mSaleToText)); + mAvatarNameCacheConnection = LLAvatarNameCache::get(auth_buyer_id, boost::bind(&LLPanelPlaceInfo::onAvatarNameCache, _1, _2, mSaleToText)); // Show sales info to a specific person or a group he belongs to. if (auth_buyer_id != gAgent.getID() && !gAgent.isInGroup(auth_buyer_id)) diff --git a/indra/newview/llpanelplaceprofile.h b/indra/newview/llpanelplaceprofile.h index f562be0f5d..0c161198f8 100644 --- a/indra/newview/llpanelplaceprofile.h +++ b/indra/newview/llpanelplaceprofile.h @@ -118,6 +118,8 @@ private: LLTextEditor* mResaleText; LLTextBox* mSaleToText; LLAccordionCtrl* mAccordionCtrl; + + boost::signals2::scoped_connection mAvatarNameCacheConnection; }; #endif // LL_LLPANELPLACEPROFILE_H diff --git a/indra/newview/llpanelsnapshotinventory.cpp b/indra/newview/llpanelsnapshotinventory.cpp index b81b891685..7396f079b0 100644 --- a/indra/newview/llpanelsnapshotinventory.cpp +++ b/indra/newview/llpanelsnapshotinventory.cpp @@ -110,7 +110,11 @@ void LLPanelSnapshotInventory::updateControls(const LLSD& info) void LLPanelSnapshotInventory::onResolutionCommit(LLUICtrl* ctrl) { - bool current_window_selected = (getChild<LLComboBox>(getImageSizeComboName())->getCurrentIndex() == 3); + LLComboBox* combo = getChild<LLComboBox>(getImageSizeComboName()); + // Current window likely won't ever change position from being the penultimate item + // Custom window is last item + S32 curent_window_index = combo->getItemCount() - 2; + bool current_window_selected = (combo->getCurrentIndex() == curent_window_index); getChild<LLSpinCtrl>(getWidthSpinnerName())->setVisible(!current_window_selected); getChild<LLSpinCtrl>(getHeightSpinnerName())->setVisible(!current_window_selected); } diff --git a/indra/newview/llviewerassetstorage.cpp b/indra/newview/llviewerassetstorage.cpp index 141f370ecb..fd462fb225 100644 --- a/indra/newview/llviewerassetstorage.cpp +++ b/indra/newview/llviewerassetstorage.cpp @@ -42,6 +42,7 @@ #include "llcoproceduremanager.h" #include "lleventcoro.h" #include "llsdutil.h" +#include "llstartup.h" #include "llworld.h" ///---------------------------------------------------------------------------- @@ -460,28 +461,78 @@ void LLViewerAssetStorage::assetRequestCoro( if (!gAgent.getRegion()) { - LL_WARNS_ONCE("ViewerAsset") << "Asset request fails: no region set" << LL_ENDL; - result_code = LL_ERR_ASSET_REQUEST_FAILED; - ext_status = LLExtStat::NONE; - removeAndCallbackPendingDownloads(uuid, atype, uuid, atype, result_code, ext_status, 0); - return; + if (STATE_WORLD_INIT <= LLStartUp::getStartupState()) + { + // Viewer isn't ready, wait for region to become available + LL_INFOS_ONCE("ViewerAsset") << "Waiting for agent region to be set" << LL_ENDL; + + LLEventStream region_init("waitForRegion", true); + std::string pump_name = region_init.getName(); + + boost::signals2::connection region_conn = + gAgent.addRegionChangedCallback([pump_name]() + { + LLEventPumps::instance().obtain(pump_name).post(LLSD()); + }); + F32Seconds timeout_seconds(LL_ASSET_STORAGE_TIMEOUT); + llcoro::suspendUntilEventOnWithTimeout(region_init, timeout_seconds, LLSDMap("timeout", LLSD::Boolean(true))); + gAgent.removeRegionChangedCallback(region_conn); + region_conn.disconnect(); + + if (LLApp::isExiting() || !gAssetStorage) + { + return; + } + + // recheck region whether suspend ended on timeout or not + if (!gAgent.getRegion()) + { + LL_WARNS_ONCE("ViewerAsset") << "Asset request fails: timeout reached while waiting for region" << LL_ENDL; + result_code = LL_ERR_NO_CAP; + ext_status = LLExtStat::NONE; + removeAndCallbackPendingDownloads(uuid, atype, uuid, atype, result_code, ext_status, 0); + return; + } + } + else + { + LL_WARNS_ONCE("ViewerAsset") << "Asset request fails: no region set" << LL_ENDL; + result_code = LL_ERR_NO_CAP; + ext_status = LLExtStat::NONE; + removeAndCallbackPendingDownloads(uuid, atype, uuid, atype, result_code, ext_status, 0); + return; + } } - else if (!gAgent.getRegion()->capabilitiesReceived()) + + if (!gAgent.getRegion()->capabilitiesReceived()) { LL_WARNS_ONCE("ViewerAsset") << "Waiting for capabilities" << LL_ENDL; LLEventStream capsRecv("waitForCaps", true); - gAgent.getRegion()->setCapabilitiesReceivedCallback( - boost::bind(&LLViewerAssetStorage::capsRecvForRegion, this, _1, capsRecv.getName())); + boost::signals2::connection caps_conn = + gAgent.getRegion()->setCapabilitiesReceivedCallback( + boost::bind(&LLViewerAssetStorage::capsRecvForRegion, this, _1, capsRecv.getName())); - llcoro::suspendUntilEventOn(capsRecv); + F32Seconds timeout_seconds(LL_ASSET_STORAGE_TIMEOUT); // from minutes to seconds, by default 5 minutes + LLSD result = llcoro::suspendUntilEventOnWithTimeout(capsRecv, timeout_seconds, LLSDMap("timeout", LLSD::Boolean(true))); + caps_conn.disconnect(); if (LLApp::isExiting() || !gAssetStorage) { return; } + if (result.has("timeout")) + { + // Caps failed to arrive in 5 minutes + LL_WARNS_ONCE("ViewerAsset") << "Asset " << uuid << " request fails : capabilities took too long to arrive" << LL_ENDL; + result_code = LL_ERR_NO_CAP; + ext_status = LLExtStat::NONE; + removeAndCallbackPendingDownloads(uuid, atype, uuid, atype, result_code, ext_status, 0); + return; + } + LL_WARNS_ONCE("ViewerAsset") << "capsRecv got event" << LL_ENDL; LL_WARNS_ONCE("ViewerAsset") << "region " << gAgent.getRegion() << " mViewerAssetUrl " << mViewerAssetUrl << LL_ENDL; } @@ -492,7 +543,7 @@ void LLViewerAssetStorage::assetRequestCoro( if (mViewerAssetUrl.empty()) { LL_WARNS_ONCE("ViewerAsset") << "asset request fails: caps received but no viewer asset cap found" << LL_ENDL; - result_code = LL_ERR_ASSET_REQUEST_FAILED; + result_code = LL_ERR_NO_CAP; ext_status = LLExtStat::NONE; removeAndCallbackPendingDownloads(uuid, atype, uuid, atype, result_code, ext_status, 0); return; diff --git a/indra/newview/llwatchdog.cpp b/indra/newview/llwatchdog.cpp deleted file mode 100644 index 072743505f..0000000000 --- a/indra/newview/llwatchdog.cpp +++ /dev/null @@ -1,277 +0,0 @@ -/** - * @file llthreadwatchdog.cpp - * @brief The LLThreadWatchdog class definitions - * - * $LicenseInfo:firstyear=2007&license=viewerlgpl$ - * Second Life Viewer Source Code - * Copyright (C) 2010, Linden Research, Inc. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; - * version 2.1 of the License only. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * - * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA - * $/LicenseInfo$ - */ - - -#include "llviewerprecompiledheaders.h" -#include "llwatchdog.h" -#include "llmutex.h" -#include "llthread.h" -#include "llappviewer.h" - -constexpr U32 WATCHDOG_SLEEP_TIME_USEC = 1000000U; - -// This class runs the watchdog timing thread. -class LLWatchdogTimerThread : public LLThread -{ -public: - LLWatchdogTimerThread() : - LLThread("Watchdog"), - mSleepMsecs(0), - mStopping(false) - { - } - - ~LLWatchdogTimerThread() {} - - void setSleepTime(long ms) { mSleepMsecs = ms; } - void stop() - { - mStopping = true; - mSleepMsecs = 1; - } - - void run() override - { - while(!mStopping) - { - LLWatchdog::getInstance()->run(); - ms_sleep(mSleepMsecs); - } - } - -private: - long mSleepMsecs; - bool mStopping; -}; - -// LLWatchdogEntry -LLWatchdogEntry::LLWatchdogEntry() -{ -} - -LLWatchdogEntry::~LLWatchdogEntry() -{ - stop(); -} - -void LLWatchdogEntry::start() -{ - LLWatchdog::getInstance()->add(this); -} - -void LLWatchdogEntry::stop() -{ - // this can happen very late in the shutdown sequence - if (!LLWatchdog::wasDeleted()) - { - LLWatchdog::getInstance()->remove(this); - } -} - -// LLWatchdogTimeout -const std::string UNINIT_STRING = "uninitialized"; - -LLWatchdogTimeout::LLWatchdogTimeout() : - mTimeout(0.0f), - mPingState(UNINIT_STRING) -{ -} - -LLWatchdogTimeout::~LLWatchdogTimeout() -{ -} - -bool LLWatchdogTimeout::isAlive() const -{ - return (mTimer.getStarted() && !mTimer.hasExpired()); -} - -void LLWatchdogTimeout::reset() -{ - mTimer.setTimerExpirySec(mTimeout); -} - -void LLWatchdogTimeout::setTimeout(F32 d) -{ - mTimeout = d; -} - -void LLWatchdogTimeout::start(std::string_view state) -{ - if (mTimeout == 0) - { - LL_WARNS() << "Cant' start watchdog entry - no timeout set" << LL_ENDL; - return; - } - // Order of operation is very important here. - // After LLWatchdogEntry::start() is called - // LLWatchdogTimeout::isAlive() will be called asynchronously. - ping(state); - mTimer.start(); - mTimer.setTimerExpirySec(mTimeout); // timer expiration set to 0 by start() - LLWatchdogEntry::start(); -} - -void LLWatchdogTimeout::stop() -{ - LLWatchdogEntry::stop(); - mTimer.stop(); -} - -void LLWatchdogTimeout::ping(std::string_view state) -{ - if (!state.empty()) - { - mPingState = state; - } - reset(); -} - -// LLWatchdog -LLWatchdog::LLWatchdog() - :mSuspectsAccessMutex() - ,mTimer(nullptr) - ,mLastClockCount(0) -{ -} - -LLWatchdog::~LLWatchdog() -{ -} - -void LLWatchdog::add(LLWatchdogEntry* e) -{ - lockThread(); - mSuspects.insert(e); - unlockThread(); -} - -void LLWatchdog::remove(LLWatchdogEntry* e) -{ - lockThread(); - mSuspects.erase(e); - unlockThread(); -} - -void LLWatchdog::init() -{ - if (!mSuspectsAccessMutex && !mTimer) - { - mSuspectsAccessMutex = new LLMutex(); - mTimer = new LLWatchdogTimerThread(); - mTimer->setSleepTime(WATCHDOG_SLEEP_TIME_USEC / 1000); - mLastClockCount = LLTimer::getTotalTime(); - - // mTimer->start() kicks off the thread, any code after - // start needs to use the mSuspectsAccessMutex - mTimer->start(); - } -} - -void LLWatchdog::cleanup() -{ - if (mTimer) - { - mTimer->stop(); - delete mTimer; - mTimer = nullptr; - } - - if (mSuspectsAccessMutex) - { - delete mSuspectsAccessMutex; - mSuspectsAccessMutex = nullptr; - } - - mLastClockCount = 0; -} - -void LLWatchdog::run() -{ - lockThread(); - - // Check the time since the last call to run... - // If the time elapsed is two times greater than the regualr sleep time - // reset the active timeouts. - constexpr U32 TIME_ELAPSED_MULTIPLIER = 2; - U64 current_time = LLTimer::getTotalTime(); - U64 current_run_delta = current_time - mLastClockCount; - mLastClockCount = current_time; - - if (current_run_delta > (WATCHDOG_SLEEP_TIME_USEC * TIME_ELAPSED_MULTIPLIER)) - { - LL_INFOS() << "Watchdog thread delayed: resetting entries." << LL_ENDL; - for (const auto& suspect : mSuspects) - { - suspect->reset(); - } - } - else - { - SuspectsRegistry::iterator result = - std::find_if(mSuspects.begin(), - mSuspects.end(), - [](const LLWatchdogEntry* suspect){ return ! suspect->isAlive(); }); - if (result != mSuspects.end()) - { - // error!!! - if(mTimer) - { - mTimer->stop(); - } - if (LLAppViewer::instance()->logoutRequestSent()) - { - LLAppViewer::instance()->createErrorMarker(LAST_EXEC_LOGOUT_FROZE); - } - else - { - LLAppViewer::instance()->createErrorMarker(LAST_EXEC_FROZE); - } - // Todo1: warn user? - // Todo2: We probably want to report even if 5 seconds passed, just not error 'yet'. - LL_ERRS() << "Watchdog timer expired; assuming viewer is hung and crashing" << LL_ENDL; - } - } - - - unlockThread(); -} - -void LLWatchdog::lockThread() -{ - if (mSuspectsAccessMutex) - { - mSuspectsAccessMutex->lock(); - } -} - -void LLWatchdog::unlockThread() -{ - if (mSuspectsAccessMutex) - { - mSuspectsAccessMutex->unlock(); - } -} diff --git a/indra/newview/llwatchdog.h b/indra/newview/llwatchdog.h deleted file mode 100644 index b7dd55577e..0000000000 --- a/indra/newview/llwatchdog.h +++ /dev/null @@ -1,99 +0,0 @@ -/** - * @file llthreadwatchdog.h - * @brief The LLThreadWatchdog class declaration - * - * $LicenseInfo:firstyear=2007&license=viewerlgpl$ - * Second Life Viewer Source Code - * Copyright (C) 2010, Linden Research, Inc. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; - * version 2.1 of the License only. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * - * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA - * $/LicenseInfo$ - */ - -#ifndef LL_LLTHREADWATCHDOG_H -#define LL_LLTHREADWATCHDOG_H - -#ifndef LL_TIMER_H - #include "lltimer.h" -#endif - -// LLWatchdogEntry is the interface used by the tasks that -// need to be watched. -class LLWatchdogEntry -{ -public: - LLWatchdogEntry(); - virtual ~LLWatchdogEntry(); - - // isAlive is accessed by the watchdog thread. - // This may mean that resources used by - // isAlive and other method may need synchronization. - virtual bool isAlive() const = 0; - virtual void reset() = 0; - virtual void start(); - virtual void stop(); -}; - -class LLWatchdogTimeout : public LLWatchdogEntry -{ -public: - LLWatchdogTimeout(); - virtual ~LLWatchdogTimeout(); - - bool isAlive() const override; - void reset() override; - void start() override { start(""); } - void stop() override; - - void start(std::string_view state); - void setTimeout(F32 d); - void ping(std::string_view state); - const std::string& getState() {return mPingState; } - -private: - LLTimer mTimer; - F32 mTimeout; - std::string mPingState; -}; - -class LLWatchdogTimerThread; // Defined in the cpp -class LLWatchdog : public LLSingleton<LLWatchdog> -{ - LLSINGLETON(LLWatchdog); - ~LLWatchdog(); - -public: - // Add an entry to the watchdog. - void add(LLWatchdogEntry* e); - void remove(LLWatchdogEntry* e); - - void init(); - void run(); - void cleanup(); - -private: - void lockThread(); - void unlockThread(); - - typedef std::set<LLWatchdogEntry*> SuspectsRegistry; - SuspectsRegistry mSuspects; - LLMutex* mSuspectsAccessMutex; - LLWatchdogTimerThread* mTimer; - U64 mLastClockCount; -}; - -#endif // LL_LLTHREADWATCHDOG_H diff --git a/indra/newview/skins/default/xui/en/menu_favorites.xml b/indra/newview/skins/default/xui/en/menu_favorites.xml index 6345394b46..f82f705fb7 100644 --- a/indra/newview/skins/default/xui/en/menu_favorites.xml +++ b/indra/newview/skins/default/xui/en/menu_favorites.xml @@ -35,6 +35,9 @@ <menu_item_call.on_click function="Favorites.DoToSelected" parameter="show_on_map" /> + <menu_item_call.on_enable + function="Favorites.EnableSelected" + parameter="show_on_map" /> </menu_item_call> <menu_item_call label="Copy SLurl" @@ -43,6 +46,9 @@ <menu_item_call.on_click function="Favorites.DoToSelected" parameter="copy_slurl" /> + <menu_item_call.on_enable + function="Favorites.EnableSelected" + parameter="copy_slurl" /> </menu_item_call> <menu_item_call label="Create Pick" diff --git a/indra/newview/skins/default/xui/en/notifications.xml b/indra/newview/skins/default/xui/en/notifications.xml index d278bac075..e8da8a6322 100644 --- a/indra/newview/skins/default/xui/en/notifications.xml +++ b/indra/newview/skins/default/xui/en/notifications.xml @@ -2466,6 +2466,17 @@ You already have a landmark for this location. </notification> <notification + icon="alert.tga" + name="LandmarkLocationUnknown" + type="alert"> +Viewer wasn't able to get region's location. Region might be temporarily unavailable, was removed or landmark failed to load. + <usetemplate + name="okbutton" + yestext="OK"/> + <tag>fail</tag> + </notification> + + <notification icon="alertmodal.tga" name="CannotCreateLandmarkNotOwner" type="alertmodal"> diff --git a/indra/newview/viewer_manifest.py b/indra/newview/viewer_manifest.py index 94d234686a..109f00c9ae 100755 --- a/indra/newview/viewer_manifest.py +++ b/indra/newview/viewer_manifest.py @@ -259,9 +259,10 @@ class ViewerManifest(LLManifest): global CHANNEL_VENDOR_BASE channel_type=self.channel_type() if channel_type == 'release': - return CHANNEL_VENDOR_BASE + app_suffix='Viewer' else: - return CHANNEL_VENDOR_BASE + ' ' + self.channel_variant() + app_suffix=self.channel_variant() + return CHANNEL_VENDOR_BASE + ' ' + app_suffix def exec_name(self): return "SecondLifeViewer" |
