diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index 7eb8317605..87a5c69e3b 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -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 }}" diff --git a/indra/llcommon/llleap.cpp b/indra/llcommon/llleap.cpp index 662a2511cd..ada6b9519e 100644 --- a/indra/llcommon/llleap.cpp +++ b/indra/llcommon/llleap.cpp @@ -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; diff --git a/indra/llimagej2coj/llimagej2coj.cpp b/indra/llimagej2coj/llimagej2coj.cpp index 1fbed9d6f1..28f1f2052b 100644 --- a/indra/llimagej2coj/llimagej2coj.cpp +++ b/indra/llimagej2coj/llimagej2coj.cpp @@ -557,11 +557,6 @@ public: } - if (!opj_setup_encoder(encoder, ¶meters, 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, ¶meters, image)) + { + return false; + } + U32 tile_count = width_tiles * height_tiles; U32 data_size_guess = tile_count * TILE_SIZE; diff --git a/indra/llui/llfolderview.cpp b/indra/llui/llfolderview.cpp index fea71f7d6e..8c46da3b5a 100644 --- a/indra/llui/llfolderview.cpp +++ b/indra/llui/llfolderview.cpp @@ -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(); diff --git a/indra/llui/lltextbase.cpp b/indra/llui/lltextbase.cpp index e06affab37..ac7649becb 100644 --- a/indra/llui/lltextbase.cpp +++ b/indra/llui/lltextbase.cpp @@ -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("zoom_in")) + { + zoom_btn->setEnabled(is_reachable); + } + } + // hide the moderation tools in the context menu unless we are in a group IM floater LLFloater* parent_floater = getParentByType(); 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 // diff --git a/indra/llui/lltextbase.h b/indra/llui/lltextbase.h index 87a5961b7d..65e7e35001 100644 --- a/indra/llui/lltextbase.h +++ b/indra/llui/lltextbase.h @@ -346,6 +346,7 @@ public: typedef boost::signals2::signal is_friend_signal_t; typedef boost::signals2::signal is_blocked_signal_t; + typedef boost::signals2::signal is_obj_reachable_signal_t; struct LineSpacingParams : public LLInitParam::ChoiceBlock { @@ -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 // Optional icon position diff --git a/indra/llwebrtc/llwebrtc.cpp b/indra/llwebrtc/llwebrtc.cpp index edba2bee9a..23e1076765 100644 --- a/indra/llwebrtc/llwebrtc.cpp +++ b/indra/llwebrtc/llwebrtc.cpp @@ -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(); } diff --git a/indra/llwebrtc/llwebrtc_impl.h b/indra/llwebrtc/llwebrtc_impl.h index 51d42c82b2..dee081119b 100644 --- a/indra/llwebrtc/llwebrtc_impl.h +++ b/indra/llwebrtc/llwebrtc_impl.h @@ -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(); } diff --git a/indra/llwindow/llwindowcallbacks.cpp b/indra/llwindow/llwindowcallbacks.cpp index 85eb15b8f4..b450d27183 100644 --- a/indra/llwindow/llwindowcallbacks.cpp +++ b/indra/llwindow/llwindowcallbacks.cpp @@ -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; diff --git a/indra/llwindow/llwindowcallbacks.h b/indra/llwindow/llwindowcallbacks.h index 2e1638c71a..85cd18a670 100644 --- a/indra/llwindow/llwindowcallbacks.h +++ b/indra/llwindow/llwindowcallbacks.h @@ -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); diff --git a/indra/llwindow/llwindowmacosx.cpp b/indra/llwindow/llwindowmacosx.cpp index ab87f9baca..c9d5d17fc1 100644 --- a/indra/llwindow/llwindowmacosx.cpp +++ b/indra/llwindow/llwindowmacosx.cpp @@ -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); } diff --git a/indra/llwindow/llwindowsdl.cpp b/indra/llwindow/llwindowsdl.cpp index ae92f993ec..66530a45a1 100644 --- a/indra/llwindow/llwindowsdl.cpp +++ b/indra/llwindow/llwindowsdl.cpp @@ -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); diff --git a/indra/llwindow/llwindowwin32.cpp b/indra/llwindow/llwindowwin32.cpp index f2071cbb8c..bd22ff5a71 100644 --- a/indra/llwindow/llwindowwin32.cpp +++ b/indra/llwindow/llwindowwin32.cpp @@ -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"); diff --git a/indra/newview/app_settings/settings.xml b/indra/newview/app_settings/settings.xml index cdfeda6eb3..dddfeb0342 100644 --- a/indra/newview/app_settings/settings.xml +++ b/indra/newview/app_settings/settings.xml @@ -12680,7 +12680,7 @@ Change of this parameter will affect the layout of buttons in notification toast RenderQualityPerformance Comment - Which graphics settings you've chosen + Which graphics settings you've chosen. Don't use this setting to change quality directly from debug settings. Persist 1 Type @@ -12688,7 +12688,18 @@ Change of this parameter will affect the layout of buttons in notification toast Value 1 - + DebugQualityPerformance + + Comment + Allows to change performance quality directly from debug settings. + Persist + 1 + Type + U32 + Value + 1 + + RenderReflectionDetail Comment diff --git a/indra/newview/fschathistory.cpp b/indra/newview/fschathistory.cpp index dfb0891dc3..8284ace393 100644 --- a/indra/newview/fschathistory.cpp +++ b/indra/newview/fschathistory.cpp @@ -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 diff --git a/indra/newview/gltf/accessor.cpp b/indra/newview/gltf/accessor.cpp index 03f7331893..f0ad3fa594 100644 --- a/indra/newview/gltf/accessor.cpp +++ b/indra/newview/gltf/accessor.cpp @@ -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()) diff --git a/indra/newview/gltf/llgltfloader.cpp b/indra/newview/gltf/llgltfloader.cpp index ce0e3b4d16..085346556a 100644 --- a/indra/newview/gltf/llgltfloader.cpp +++ b/indra/newview/gltf/llgltfloader.cpp @@ -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; diff --git a/indra/newview/llappviewer.cpp b/indra/newview/llappviewer.cpp index e3abcc9ef2..ca579c22c4 100644 --- a/indra/newview/llappviewer.cpp +++ b/indra/newview/llappviewer.cpp @@ -1427,19 +1427,10 @@ bool LLAppViewer::init() LLStringOps::setupMonthShortNames(LLTrans::getString("dateTimeMonthShortNames")); LLStringOps::setupDayFormat(LLTrans::getString("dateTimeDayFormat")); - // 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"); - // + LLStringOps::sAM = LLTrans::getString("dateTimeAM"); + LLStringOps::sPM = LLTrans::getString("dateTimePM"); } - // 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"); - // - 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) diff --git a/indra/newview/llappviewer.h b/indra/newview/llappviewer.h index 044e3fc3af..5cba00be5a 100644 --- a/indra/newview/llappviewer.h +++ b/indra/newview/llappviewer.h @@ -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; diff --git a/indra/newview/llappviewerwin32.cpp b/indra/newview/llappviewerwin32.cpp index fbdb48cf0c..64a92b0968 100644 --- a/indra/newview/llappviewerwin32.cpp +++ b/indra/newview/llappviewerwin32.cpp @@ -233,10 +233,20 @@ namespace BugSplatAttributes::instance().setAttribute("Location", std::string(fullLocation)); // } + // Improve bugsplat reporting with attributes LLAppViewer::instance()->writeDebugInfo(); sBugSplatSender->sendAdditionalFile(WCSTR(BugSplatAttributes::getCrashContextFileName())); // Add the new attributes file // + + 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; diff --git a/indra/newview/llchathistory.cpp b/indra/newview/llchathistory.cpp index d955bdf613..d83d2364bc 100644 --- a/indra/newview/llchathistory.cpp +++ b/indra/newview/llchathistory.cpp @@ -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(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 diff --git a/indra/newview/lleventpoll.cpp b/indra/newview/lleventpoll.cpp index 1d70e56d13..533694cd71 100644 --- a/indra/newview/lleventpoll.cpp +++ b/indra/newview/lleventpoll.cpp @@ -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; // 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(), // 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); - // 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 - // mSenderIp = sender.getIPandPort(); } @@ -122,13 +107,11 @@ namespace Details { message["sender"] = mSenderIp; // 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; // } 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); + // Restore pre-coro behavior (60s timeout, no retries) +#ifdef OPENSIM + if (LLGridManager::instance().isInOpenSim()) + { + httpOpts->setTransferTimeout(60); + } +#endif + // + 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; - // Restore pre-coro behavior (60s timeout, no retries) - //LLSD result = httpAdapter->postAndSuspend(mHttpRequest, url, request); - LLSD result = httpAdapter->postAndSuspend(mHttpRequest, url, request, mHttpOptions); - // + 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; + } } // 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; } } diff --git a/indra/newview/lleventpoll.h b/indra/newview/lleventpoll.h index bb407b3799..ea186aa803 100644 --- a/indra/newview/lleventpoll.h +++ b/indra/newview/lleventpoll.h @@ -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); diff --git a/indra/newview/llfloaterpreference.cpp b/indra/newview/llfloaterpreference.cpp index 28ccd015f5..a4a6307c4f 100644 --- a/indra/newview/llfloaterpreference.cpp +++ b/indra/newview/llfloaterpreference.cpp @@ -2421,6 +2421,7 @@ void LLFloaterPreference::onChangeQuality(const LLSD& data) } mLastQualityLevel = level; LLFeatureManager::getInstance()->setGraphicsLevel(level, true); + gSavedSettings.setU32("DebugQualityPerformance", level); refreshEnabledGraphics(); refresh(); } diff --git a/indra/newview/llpanelvoicedevicesettings.cpp b/indra/newview/llpanelvoicedevicesettings.cpp index 60877494e7..d8d6bcf5fd 100644 --- a/indra/newview/llpanelvoicedevicesettings.cpp +++ b/indra/newview/llpanelvoicedevicesettings.cpp @@ -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); } } diff --git a/indra/newview/llsettingspicker.cpp b/indra/newview/llsettingspicker.cpp index 619882dc5e..5fc39b0dd8 100644 --- a/indra/newview/llsettingspicker.cpp +++ b/indra/newview/llsettingspicker.cpp @@ -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) diff --git a/indra/newview/llstartup.cpp b/indra/newview/llstartup.cpp index 34af7617ea..91485702ab 100644 --- a/indra/newview/llstartup.cpp +++ b/indra/newview/llstartup.cpp @@ -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) ; diff --git a/indra/newview/lltexturectrl.cpp b/indra/newview/lltexturectrl.cpp index f8a9e653cc..9b8c9d0356 100644 --- a/indra/newview/lltexturectrl.cpp +++ b/indra/newview/lltexturectrl.cpp @@ -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(); } diff --git a/indra/newview/llviewercontrol.cpp b/indra/newview/llviewercontrol.cpp index a4af0ccc34..3a2031dd61 100644 --- a/indra/newview/llviewercontrol.cpp +++ b/indra/newview/llviewercontrol.cpp @@ -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 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("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); diff --git a/indra/newview/llviewermenu.cpp b/indra/newview/llviewermenu.cpp index e8d44c4fec..72aaea21ca 100644 --- a/indra/newview/llviewermenu.cpp +++ b/indra/newview/llviewermenu.cpp @@ -8096,7 +8096,7 @@ bool handle_zoom_to_object(const LLUUID& object_id, const std::optionalisReachable()) { gAgentCamera.setFocusOnAvatar(false, ANIMATE); diff --git a/indra/newview/llviewerobject.cpp b/indra/newview/llviewerobject.cpp index feeefe0ce5..3329617d2b 100644 --- a/indra/newview/llviewerobject.cpp +++ b/indra/newview/llviewerobject.cpp @@ -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 visited; + std::queue pending; + visited.insert(agent_region); + pending.push(agent_region); + + while (!pending.empty()) + { + LLViewerRegion* current = pending.front(); + pending.pop(); + + std::vector 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: diff --git a/indra/newview/llviewerobject.h b/indra/newview/llviewerobject.h index 0544286636..11af8de290 100644 --- a/indra/newview/llviewerobject.h +++ b/indra/newview/llviewerobject.h @@ -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; diff --git a/indra/newview/llviewerwindow.cpp b/indra/newview/llviewerwindow.cpp index a0be475a7a..151b90b4c8 100644 --- a/indra/newview/llviewerwindow.cpp +++ b/indra/newview/llviewerwindow.cpp @@ -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) diff --git a/indra/newview/llviewerwindow.h b/indra/newview/llviewerwindow.h index 0930edd21c..bc83eb366c 100644 --- a/indra/newview/llviewerwindow.h +++ b/indra/newview/llviewerwindow.h @@ -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); diff --git a/indra/newview/llvoicewebrtc.cpp b/indra/newview/llvoicewebrtc.cpp index 5f19ea44f9..693fb5a98b 100644 --- a/indra/newview/llvoicewebrtc.cpp +++ b/indra/newview/llvoicewebrtc.cpp @@ -288,6 +288,8 @@ void LLWebRTCVoiceClient::terminate() return; } + LL_INFOS("Voice") << "Terminating WebRTC" << LL_ENDL; + mVoiceEnabled = false; llwebrtc::terminate(); diff --git a/indra/newview/pipeline.cpp b/indra/newview/pipeline.cpp index 9482409df9..77f7614842 100644 --- a/indra/newview/pipeline.cpp +++ b/indra/newview/pipeline.cpp @@ -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); + } } } diff --git a/indra/newview/skins/default/xui/de/panel_sound_devices.xml b/indra/newview/skins/default/xui/de/panel_sound_devices.xml index 837defd6ab..94ed60809a 100644 --- a/indra/newview/skins/default/xui/de/panel_sound_devices.xml +++ b/indra/newview/skins/default/xui/de/panel_sound_devices.xml @@ -9,6 +9,9 @@ Standardsystemgerät + + Gerät nicht geladen + Eingabe diff --git a/indra/newview/skins/default/xui/en/panel_sound_devices.xml b/indra/newview/skins/default/xui/en/panel_sound_devices.xml index cffa060b52..fffd1396d0 100644 --- a/indra/newview/skins/default/xui/en/panel_sound_devices.xml +++ b/indra/newview/skins/default/xui/en/panel_sound_devices.xml @@ -24,6 +24,10 @@ name="name_default_system_device"> Default System Device + + Device not loaded +