diff options
Diffstat (limited to 'indra/newview/llappviewer.cpp')
| -rw-r--r-- | indra/newview/llappviewer.cpp | 11388 | 
1 files changed, 5691 insertions, 5697 deletions
diff --git a/indra/newview/llappviewer.cpp b/indra/newview/llappviewer.cpp index 56521895c1..f41003c509 100644 --- a/indra/newview/llappviewer.cpp +++ b/indra/newview/llappviewer.cpp @@ -1,5697 +1,5691 @@ -/** - * @file llappviewer.cpp - * @brief The LLAppViewer class definitions - * - * $LicenseInfo:firstyear=2007&license=viewerlgpl$ - * Second Life Viewer Source Code - * Copyright (C) 2012, 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 "llappviewer.h" - -// Viewer includes -#include "llversioninfo.h" -#include "llfeaturemanager.h" -#include "lluictrlfactory.h" -#include "lltexteditor.h" -#include "llenvironment.h" -#include "llerrorcontrol.h" -#include "lleventtimer.h" -#include "llfile.h" -#include "llviewertexturelist.h" -#include "llgroupmgr.h" -#include "llagent.h" -#include "llagentcamera.h" -#include "llagentlanguage.h" -#include "llagentui.h" -#include "llagentwearables.h" -#include "lldirpicker.h" -#include "llfloaterimcontainer.h" -#include "llimprocessing.h" -#include "llwindow.h" -#include "llviewerstats.h" -#include "llviewerstatsrecorder.h" -#include "llkeyconflict.h" // for legacy keybinding support, remove later -#include "llmarketplacefunctions.h" -#include "llmarketplacenotifications.h" -#include "llmd5.h" -#include "llmeshrepository.h" -#include "llpumpio.h" -#include "llmimetypes.h" -#include "llslurl.h" -#include "llstartup.h" -#include "llfocusmgr.h" -#include "llurlfloaterdispatchhandler.h" -#include "llviewerjoystick.h" -#include "llallocator.h" -#include "llcalc.h" -#include "llconversationlog.h" -#if LL_WINDOWS -#include "lldxhardware.h" -#endif -#include "lltexturestats.h" -#include "lltrace.h" -#include "lltracethreadrecorder.h" -#include "llviewerwindow.h" -#include "llviewerdisplay.h" -#include "llviewermedia.h" -#include "llviewerparcelaskplay.h" -#include "llviewerparcelmedia.h" -#include "llviewershadermgr.h" -#include "llviewermediafocus.h" -#include "llviewermessage.h" -#include "llviewerobjectlist.h" -#include "llworldmap.h" -#include "llmutelist.h" -#include "llviewerhelp.h" -#include "lluicolortable.h" -#include "llurldispatcher.h" -#include "llurlhistory.h" -#include "llrender.h" -#include "llteleporthistory.h" -#include "lltoast.h" -#include "llsdutil_math.h" -#include "lllocationhistory.h" -#include "llfasttimerview.h" -#include "llvector4a.h" -#include "llviewermenufile.h" -#include "llvoicechannel.h" -#include "llvoavatarself.h" -#include "llurlmatch.h" -#include "lltextutil.h" -#include "lllogininstance.h" -#include "llprogressview.h" -#include "llvocache.h" -#include "lldiskcache.h" -#include "llvopartgroup.h" -#include "llweb.h" -#include "llspellcheck.h" -#include "llscenemonitor.h" -#include "llavatarrenderinfoaccountant.h" -#include "lllocalbitmaps.h" -#include "llperfstats.h"  -#include "llgltfmateriallist.h" - -// Linden library includes -#include "llavatarnamecache.h" -#include "lldiriterator.h" -#include "llexperiencecache.h" -#include "llimagej2c.h" -#include "llmemory.h" -#include "llprimitive.h" -#include "llurlaction.h" -#include "llurlentry.h" -#include "llvolumemgr.h" -#include "llxfermanager.h" -#include "llphysicsextensions.h" - -#include "llnotificationmanager.h" -#include "llnotifications.h" -#include "llnotificationsutil.h" - -#include "llleap.h" -#include "stringize.h" -#include "llcoros.h" -#include "llexception.h" -#if !LL_LINUX -#include "cef/dullahan_version.h" -#include "vlc/libvlc_version.h" -#endif // LL_LINUX - -#if LL_DARWIN -#include "llwindowmacosx.h" -#endif - -// Third party library includes -#include <boost/bind.hpp> -#include <boost/algorithm/string.hpp> -#include <boost/regex.hpp> -#include <boost/throw_exception.hpp> - -#if LL_WINDOWS -#	include <share.h> // For _SH_DENYWR in processMarkerFiles -#else -#   include <sys/file.h> // For processMarkerFiles -#endif - -#include "llapr.h" -#include <boost/lexical_cast.hpp> - -#include "llviewerinput.h" -#include "lllfsthread.h" -#include "llworkerthread.h" -#include "lltexturecache.h" -#include "lltexturefetch.h" -#include "llimageworker.h" -#include "llevents.h" - -// The files below handle dependencies from cleanup. -#include "llkeyframemotion.h" -#include "llworldmap.h" -#include "llhudmanager.h" -#include "lltoolmgr.h" -#include "llassetstorage.h" -#include "llpolymesh.h" -#include "llproxy.h" -#include "llaudioengine.h" -#include "llstreamingaudio.h" -#include "llviewermenu.h" -#include "llselectmgr.h" -#include "lltrans.h" -#include "lltransutil.h" -#include "lltracker.h" -#include "llviewerparcelmgr.h" -#include "llworldmapview.h" -#include "llpostprocess.h" - -#include "lldebugview.h" -#include "llconsole.h" -#include "llcontainerview.h" -#include "lltooltip.h" - -#include "llsdutil.h" -#include "llsdserialize.h" - -#include "llworld.h" -#include "llhudeffecttrail.h" -#include "llslurl.h" -#include "llurlregistry.h" -#include "llwatchdog.h" - -// Included so that constants/settings might be initialized -// in save_settings_to_globals() -#include "llbutton.h" -#include "llstatusbar.h" -#include "llsurface.h" -#include "llvosky.h" -#include "llvotree.h" -#include "llvoavatar.h" -#include "llfolderview.h" -#include "llagentpilot.h" -#include "llvovolume.h" -#include "llflexibleobject.h" -#include "llvosurfacepatch.h" -#include "llviewerfloaterreg.h" -#include "llcommandlineparser.h" -#include "llfloatermemleak.h" -#include "llfloaterreg.h" -#include "llfloatersimplesnapshot.h" -#include "llfloatersnapshot.h" -#include "llsidepanelinventory.h" -#include "llatmosphere.h" - -// includes for idle() idleShutdown() -#include "llviewercontrol.h" -#include "lleventnotifier.h" -#include "llcallbacklist.h" -#include "lldeferredsounds.h" -#include "pipeline.h" -#include "llgesturemgr.h" -#include "llsky.h" -#include "llvlmanager.h" -#include "llviewercamera.h" -#include "lldrawpoolbump.h" -#include "llvieweraudio.h" -#include "llimview.h" -#include "llviewerthrottle.h" -#include "llparcel.h" -#include "llavatariconctrl.h" -#include "llgroupiconctrl.h" -#include "llviewerassetstats.h" -#include "workqueue.h" -using namespace LL; - -// Include for security api initialization -#include "llsecapi.h" -#include "llmachineid.h" -#include "llcleanup.h" - -#include "llcoproceduremanager.h" -#include "llviewereventrecorder.h" - -// *FIX: These extern globals should be cleaned up. -// The globals either represent state/config/resource-storage of either -// this app, or another 'component' of the viewer. App globals should be -// moved into the app class, where as the other globals should be -// moved out of here. -// If a global symbol reference seems valid, it will be included -// via header files above. - -//---------------------------------------------------------------------------- -// llviewernetwork.h -#include "llviewernetwork.h" -// define a self-registering event API object -#include "llappviewerlistener.h" - -#if LL_LINUX && LL_GTK -#include "glib.h" -#endif // (LL_LINUX) && LL_GTK - -#if LL_MSVC -// disable boost::lexical_cast warning -#pragma warning (disable:4702) -#endif - -static LLAppViewerListener sAppViewerListener(LLAppViewer::instance); - -////// Windows-specific includes to the bottom - nasty defines in these pollute the preprocessor -// -//---------------------------------------------------------------------------- -// viewer.cpp - these are only used in viewer, should be easily moved. - -#if LL_DARWIN -extern void init_apple_menu(const char* product); -#endif // LL_DARWIN - -extern bool gRandomizeFramerate; -extern bool gPeriodicSlowFrame; -extern bool gDebugGL; - -#if LL_DARWIN -extern bool gHiDPISupport; -#endif - -//////////////////////////////////////////////////////////// -// All from the last globals push... - -F32 gSimLastTime; // Used in LLAppViewer::init and send_viewer_stats() -F32 gSimFrames; - -bool gShowObjectUpdates = false; -bool gUseQuickTime = true; - -eLastExecEvent gLastExecEvent = LAST_EXEC_NORMAL; -S32 gLastExecDuration = -1; // (<0 indicates unknown) - -#if LL_WINDOWS -#   define LL_PLATFORM_KEY "win" -#elif LL_DARWIN -#   define LL_PLATFORM_KEY "mac" -#elif LL_LINUX -#   define LL_PLATFORM_KEY "lnx" -#else -#   error "Unknown Platform" -#endif -const char* gPlatform = LL_PLATFORM_KEY; - -LLSD gDebugInfo; - -U32	gFrameCount = 0; -U32 gForegroundFrameCount = 0; // number of frames that app window was in foreground -LLPumpIO* gServicePump = NULL; - -U64MicrosecondsImplicit gFrameTime = 0; -F32SecondsImplicit gFrameTimeSeconds = 0.f; -F32SecondsImplicit gFrameIntervalSeconds = 0.f; -F32 gFPSClamped = 10.f;						// Pretend we start at target rate. -F32 gFrameDTClamped = 0.f;					// Time between adjacent checks to network for packets -U64MicrosecondsImplicit	gStartTime = 0; // gStartTime is "private", used only to calculate gFrameTimeSeconds - -LLTimer gRenderStartTime; -LLFrameTimer gForegroundTime; -LLFrameTimer gLoggedInTime; -LLTimer gLogoutTimer; -static const F32 LOGOUT_REQUEST_TIME = 6.f;  // this will be cut short by the LogoutReply msg. -F32 gLogoutMaxTime = LOGOUT_REQUEST_TIME; - - -S32 gPendingMetricsUploads = 0; - - -bool				gDisconnected = false; - -// used to restore texture state after a mode switch -LLFrameTimer	gRestoreGLTimer; -bool			gRestoreGL = false; -bool			gUseWireframe = false; - -LLMemoryInfo gSysMemory; -U64Bytes gMemoryAllocated(0); // updated in display_stats() in llviewerdisplay.cpp - -std::string gLastVersionChannel; - -LLVector3			gWindVec(3.0, 3.0, 0.0); -LLVector3			gRelativeWindVec(0.0, 0.0, 0.0); - -U32		gPacketsIn = 0; - -bool				gPrintMessagesThisFrame = false; - -bool gRandomizeFramerate = false; -bool gPeriodicSlowFrame = false; - -bool gCrashOnStartup = false; -bool gLLErrorActivated = false; -bool gLogoutInProgress = false; - -bool gSimulateMemLeak = false; - -// We don't want anyone, especially threads working on the graphics pipeline, -// to have to block due to this WorkQueue being full. -WorkQueue gMainloopWork("mainloop", 1024*1024); - -//////////////////////////////////////////////////////////// -// Internal globals... that should be removed. -static std::string gArgs; -const int MAX_MARKER_LENGTH = 1024; -const std::string MARKER_FILE_NAME("SecondLife.exec_marker"); -const std::string START_MARKER_FILE_NAME("SecondLife.start_marker"); -const std::string ERROR_MARKER_FILE_NAME("SecondLife.error_marker"); -const std::string LLERROR_MARKER_FILE_NAME("SecondLife.llerror_marker"); -const std::string LOGOUT_MARKER_FILE_NAME("SecondLife.logout_marker"); -static bool gDoDisconnect = false; -static std::string gLaunchFileOnQuit; - -// Used on Win32 for other apps to identify our window (eg, win_setup) -const char* const VIEWER_WINDOW_CLASSNAME = "Second Life"; - -//---------------------------------------------------------------------------- - -// List of entries from strings.xml to always replace -static std::set<std::string> default_trans_args; -void init_default_trans_args() -{ -	default_trans_args.insert("SECOND_LIFE"); // World -	default_trans_args.insert("APP_NAME"); -	default_trans_args.insert("CAPITALIZED_APP_NAME"); -	default_trans_args.insert("SECOND_LIFE_GRID"); -	default_trans_args.insert("SUPPORT_SITE"); -	// This URL shows up in a surprising number of places in various skin -	// files. We really only want to have to maintain a single copy of it. -	default_trans_args.insert("create_account_url"); -} - -struct SettingsFile : public LLInitParam::Block<SettingsFile> -{ -	Mandatory<std::string>	name; -	Optional<std::string>	file_name; -	Optional<bool>			required, -							persistent; -	Optional<std::string>	file_name_setting; - -	SettingsFile() -	:	name("name"), -		file_name("file_name"), -		required("required", false), -		persistent("persistent", true), -		file_name_setting("file_name_setting") -	{} -}; - -struct SettingsGroup : public LLInitParam::Block<SettingsGroup> -{ -	Mandatory<std::string>	name; -	Mandatory<S32>			path_index; -	Multiple<SettingsFile>	files; - -	SettingsGroup() -	:	name("name"), -		path_index("path_index"), -		files("file") -	{} -}; - -struct SettingsFiles : public LLInitParam::Block<SettingsFiles> -{ -	Multiple<SettingsGroup>	groups; - -	SettingsFiles() -	: groups("group") -	{} -}; - -static std::string gWindowTitle; - -//---------------------------------------------------------------------------- -// Metrics logging control constants -//---------------------------------------------------------------------------- -static const F32 METRICS_INTERVAL_DEFAULT = 600.0; -static const F32 METRICS_INTERVAL_QA = 30.0; -static F32 app_metrics_interval = METRICS_INTERVAL_DEFAULT; -static bool app_metrics_qa_mode = false; - -void idle_afk_check() -{ -	// check idle timers -	F32 current_idle = gAwayTriggerTimer.getElapsedTimeF32(); -	F32 afk_timeout  = gSavedSettings.getS32("AFKTimeout"); -	if (afk_timeout && (current_idle > afk_timeout) && ! gAgent.getAFK()) -	{ -		LL_INFOS("IdleAway") << "Idle more than " << afk_timeout << " seconds: automatically changing to Away status" << LL_ENDL; -		gAgent.setAFK(); -	} -} - -// A callback set in LLAppViewer::init() -static void ui_audio_callback(const LLUUID& uuid) -{ -	if (gAudiop) -	{ -		SoundData soundData(uuid, gAgent.getID(), 1.0f, LLAudioEngine::AUDIO_TYPE_UI); -		gAudiop->triggerSound(soundData); -	} -} - -// A callback set in LLAppViewer::init() -static void deferred_ui_audio_callback(const LLUUID& uuid) -{ -	if (gAudiop) -	{ -		SoundData soundData(uuid, gAgent.getID(), 1.0f, LLAudioEngine::AUDIO_TYPE_UI); -		LLDeferredSounds::instance().deferSound(soundData); -	} -} - -bool	create_text_segment_icon_from_url_match(LLUrlMatch* match,LLTextBase* base) -{ -	if(!match || !base || base->getPlainText()) -		return false; - -	LLUUID match_id = match->getID(); - -	LLIconCtrl* icon; - -	if( match->getMenuName() == "menu_url_group.xml" // See LLUrlEntryGroup constructor -		|| gAgent.isInGroup(match_id, true)) //This check seems unfiting, urls are either /agent or /group -	{ -		LLGroupIconCtrl::Params icon_params; -		icon_params.group_id = match_id; -		icon_params.rect = LLRect(0, 16, 16, 0); -		icon_params.visible = true; -		icon = LLUICtrlFactory::instance().create<LLGroupIconCtrl>(icon_params); -	} -	else -	{ -		LLAvatarIconCtrl::Params icon_params; -		icon_params.avatar_id = match_id; -		icon_params.rect = LLRect(0, 16, 16, 0); -		icon_params.visible = true; -		icon = LLUICtrlFactory::instance().create<LLAvatarIconCtrl>(icon_params); -	} - -	LLInlineViewSegment::Params params; -	params.force_newline = false; -	params.view = icon; -	params.left_pad = 4; -	params.right_pad = 4; -	params.top_pad = -2; -	params.bottom_pad = 2; - -	base->appendWidget(params," ",false); - -	return true; -} - -// Use these strictly for things that are constructed at startup, -// or for things that are performance critical.  JC -static void settings_to_globals() -{ -	LLBUTTON_H_PAD		= gSavedSettings.getS32("ButtonHPad"); -	BTN_HEIGHT_SMALL	= gSavedSettings.getS32("ButtonHeightSmall"); -	BTN_HEIGHT			= gSavedSettings.getS32("ButtonHeight"); - -	MENU_BAR_HEIGHT		= gSavedSettings.getS32("MenuBarHeight"); -	MENU_BAR_WIDTH		= gSavedSettings.getS32("MenuBarWidth"); - -	LLSurface::setTextureSize(gSavedSettings.getU32("RegionTextureSize")); - -#if LL_DARWIN -    LLRender::sGLCoreProfile = true; -#else -    LLRender::sGLCoreProfile = gSavedSettings.getBOOL("RenderGLContextCoreProfile"); -#endif -	LLRender::sNsightDebugSupport = gSavedSettings.getBOOL("RenderNsightDebugSupport"); -	LLImageGL::sGlobalUseAnisotropic	= gSavedSettings.getBOOL("RenderAnisotropic"); -	LLImageGL::sCompressTextures		= gSavedSettings.getBOOL("RenderCompressTextures"); -	LLVOVolume::sLODFactor				= llclamp(gSavedSettings.getF32("RenderVolumeLODFactor"), 0.01f, MAX_LOD_FACTOR); -	LLVOVolume::sDistanceFactor			= 1.f-LLVOVolume::sLODFactor * 0.1f; -	LLVolumeImplFlexible::sUpdateFactor = gSavedSettings.getF32("RenderFlexTimeFactor"); -	LLVOTree::sTreeFactor				= gSavedSettings.getF32("RenderTreeLODFactor"); -	LLVOAvatar::sLODFactor				= llclamp(gSavedSettings.getF32("RenderAvatarLODFactor"), 0.f, MAX_AVATAR_LOD_FACTOR); -	LLVOAvatar::sPhysicsLODFactor		= llclamp(gSavedSettings.getF32("RenderAvatarPhysicsLODFactor"), 0.f, MAX_AVATAR_LOD_FACTOR); -	LLVOAvatar::updateImpostorRendering(gSavedSettings.getU32("RenderAvatarMaxNonImpostors")); -	LLVOAvatar::sVisibleInFirstPerson	= gSavedSettings.getBOOL("FirstPersonAvatarVisible"); -	// clamp auto-open time to some minimum usable value -	LLFolderView::sAutoOpenTime			= llmax(0.25f, gSavedSettings.getF32("FolderAutoOpenDelay")); -	LLSelectMgr::sRectSelectInclusive	= gSavedSettings.getBOOL("RectangleSelectInclusive"); -	LLSelectMgr::sRenderHiddenSelections = gSavedSettings.getBOOL("RenderHiddenSelections"); -	LLSelectMgr::sRenderLightRadius = gSavedSettings.getBOOL("RenderLightRadius"); - -	gAgentPilot.setNumRuns(gSavedSettings.getS32("StatsNumRuns")); -	gAgentPilot.setQuitAfterRuns(gSavedSettings.getBOOL("StatsQuitAfterRuns")); -	gAgent.setHideGroupTitle(gSavedSettings.getBOOL("RenderHideGroupTitle")); - -	gDebugWindowProc = gSavedSettings.getBOOL("DebugWindowProc"); -	gShowObjectUpdates = gSavedSettings.getBOOL("ShowObjectUpdates"); -    LLWorldMapView::setScaleSetting(gSavedSettings.getF32("MapScale")); -	 -#if LL_DARWIN -    LLWindowMacOSX::sUseMultGL = gSavedSettings.getBOOL("RenderAppleUseMultGL"); -	gHiDPISupport = gSavedSettings.getBOOL("RenderHiDPI"); -#endif -} - -static void settings_modify() -{ -    LLPipeline::sRenderTransparentWater = gSavedSettings.getBOOL("RenderTransparentWater"); -    LLPipeline::sRenderDeferred = true; // false is deprecated -    LLRenderTarget::sUseFBO             = LLPipeline::sRenderDeferred; -    LLVOSurfacePatch::sLODFactor        = gSavedSettings.getF32("RenderTerrainLODFactor"); -    LLVOSurfacePatch::sLODFactor *= LLVOSurfacePatch::sLODFactor; //square lod factor to get exponential range of [1,4] -    gDebugGL       = gDebugGLSession || gDebugSession; -    gDebugPipeline = gSavedSettings.getBOOL("RenderDebugPipeline"); -} - -class LLFastTimerLogThread : public LLThread -{ -public: -	std::string mFile; - -	LLFastTimerLogThread(std::string& test_name) : LLThread("fast timer log") - 	{ -		std::string file_name = test_name + std::string(".slp"); -		mFile = gDirUtilp->getExpandedFilename(LL_PATH_LOGS, file_name); -	} - -	void run() -	{ -		llofstream os(mFile.c_str()); - -		while (!LLAppViewer::instance()->isQuitting()) -		{ -			LLTrace::BlockTimer::writeLog(os); -			os.flush(); -			ms_sleep(32); -		} - -		os.close(); -	} -}; - -//virtual -bool LLAppViewer::initSLURLHandler() -{ -	// does nothing unless subclassed -	return false; -} - -//virtual -bool LLAppViewer::sendURLToOtherInstance(const std::string& url) -{ -	// does nothing unless subclassed -	return false; -} - -//---------------------------------------------------------------------------- -// LLAppViewer definition - -// Static members. -// The single viewer app. -LLAppViewer* LLAppViewer::sInstance = NULL; -LLTextureCache* LLAppViewer::sTextureCache = NULL; -LLImageDecodeThread* LLAppViewer::sImageDecodeThread = NULL; -LLTextureFetch* LLAppViewer::sTextureFetch = NULL; -LLPurgeDiskCacheThread* LLAppViewer::sPurgeDiskCacheThread = NULL; - -std::string getRuntime() -{ -	return llformat("%.4f", (F32)LLTimer::getElapsedSeconds().value()); -} - -LLAppViewer::LLAppViewer() -:	mMarkerFile(), -	mLogoutMarkerFile(), -	mReportedCrash(false), -	mNumSessions(0), -    mGeneralThreadPool(nullptr), -	mPurgeCache(false), -	mPurgeCacheOnExit(false), -	mPurgeUserDataOnExit(false), -	mSecondInstance(false), -	mUpdaterNotFound(false), -	mSavedFinalSnapshot(false), -	mSavePerAccountSettings(false),		// don't save settings on logout unless login succeeded. -	mQuitRequested(false), -	mLogoutRequestSent(false), -	mLastAgentControlFlags(0), -	mLastAgentForceUpdate(0), -	mMainloopTimeout(NULL), -	mAgentRegionLastAlive(false), -	mRandomizeFramerate(LLCachedControl<bool>(gSavedSettings,"Randomize Framerate", false)), -	mPeriodicSlowFrame(LLCachedControl<bool>(gSavedSettings,"Periodic Slow Frame", false)), -	mFastTimerLogThread(NULL), -	mSettingsLocationList(NULL), -	mIsFirstRun(false) -{ -	if(NULL != sInstance) -	{ -		LL_ERRS() << "Oh no! An instance of LLAppViewer already exists! LLAppViewer is sort of like a singleton." << LL_ENDL; -	} - -    mDumpPath =""; - -	// Need to do this initialization before we do anything else, since anything -	// that touches files should really go through the lldir API -	gDirUtilp->initAppDirs("SecondLife"); -	// -	// IMPORTANT! Do NOT put anything that will write -	// into the log files during normal startup until AFTER -	// we run the "program crashed last time" error handler below. -	// -	sInstance = this; - -	gLoggedInTime.stop(); - -	processMarkerFiles(); -	// -	// OK to write stuff to logs now, we've now crash reported if necessary -	// - -	LLLoginInstance::instance().setPlatformInfo(gPlatform, LLOSInfo::instance().getOSVersionString(), LLOSInfo::instance().getOSStringSimple()); - -	// Under some circumstances we want to read the static_debug_info.log file -	// from the previous viewer run between this constructor call and the -	// init() call, which will overwrite the static_debug_info.log file for -	// THIS run. So setDebugFileNames() early. -#   ifdef LL_BUGSPLAT -	// MAINT-8917: don't create a dump directory just for the -	// static_debug_info.log file -	std::string logdir = gDirUtilp->getExpandedFilename(LL_PATH_LOGS, ""); -#   else // ! LL_BUGSPLAT -	// write Google Breakpad minidump files to a per-run dump directory to avoid multiple viewer issues. -	std::string logdir = gDirUtilp->getExpandedFilename(LL_PATH_DUMP, ""); -#   endif // ! LL_BUGSPLAT -	mDumpPath = logdir; - -	setDebugFileNames(logdir); -} - -LLAppViewer::~LLAppViewer() -{ -	delete mSettingsLocationList; - -	destroyMainloopTimeout(); - -	// If we got to this destructor somehow, the app didn't hang. -	removeMarkerFiles(); -} - -class LLUITranslationBridge : public LLTranslationBridge -{ -public: -	virtual std::string getString(const std::string &xml_desc) -	{ -		return LLTrans::getString(xml_desc); -	} -}; - - -bool LLAppViewer::init() -{ -	setupErrorHandling(mSecondInstance); - -	// -	// Start of the application -	// - -    // initialize the LLSettingsType translation bridge. -    LLTranslationBridge::ptr_t trans = std::make_shared<LLUITranslationBridge>(); -    LLSettingsType::initParamSingleton(trans); - -	// initialize SSE options -	LLVector4a::initClass(); - -	//initialize particle index pool -	LLVOPartGroup::initClass(); - -	// set skin search path to default, will be overridden later -	// this allows simple skinned file lookups to work -	gDirUtilp->setSkinFolder("default", "en"); - -//	initLoggingAndGetLastDuration(); - -	// -	// OK to write stuff to logs now, we've now crash reported if necessary -	// -	init_default_trans_args(); - -    // inits from settings.xml and from strings.xml -	if (!initConfiguration()) -		return false; - -	LL_INFOS("InitInfo") << "Configuration initialized." << LL_ENDL ; - -	//set the max heap size. -	initMaxHeapSize() ; -	LLCoros::instance().setStackSize(gSavedSettings.getS32("CoroutineStackSize")); - - -	// Although initLoggingAndGetLastDuration() is the right place to mess with -	// setFatalFunction(), we can't query gSavedSettings until after -	// initConfiguration(). -	S32 rc(gSavedSettings.getS32("QAModeTermCode")); -	if (rc >= 0) -	{ -		// QAModeTermCode set, terminate with that rc on LL_ERRS. Use -		// _exit() rather than exit() because normal cleanup depends too -		// much on successful startup! -		LLError::setFatalFunction([rc](const std::string&){ _exit(rc); }); -	} - -    mAlloc.setProfilingEnabled(gSavedSettings.getBOOL("MemProfiling")); - -	// Initialize the non-LLCurl libcurl library.  Should be called -	// before consumers (LLTextureFetch). -	mAppCoreHttp.init(); - -	LL_INFOS("InitInfo") << "LLCore::Http initialized." << LL_ENDL ; - -    LLMachineID::init(); - -	{ -		if (gSavedSettings.getBOOL("QAModeMetrics")) -		{ -			app_metrics_qa_mode = true; -			app_metrics_interval = METRICS_INTERVAL_QA; -		} -		LLViewerAssetStatsFF::init(); -	} - -	initThreads(); -	LL_INFOS("InitInfo") << "Threads initialized." << LL_ENDL ; - -	// Initialize settings early so that the defaults for ignorable dialogs are -	// picked up and then correctly re-saved after launching the updater (STORM-1268). -	LLUI::settings_map_t settings_map; -	settings_map["config"] = &gSavedSettings; -	settings_map["ignores"] = &gWarningSettings; -	settings_map["floater"] = &gSavedSettings; // *TODO: New settings file -	settings_map["account"] = &gSavedPerAccountSettings; - -	LLUI::initParamSingleton(settings_map, -		LLUIImageList::getInstance(), -		ui_audio_callback, -		deferred_ui_audio_callback); -	LL_INFOS("InitInfo") << "UI initialized." << LL_ENDL ; - -	// NOW LLUI::getLanguage() should work. gDirUtilp must know the language -	// for this session ASAP so all the file-loading commands that follow, -	// that use findSkinnedFilenames(), will include the localized files. -	gDirUtilp->setSkinFolder(gDirUtilp->getSkinFolder(), LLUI::getLanguage()); - -	// Setup LLTrans after LLUI::initClass has been called. -	initStrings(); - -    // initialize LLWearableType translation bridge. -    // Will immediately use LLTranslationBridge to init LLWearableDictionary -    LLWearableType::initParamSingleton(trans); - -	// Setup notifications after LLUI::initClass() has been called. -	LLNotifications::instance(); -	LL_INFOS("InitInfo") << "Notifications initialized." << LL_ENDL ; - -	////////////////////////////////////////////////////////////////////////////// -	////////////////////////////////////////////////////////////////////////////// -	////////////////////////////////////////////////////////////////////////////// -	////////////////////////////////////////////////////////////////////////////// -	// *FIX: The following code isn't grouped into functions yet. - -	// -	// Various introspection concerning the libs we're using - particularly -	// the libs involved in getting to a full login screen. -	// -	LL_INFOS("InitInfo") << "J2C Engine is: " << LLImageJ2C::getEngineInfo() << LL_ENDL; -	LL_INFOS("InitInfo") << "libcurl version is: " << LLCore::LLHttp::getCURLVersion() << LL_ENDL; - -	///////////////////////////////////////////////// -	// OS-specific login dialogs -	///////////////////////////////////////////////// - -	//test_cached_control(); - -	// track number of times that app has run -	mNumSessions = gSavedSettings.getS32("NumSessions"); -	mNumSessions++; -	gSavedSettings.setS32("NumSessions", mNumSessions); - -	// LLKeyboard relies on LLUI to know what some accelerator keys are called. -	LLKeyboard::setStringTranslatorFunc( LLTrans::getKeyboardString ); - -	// Provide the text fields with callbacks for opening Urls -	LLUrlAction::setOpenURLCallback(boost::bind(&LLWeb::loadURL, _1, LLStringUtil::null, LLStringUtil::null)); -	LLUrlAction::setOpenURLInternalCallback(boost::bind(&LLWeb::loadURLInternal, _1, LLStringUtil::null, LLStringUtil::null, false)); -	LLUrlAction::setOpenURLExternalCallback(boost::bind(&LLWeb::loadURLExternal, _1, true, LLStringUtil::null)); -	LLUrlAction::setExecuteSLURLCallback(&LLURLDispatcher::dispatchFromTextEditor); - -	// Let code in llui access the viewer help floater -	LLUI::getInstance()->mHelpImpl = LLViewerHelp::getInstance(); - -	LL_INFOS("InitInfo") << "UI initialization is done." << LL_ENDL ; - -	// Load translations for tooltips -	LLFloater::initClass(); -	LLUrlFloaterDispatchHandler::registerInDispatcher(); - -	///////////////////////////////////////////////// - -	LLToolMgr::getInstance(); // Initialize tool manager if not already instantiated - -	LLViewerFloaterReg::registerFloaters(); - -	///////////////////////////////////////////////// -	// -	// Load settings files -	// -	// -	LLGroupMgr::parseRoleActions("role_actions.xml"); - -	LLAgent::parseTeleportMessages("teleport_strings.xml"); - -	// load MIME type -> media impl mappings -	std::string mime_types_name; -#if LL_DARWIN -	mime_types_name = "mime_types_mac.xml"; -#elif LL_LINUX -	mime_types_name = "mime_types_linux.xml"; -#else -	mime_types_name = "mime_types.xml"; -#endif -	LLMIMETypes::parseMIMETypes( mime_types_name ); - -	// Copy settings to globals. *TODO: Remove or move to appropriage class initializers -	settings_to_globals(); -	// Setup settings listeners -	settings_setup_listeners(); -	// Modify settings based on system configuration and compile options -	settings_modify(); - -	// Find partition serial number (Windows) or hardware serial (Mac) -	mSerialNumber = generateSerialNumber(); - -	// do any necessary set-up for accepting incoming SLURLs from apps -	initSLURLHandler(); - -	if(false == initHardwareTest()) -	{ -		// Early out from user choice. -		return false; -	} -	LL_INFOS("InitInfo") << "Hardware test initialization done." << LL_ENDL ; - -	// Prepare for out-of-memory situations, during which we will crash on -	// purpose and save a dump. -#if LL_WINDOWS && LL_RELEASE_FOR_DOWNLOAD && LL_USE_SMARTHEAP -	MemSetErrorHandler(first_mem_error_handler); -#endif // LL_WINDOWS && LL_RELEASE_FOR_DOWNLOAD && LL_USE_SMARTHEAP - -	// *Note: this is where gViewerStats used to be created. - -	if (!initCache()) -	{ -		LL_WARNS("InitInfo") << "Failed to init cache" << LL_ENDL; -		std::ostringstream msg; -		msg << LLTrans::getString("MBUnableToAccessFile"); -		OSMessageBox(msg.str(),LLStringUtil::null,OSMB_OK); -		return 0; -	} -	LL_INFOS("InitInfo") << "Cache initialization is done." << LL_ENDL ; - -    // Initialize event recorder -    LLViewerEventRecorder::createInstance(); - -	// -	// Initialize the window -	// -	gGLActive = true; -	initWindow(); -	LL_INFOS("InitInfo") << "Window is initialized." << LL_ENDL ; - -    // writeSystemInfo can be called after window is initialized (gViewerWindow non-null) -    writeSystemInfo(); - -	// initWindow also initializes the Feature List, so now we can initialize this global. -	LLCubeMap::sUseCubeMaps = LLFeatureManager::getInstance()->isFeatureAvailable("RenderCubeMap"); - -	// call all self-registered classes -	LLInitClassList::instance().fireCallbacks(); - -	LLFolderViewItem::initClass(); // SJB: Needs to happen after initWindow(), not sure why but related to fonts - -	gGLManager.getGLInfo(gDebugInfo); -	gGLManager.printGLInfoString(); - -	// If we don't have the right GL requirements, exit. -	if (!gGLManager.mHasRequirements) -	{ -        // already handled with a MBVideoDrvErr -		return 0; -	} - -	// Without SSE2 support we will crash almost immediately, warn here. -	if (!gSysCPU.hasSSE2()) -	{ -		// can't use an alert here since we're exiting and -		// all hell breaks lose. -		OSMessageBox( -			LLNotifications::instance().getGlobalString("UnsupportedCPUSSE2"), -			LLStringUtil::null, -			OSMB_OK); -		return 0; -	} - -	// alert the user if they are using unsupported hardware -	if(!gSavedSettings.getBOOL("AlertedUnsupportedHardware")) -	{ -		bool unsupported = false; -		LLSD args; -		std::string minSpecs; - -		// get cpu data from xml -		std::stringstream minCPUString(LLNotifications::instance().getGlobalString("UnsupportedCPUAmount")); -		S32 minCPU = 0; -		minCPUString >> minCPU; - -		// get RAM data from XML -		std::stringstream minRAMString(LLNotifications::instance().getGlobalString("UnsupportedRAMAmount")); -		U64Bytes minRAM; -		minRAMString >> minRAM; - -		if(!LLFeatureManager::getInstance()->isGPUSupported() && LLFeatureManager::getInstance()->getGPUClass() != GPU_CLASS_UNKNOWN) -		{ -			minSpecs += LLNotifications::instance().getGlobalString("UnsupportedGPU"); -			minSpecs += "\n"; -			unsupported = true; -		} -		if(gSysCPU.getMHz() < minCPU) -		{ -			minSpecs += LLNotifications::instance().getGlobalString("UnsupportedCPU"); -			minSpecs += "\n"; -			unsupported = true; -		} -		if(gSysMemory.getPhysicalMemoryKB() < minRAM) -		{ -			minSpecs += LLNotifications::instance().getGlobalString("UnsupportedRAM"); -			minSpecs += "\n"; -			unsupported = true; -		} - -		if (LLFeatureManager::getInstance()->getGPUClass() == GPU_CLASS_UNKNOWN) -		{ -			LLNotificationsUtil::add("UnknownGPU"); -		} - -		if(unsupported) -		{ -			if(!gSavedSettings.controlExists("WarnUnsupportedHardware") -				|| gSavedSettings.getBOOL("WarnUnsupportedHardware")) -			{ -				args["MINSPECS"] = minSpecs; -				LLNotificationsUtil::add("UnsupportedHardware", args ); -			} - -		} -	} - -#if LL_WINDOWS && ADDRESS_SIZE == 64 -    if (gGLManager.mIsIntel) -    { -        // Check intel driver's version -        // Ex: "3.1.0 - Build 8.15.10.2559"; -        std::string version = ll_safe_string((const char *)glGetString(GL_VERSION)); - -        const boost::regex is_intel_string("[0-9].[0-9].[0-9] - Build [0-9]{1,2}.[0-9]{2}.[0-9]{2}.[0-9]{4}"); - -        if (boost::regex_search(version, is_intel_string)) -        { -            // Valid string, extract driver version -            std::size_t found = version.find("Build "); -            std::string driver = version.substr(found + 6); -            S32 v1, v2, v3, v4; -            S32 count = sscanf(driver.c_str(), "%d.%d.%d.%d", &v1, &v2, &v3, &v4); -            if (count > 0 && v1 <= 10) -            { -                LL_INFOS("AppInit") << "Detected obsolete intel driver: " << driver << LL_ENDL; - -                if (!gViewerWindow->getInitAlert().empty() // graphic initialization crashed on last run -                    || LLVersionInfo::getInstance()->getChannelAndVersion() != gLastRunVersion // viewer was updated -                    || mNumSessions % 20 == 0 //periodically remind user to update driver -                    ) -                { -                    LLUIString details = LLNotifications::instance().getGlobalString("UnsupportedIntelDriver"); -                    std::string gpu_name = ll_safe_string((const char *)glGetString(GL_RENDERER)); -                    LL_INFOS("AppInit") << "Notifying user about obsolete intel driver for " << gpu_name << LL_ENDL; -                    details.setArg("[VERSION]", driver); -                    details.setArg("[GPUNAME]", gpu_name); -                    S32 button = OSMessageBox(details.getString(), -                        LLStringUtil::null, -                        OSMB_YESNO); -                    if (OSBTN_YES == button && gViewerWindow) -                    { -                        std::string url = LLWeb::escapeURL(LLTrans::getString("IntelDriverPage")); -                        if (gViewerWindow->getWindow()) -                        { -                            gViewerWindow->getWindow()->spawnWebBrowser(url, false); -                        } -                    } -                } -            } -        } -    } -#endif - -    // Obsolete? mExpectedGLVersion is always zero -#if LL_WINDOWS -	if (gGLManager.mGLVersion < LLFeatureManager::getInstance()->getExpectedGLVersion()) -	{ -		std::string url; -		if (gGLManager.mIsIntel) -		{ -			url = LLTrans::getString("IntelDriverPage"); -		} -		else if (gGLManager.mIsNVIDIA) -		{ -			url = LLTrans::getString("NvidiaDriverPage"); -		} -		else if (gGLManager.mIsAMD) -		{ -			url = LLTrans::getString("AMDDriverPage"); -		} - -		if (!url.empty()) -		{ -			LLNotificationsUtil::add("OldGPUDriver", LLSD().with("URL", url)); -		} -	} -#endif - - -	// save the graphics card -	gDebugInfo["GraphicsCard"] = LLFeatureManager::getInstance()->getGPUString(); - -	// Save the current version to the prefs file -	gSavedSettings.setString("LastRunVersion", -							 LLVersionInfo::instance().getChannelAndVersion()); - -	gSimLastTime = gRenderStartTime.getElapsedTimeF32(); -	gSimFrames = (F32)gFrameCount; - -    if (gSavedSettings.getBOOL("JoystickEnabled")) -    { -        LLViewerJoystick::getInstance()->init(false); -    } - -	try { -		initializeSecHandler(); -	} -	catch (LLProtectedDataException&) -	{ -	  LLNotificationsUtil::add("CorruptedProtectedDataStore"); -	} - -	gGLActive = false; - -#if LL_RELEASE_FOR_DOWNLOAD -    // Skip updater if this is a non-interactive instance -    if (!gSavedSettings.getBOOL("CmdLineSkipUpdater") && !gNonInteractive) -    { -        LLProcess::Params updater; -        updater.desc = "updater process"; -        // Because it's the updater, it MUST persist beyond the lifespan of the -        // viewer itself. -        updater.autokill = false; -        std::string updater_file; -#if LL_WINDOWS -        updater_file = "SLVersionChecker.exe"; -        updater.executable = gDirUtilp->getExpandedFilename(LL_PATH_EXECUTABLE, updater_file); -#elif LL_DARWIN -        updater_file = "SLVersionChecker"; -        updater.executable = gDirUtilp->add(gDirUtilp->getAppRODataDir(), "updater", updater_file); -#else -        updater_file = "SLVersionChecker"; -        updater.executable = gDirUtilp->getExpandedFilename(LL_PATH_EXECUTABLE, updater_file); -#endif -        // add LEAP mode command-line argument to whichever of these we selected -        updater.args.add("leap"); -        // UpdaterServiceSettings -        if (gSavedSettings.getBOOL("FirstLoginThisInstall")) -        { -            // Befor first login, treat this as 'manual' updates, -            // updater won't install anything, but required updates -            updater.args.add("0"); -        } -        else -        { -            updater.args.add(stringize(gSavedSettings.getU32("UpdaterServiceSetting"))); -        } -        // channel -        updater.args.add(LLVersionInfo::instance().getChannel()); -        // testok -        updater.args.add(stringize(gSavedSettings.getBOOL("UpdaterWillingToTest"))); -        // ForceAddressSize -        updater.args.add(stringize(gSavedSettings.getU32("ForceAddressSize"))); - -        try -        { -            // Run the updater. An exception from launching the updater should bother us. -            LLLeap::create(updater, true); -            mUpdaterNotFound = false; -        } -        catch (...) -        { -            LLUIString details = LLNotifications::instance().getGlobalString("LLLeapUpdaterFailure"); -            details.setArg("[UPDATER_APP]", updater_file); -            OSMessageBox( -                details.getString(), -                LLStringUtil::null, -                OSMB_OK); -            mUpdaterNotFound = true; -        } -    } -    else -    { -        LL_WARNS("InitInfo") << "Skipping updater check." << LL_ENDL; -    } -#endif //LL_RELEASE_FOR_DOWNLOAD - -    { -        // Iterate over --leap command-line options. But this is a bit tricky: if -        // there's only one, it won't be an array at all. -        LLSD LeapCommand(gSavedSettings.getLLSD("LeapCommand")); -        LL_DEBUGS("InitInfo") << "LeapCommand: " << LeapCommand << LL_ENDL; -        if (LeapCommand.isDefined() && !LeapCommand.isArray()) -        { -            // If LeapCommand is actually a scalar value, make an array of it. -            // Have to do it in two steps because LeapCommand.append(LeapCommand) -            // trashes content! :-P -            LLSD item(LeapCommand); -            LeapCommand.append(item); -        } -        for (const auto& leap : llsd::inArray(LeapCommand)) -        { -            LL_INFOS("InitInfo") << "processing --leap \"" << leap << '"' << LL_ENDL; -            // We don't have any better description of this plugin than the -            // user-specified command line. Passing "" causes LLLeap to derive a -            // description from the command line itself. -            // Suppress LLLeap::Error exception: trust LLLeap's own logging. We -            // don't consider any one --leap command mission-critical, so if one -            // fails, log it, shrug and carry on. -            LLLeap::create("", leap, false); // exception=false -        } -    } - -    if (gSavedSettings.getBOOL("QAMode") && gSavedSettings.getS32("QAModeEventHostPort") > 0) -    { -        LL_WARNS("InitInfo") << "QAModeEventHostPort DEPRECATED: " -                             << "lleventhost no longer supported as a dynamic library" -                             << LL_ENDL; -    } - -	LLTextUtil::TextHelpers::iconCallbackCreationFunction = create_text_segment_icon_from_url_match; - -	//EXT-7013 - On windows for some locale (Japanese) standard -	//datetime formatting functions didn't support some parameters such as "weekday". -	//Names for days and months localized in xml are also useful for Polish locale(STORM-107). -	std::string language = gSavedSettings.getString("Language"); -	if(language == "ja" || language == "pl") -	{ -		LLStringOps::setupWeekDaysNames(LLTrans::getString("dateTimeWeekdaysNames")); -		LLStringOps::setupWeekDaysShortNames(LLTrans::getString("dateTimeWeekdaysShortNames")); -		LLStringOps::setupMonthNames(LLTrans::getString("dateTimeMonthNames")); -		LLStringOps::setupMonthShortNames(LLTrans::getString("dateTimeMonthShortNames")); -		LLStringOps::setupDayFormat(LLTrans::getString("dateTimeDayFormat")); - -		LLStringOps::sAM = LLTrans::getString("dateTimeAM"); -		LLStringOps::sPM = LLTrans::getString("dateTimePM"); -	} - -	LLAgentLanguage::init(); - -    /// Tell the Coprocedure manager how to discover and store the pool sizes -    // what I wanted -    LLCoprocedureManager::getInstance()->setPropertyMethods( -        boost::bind(&LLControlGroup::getU32, boost::ref(gSavedSettings), _1), -        boost::bind(&LLControlGroup::declareU32, boost::ref(gSavedSettings), _1, _2, _3, LLControlVariable::PERSIST_ALWAYS)); - -	// TODO: consider moving proxy initialization here or LLCopocedureManager after proxy initialization, may be implement -	// some other protection to make sure we don't use network before initializng proxy - -	/*----------------------------------------------------------------------*/ -	// nat 2016-06-29 moved the following here from the former mainLoop(). -	mMainloopTimeout = new LLWatchdogTimeout(); - -	// Create IO Pump to use for HTTP Requests. -	gServicePump = new LLPumpIO(gAPRPoolp); - -	// Note: this is where gLocalSpeakerMgr and gActiveSpeakerMgr used to be instantiated. - -	LLVoiceChannel::initClass(); -	LLVoiceClient::initParamSingleton(gServicePump); -	LLVoiceChannel::setCurrentVoiceChannelChangedCallback(boost::bind(&LLFloaterIMContainer::onCurrentChannelChanged, _1), true); - -	joystick = LLViewerJoystick::getInstance(); -	joystick->setNeedsReset(true); -	/*----------------------------------------------------------------------*/ -	// Load User's bindings -	loadKeyBindings(); - -    //LLSimpleton creations -    LLEnvironment::createInstance(); -    LLWorld::createInstance(); -    LLSelectMgr::createInstance(); -    LLViewerCamera::createInstance(); - -#if LL_WINDOWS -    if (!mSecondInstance) -    { -        gDirUtilp->deleteDirAndContents(gDirUtilp->getDumpLogsDirPath()); -    } -#endif - -	return true; -} - -void LLAppViewer::initMaxHeapSize() -{ -	//set the max heap size. -	//here is some info regarding to the max heap size: -	//------------------------------------------------------------------------------------------ -	// OS       | setting | SL address bits | max manageable memory space | max heap size -	// Win 32   | default | 32-bit          | 2GB                         | < 1.7GB -	// Win 32   | /3G     | 32-bit          | 3GB                         | < 1.7GB or 2.7GB -	//Linux 32  | default | 32-bit          | 3GB                         | < 2.7GB -	//Linux 32  |HUGEMEM  | 32-bit          | 4GB                         | < 3.7GB -	//64-bit OS |default  | 32-bit          | 4GB                         | < 3.7GB -	//64-bit OS |default  | 64-bit          | N/A (> 4GB)                 | N/A (> 4GB) -	//------------------------------------------------------------------------------------------ -	//currently SL is built under 32-bit setting, we set its max heap size no more than 1.6 GB. - - #ifndef LL_X86_64 -    F32Gigabytes max_heap_size_gb = (F32Gigabytes)gSavedSettings.getF32("MaxHeapSize") ; -#else -    F32Gigabytes max_heap_size_gb = (F32Gigabytes)gSavedSettings.getF32("MaxHeapSize64"); -#endif - -    LLMemory::initMaxHeapSizeGB(max_heap_size_gb); -} - - -// externally visible timers -LLTrace::BlockTimerStatHandle FTM_FRAME("Frame"); - -bool LLAppViewer::frame() -{ -	bool ret = false; - -	if (gSimulateMemLeak) -	{ -		try -		{ -			ret = doFrame(); -		} -		catch (const LLContinueError&) -		{ -			LOG_UNHANDLED_EXCEPTION(""); -		} -		catch (std::bad_alloc&) -		{ -			LLMemory::logMemoryInfo(true); -			LLFloaterMemLeak* mem_leak_instance = LLFloaterReg::findTypedInstance<LLFloaterMemLeak>("mem_leaking"); -			if (mem_leak_instance) -			{ -				mem_leak_instance->stop(); -			} -			LL_WARNS() << "Bad memory allocation in LLAppViewer::frame()!" << LL_ENDL; -		} -	} -	else -	{  -		try -		{ -			ret = doFrame(); -		} -		catch (const LLContinueError&) -		{ -			LOG_UNHANDLED_EXCEPTION(""); -		} -	} - -	return ret; -} - -bool LLAppViewer::doFrame() -{ -    LL_RECORD_BLOCK_TIME(FTM_FRAME); -    { -    // and now adjust the visuals from previous frame. -    if(LLPerfStats::tunables.userAutoTuneEnabled && LLPerfStats::tunables.tuningFlag != LLPerfStats::Tunables::Nothing) -    { -        LLPerfStats::tunables.applyUpdates(); -    } - -    LLPerfStats::RecordSceneTime T (LLPerfStats::StatType_t::RENDER_FRAME); -    if (!LLWorld::instanceExists()) -    { -        LLWorld::createInstance(); -    } - -    LLEventPump& mainloop(LLEventPumps::instance().obtain("mainloop")); -    LLSD newFrame; -    { -        LLPerfStats::RecordSceneTime T (LLPerfStats::StatType_t::RENDER_IDLE); // perf stats -        { -            LL_PROFILE_ZONE_NAMED_CATEGORY_APP("df LLTrace"); -            if (LLFloaterReg::instanceVisible("block_timers")) -            { -                LLTrace::BlockTimer::processTimes(); -            } - -            LLTrace::get_frame_recording().nextPeriod(); -            LLTrace::BlockTimer::logStats(); -        } - -        LLTrace::get_thread_recorder()->pullFromChildren(); - -        //clear call stack records -        LL_CLEAR_CALLSTACKS(); -    } -    { -        { -            LLPerfStats::RecordSceneTime T(LLPerfStats::StatType_t::RENDER_IDLE); // ensure we have the entire top scope of frame covered (input event and coro) -            LL_PROFILE_ZONE_NAMED_CATEGORY_APP("df processMiscNativeEvents") -            pingMainloopTimeout("Main:MiscNativeWindowEvents"); - -            if (gViewerWindow) -            { -                LL_PROFILE_ZONE_NAMED_CATEGORY_APP("System Messages"); -                gViewerWindow->getWindow()->processMiscNativeEvents(); -            } - -            { -                LL_PROFILE_ZONE_NAMED_CATEGORY_APP("df gatherInput") -                pingMainloopTimeout("Main:GatherInput"); -            } - -            if (gViewerWindow) -            { -                LL_PROFILE_ZONE_NAMED_CATEGORY_APP("System Messages"); -                if (!restoreErrorTrap()) -                { -                    LL_WARNS() << " Someone took over my signal/exception handler (post messagehandling)!" << LL_ENDL; -                } - -                gViewerWindow->getWindow()->gatherInput(); -            } - -            //memory leaking simulation -            if (gSimulateMemLeak) -            { -                LLFloaterMemLeak* mem_leak_instance = -                    LLFloaterReg::findTypedInstance<LLFloaterMemLeak>("mem_leaking"); -                if (mem_leak_instance) -                { -                    mem_leak_instance->idle(); -                } -            } - -            { -                LL_PROFILE_ZONE_NAMED_CATEGORY_APP("df mainloop") -                // canonical per-frame event -                mainloop.post(newFrame); -            } - -            { -                LL_PROFILE_ZONE_NAMED_CATEGORY_APP("df suspend") -                // give listeners a chance to run -                llcoro::suspend(); -                // if one of our coroutines threw an uncaught exception, rethrow it now -                LLCoros::instance().rethrow(); -            } -        } - -		if (!LLApp::isExiting()) -		{ -			LL_PROFILE_ZONE_NAMED_CATEGORY_APP( "df JoystickKeyboard" ) -			pingMainloopTimeout("Main:JoystickKeyboard"); - -			// Scan keyboard for movement keys.  Command keys and typing -			// are handled by windows callbacks.  Don't do this until we're -			// done initializing.  JC -			if (gViewerWindow -				&& (gHeadlessClient || gViewerWindow->getWindow()->getVisible()) -				&& gViewerWindow->getActive() -				&& !gViewerWindow->getWindow()->getMinimized() -				&& LLStartUp::getStartupState() == STATE_STARTED -				&& (gHeadlessClient || !gViewerWindow->getShowProgress()) -				&& !gFocusMgr.focusLocked()) -			{ -                LLPerfStats::RecordSceneTime T (LLPerfStats::StatType_t::RENDER_IDLE); -				joystick->scanJoystick(); -				gKeyboard->scanKeyboard(); -                gViewerInput.scanMouse(); -			} - -			// Update state based on messages, user input, object idle. -			{ -				{ -					LL_PROFILE_ZONE_NAMED_CATEGORY_APP( "df pauseMainloopTimeout" ) -					pauseMainloopTimeout(); // *TODO: Remove. Messages shouldn't be stalling for 20+ seconds! -				} - -				{ -                    LLPerfStats::RecordSceneTime T (LLPerfStats::StatType_t::RENDER_IDLE); -                    LL_PROFILE_ZONE_NAMED_CATEGORY_APP("df idle"); -					idle(); -				} - -				{ -					LL_PROFILE_ZONE_NAMED_CATEGORY_APP( "df resumeMainloopTimeout" ) -					resumeMainloopTimeout(); -				} -			} - -			if (gDoDisconnect && (LLStartUp::getStartupState() == STATE_STARTED)) -			{ -				pauseMainloopTimeout(); -				saveFinalSnapshot(); - -                if (LLVoiceClient::instanceExists()) -                { -                    LLVoiceClient::getInstance()->terminate(); -                } - -				disconnectViewer(); -				resumeMainloopTimeout(); -			} - -			// Render scene. -			// *TODO: Should we run display() even during gHeadlessClient?  DK 2011-02-18 -            if (!LLApp::isExiting() && !gHeadlessClient && gViewerWindow) -            { -                LL_PROFILE_ZONE_NAMED_CATEGORY_APP("df Display"); -                pingMainloopTimeout("Main:Display"); -                gGLActive = true; - -                display(); - -                { -                    LLPerfStats::RecordSceneTime T(LLPerfStats::StatType_t::RENDER_IDLE); -                    LL_PROFILE_ZONE_NAMED_CATEGORY_APP("df Snapshot"); -                    pingMainloopTimeout("Main:Snapshot"); -                    gPipeline.mReflectionMapManager.update(); -                    LLFloaterSnapshot::update(); // take snapshots -                    LLFloaterSimpleSnapshot::update(); -                    gGLActive = false; -                } - -                if (LLViewerStatsRecorder::instanceExists()) -                { -                    LLViewerStatsRecorder::instance().idle(); -                } -            } -		} - -		{ -			LL_PROFILE_ZONE_NAMED_CATEGORY_APP( "df pauseMainloopTimeout" ) -		pingMainloopTimeout("Main:Sleep"); - -		pauseMainloopTimeout(); -		} - -		// Sleep and run background threads -		{ -			//LL_RECORD_BLOCK_TIME(SLEEP2); -			LL_PROFILE_ZONE_WARN( "Sleep2" ) - -			// yield some time to the os based on command line option -			static LLCachedControl<S32> yield_time(gSavedSettings, "YieldTime", -1); -			if(yield_time >= 0) -			{ -                LL_PROFILE_ZONE_NAMED_CATEGORY_APP("Yield"); -				LL_PROFILE_ZONE_NUM( yield_time ) -				ms_sleep(yield_time); -			} - -			if (gNonInteractive) -			{ -				S32 non_interactive_ms_sleep_time = 100; -				LLAppViewer::getTextureCache()->pause(); -				ms_sleep(non_interactive_ms_sleep_time); -			} - -			// yield cooperatively when not running as foreground window -			// and when not quiting (causes trouble at mac's cleanup stage) -			if (!LLApp::isExiting() -				&& ((gViewerWindow && !gViewerWindow->getWindow()->getVisible()) -					|| !gFocusMgr.getAppHasFocus())) -			{ -				// Sleep if we're not rendering, or the window is minimized. -				static LLCachedControl<S32> s_background_yield_time(gSavedSettings, "BackgroundYieldTime", 40); -				S32 milliseconds_to_sleep = llclamp((S32)s_background_yield_time, 0, 1000); -				// don't sleep when BackgroundYieldTime set to 0, since this will still yield to other threads -				// of equal priority on Windows -				if (milliseconds_to_sleep > 0) -				{ -                    LLPerfStats::RecordSceneTime T ( LLPerfStats::StatType_t::RENDER_SLEEP ); -                    ms_sleep(milliseconds_to_sleep); -					// also pause worker threads during this wait period -					LLAppViewer::getTextureCache()->pause(); -				} -			} - -			if (mRandomizeFramerate) -			{ -				ms_sleep(rand() % 200); -			} - -			if (mPeriodicSlowFrame -				&& (gFrameCount % 10 == 0)) -			{ -				LL_INFOS() << "Periodic slow frame - sleeping 500 ms" << LL_ENDL; -				ms_sleep(500); -			} - -			S32 total_work_pending = 0; -			S32 total_io_pending = 0; -			{ -				S32 work_pending = 0; -				S32 io_pending = 0; -				F32 max_time = llmin(gFrameIntervalSeconds.value() *10.f, 1.f); - -				work_pending += updateTextureThreads(max_time); - -				{ -                    LL_PROFILE_ZONE_NAMED_CATEGORY_APP("LFS Thread"); - 					io_pending += LLLFSThread::updateClass(1); -				} - -				if (io_pending > 1000) -				{ -					ms_sleep(llmin(io_pending/100,100)); // give the lfs some time to catch up -				} - -				total_work_pending += work_pending ; -				total_io_pending += io_pending ; - -			} - -			{ -				LL_PROFILE_ZONE_NAMED_CATEGORY_APP( "df gMeshRepo" ) -			gMeshRepo.update() ; -			} - -			if(!total_work_pending) //pause texture fetching threads if nothing to process. -			{ -				LL_PROFILE_ZONE_NAMED_CATEGORY_APP( "df getTextureCache" ) -				LLAppViewer::getTextureCache()->pause(); -				LLAppViewer::getTextureFetch()->pause(); -			} -			if(!total_io_pending) //pause file threads if nothing to process. -			{ -				LL_PROFILE_ZONE_NAMED_CATEGORY_APP( "df LLVFSThread" ) -				LLLFSThread::sLocal->pause(); -			} - -			{ -				LL_PROFILE_ZONE_NAMED_CATEGORY_APP( "df resumeMainloopTimeout" ) -			    resumeMainloopTimeout(); -			} -			pingMainloopTimeout("Main:End"); -		} -	} - -	if (LLApp::isExiting()) -	{ -		// Save snapshot for next time, if we made it through initialization -		if (STATE_STARTED == LLStartUp::getStartupState()) -		{ -			saveFinalSnapshot(); -		} - -		if (LLVoiceClient::instanceExists()) -		{ -			LLVoiceClient::getInstance()->terminate(); -		} - -		delete gServicePump; -		gServicePump = NULL; - -		destroyMainloopTimeout(); - -		LL_INFOS() << "Exiting main_loop" << LL_ENDL; -	} -    }LLPerfStats::StatsRecorder::endFrame(); -    LL_PROFILER_FRAME_END - -	return ! LLApp::isRunning(); -} - -S32 LLAppViewer::updateTextureThreads(F32 max_time) -{ -	S32 work_pending = 0; -	{ -        LL_PROFILE_ZONE_NAMED_CATEGORY_APP("Texture Cache"); - 		work_pending += LLAppViewer::getTextureCache()->update(max_time); // unpauses the texture cache thread -	} -	{ -        LL_PROFILE_ZONE_NAMED_CATEGORY_APP("Image Decode"); -	 	work_pending += LLAppViewer::getImageDecodeThread()->update(max_time); // unpauses the image thread -	} -	{ -        LL_PROFILE_ZONE_NAMED_CATEGORY_APP("Image Fetch"); -	 	work_pending += LLAppViewer::getTextureFetch()->update(max_time); // unpauses the texture fetch thread -	} -	return work_pending; -} - -void LLAppViewer::flushLFSIO() -{ -    S32 pending = LLLFSThread::updateClass(0); -    if (pending > 0) -    { -        LL_INFOS() << "Waiting for pending IO to finish: " << pending << LL_ENDL; -        while (1) -        { -            pending = LLLFSThread::updateClass(0); -            if (!pending) -            { -                break; -            } -            ms_sleep(100); -        } -    } -} - -bool LLAppViewer::cleanup() -{ -    LLAtmosphere::cleanupClass(); - -	//ditch LLVOAvatarSelf instance -	gAgentAvatarp = NULL; - -    LLNotifications::instance().clear(); - -	// workaround for DEV-35406 crash on shutdown -	LLEventPumps::instance().reset(true); - -	//dump scene loading monitor results -	if (LLSceneMonitor::instanceExists()) -	{ -		if (!isSecondInstance()) -		{ -            std::string dump_path = gDirUtilp->getExpandedFilename(LL_PATH_LOGS, "scene_monitor_results.csv"); -			LLSceneMonitor::instance().dumpToFile(dump_path); -		} -		LLSceneMonitor::deleteSingleton(); -	} - -	// There used to be an 'if (LLFastTimerView::sAnalyzePerformance)' block -	// here, completely redundant with the one that occurs later in this same -	// function. Presumably the duplication was due to an automated merge gone -	// bad. Not knowing which instance to prefer, we chose to retain the later -	// one because it happens just after mFastTimerLogThread is deleted. This -	// comment is in case we guessed wrong, so we can move it here instead. - -#if LL_LINUX -	// remove any old breakpad minidump files from the log directory -	if (! isError()) -	{ -		std::string logdir = gDirUtilp->getExpandedFilename(LL_PATH_LOGS, ""); -		gDirUtilp->deleteFilesInDir(logdir, "*-*-*-*-*.dmp"); -	} -#endif - -	// Kill off LLLeap objects. We can find them all because LLLeap is derived -	// from LLInstanceTracker. -	LLLeap::instance_snapshot().deleteAll(); - -	//flag all elements as needing to be destroyed immediately -	// to ensure shutdown order -	LLMortician::setZealous(true); - -    // Give any remaining SLPlugin instances a chance to exit cleanly. -    LLPluginProcessParent::shutdown(); - -	disconnectViewer(); -	LLViewerCamera::deleteSingleton(); - -	LL_INFOS() << "Viewer disconnected" << LL_ENDL; -	 -	if (gKeyboard) -	{ -		gKeyboard->resetKeys(); -	} - -	display_cleanup(); - -	release_start_screen(); // just in case - -	LLError::logToFixedBuffer(NULL); // stop the fixed buffer recorder - -	LL_INFOS() << "Cleaning Up" << LL_ENDL; - -	// shut down mesh streamer -	gMeshRepo.shutdown(); - -	// shut down Havok -	LLPhysicsExtensions::quitSystem(); - -	// Must clean up texture references before viewer window is destroyed. -	if(LLHUDManager::instanceExists()) -	{ -		LLHUDManager::getInstance()->updateEffects(); -		LLHUDObject::updateAll(); -		LLHUDManager::getInstance()->cleanupEffects(); -		LLHUDObject::cleanupHUDObjects(); -		LL_INFOS() << "HUD Objects cleaned up" << LL_ENDL; -	} - -	LLKeyframeDataCache::clear(); - - 	// End TransferManager before deleting systems it depends on (Audio, AssetStorage) -#if 0 // this seems to get us stuck in an infinite loop... -	gTransferManager.cleanup(); -#endif - -	// Note: this is where gWorldMap used to be deleted. - -	// Note: this is where gHUDManager used to be deleted. -	if(LLHUDManager::instanceExists()) -	{ -		LLHUDManager::getInstance()->shutdownClass(); -	} - -	delete gAssetStorage; -	gAssetStorage = NULL; - -	LLPolyMesh::freeAllMeshes(); - -	LLStartUp::cleanupNameCache(); - -	// Note: this is where gLocalSpeakerMgr and gActiveSpeakerMgr used to be deleted. - -	if (LLWorldMap::instanceExists()) -	{ -		LLWorldMap::getInstance()->reset(); // release any images -	} - -	LLCalc::cleanUp(); - -	LL_INFOS() << "Global stuff deleted" << LL_ENDL; - -	if (gAudiop) -	{ -        LL_INFOS() << "Shutting down audio" << LL_ENDL; - -        // be sure to stop the internet stream cleanly BEFORE destroying the interface to stop it. -        gAudiop->stopInternetStream(); -        // shut down the streaming audio sub-subsystem first, in case it relies on not outliving the general audio subsystem. -        LLStreamingAudioInterface *sai = gAudiop->getStreamingAudioImpl(); -		delete sai; -		gAudiop->setStreamingAudioImpl(NULL); - -        // shut down the audio subsystem -        gAudiop->shutdown(); - -		delete gAudiop; -		gAudiop = NULL; -	} - -	// Note: this is where LLFeatureManager::getInstance()-> used to be deleted. - -	// Patch up settings for next time -	// Must do this before we delete the viewer window, -	// such that we can suck rectangle information out of -	// it. -	cleanupSavedSettings(); -	LL_INFOS() << "Settings patched up" << LL_ENDL; - -	// delete some of the files left around in the cache. -	removeCacheFiles("*.wav"); -	removeCacheFiles("*.tmp"); -	removeCacheFiles("*.lso"); -	removeCacheFiles("*.out"); -	removeCacheFiles("*.dsf"); -	removeCacheFiles("*.bodypart"); -	removeCacheFiles("*.clothing"); - -	LL_INFOS() << "Cache files removed" << LL_ENDL; - -	LL_INFOS() << "Shutting down Views" << LL_ENDL; - -	// Destroy the UI -	if( gViewerWindow) -		gViewerWindow->shutdownViews(); - -	LL_INFOS() << "Cleaning up Inventory" << LL_ENDL; - -	// Cleanup Inventory after the UI since it will delete any remaining observers -	// (Deleted observers should have already removed themselves) -	gInventory.cleanupInventory(); - -	LLCoros::getInstance()->printActiveCoroutines(); - -	LL_INFOS() << "Cleaning up Selections" << LL_ENDL; - -	// Clean up selection managers after UI is destroyed, as UI may be observing them. -	// Clean up before GL is shut down because we might be holding on to objects with texture references -	LLSelectMgr::cleanupGlobals(); - -	LL_INFOS() << "Shutting down OpenGL" << LL_ENDL; - -	// Shut down OpenGL -	if( gViewerWindow) -	{ -		gViewerWindow->shutdownGL(); - -		// Destroy window, and make sure we're not fullscreen -		// This may generate window reshape and activation events. -		// Therefore must do this before destroying the message system. -		delete gViewerWindow; -		gViewerWindow = NULL; -		LL_INFOS() << "ViewerWindow deleted" << LL_ENDL; -	} - -    LLSplashScreen::show(); -    LLSplashScreen::update(LLTrans::getString("ShuttingDown")); - -	LL_INFOS() << "Cleaning up Keyboard & Joystick" << LL_ENDL; - -	// viewer UI relies on keyboard so keep it aound until viewer UI isa gone -	delete gKeyboard; -	gKeyboard = NULL; - -    if (LLViewerJoystick::instanceExists()) -    { -        // Turn off Space Navigator and similar devices -        LLViewerJoystick::getInstance()->terminate(); -    } - -	LL_INFOS() << "Cleaning up Objects" << LL_ENDL; - -	LLViewerObject::cleanupVOClasses(); - -	SUBSYSTEM_CLEANUP(LLAvatarAppearance); - -	SUBSYSTEM_CLEANUP(LLPostProcess); - -	LLTracker::cleanupInstance(); - -	// *FIX: This is handled in LLAppViewerWin32::cleanup(). -	// I'm keeping the comment to remember its order in cleanup, -	// in case of unforseen dependency. -	//#if LL_WINDOWS -	//	gDXHardware.cleanup(); -	//#endif // LL_WINDOWS - -	LLVolumeMgr* volume_manager = LLPrimitive::getVolumeManager(); -	if (!volume_manager->cleanup()) -	{ -		LL_WARNS() << "Remaining references in the volume manager!" << LL_ENDL; -	} -	LLPrimitive::cleanupVolumeManager(); - -	LL_INFOS() << "Additional Cleanup..." << LL_ENDL; - -	LLViewerParcelMgr::cleanupGlobals(); - -	// *Note: this is where gViewerStats used to be deleted. - - 	//end_messaging_system(); - -	LLPrimitive::cleanupVolumeManager(); -	SUBSYSTEM_CLEANUP(LLWorldMapView); -	SUBSYSTEM_CLEANUP(LLFolderViewItem); - -	LL_INFOS() << "Saving Data" << LL_ENDL; - -	// Store the time of our current logoff -	gSavedPerAccountSettings.setU32("LastLogoff", time_corrected()); - -    if (LLEnvironment::instanceExists()) -    { -		//Store environment settings if necessary -        LLEnvironment::getInstance()->saveToSettings(); -    } - -	// Must do this after all panels have been deleted because panels that have persistent rects -	// save their rects on delete. -	gSavedSettings.saveToFile(gSavedSettings.getString("ClientSettingsFile"), true); - -	LLUIColorTable::instance().saveUserSettings(); - -	// PerAccountSettingsFile should be empty if no user has been logged on. -	// *FIX:Mani This should get really saved in a "logoff" mode. -	if (gSavedSettings.getString("PerAccountSettingsFile").empty()) -	{ -		LL_INFOS() << "Not saving per-account settings; don't know the account name yet." << LL_ENDL; -	} -	// Only save per account settings if the previous login succeeded, otherwise -	// we might end up with a cleared out settings file in case a previous login -	// failed after loading per account settings. -	else if (!mSavePerAccountSettings) -	{ -		LL_INFOS() << "Not saving per-account settings; last login was not successful." << LL_ENDL; -	} -	else -	{ -		gSavedPerAccountSettings.saveToFile(gSavedSettings.getString("PerAccountSettingsFile"), true); -		LL_INFOS() << "Saved settings" << LL_ENDL; - -		if (LLViewerParcelAskPlay::instanceExists()) -		{ -			LLViewerParcelAskPlay::getInstance()->saveSettings(); -		} -	} - -	std::string warnings_settings_filename = gDirUtilp->getExpandedFilename(LL_PATH_USER_SETTINGS, getSettingsFilename("Default", "Warnings")); -	gWarningSettings.saveToFile(warnings_settings_filename, true); - -	// Save URL history file -	LLURLHistory::saveFile("url_history.xml"); - -	// save mute list. gMuteList used to also be deleted here too. -	if (gAgent.isInitialized() && LLMuteList::instanceExists()) -	{ -		LLMuteList::getInstance()->cache(gAgent.getID()); -	} - -	//save call log list -	if (LLConversationLog::instanceExists()) -	{ -		LLConversationLog::instance().cache(); -    } - -    clearSecHandler(); - -	if (mPurgeCacheOnExit) -	{ -		LL_INFOS() << "Purging all cache files on exit" << LL_ENDL; -		gDirUtilp->deleteFilesInDir(gDirUtilp->getExpandedFilename(LL_PATH_CACHE,""), "*.*"); -	} - -	writeDebugInfo(); - -	LLLocationHistory::getInstance()->save(); - -	LLAvatarIconIDCache::getInstance()->save(); - -	// Stop the plugin read thread if it's running. -	LLPluginProcessParent::setUseReadThread(false); - -	LL_INFOS() << "Shutting down Threads" << LL_ENDL; - -	// Let threads finish -	LLTimer idleTimer; -	idleTimer.reset(); -	const F64 max_idle_time = 5.f; // 5 seconds -	while(1) -	{ -		S32 pending = 0; -		pending += LLAppViewer::getTextureCache()->update(1); // unpauses the worker thread -		pending += LLAppViewer::getImageDecodeThread()->update(1); // unpauses the image thread -		pending += LLAppViewer::getTextureFetch()->update(1); // unpauses the texture fetch thread -		pending += LLLFSThread::updateClass(0); -		F64 idle_time = idleTimer.getElapsedTimeF64(); -		if(!pending) -		{ -			break ; //done -		} -		else if(idle_time >= max_idle_time) -		{ -			LL_WARNS() << "Quitting with pending background tasks." << LL_ENDL; -			break; -		} -	} - -    if (mPurgeUserDataOnExit) -    { -        // Ideally we should not save anything from this session since it is going to be purged now, -        // but this is a very 'rare' case (user deleting himself), not worth overcomplicating 'save&cleanup' code -        std::string user_path = gDirUtilp->getOSUserAppDir() + gDirUtilp->getDirDelimiter() + LLStartUp::getUserId(); -        gDirUtilp->deleteDirAndContents(user_path); -    } - -	// Delete workers first -	// shotdown all worker threads before deleting them in case of co-dependencies -	mAppCoreHttp.requestStop(); -	sTextureFetch->shutdown(); -	sTextureCache->shutdown(); -	sImageDecodeThread->shutdown(); -	sPurgeDiskCacheThread->shutdown(); -	if (mGeneralThreadPool) -	{ -		mGeneralThreadPool->close(); -	} - -	sTextureFetch->shutDownTextureCacheThread() ; -    LLLFSThread::sLocal->shutdown(); - -	LL_INFOS() << "Shutting down message system" << LL_ENDL; -	end_messaging_system(); - -	// Non-LLCurl libcurl library -	mAppCoreHttp.cleanup(); - -	SUBSYSTEM_CLEANUP(LLFilePickerThread); -	SUBSYSTEM_CLEANUP(LLDirPickerThread); - -	//MUST happen AFTER SUBSYSTEM_CLEANUP(LLCurl) -	delete sTextureCache; -    sTextureCache = NULL; -    if (sTextureFetch) -    { -        sTextureFetch->shutdown(); -        sTextureFetch->waitOnPending(); -        delete sTextureFetch; -        sTextureFetch = NULL; -    } -	delete sImageDecodeThread; -    sImageDecodeThread = NULL; -	delete mFastTimerLogThread; -	mFastTimerLogThread = NULL; -	delete sPurgeDiskCacheThread; -	sPurgeDiskCacheThread = NULL; -    delete mGeneralThreadPool; -    mGeneralThreadPool = NULL; - -	if (LLFastTimerView::sAnalyzePerformance) -	{ -		LL_INFOS() << "Analyzing performance" << LL_ENDL; - -		std::string baseline_name = LLTrace::BlockTimer::sLogName + "_baseline.slp"; -		std::string current_name  = LLTrace::BlockTimer::sLogName + ".slp"; -		std::string report_name   = LLTrace::BlockTimer::sLogName + "_report.csv"; - -		LLFastTimerView::doAnalysis( -			gDirUtilp->getExpandedFilename(LL_PATH_LOGS, baseline_name), -			gDirUtilp->getExpandedFilename(LL_PATH_LOGS, current_name), -			gDirUtilp->getExpandedFilename(LL_PATH_LOGS, report_name)); -	} - -	SUBSYSTEM_CLEANUP(LLMetricPerformanceTesterBasic) ; - -	LL_INFOS() << "Cleaning up Media and Textures" << LL_ENDL; - -	//Note: -	//SUBSYSTEM_CLEANUP(LLViewerMedia) has to be put before gTextureList.shutdown() -	//because some new image might be generated during cleaning up media. --bao -	gTextureList.shutdown(); // shutdown again in case a callback added something -	LLUIImageList::getInstance()->cleanUp(); - -	SUBSYSTEM_CLEANUP(LLImage); -	SUBSYSTEM_CLEANUP(LLLFSThread); - -	LL_INFOS() << "Misc Cleanup" << LL_ENDL; - -	gSavedSettings.cleanup(); -	LLUIColorTable::instance().clear(); - -	LLWatchdog::getInstance()->cleanup(); - -	LLViewerAssetStatsFF::cleanup(); - -	// If we're exiting to launch an URL, do that here so the screen -	// is at the right resolution before we launch IE. -	if (!gLaunchFileOnQuit.empty()) -	{ -		LL_INFOS() << "Launch file on quit." << LL_ENDL; -#if LL_WINDOWS -		// Indicate an application is starting. -		SetCursor(LoadCursor(NULL, IDC_WAIT)); -#endif - -		// HACK: Attempt to wait until the screen res. switch is complete. -		ms_sleep(1000); - -		LLWeb::loadURLExternal( gLaunchFileOnQuit, false ); -		LL_INFOS() << "File launched." << LL_ENDL; -	} -	// make sure nothing uses applyProxySettings by this point. -	LL_INFOS() << "Cleaning up LLProxy." << LL_ENDL; -	SUBSYSTEM_CLEANUP(LLProxy); -    LLCore::LLHttp::cleanup(); - -	ll_close_fail_log(); - -	LLError::LLCallStacks::cleanup(); - -	LLEnvironment::deleteSingleton(); -	LLSelectMgr::deleteSingleton(); -	LLViewerEventRecorder::deleteSingleton(); -    LLWorld::deleteSingleton(); -    LLVoiceClient::deleteSingleton(); - -	// It's not at first obvious where, in this long sequence, a generic cleanup -	// call OUGHT to go. So let's say this: as we migrate cleanup from -	// explicit hand-placed calls into the generic mechanism, eventually -	// all cleanup will get subsumed into the generic call. So the calls you -	// still see above are calls that MUST happen before the generic cleanup -	// kicks in. - -	// This calls every remaining LLSingleton's cleanupSingleton() and -	// deleteSingleton() methods. -	LLSingletonBase::deleteAll(); - -    LLSplashScreen::hide(); - -    LL_INFOS() << "Goodbye!" << LL_ENDL; - -	removeDumpDir(); - -	// return 0; -	return true; -} - -void LLAppViewer::initGeneralThread() -{ -    if (mGeneralThreadPool) -    { -        return; -    } - -    mGeneralThreadPool = new LL::ThreadPool("General", 3); -    mGeneralThreadPool->start(); -} - -bool LLAppViewer::initThreads() -{ -	static const bool enable_threads = true; - -	LLImage::initClass(gSavedSettings.getBOOL("TextureNewByteRange"),gSavedSettings.getS32("TextureReverseByteRange")); - -	LLLFSThread::initClass(enable_threads && true); // TODO: fix crashes associated with this shutdo - -    //auto configure thread count -    LLSD threadCounts = gSavedSettings.getLLSD("ThreadPoolSizes"); - -    // get the number of concurrent threads that can run -    S32 cores = std::thread::hardware_concurrency(); - -    U32 max_cores = gSavedSettings.getU32("EmulateCoreCount"); -    if (max_cores != 0) -    { -        cores = llmin(cores, (S32) max_cores); -    } - -    // The only configurable thread count right now is ImageDecode -    // The viewer typically starts around 8 threads not including image decode,  -    // so try to leave at least one core free -    S32 image_decode_count = llclamp(cores - 9, 1, 8); -    threadCounts["ImageDecode"] = image_decode_count; -    gSavedSettings.setLLSD("ThreadPoolSizes", threadCounts); - -	// Image decoding -	LLAppViewer::sImageDecodeThread = new LLImageDecodeThread(enable_threads && true); -	LLAppViewer::sTextureCache = new LLTextureCache(enable_threads && true); -	LLAppViewer::sTextureFetch = new LLTextureFetch(LLAppViewer::getTextureCache(), -													enable_threads && true, -													app_metrics_qa_mode); - -    // general task background thread (LLPerfStats, etc) -    LLAppViewer::instance()->initGeneralThread(); - -	LLAppViewer::sPurgeDiskCacheThread = new LLPurgeDiskCacheThread(); - -	if (LLTrace::BlockTimer::sLog || LLTrace::BlockTimer::sMetricLog) -	{ -		LLTrace::BlockTimer::setLogLock(new LLMutex()); -		mFastTimerLogThread = new LLFastTimerLogThread(LLTrace::BlockTimer::sLogName); -		mFastTimerLogThread->start(); -	} - -	// Mesh streaming and caching -	gMeshRepo.init(); - -	LLFilePickerThread::initClass(); -	LLDirPickerThread::initClass(); - -	// *FIX: no error handling here! -	return true; -} - -void errorCallback(LLError::ELevel level, const std::string &error_string) -{ -    if (level == LLError::LEVEL_ERROR) -    { -#ifndef LL_RELEASE_FOR_DOWNLOAD -        OSMessageBox(error_string, LLTrans::getString("MBFatalError"), OSMB_OK); -#endif - -        gDebugInfo["FatalMessage"] = error_string; -        // We're not already crashing -- we simply *intend* to crash. Since we -        // haven't actually trashed anything yet, we can afford to write the whole -        // static info file. -        LLAppViewer::instance()->writeDebugInfo(); -    } -} - -void errorMSG(const std::string& title_string, const std::string& message_string) -{ -    if (!message_string.empty()) -    { -        OSMessageBox(message_string, title_string.empty() ? LLTrans::getString("MBFatalError") : title_string, OSMB_OK); -    } -} - -void LLAppViewer::initLoggingAndGetLastDuration() -{ -    // -    // Set up logging defaults for the viewer -    // -    LLError::initForApplication( gDirUtilp->getExpandedFilename(LL_PATH_USER_SETTINGS, "") -                                ,gDirUtilp->getExpandedFilename(LL_PATH_APP_SETTINGS, "") -                                ); -    LLError::addGenericRecorder(&errorCallback); -    //LLError::setTimeFunction(getRuntime); - -    LLError::LLUserWarningMsg::setHandler(errorMSG); - - -    if (mSecondInstance) -    { -        LLFile::mkdir(gDirUtilp->getDumpLogsDirPath()); -  -        LLUUID uid; -        uid.generate(); -        LLError::logToFile(gDirUtilp->getDumpLogsDirPath(uid.asString() + ".log")); -    } -    else -    { -        // Remove the last ".old" log file. -        std::string old_log_file = gDirUtilp->getExpandedFilename(LL_PATH_LOGS, -            "SecondLife.old"); -        LLFile::remove(old_log_file); - -        // Get name of the log file -        std::string log_file = gDirUtilp->getExpandedFilename(LL_PATH_LOGS, -            "SecondLife.log"); -        /* -        * Before touching any log files, compute the duration of the last run -        * by comparing the ctime of the previous start marker file with the ctime -        * of the last log file. -        */ -        std::string start_marker_file_name = gDirUtilp->getExpandedFilename(LL_PATH_LOGS, START_MARKER_FILE_NAME); -        llstat start_marker_stat; -        llstat log_file_stat; -        std::ostringstream duration_log_stream; // can't log yet, so save any message for when we can below -        int start_stat_result = LLFile::stat(start_marker_file_name, &start_marker_stat); -        int log_stat_result = LLFile::stat(log_file, &log_file_stat); -        if (0 == start_stat_result && 0 == log_stat_result) -        { -            int elapsed_seconds = log_file_stat.st_ctime - start_marker_stat.st_ctime; -            // only report a last run time if the last viewer was the same version -            // because this stat will be counted against this version -            if (markerIsSameVersion(start_marker_file_name)) -            { -                gLastExecDuration = elapsed_seconds; -            } -            else -            { -                duration_log_stream << "start marker from some other version; duration is not reported"; -                gLastExecDuration = -1; -            } -        } -        else -        { -            // at least one of the LLFile::stat calls failed, so we can't compute the run time -            duration_log_stream << "duration stat failure; start: " << start_stat_result << " log: " << log_stat_result; -            gLastExecDuration = -1; // unknown -        } -        std::string duration_log_msg(duration_log_stream.str()); - -        // Create a new start marker file for comparison with log file time for the next run -        LLAPRFile start_marker_file; -        start_marker_file.open(start_marker_file_name, LL_APR_WB); -        if (start_marker_file.getFileHandle()) -        { -            recordMarkerVersion(start_marker_file); -            start_marker_file.close(); -        } - -        // Rename current log file to ".old" -        LLFile::rename(log_file, old_log_file); - -        // Set the log file to SecondLife.log -        LLError::logToFile(log_file); -        LL_INFOS() << "Started logging to " << log_file << LL_ENDL; -        if (!duration_log_msg.empty()) -        { -            LL_WARNS("MarkerFile") << duration_log_msg << LL_ENDL; -        } -    } -} - -bool LLAppViewer::loadSettingsFromDirectory(const std::string& location_key, -					    bool set_defaults) -{ -	if (!mSettingsLocationList) -	{ -		LL_ERRS() << "Invalid settings location list" << LL_ENDL; -	} - -	for (const SettingsGroup& group : mSettingsLocationList->groups) -	{ -		// skip settings groups that aren't the one we requested -		if (group.name() != location_key) continue; - -		ELLPath path_index = (ELLPath)group.path_index(); -		if(path_index <= LL_PATH_NONE || path_index >= LL_PATH_LAST) -		{ -			LL_ERRS() << "Out of range path index in app_settings/settings_files.xml" << LL_ENDL; -			return false; -		} - -		for (const SettingsFile& file : group.files) -		{ -			LL_INFOS("Settings") << "Attempting to load settings for the group " << file.name() -			    << " - from location " << location_key << LL_ENDL; - -			auto settings_group = LLControlGroup::getInstance(file.name); -			if(!settings_group) -			{ -				LL_WARNS("Settings") << "No matching settings group for name " << file.name() << LL_ENDL; -				continue; -			} - -			std::string full_settings_path; - -			if (file.file_name_setting.isProvided() -				&& gSavedSettings.controlExists(file.file_name_setting)) -			{ -				// try to find filename stored in file_name_setting control -				full_settings_path = gSavedSettings.getString(file.file_name_setting()); -				if (full_settings_path.empty()) -				{ -					continue; -				} -				else if (!gDirUtilp->fileExists(full_settings_path)) -				{ -					// search in default path -					full_settings_path = gDirUtilp->getExpandedFilename((ELLPath)path_index, full_settings_path); -				} -			} -			else -			{ -				// by default, use specified file name -				full_settings_path = gDirUtilp->getExpandedFilename((ELLPath)path_index, file.file_name()); -			} - -			if(settings_group->loadFromFile(full_settings_path, set_defaults, file.persistent)) -			{	// success! -				LL_INFOS("Settings") << "Loaded settings file " << full_settings_path << LL_ENDL; -			} -			else -			{	// failed to load -				if(file.required) -				{ -                    LLError::LLUserWarningMsg::showMissingFiles(); -					LL_ERRS() << "Error: Cannot load required settings file from: " << full_settings_path << LL_ENDL; -					return false; -				} -				else -				{ -					// only complain if we actually have a filename at this point -					if (!full_settings_path.empty()) -					{ -						LL_INFOS("Settings") << "Cannot load " << full_settings_path << " - No settings found." << LL_ENDL; -					} -				} -			} -		} -	} - -	return true; -} - -std::string LLAppViewer::getSettingsFilename(const std::string& location_key, -											 const std::string& file) -{ -	for (const SettingsGroup& group : mSettingsLocationList->groups) -	{ -		if (group.name() == location_key) -		{ -			for (const SettingsFile& settings_file : group.files) -			{ -				if (settings_file.name() == file) -				{ -					return settings_file.file_name; -				} -			} -		} -	} - -	return std::string(); -} - -void LLAppViewer::loadColorSettings() -{ -	LLUIColorTable::instance().loadFromSettings(); -} - -namespace -{ -    void handleCommandLineError(LLControlGroupCLP& clp) -    { -		LL_WARNS() << "Error parsing command line options. Command Line options ignored."  << LL_ENDL; - -		LL_INFOS() << "Command line usage:\n" << clp << LL_ENDL; - -		OSMessageBox(STRINGIZE(LLTrans::getString("MBCmdLineError") << clp.getErrorMessage()), -					 LLStringUtil::null, -					 OSMB_OK); -    } -} // anonymous namespace - -// Set a named control temporarily for this session, as when set via the command line --set option. -// Name can be specified as "<control_group>.<control_name>", with default group being Global. -bool tempSetControl(const std::string& name, const std::string& value) -{ -	std::string name_part; -	std::string group_part; -	LLControlVariable* control = NULL; - -	// Name can be further split into ControlGroup.Name, with the default control group being Global -	size_t pos = name.find('.'); -	if (pos != std::string::npos) -	{ -		group_part = name.substr(0, pos); -		name_part = name.substr(pos+1); -		LL_INFOS() << "Setting " << group_part << "." << name_part << " to " << value << LL_ENDL; -		auto g = LLControlGroup::getInstance(group_part); -		if (g) control = g->getControl(name_part); -	} -	else -	{ -		LL_INFOS() << "Setting Global." << name << " to " << value << LL_ENDL; -		control = gSavedSettings.getControl(name); -	} - -	if (control) -	{ -		control->setValue(value, false); -		return true; -	} -	return false; -} - -bool LLAppViewer::initConfiguration() -{ -	//Load settings files list -	std::string settings_file_list = gDirUtilp->getExpandedFilename(LL_PATH_APP_SETTINGS, "settings_files.xml"); -	LLXMLNodePtr root; -	bool success = LLXMLNode::parseFile(settings_file_list, root, NULL); -	if (!success) -	{ -        LL_WARNS() << "Cannot load default configuration file " << settings_file_list << LL_ENDL; -        LLError::LLUserWarningMsg::showMissingFiles(); -        if (gDirUtilp->fileExists(settings_file_list)) -        { -            LL_ERRS() << "Cannot load default configuration file settings_files.xml. " -                << "Please reinstall viewer from https://secondlife.com/support/downloads/ " -                << "and contact https://support.secondlife.com if issue persists after reinstall." -                << LL_ENDL; -        } -        else -        { -            LL_ERRS() << "Default configuration file settings_files.xml not found. " -                << "Please reinstall viewer from https://secondlife.com/support/downloads/ " -                << "and contact https://support.secondlife.com if issue persists after reinstall." -                << LL_ENDL; -        } -	} - -	mSettingsLocationList = new SettingsFiles(); - -	LLXUIParser parser; -	parser.readXUI(root, *mSettingsLocationList, settings_file_list); - -	if (!mSettingsLocationList->validateBlock()) -	{ -        LLError::LLUserWarningMsg::showMissingFiles(); -        LL_ERRS() << "Invalid settings file list " << settings_file_list << LL_ENDL; -	} - -	// The settings and command line parsing have a fragile -	// order-of-operation: -	// - load defaults from app_settings -	// - set procedural settings values -	// - read command line settings -	// - selectively apply settings needed to load user settings. -    // - load overrides from user_settings -	// - apply command line settings (to override the overrides) -	// - load per account settings (happens in llstartup - -	// - load defaults -	bool set_defaults = true; -	if(!loadSettingsFromDirectory("Default", set_defaults)) -	{ -		OSMessageBox( -			"Unable to load default settings file. The installation may be corrupted.", -			LLStringUtil::null,OSMB_OK); -		return false; -	} - -	initStrings(); // setup paths for LLTrans based on settings files only -	// - set procedural settings -	// Note: can't use LL_PATH_PER_SL_ACCOUNT for any of these since we haven't logged in yet -	gSavedSettings.setString("ClientSettingsFile", -        gDirUtilp->getExpandedFilename(LL_PATH_USER_SETTINGS, getSettingsFilename("Default", "Global"))); - -#ifndef	LL_RELEASE_FOR_DOWNLOAD -	// provide developer build only overrides for these control variables that are not -	// persisted to settings.xml -	LLControlVariable* c = gSavedSettings.getControl("AllowMultipleViewers"); -	if (c) -	{ -		c->setValue(true, false); -	} - -	gSavedSettings.setBOOL("QAMode", true ); -	gSavedSettings.setS32("WatchdogEnabled", 0); -#endif - -	// These are warnings that appear on the first experience of that condition. -	// They are already set in the settings_default.xml file, but still need to be added to LLFirstUse -	// for disable/reset ability -//	LLFirstUse::addConfigVariable("FirstBalanceIncrease"); -//	LLFirstUse::addConfigVariable("FirstBalanceDecrease"); -//	LLFirstUse::addConfigVariable("FirstSit"); -//	LLFirstUse::addConfigVariable("FirstMap"); -//	LLFirstUse::addConfigVariable("FirstGoTo"); -//	LLFirstUse::addConfigVariable("FirstBuild"); -//	LLFirstUse::addConfigVariable("FirstLeftClickNoHit"); -//	LLFirstUse::addConfigVariable("FirstTeleport"); -//	LLFirstUse::addConfigVariable("FirstOverrideKeys"); -//	LLFirstUse::addConfigVariable("FirstAttach"); -//	LLFirstUse::addConfigVariable("FirstAppearance"); -//	LLFirstUse::addConfigVariable("FirstInventory"); -//	LLFirstUse::addConfigVariable("FirstSandbox"); -//	LLFirstUse::addConfigVariable("FirstFlexible"); -//	LLFirstUse::addConfigVariable("FirstDebugMenus"); -//	LLFirstUse::addConfigVariable("FirstSculptedPrim"); -//	LLFirstUse::addConfigVariable("FirstVoice"); -//	LLFirstUse::addConfigVariable("FirstMedia"); - -	// - read command line settings. -	LLControlGroupCLP clp; -	std::string	cmd_line_config	= gDirUtilp->getExpandedFilename(LL_PATH_APP_SETTINGS, -														  "cmd_line.xml"); - -	clp.configure(cmd_line_config, &gSavedSettings); - -	if(!initParseCommandLine(clp)) -	{ -		handleCommandLineError(clp); -		return false; -	} - -	// - selectively apply settings - -	// If the user has specified a alternate settings file name. -	// Load	it now before loading the user_settings/settings.xml -	if(clp.hasOption("settings")) -	{ -		std::string	user_settings_filename = -			gDirUtilp->getExpandedFilename(LL_PATH_USER_SETTINGS, -										   clp.getOption("settings")[0]); -		gSavedSettings.setString("ClientSettingsFile", user_settings_filename); -		LL_INFOS("Settings")	<< "Using command line specified settings filename: " -			<< user_settings_filename << LL_ENDL; -	} - -	// - load overrides from user_settings -	loadSettingsFromDirectory("User"); - -	if (gSavedSettings.getBOOL("FirstRunThisInstall")) -	{ -		// Set firstrun flag to indicate that some further init actiona should be taken -		// like determining screen DPI value and so on -		mIsFirstRun = true; - -		gSavedSettings.setBOOL("FirstRunThisInstall", false); -	} - -	if (clp.hasOption("sessionsettings")) -	{ -		std::string session_settings_filename = clp.getOption("sessionsettings")[0]; -		gSavedSettings.setString("SessionSettingsFile", session_settings_filename); -		LL_INFOS("Settings")	<< "Using session settings filename: " -			<< session_settings_filename << LL_ENDL; -	} -	loadSettingsFromDirectory("Session"); - -	if (clp.hasOption("usersessionsettings")) -	{ -		std::string user_session_settings_filename = clp.getOption("usersessionsettings")[0]; -		gSavedSettings.setString("UserSessionSettingsFile", user_session_settings_filename); -		LL_INFOS("Settings") << "Using user session settings filename: " -			<< user_session_settings_filename << LL_ENDL; - -	} -	loadSettingsFromDirectory("UserSession"); - -	// - apply command line settings -	if (! clp.notify()) -	{ -		handleCommandLineError(clp); -		return false; -	} - -	// Register the core crash option as soon as we can -	// if we want gdb post-mortem on cores we need to be up and running -	// ASAP or we might miss init issue etc. -	if(gSavedSettings.getBOOL("DisableCrashLogger")) -	{ -		LL_WARNS() << "Crashes will be handled by system, stack trace logs and crash logger are both disabled" << LL_ENDL; -		disableCrashlogger(); -	} - -	gNonInteractive = gSavedSettings.getBOOL("NonInteractive"); -	// Handle initialization from settings. -	// Start up the debugging console before handling other options. -	if (gSavedSettings.getBOOL("ShowConsoleWindow") && !gNonInteractive) -	{ -		initConsole(); -	} - -	if(clp.hasOption("help")) -	{ -		std::ostringstream msg; -		msg << LLTrans::getString("MBCmdLineUsg") << "\n" << clp; -		LL_INFOS()	<< msg.str() << LL_ENDL; - -		OSMessageBox( -			msg.str(), -			LLStringUtil::null, -			OSMB_OK); - -		return false; -	} - -    if(clp.hasOption("set")) -    { -        const LLCommandLineParser::token_vector_t& set_values = clp.getOption("set"); -        if(0x1 & set_values.size()) -        { -            LL_WARNS() << "Invalid '--set' parameter count." << LL_ENDL; -        } -        else -        { -            LLCommandLineParser::token_vector_t::const_iterator itr = set_values.begin(); -            for(; itr != set_values.end(); ++itr) -            { -                const std::string& name = *itr; -                const std::string& value = *(++itr); -                if (!tempSetControl(name,value)) -                { -					LL_WARNS() << "Failed --set " << name << ": setting name unknown." << LL_ENDL; -                } -            } -        } -    } - -    if  (clp.hasOption("logevents")) { -		LLViewerEventRecorder::instance().setEventLoggingOn(); -    } - -	std::string CmdLineChannel(gSavedSettings.getString("CmdLineChannel")); -	if(! CmdLineChannel.empty()) -    { -		LLVersionInfo::instance().resetChannel(CmdLineChannel); -	} - -	// If we have specified crash on startup, set the global so we'll trigger the crash at the right time -	gCrashOnStartup = gSavedSettings.getBOOL("CrashOnStartup"); - -	if (gSavedSettings.getBOOL("LogPerformance")) -	{ -		LLTrace::BlockTimer::sLog = true; -		LLTrace::BlockTimer::sLogName = std::string("performance"); -	} - -	std::string test_name(gSavedSettings.getString("LogMetrics")); -	if (! test_name.empty()) - 	{ -		LLTrace::BlockTimer::sMetricLog = true; -		// '--logmetrics' is specified with a named test metric argument so the data gathering is done only on that test -		// In the absence of argument, every metric would be gathered (makes for a rather slow run and hard to decipher report...) -		LL_INFOS() << "'--logmetrics' argument : " << test_name << LL_ENDL; -		LLTrace::BlockTimer::sLogName = test_name; -	} - -	if (clp.hasOption("graphicslevel")) -	{ -        // User explicitly requested --graphicslevel on the command line. We -        // expect this switch has already set RenderQualityPerformance. Check -        // that value for validity later. -        // Capture the requested value separately from the settings variable -        // because, if this is the first run, LLViewerWindow's constructor -        // will call LLFeatureManager::applyRecommendedSettings(), which -        // overwrites this settings variable! -        mForceGraphicsLevel = gSavedSettings.getU32("RenderQualityPerformance"); -	} - -	LLFastTimerView::sAnalyzePerformance = gSavedSettings.getBOOL("AnalyzePerformance"); -	gAgentPilot.setReplaySession(gSavedSettings.getBOOL("ReplaySession")); - -	if (gSavedSettings.getBOOL("DebugSession")) -	{ -		gDebugSession = true; -		gDebugGL = true; - -		ll_init_fail_log(gDirUtilp->getExpandedFilename(LL_PATH_LOGS, "test_failures.log")); -	} - -    if (gSavedSettings.getBOOL("RenderDebugGLSession")) -    { -        gDebugGLSession = true; -        gDebugGL = true; -        // gDebugGL can cause excessive logging -        // so it's limited to a single session -        gSavedSettings.setBOOL("RenderDebugGLSession", false); -    } - -	const LLControlVariable* skinfolder = gSavedSettings.getControl("SkinCurrent"); -	if(skinfolder && LLStringUtil::null != skinfolder->getValue().asString()) -	{ -		// Examining "Language" may not suffice -- see LLUI::getLanguage() -		// logic. Unfortunately LLUI::getLanguage() doesn't yet do us much -		// good because we haven't yet called LLUI::initClass(). -		gDirUtilp->setSkinFolder(skinfolder->getValue().asString(), -								 gSavedSettings.getString("Language")); -	} - -	if (gSavedSettings.getBOOL("SpellCheck")) -	{ -		std::list<std::string> dict_list; -		std::string dict_setting = gSavedSettings.getString("SpellCheckDictionary"); -		boost::split(dict_list, dict_setting, boost::is_any_of(std::string(","))); -		if (!dict_list.empty()) -		{ -			LLSpellChecker::setUseSpellCheck(dict_list.front()); -			dict_list.pop_front(); -			LLSpellChecker::instance().setSecondaryDictionaries(dict_list); -		} -	} - -	if (gNonInteractive) -	{ -		tempSetControl("AllowMultipleViewers", "true"); -		tempSetControl("SLURLPassToOtherInstance", "false"); -		tempSetControl("RenderWater", "false"); -		tempSetControl("FlyingAtExit", "false"); -		tempSetControl("WindowWidth", "1024"); -		tempSetControl("WindowHeight", "200"); -		LLError::setEnabledLogTypesMask(0); -		llassert_always(!gSavedSettings.getBOOL("SLURLPassToOtherInstance")); -	} - - -	// Handle slurl use. NOTE: Don't let SL-55321 reappear. -	// This initial-SLURL logic, up through the call to -	// sendURLToOtherInstance(), must precede LLSplashScreen::show() -- -	// because if sendURLToOtherInstance() succeeds, we take a fast exit, -	// SKIPPING the splash screen and everything else. - -    // *FIX: This init code should be made more robust to prevent -    // the issue SL-55321 from returning. One thought is to allow -    // only select options to be set from command line when a slurl -    // is specified. More work on the settings system is needed to -    // achieve this. For now... - -    // *NOTE:Mani The command line parser parses tokens and is -    // setup to bail after parsing the '--url' option or the -    // first option specified without a '--option' flag (or -    // any other option that uses the 'last_option' setting - -    // see LLControlGroupCLP::configure()) - -    // What can happen is that someone can use IE (or potentially -    // other browsers) and do the rough equivalent of command -    // injection and steal passwords. Phoenix. SL-55321 - -	std::string starting_location; - -	std::string cmd_line_login_location(gSavedSettings.getString("CmdLineLoginLocation")); -	if(! cmd_line_login_location.empty()) -	{ -		starting_location = cmd_line_login_location; -	} -	else -	{ -		std::string default_login_location(gSavedSettings.getString("DefaultLoginLocation")); -		if (! default_login_location.empty()) -		{ -			starting_location = default_login_location; -		} -	} - -	LLSLURL start_slurl; -	if (! starting_location.empty()) -    { -		start_slurl = starting_location; -		LLStartUp::setStartSLURL(start_slurl); -		if(start_slurl.getType() == LLSLURL::LOCATION) -		{ -			LLGridManager::getInstance()->setGridChoice(start_slurl.getGrid()); -		} -	} - -	// NextLoginLocation is set as a side effect of LLStartUp::setStartSLURL() -	std::string nextLoginLocation = gSavedSettings.getString( "NextLoginLocation" ); -	if ( !nextLoginLocation.empty() ) -	{ -		LL_DEBUGS("AppInit")<<"set start from NextLoginLocation: "<<nextLoginLocation<<LL_ENDL; -		LLStartUp::setStartSLURL(LLSLURL(nextLoginLocation)); -	} -	else if (   (   clp.hasOption("login") || clp.hasOption("autologin")) -			 && gSavedSettings.getString("CmdLineLoginLocation").empty()) -	{ -		// If automatic login from command line with --login switch -		// init StartSLURL location. -		std::string start_slurl_setting = gSavedSettings.getString("LoginLocation"); -		LL_DEBUGS("AppInit") << "start slurl setting '" << start_slurl_setting << "'" << LL_ENDL; -		LLStartUp::setStartSLURL(LLSLURL(start_slurl_setting)); -	} -	else -	{ -		// the login location will be set by the login panel (see LLPanelLogin) -	} - -	//RN: if we received a URL, hand it off to the existing instance. -	// don't call anotherInstanceRunning() when doing URL handoff, as -	// it relies on checking a marker file which will not work when running -	// out of different directories - -	if (start_slurl.isValid() && -		(gSavedSettings.getBOOL("SLURLPassToOtherInstance"))) -	{ -		if (sendURLToOtherInstance(start_slurl.getSLURLString())) -		{ -			// successfully handed off URL to existing instance, exit -			return false; -		} -    } - -	// Display splash screen.  Must be after above check for previous -	// crash as this dialog is always frontmost. -	std::string splash_msg; -	LLStringUtil::format_map_t args; -	args["[APP_NAME]"] = LLTrans::getString("SECOND_LIFE"); -	splash_msg = LLTrans::getString("StartupLoading", args); -	LLSplashScreen::show(); -	LLSplashScreen::update(splash_msg); - -	//LLVolumeMgr::initClass(); -	LLVolumeMgr* volume_manager = new LLVolumeMgr(); -	volume_manager->useMutex();	// LLApp and LLMutex magic must be manually enabled -	LLPrimitive::setVolumeManager(volume_manager); - -	// Note: this is where we used to initialize gFeatureManagerp. - -	gStartTime = totalTime(); - -	// -	// Set the name of the window -	// -	gWindowTitle = LLTrans::getString("APP_NAME"); -#if LL_DEBUG -	gWindowTitle += std::string(" [DEBUG]"); -#endif -	if (!gArgs.empty()) -	{ -	gWindowTitle += std::string(" ") + gArgs; -	} -	LLStringUtil::truncate(gWindowTitle, 255); - -	// -	// Check for another instance of the app running -	// This happens AFTER LLSplashScreen::show(). That may or may not be -	// important. -	// -	if (mSecondInstance && !gSavedSettings.getBOOL("AllowMultipleViewers")) -	{ -		OSMessageBox( -			LLTrans::getString("MBAlreadyRunning"), -			LLStringUtil::null, -			OSMB_OK); -		return false; -	} - -	if (mSecondInstance) -	{ -		// This is the second instance of SL. Mute voice, -		// but make sure the setting is *not* persisted. -		// Also see LLVivoxVoiceClient::voiceEnabled() -		LLControlVariable* enable_voice = gSavedSettings.getControl("EnableVoiceChat"); -		if(enable_voice) -		{ -			const bool DO_NOT_PERSIST = false; -			enable_voice->setValue(LLSD(false), DO_NOT_PERSIST); -		} -	} - -	gLastRunVersion = gSavedSettings.getString("LastRunVersion"); - -	loadColorSettings(); - -	// Let anyone else who cares know that we've populated our settings -	// variables. -	for (const auto& key : LLControlGroup::key_snapshot()) -	{ -		// For each named instance of LLControlGroup, send an event saying -		// we've initialized an LLControlGroup instance by that name. -		LLEventPumps::instance().obtain("LLControlGroup").post(LLSDMap("init", key)); -	} - -    LLError::LLUserWarningMsg::setOutOfMemoryStrings(LLTrans::getString("MBOutOfMemoryTitle"), LLTrans::getString("MBOutOfMemoryErr")); - -	return true; // Config was successful. -} - -// The following logic is replicated in initConfiguration() (to be able to get -// some initial strings before we've finished initializing enough to know the -// current language) and also in init() (to initialize for real). Somehow it -// keeps growing, necessitating a method all its own. -void LLAppViewer::initStrings() -{ -	std::string strings_file = "strings.xml"; -	std::string strings_path_full = gDirUtilp->findSkinnedFilenameBaseLang(LLDir::XUI, strings_file); -	if (strings_path_full.empty() || !LLFile::isfile(strings_path_full)) -	{ -		if (strings_path_full.empty()) -		{ -			LL_WARNS() << "The file '" << strings_file << "' is not found" << LL_ENDL; -		} -		else -		{ -			llstat st; -			int rc = LLFile::stat(strings_path_full, &st); -			if (rc != 0) -			{ -				LL_WARNS() << "The file '" << strings_path_full << "' failed to get status. Error code: " << rc << LL_ENDL; -			} -			else if (S_ISDIR(st.st_mode)) -			{ -				LL_WARNS() << "The filename '" << strings_path_full << "' is a directory name" << LL_ENDL; -			} -			else -			{ -				LL_WARNS() << "The filename '" << strings_path_full << "' doesn't seem to be a regular file name" << LL_ENDL; -			} -		} - -		// initial check to make sure files are there failed -		gDirUtilp->dumpCurrentDirectories(LLError::LEVEL_WARN); -        LLError::LLUserWarningMsg::showMissingFiles(); -		LL_ERRS() << "Viewer failed to find localization and UI files." -			<< " Please reinstall viewer from https://secondlife.com/support/downloads" -			<< " and contact https://support.secondlife.com if issue persists after reinstall." << LL_ENDL; -	} -	LLTransUtil::parseStrings(strings_file, default_trans_args); -	LLTransUtil::parseLanguageStrings("language_settings.xml"); - -	// parseStrings() sets up the LLTrans substitution table. Add this one item. -	LLTrans::setDefaultArg("[sourceid]", gSavedSettings.getString("sourceid")); - -	// Now that we've set "[sourceid]", have to go back through -	// default_trans_args and reinitialize all those other keys because some -	// of them, in turn, reference "[sourceid]". -	for (const std::string& key : default_trans_args) -	{ -		std::string brackets(key), nobrackets(key); -		// Invalid to inspect key[0] if key is empty(). But then, the entire -		// body of this loop is pointless if key is empty(). -		if (key.empty()) -			continue; - -		if (key[0] != '[') -		{ -			// key was passed without brackets. That means that 'nobrackets' -			// is correct but 'brackets' is not. -			brackets = STRINGIZE('[' << brackets << ']'); -		} -		else -		{ -			// key was passed with brackets. That means that 'brackets' is -			// correct but 'nobrackets' is not. Erase the left bracket. -			nobrackets.erase(0, 1); -			std::string::size_type length(nobrackets.length()); -			if (length && nobrackets[length - 1] == ']') -			{ -				nobrackets.erase(length - 1); -			} -		} -		// Calling LLTrans::getString() is what embeds the other default -		// translation strings into this one. -		LLTrans::setDefaultArg(brackets, LLTrans::getString(nobrackets)); -	} -} - -bool LLAppViewer::meetsRequirementsForMaximizedStart() -{ -    bool maximizedOk = (gSysMemory.getPhysicalMemoryKB() >= U32Gigabytes(1)); - -	return maximizedOk; -} - -bool LLAppViewer::initWindow() -{ -	LL_INFOS("AppInit") << "Initializing window..." << LL_ENDL; - -	// store setting in a global for easy access and modification -	gHeadlessClient = gSavedSettings.getBOOL("HeadlessClient"); - -	// always start windowed -	bool ignorePixelDepth = gSavedSettings.getBOOL("IgnorePixelDepth"); - -	LLViewerWindow::Params window_params; -	window_params -		.title(gWindowTitle) -		.name(VIEWER_WINDOW_CLASSNAME) -		.x(gSavedSettings.getS32("WindowX")) -		.y(gSavedSettings.getS32("WindowY")) -		.width(gSavedSettings.getU32("WindowWidth")) -		.height(gSavedSettings.getU32("WindowHeight")) -		.min_width(gSavedSettings.getU32("MinWindowWidth")) -		.min_height(gSavedSettings.getU32("MinWindowHeight")) -		.fullscreen(gSavedSettings.getBOOL("FullScreen")) -		.ignore_pixel_depth(ignorePixelDepth) -		.first_run(mIsFirstRun); - -	gViewerWindow = new LLViewerWindow(window_params); - -	LL_INFOS("AppInit") << "gViewerwindow created." << LL_ENDL; - -	// Need to load feature table before cheking to start watchdog. -	bool use_watchdog = false; -	int watchdog_enabled_setting = gSavedSettings.getS32("WatchdogEnabled"); -	if (watchdog_enabled_setting == -1) -	{ -		use_watchdog = !LLFeatureManager::getInstance()->isFeatureAvailable("WatchdogDisabled"); -	} -	else -	{ -		// The user has explicitly set this setting; always use that value. -		use_watchdog = bool(watchdog_enabled_setting); -	} - -	LL_INFOS("AppInit") << "watchdog" -						<< (use_watchdog ? " " : " NOT ") -						<< "enabled" -						<< " (setting = " << watchdog_enabled_setting << ")" -						<< LL_ENDL; - -	if (use_watchdog) -	{ -		LLWatchdog::getInstance()->init(); -	} - -	LLNotificationsUI::LLNotificationManager::getInstance(); - - -#ifdef LL_DARWIN -	//Satisfy both MAINT-3135 (OSX 10.6 and earlier) MAINT-3288 (OSX 10.7 and later) -	LLOSInfo& os_info = LLOSInfo::instance(); -	if (os_info.mMajorVer == 10 && os_info.mMinorVer < 7) -	{ -		if ( os_info.mMinorVer == 6 && os_info.mBuild < 8 ) -			gViewerWindow->getWindow()->setOldResize(true); -	} -#endif - -	if (gSavedSettings.getBOOL("WindowMaximized")) -	{ -		gViewerWindow->getWindow()->maximize(); -	} - -	// -	// Initialize GL stuff -	// - -	if (mForceGraphicsLevel && (LLFeatureManager::instance().isValidGraphicsLevel(*mForceGraphicsLevel))) -	{ -		LLFeatureManager::getInstance()->setGraphicsLevel(*mForceGraphicsLevel, false); -		gSavedSettings.setU32("RenderQualityPerformance", *mForceGraphicsLevel); -	} - -	// Set this flag in case we crash while initializing GL -	gSavedSettings.setBOOL("RenderInitError", true); -	gSavedSettings.saveToFile( gSavedSettings.getString("ClientSettingsFile"), true ); - -	gPipeline.init(); -	LL_INFOS("AppInit") << "gPipeline Initialized" << LL_ENDL; - -	stop_glerror(); -	gViewerWindow->initGLDefaults(); - -	gSavedSettings.setBOOL("RenderInitError", false); -	gSavedSettings.saveToFile( gSavedSettings.getString("ClientSettingsFile"), true ); - -	//If we have a startup crash, it's usually near GL initialization, so simulate that. -	if(gCrashOnStartup) -	{ -		LLAppViewer::instance()->forceErrorLLError(); -	} - -	// -	// Determine if the window should start maximized on initial run based -	// on graphics capability -	// -	if (gSavedSettings.getBOOL("FirstLoginThisInstall") && meetsRequirementsForMaximizedStart()) -	{ -		LL_INFOS("AppInit") << "This client met the requirements for a maximized initial screen." << LL_ENDL; -		gSavedSettings.setBOOL("WindowMaximized", true); -	} - -	if (gSavedSettings.getBOOL("WindowMaximized")) -	{ -		gViewerWindow->getWindow()->maximize(); -	} - -	LLUI::getInstance()->mWindow = gViewerWindow->getWindow(); - -	// Show watch cursor -	gViewerWindow->setCursor(UI_CURSOR_WAIT); - -	// Finish view initialization -	gViewerWindow->initBase(); - -	// show viewer window -	//gViewerWindow->getWindow()->show(); - -	LL_INFOS("AppInit") << "Window initialization done." << LL_ENDL; - -	return true; -} - -bool LLAppViewer::isUpdaterMissing() -{ -    return mUpdaterNotFound; -} - -bool LLAppViewer::waitForUpdater() -{ -    return !gSavedSettings.getBOOL("CmdLineSkipUpdater") && !mUpdaterNotFound && !gNonInteractive; -} - -void LLAppViewer::writeDebugInfo(bool isStatic) -{ -#if LL_WINDOWS && LL_BUGSPLAT -    // bugsplat does not create dump folder and debug logs are written directly -    // to logs folder, so it conflicts with main instance -    if (mSecondInstance) -    { -        return; -    } -#endif - -    //Try to do the minimum when writing data during a crash. -    std::string* debug_filename; -    debug_filename = ( isStatic -        ? getStaticDebugFile() -        : getDynamicDebugFile() ); - -    LL_INFOS() << "Writing debug file " << *debug_filename << LL_ENDL; -    llofstream out_file(debug_filename->c_str()); - -    isStatic ?  LLSDSerialize::toPrettyXML(gDebugInfo, out_file) -             :  LLSDSerialize::toPrettyXML(gDebugInfo["Dynamic"], out_file); -} - -LLSD LLAppViewer::getViewerInfo() const -{ -	// The point of having one method build an LLSD info block and the other -	// construct the user-visible About string is to ensure that the same info -	// is available to a getInfo() caller as to the user opening -	// LLFloaterAbout. -	LLSD info; -	auto& versionInfo(LLVersionInfo::instance()); -	// With GitHub builds, the build number is too big to fit in a 32-bit int, -	// and LLSD doesn't deal with integers wider than int. Use string. -	info["VIEWER_VERSION"] = llsd::array(versionInfo.getMajor(), versionInfo.getMinor(), -										 versionInfo.getPatch(), stringize(versionInfo.getBuild())); -	info["VIEWER_VERSION_STR"] = versionInfo.getVersion(); -	info["CHANNEL"] = versionInfo.getChannel(); -	info["ADDRESS_SIZE"] = ADDRESS_SIZE; -	std::string build_config = versionInfo.getBuildConfig(); -	if (build_config != "Release") -	{ -		info["BUILD_CONFIG"] = build_config; -	} - -	// return a URL to the release notes for this viewer, such as: -	// https://releasenotes.secondlife.com/viewer/2.1.0.123456.html -	std::string url = versionInfo.getReleaseNotes(); // VVM supplied -    if (url.empty()) -    { -        url = LLTrans::getString("RELEASE_NOTES_BASE_URL"); -        if (!LLStringUtil::endsWith(url, "/")) -            url += "/"; -        url += LLURI::escape(versionInfo.getVersion()) + ".html"; -    } -	info["VIEWER_RELEASE_NOTES_URL"] = url; - -	// Position -	LLViewerRegion* region = gAgent.getRegion(); -	if (region) -	{ -		LLVector3d pos = gAgent.getPositionGlobal(); -		info["POSITION"] = ll_sd_from_vector3d(pos); -		info["POSITION_LOCAL"] = ll_sd_from_vector3(gAgent.getPosAgentFromGlobal(pos)); -		info["REGION"] = gAgent.getRegion()->getName(); - -		boost::regex regex("\\.(secondlife|lindenlab)\\..*"); -		info["HOSTNAME"] = boost::regex_replace(gAgent.getRegion()->getSimHostName(), regex, ""); -		info["SERVER_VERSION"] = gLastVersionChannel; -		LLSLURL slurl; -		LLAgentUI::buildSLURL(slurl); -		info["SLURL"] = slurl.getSLURLString(); -	} - -	// CPU -	info["CPU"] = gSysCPU.getCPUString(); -	info["MEMORY_MB"] = LLSD::Integer(gSysMemory.getPhysicalMemoryKB().valueInUnits<LLUnits::Megabytes>()); -	// Moved hack adjustment to Windows memory size into llsys.cpp -	info["OS_VERSION"] = LLOSInfo::instance().getOSString(); -	info["GRAPHICS_CARD_VENDOR"] = ll_safe_string((const char*)(glGetString(GL_VENDOR))); -	info["GRAPHICS_CARD"] = ll_safe_string((const char*)(glGetString(GL_RENDERER))); - -#if LL_WINDOWS -    std::string drvinfo; - -    if (gGLManager.mIsIntel) -    { -        drvinfo = gDXHardware.getDriverVersionWMI(LLDXHardware::GPU_INTEL); -    } -    else if (gGLManager.mIsNVIDIA) -    { -        drvinfo = gDXHardware.getDriverVersionWMI(LLDXHardware::GPU_NVIDIA); -    } -    else if (gGLManager.mIsAMD) -    { -        drvinfo = gDXHardware.getDriverVersionWMI(LLDXHardware::GPU_AMD); -    } - -    if (drvinfo.empty()) -    { -        // Generic/substitute windows driver? Unknown vendor? -        LL_WARNS("DriverVersion") << "Vendor based driver search failed, searching for any driver" << LL_ENDL; -        drvinfo = gDXHardware.getDriverVersionWMI(LLDXHardware::GPU_ANY); -    } - -	if (!drvinfo.empty()) -	{ -		info["GRAPHICS_DRIVER_VERSION"] = drvinfo; -	} -	else -	{ -		LL_WARNS("DriverVersion")<< "Cannot get driver version from getDriverVersionWMI" << LL_ENDL; -		LLSD driver_info = gDXHardware.getDisplayInfo(); -		if (driver_info.has("DriverVersion")) -		{ -			info["GRAPHICS_DRIVER_VERSION"] = driver_info["DriverVersion"]; -		} -	} -#endif - -	info["OPENGL_VERSION"] = ll_safe_string((const char*)(glGetString(GL_VERSION))); - -    // Settings - -    LLRect window_rect = gViewerWindow->getWindowRectRaw(); -    info["WINDOW_WIDTH"] = window_rect.getWidth(); -    info["WINDOW_HEIGHT"] = window_rect.getHeight(); -    info["FONT_SIZE_ADJUSTMENT"] = gSavedSettings.getF32("FontScreenDPI"); -    info["UI_SCALE"] = gSavedSettings.getF32("UIScaleFactor"); -    info["DRAW_DISTANCE"] = gSavedSettings.getF32("RenderFarClip"); -    info["NET_BANDWITH"] = gSavedSettings.getF32("ThrottleBandwidthKBPS"); -    info["LOD_FACTOR"] = gSavedSettings.getF32("RenderVolumeLODFactor"); -    info["RENDER_QUALITY"] = (F32)gSavedSettings.getU32("RenderQualityPerformance"); -    info["TEXTURE_MEMORY"] = gGLManager.mVRAM; - -#if LL_DARWIN -    info["HIDPI"] = gHiDPISupport; -#endif - -	// Libraries - -	info["J2C_VERSION"] = LLImageJ2C::getEngineInfo(); -	bool want_fullname = true; -	info["AUDIO_DRIVER_VERSION"] = gAudiop ? LLSD(gAudiop->getDriverName(want_fullname)) : "Undefined"; -	if(LLVoiceClient::getInstance()->voiceEnabled()) -	{ -        LLVoiceVersionInfo version = LLVoiceClient::getInstance()->getVersion(); -        const std::string build_version = version.mBuildVersion; -		std::ostringstream version_string; -        if (std::equal(build_version.begin(), build_version.begin() + version.serverVersion.size(), -                       version.serverVersion.begin())) -        {  // Normal case: Show type and build version. -            version_string << version.serverType << " " << build_version << std::endl; -        } -        else -        {  // Mismatch: Show both versions. -            version_string << version.serverVersion << "/" << build_version << std::endl; -        } -		info["VOICE_VERSION"] = version_string.str(); -	} -	else -	{ -		info["VOICE_VERSION"] = LLTrans::getString("NotConnected"); -	} - -#if !LL_LINUX -	std::ostringstream cef_ver_codec; -	cef_ver_codec << "Dullahan: "; -	cef_ver_codec << DULLAHAN_VERSION_MAJOR; -	cef_ver_codec << "."; -	cef_ver_codec << DULLAHAN_VERSION_MINOR; -	cef_ver_codec << "."; -	cef_ver_codec << DULLAHAN_VERSION_POINT; -	cef_ver_codec << "."; -	cef_ver_codec << DULLAHAN_VERSION_BUILD; - -	cef_ver_codec << std::endl; -	cef_ver_codec << "  CEF: "; -	cef_ver_codec << CEF_VERSION; - -	cef_ver_codec << std::endl; -	cef_ver_codec << "  Chromium: "; -	cef_ver_codec << CHROME_VERSION_MAJOR; -	cef_ver_codec << "."; -	cef_ver_codec << CHROME_VERSION_MINOR; -	cef_ver_codec << "."; -	cef_ver_codec << CHROME_VERSION_BUILD; -	cef_ver_codec << "."; -	cef_ver_codec << CHROME_VERSION_PATCH; - -	info["LIBCEF_VERSION"] = cef_ver_codec.str(); -#else -	info["LIBCEF_VERSION"] = "Undefined"; -#endif - -#if !LL_LINUX -	std::ostringstream vlc_ver_codec; -	vlc_ver_codec << LIBVLC_VERSION_MAJOR; -	vlc_ver_codec << "."; -	vlc_ver_codec << LIBVLC_VERSION_MINOR; -	vlc_ver_codec << "."; -	vlc_ver_codec << LIBVLC_VERSION_REVISION; -	info["LIBVLC_VERSION"] = vlc_ver_codec.str(); -#else -	info["LIBVLC_VERSION"] = "Undefined"; -#endif - -	S32 packets_in = LLViewerStats::instance().getRecording().getSum(LLStatViewer::PACKETS_IN); -	if (packets_in > 0) -	{ -		info["PACKETS_LOST"] = LLViewerStats::instance().getRecording().getSum(LLStatViewer::PACKETS_LOST); -		info["PACKETS_IN"] = packets_in; -		info["PACKETS_PCT"] = 100.f*info["PACKETS_LOST"].asReal() / info["PACKETS_IN"].asReal(); -	} - -	if (mServerReleaseNotesURL.empty()) -	{ -		if (gAgent.getRegion()) -		{ -			info["SERVER_RELEASE_NOTES_URL"] = LLTrans::getString("RetrievingData"); -		} -		else -		{ -			info["SERVER_RELEASE_NOTES_URL"] = LLTrans::getString("NotConnected"); -		} -	} -	else if (LLStringUtil::startsWith(mServerReleaseNotesURL, "http")) // it's an URL -	{ -		info["SERVER_RELEASE_NOTES_URL"] = "[" + LLWeb::escapeURL(mServerReleaseNotesURL) + " " + LLTrans::getString("ReleaseNotes") + "]"; -	} -	else -	{ -		info["SERVER_RELEASE_NOTES_URL"] = mServerReleaseNotesURL; -	} - -    // populate field for new local disk cache with some details -    info["DISK_CACHE_INFO"] = LLDiskCache::getInstance()->getCacheInfo(); - -	return info; -} - -std::string LLAppViewer::getViewerInfoString(bool default_string) const -{ -	std::ostringstream support; - -	LLSD info(getViewerInfo()); - -	// Render the LLSD from getInfo() as a format_map_t -	LLStringUtil::format_map_t args; - -	// allow the "Release Notes" URL label to be localized -	args["ReleaseNotes"] = LLTrans::getString("ReleaseNotes", default_string); - -	for (LLSD::map_const_iterator ii(info.beginMap()), iend(info.endMap()); -		ii != iend; ++ii) -	{ -		if (! ii->second.isArray()) -		{ -			// Scalar value -			if (ii->second.isUndefined()) -			{ -				args[ii->first] = LLTrans::getString("none_text", default_string); -			} -			else -			{ -				// don't forget to render value asString() -				args[ii->first] = ii->second.asString(); -			} -		} -		else -		{ -			// array value: build KEY_0, KEY_1 etc. entries -			for (LLSD::Integer n(0), size(ii->second.size()); n < size; ++n) -			{ -				args[STRINGIZE(ii->first << '_' << n)] = ii->second[n].asString(); -			} -		} -	} - -	// Now build the various pieces -	support << LLTrans::getString("AboutHeader", args, default_string); -	if (info.has("BUILD_CONFIG")) -	{ -		support << "\n" << LLTrans::getString("BuildConfig", args, default_string); -	} -	if (info.has("REGION")) -	{ -		support << "\n\n" << LLTrans::getString("AboutPosition", args, default_string); -	} -	support << "\n\n" << LLTrans::getString("AboutSystem", args, default_string); -	support << "\n"; -	if (info.has("GRAPHICS_DRIVER_VERSION")) -	{ -		support << "\n" << LLTrans::getString("AboutDriver", args, default_string); -	} -	support << "\n" << LLTrans::getString("AboutOGL", args, default_string); -	support << "\n\n" << LLTrans::getString("AboutSettings", args, default_string); -#if LL_DARWIN -	support << "\n" << LLTrans::getString("AboutOSXHiDPI", args, default_string); -#endif -	support << "\n\n" << LLTrans::getString("AboutLibs", args, default_string); -	if (info.has("COMPILER")) -	{ -		support << "\n" << LLTrans::getString("AboutCompiler", args, default_string); -	} -	if (info.has("PACKETS_IN")) -	{ -		support << '\n' << LLTrans::getString("AboutTraffic", args, default_string); -	} - -	// SLT timestamp -	LLSD substitution; -	substitution["datetime"] = (S32)time(NULL);//(S32)time_corrected(); -	support << "\n" << LLTrans::getString("AboutTime", substitution, default_string); - -	return support.str(); -} - -void LLAppViewer::cleanupSavedSettings() -{ -	gSavedSettings.setBOOL("MouseSun", false); - -	gSavedSettings.setBOOL("UseEnergy", true);				// force toggle to turn off, since sends message to simulator - -	gSavedSettings.setBOOL("DebugWindowProc", gDebugWindowProc); - -	gSavedSettings.setBOOL("ShowObjectUpdates", gShowObjectUpdates); - -	if (gDebugView) -	{ -		gSavedSettings.setBOOL("ShowDebugConsole", gDebugView->mDebugConsolep->getVisible()); -	} - -	// save window position if not maximized -	// as we don't track it in callbacks -	if(NULL != gViewerWindow) -	{ -		bool maximized = gViewerWindow->getWindow()->getMaximized(); -		if (!maximized) -		{ -			LLCoordScreen window_pos; - -			if (gViewerWindow->getWindow()->getPosition(&window_pos)) -			{ -				gSavedSettings.setS32("WindowX", window_pos.mX); -				gSavedSettings.setS32("WindowY", window_pos.mY); -			} -		} -	} - -    gSavedSettings.setF32("MapScale", LLWorldMapView::getScaleSetting()); - -	// Some things are cached in LLAgent. -	if (gAgent.isInitialized()) -	{ -		gSavedSettings.setF32("RenderFarClip", gAgentCamera.mDrawDistance); -	} -} - -void LLAppViewer::removeCacheFiles(const std::string& file_mask) -{ -	gDirUtilp->deleteFilesInDir(gDirUtilp->getExpandedFilename(LL_PATH_CACHE, ""), file_mask); -} - -void LLAppViewer::writeSystemInfo() -{ - -    if (! gDebugInfo.has("Dynamic") ) -        gDebugInfo["Dynamic"] = LLSD::emptyMap(); - -#if LL_WINDOWS && !LL_BUGSPLAT -	gDebugInfo["SLLog"] = gDirUtilp->getExpandedFilename(LL_PATH_DUMP,"SecondLife.log"); -#else -    //Not ideal but sufficient for good reporting. -    gDebugInfo["SLLog"] = gDirUtilp->getExpandedFilename(LL_PATH_LOGS,"SecondLife.old");  //LLError::logFileName(); -#endif - -	gDebugInfo["ClientInfo"]["Name"] = LLVersionInfo::instance().getChannel(); -	gDebugInfo["ClientInfo"]["MajorVersion"] = LLVersionInfo::instance().getMajor(); -	gDebugInfo["ClientInfo"]["MinorVersion"] = LLVersionInfo::instance().getMinor(); -	gDebugInfo["ClientInfo"]["PatchVersion"] = LLVersionInfo::instance().getPatch(); -	gDebugInfo["ClientInfo"]["BuildVersion"] = std::to_string(LLVersionInfo::instance().getBuild()); -	gDebugInfo["ClientInfo"]["AddressSize"] = LLVersionInfo::instance().getAddressSize(); - -	gDebugInfo["CAFilename"] = gDirUtilp->getCAFile(); - -	gDebugInfo["CPUInfo"]["CPUString"] = gSysCPU.getCPUString(); -	gDebugInfo["CPUInfo"]["CPUFamily"] = gSysCPU.getFamily(); -	gDebugInfo["CPUInfo"]["CPUMhz"] = (S32)gSysCPU.getMHz(); -	gDebugInfo["CPUInfo"]["CPUAltivec"] = gSysCPU.hasAltivec(); -	gDebugInfo["CPUInfo"]["CPUSSE"] = gSysCPU.hasSSE(); -	gDebugInfo["CPUInfo"]["CPUSSE2"] = gSysCPU.hasSSE2(); - -	gDebugInfo["RAMInfo"]["Physical"] = LLSD::Integer(gSysMemory.getPhysicalMemoryKB().value()); -	gDebugInfo["RAMInfo"]["Allocated"] = LLSD::Integer(gMemoryAllocated.valueInUnits<LLUnits::Kilobytes>()); -	gDebugInfo["OSInfo"] = LLOSInfo::instance().getOSStringSimple(); - -	// The user is not logged on yet, but record the current grid choice login url -	// which may have been the intended grid. -	gDebugInfo["GridName"] = LLGridManager::getInstance()->getGridId(); - -	// *FIX:Mani - move this down in llappviewerwin32 -#ifdef LL_WINDOWS -	DWORD thread_id = GetCurrentThreadId(); -	gDebugInfo["MainloopThreadID"] = (S32)thread_id; -#endif - -#ifndef LL_BUGSPLAT -	// "CrashNotHandled" is set here, while things are running well, -	// in case of a freeze. If there is a freeze, the crash logger will be launched -	// and can read this value from the debug_info.log. -	gDebugInfo["CrashNotHandled"] = LLSD::Boolean(true); -#else // LL_BUGSPLAT -	// "CrashNotHandled" is obsolete; it used (not very successsfully) -    // to try to distinguish crashes from freezes - the intent here to to avoid calling it a freeze -	gDebugInfo["CrashNotHandled"] = LLSD::Boolean(false); -#endif // ! LL_BUGSPLAT - -	// Insert crash host url (url to post crash log to) if configured. This insures -	// that the crash report will go to the proper location in the case of a -	// prior freeze. -	std::string crashHostUrl = gSavedSettings.get<std::string>("CrashHostUrl"); -	if(crashHostUrl != "") -	{ -		gDebugInfo["CrashHostUrl"] = crashHostUrl; -	} - -	// Dump some debugging info -	LL_INFOS("SystemInfo") << "Application: " << LLTrans::getString("APP_NAME") << LL_ENDL; -	LL_INFOS("SystemInfo") << "Version: " << LLVersionInfo::instance().getChannelAndVersion() << LL_ENDL; - -	// Dump the local time and time zone -	time_t now; -	time(&now); -	char tbuffer[256];		/* Flawfinder: ignore */ -	strftime(tbuffer, 256, "%Y-%m-%dT%H:%M:%S %Z", localtime(&now)); -	LL_INFOS("SystemInfo") << "Local time: " << tbuffer << LL_ENDL; - -	// query some system information -	LL_INFOS("SystemInfo") << "CPU info:\n" << gSysCPU << LL_ENDL; -	LL_INFOS("SystemInfo") << "Memory info:\n" << gSysMemory << LL_ENDL; -	LL_INFOS("SystemInfo") << "OS: " << LLOSInfo::instance().getOSStringSimple() << LL_ENDL; -	LL_INFOS("SystemInfo") << "OS info: " << LLOSInfo::instance() << LL_ENDL; - -    gDebugInfo["SettingsFilename"] = gSavedSettings.getString("ClientSettingsFile"); -	gDebugInfo["ViewerExePath"] = gDirUtilp->getExecutablePathAndName(); -	gDebugInfo["CurrentPath"] = gDirUtilp->getCurPath(); -	gDebugInfo["FirstLogin"] = LLSD::Boolean(gAgent.isFirstLogin()); -	gDebugInfo["FirstRunThisInstall"] = gSavedSettings.getBOOL("FirstRunThisInstall"); -    gDebugInfo["StartupState"] = LLStartUp::getStartupStateString(); - -    if (gViewerWindow) -    { -	std::vector<std::string> resolutions = gViewerWindow->getWindow()->getDisplaysResolutionList(); -	for (auto res_iter : resolutions) -	{ -		gDebugInfo["DisplayInfo"].append(res_iter); -	} -    } - -	writeDebugInfo(); // Save out debug_info.log early, in case of crash. -} - -#ifdef LL_WINDOWS -//For whatever reason, in Windows when using OOP server for breakpad, the callback to get the -//name of the dump file is not getting triggered by the breakpad library.   Unfortunately they -//also didn't see fit to provide a simple query request across the pipe to get this name either. -//Since we are putting our output in a runtime generated directory and we know the header data in -//the dump format, we can however use the following hack to identify our file. -// TODO make this a member function. -void getFileList() -{ -	std::stringstream filenames; - -	typedef std::vector<std::string> vec; -	std::string pathname = gDirUtilp->getExpandedFilename(LL_PATH_DUMP,""); -	vec file_vec = gDirUtilp->getFilesInDir(pathname); -	for(vec::const_iterator iter=file_vec.begin(); iter!=file_vec.end(); ++iter) -	{ -		filenames << *iter << " "; -		if ( ( iter->length() > 30 ) && (iter->rfind(".dmp") == (iter->length()-4) ) ) -		{ -			std::string fullname = pathname + *iter; -			llifstream fdat( fullname.c_str(), std::ifstream::binary); -			if (fdat) -			{ -				char buf[5]; -				fdat.read(buf,4); -				fdat.close(); -				if (!strncmp(buf,"MDMP",4)) -				{ -					gDebugInfo["Dynamic"]["MinidumpPath"] = fullname; -					break; -				} -			} -		} -	} -	filenames << std::endl; -	gDebugInfo["Dynamic"]["DumpDirContents"] = filenames.str(); -} -#endif - -// static -void LLAppViewer::recordMarkerVersion(LLAPRFile& marker_file) -{ -	std::string marker_version(LLVersionInfo::instance().getChannelAndVersion()); -	if ( marker_version.length() > MAX_MARKER_LENGTH ) -	{ -		LL_WARNS_ONCE("MarkerFile") << "Version length ("<< marker_version.length()<< ")" -									<< " greater than maximum (" << MAX_MARKER_LENGTH << ")" -									<< ": marker matching may be incorrect" -									<< LL_ENDL; -	} - -	// record the viewer version in the marker file -	marker_file.write(marker_version.data(), marker_version.length()); -} - -bool LLAppViewer::markerIsSameVersion(const std::string& marker_name) const -{ -	bool sameVersion = false; - -	std::string my_version(LLVersionInfo::instance().getChannelAndVersion()); -	char marker_version[MAX_MARKER_LENGTH]; -	S32  marker_version_length; - -	LLAPRFile marker_file; -	marker_file.open(marker_name, LL_APR_RB); -	if (marker_file.getFileHandle()) -	{ -		marker_version_length = marker_file.read(marker_version, sizeof(marker_version)); -		std::string marker_string(marker_version, marker_version_length); -		if ( 0 == my_version.compare( 0, my_version.length(), marker_version, 0, marker_version_length ) ) -		{ -			sameVersion = true; -		} -		LL_DEBUGS("MarkerFile") << "Compare markers for '" << marker_name << "': " -								<< "\n   mine '" << my_version    << "'" -								<< "\n marker '" << marker_string << "'" -								<< "\n " << ( sameVersion ? "same" : "different" ) << " version" -								<< LL_ENDL; -		marker_file.close(); -	} -	return sameVersion; -} - -void LLAppViewer::processMarkerFiles() -{ -	//We've got 4 things to test for here -	// - Other Process Running (SecondLife.exec_marker present, locked) -	// - Freeze (SecondLife.exec_marker present, not locked) -	// - LLError Crash (SecondLife.llerror_marker present) -	// - Other Crash (SecondLife.error_marker present) -	// These checks should also remove these files for the last 2 cases if they currently exist - -	std::ostringstream marker_log_stream; -	bool marker_is_same_version = true; -	// first, look for the marker created at startup and deleted on a clean exit -	mMarkerFileName = gDirUtilp->getExpandedFilename(LL_PATH_LOGS,MARKER_FILE_NAME); -	if (LLAPRFile::isExist(mMarkerFileName, NULL, LL_APR_RB)) -	{ -		// File exists... -		// first, read it to see if it was created by the same version (we need this later) -		marker_is_same_version = markerIsSameVersion(mMarkerFileName); - -		// now test to see if this file is locked by a running process (try to open for write) -		marker_log_stream << "Checking exec marker file for lock..."; -		mMarkerFile.open(mMarkerFileName, LL_APR_WB); -		apr_file_t* fMarker = mMarkerFile.getFileHandle() ; -		if (!fMarker) -		{ -			marker_log_stream << "Exec marker file open failed - assume it is locked."; -			mSecondInstance = true; // lock means that instance is running. -		} -		else -		{ -			// We were able to open it, now try to lock it ourselves... -			if (apr_file_lock(fMarker, APR_FLOCK_NONBLOCK | APR_FLOCK_EXCLUSIVE) != APR_SUCCESS) -			{ -				marker_log_stream << "Locking exec marker failed."; -				mSecondInstance = true; // lost a race? be conservative -			} -			else -			{ -				// No other instances; we've locked this file now, so record our version; delete on quit. -				recordMarkerVersion(mMarkerFile); -				marker_log_stream << "Exec marker file existed but was not locked; rewritten."; -			} -		} -		initLoggingAndGetLastDuration(); - -		std::string marker_log_msg(marker_log_stream.str()); -		LL_INFOS("MarkerFile") << marker_log_msg << LL_ENDL; - -		if (mSecondInstance) -		{ -			LL_INFOS("MarkerFile") << "Exec marker '"<< mMarkerFileName << "' owned by another instance" << LL_ENDL; -		} -		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; -			gLastExecEvent = LAST_EXEC_OTHER_CRASH; -		} -		else -		{ -			LL_INFOS("MarkerFile") << "Exec marker '"<< mMarkerFileName << "' found, but versions did not match" << LL_ENDL; -		} -	} -	else // marker did not exist... last exec (if any) did not freeze -	{ -		initLoggingAndGetLastDuration(); -		// Create the marker file for this execution & lock it; it will be deleted on a clean exit -		apr_status_t s; -		s = mMarkerFile.open(mMarkerFileName, LL_APR_WB, true); - -		if (s == APR_SUCCESS && mMarkerFile.getFileHandle()) -		{ -			LL_DEBUGS("MarkerFile") << "Exec marker file '"<< mMarkerFileName << "' created." << LL_ENDL; -			if (APR_SUCCESS == apr_file_lock(mMarkerFile.getFileHandle(), APR_FLOCK_NONBLOCK | APR_FLOCK_EXCLUSIVE)) -			{ -				recordMarkerVersion(mMarkerFile); -				LL_DEBUGS("MarkerFile") << "Exec marker file locked." << LL_ENDL; -			} -			else -			{ -				LL_WARNS("MarkerFile") << "Exec marker file cannot be locked." << LL_ENDL; -			} -		} -		else -		{ -			LL_WARNS("MarkerFile") << "Failed to create exec marker file '"<< mMarkerFileName << "'." << LL_ENDL; -		} -	} - -	// now check for cases in which the exec marker may have been cleaned up by crash handlers - -	// check for any last exec event report based on whether or not it happened during logout -	// (the logout marker is created when logout begins) -	std::string logout_marker_file =  gDirUtilp->getExpandedFilename(LL_PATH_LOGS, LOGOUT_MARKER_FILE_NAME); -	if(LLAPRFile::isExist(logout_marker_file, NULL, LL_APR_RB)) -	{ -		if (markerIsSameVersion(logout_marker_file)) -		{ -			gLastExecEvent = LAST_EXEC_LOGOUT_FROZE; -			LL_INFOS("MarkerFile") << "Logout crash marker '"<< logout_marker_file << "', changing LastExecEvent to LOGOUT_FROZE" << LL_ENDL; -		} -		else -		{ -			LL_INFOS("MarkerFile") << "Logout crash marker '"<< logout_marker_file << "' found, but versions did not match" << LL_ENDL; -		} -		LLAPRFile::remove(logout_marker_file); -	} -	// further refine based on whether or not a marker created during an llerr crash is found -	std::string llerror_marker_file = gDirUtilp->getExpandedFilename(LL_PATH_LOGS, LLERROR_MARKER_FILE_NAME); -	if(LLAPRFile::isExist(llerror_marker_file, NULL, LL_APR_RB)) -	{ -		if (markerIsSameVersion(llerror_marker_file)) -		{ -			if ( gLastExecEvent == LAST_EXEC_LOGOUT_FROZE ) -			{ -				gLastExecEvent = LAST_EXEC_LOGOUT_CRASH; -				LL_INFOS("MarkerFile") << "LLError marker '"<< llerror_marker_file << "' crashed, setting LastExecEvent to LOGOUT_CRASH" << LL_ENDL; -			} -			else -			{ -				gLastExecEvent = LAST_EXEC_LLERROR_CRASH; -				LL_INFOS("MarkerFile") << "LLError marker '"<< llerror_marker_file << "' crashed, setting LastExecEvent to LLERROR_CRASH" << LL_ENDL; -			} -		} -		else -		{ -			LL_INFOS("MarkerFile") << "LLError marker '"<< llerror_marker_file << "' found, but versions did not match" << LL_ENDL; -		} -		LLAPRFile::remove(llerror_marker_file); -	} -	// and last refine based on whether or not a marker created during a non-llerr crash is found -	std::string error_marker_file = gDirUtilp->getExpandedFilename(LL_PATH_LOGS, ERROR_MARKER_FILE_NAME); -	if(LLAPRFile::isExist(error_marker_file, NULL, LL_APR_RB)) -	{ -		if (markerIsSameVersion(error_marker_file)) -		{ -			if (gLastExecEvent == LAST_EXEC_LOGOUT_FROZE) -			{ -				gLastExecEvent = LAST_EXEC_LOGOUT_CRASH; -				LL_INFOS("MarkerFile") << "Error marker '"<< error_marker_file << "' crashed, setting LastExecEvent to LOGOUT_CRASH" << LL_ENDL; -			} -			else -			{ -				gLastExecEvent = LAST_EXEC_OTHER_CRASH; -				LL_INFOS("MarkerFile") << "Error marker '"<< error_marker_file << "' crashed, setting LastExecEvent to " << gLastExecEvent << LL_ENDL; -			} -		} -		else -		{ -			LL_INFOS("MarkerFile") << "Error marker '"<< error_marker_file << "' marker found, but versions did not match" << LL_ENDL; -		} -		LLAPRFile::remove(error_marker_file); -	} -} - -void LLAppViewer::removeMarkerFiles() -{ -	if (!mSecondInstance) -	{ -		if (mMarkerFile.getFileHandle()) -		{ -			mMarkerFile.close() ; -			LLAPRFile::remove( mMarkerFileName ); -			LL_DEBUGS("MarkerFile") << "removed exec marker '"<<mMarkerFileName<<"'"<< LL_ENDL; -		} -		else -		{ -			LL_WARNS("MarkerFile") << "marker '"<<mMarkerFileName<<"' not open"<< LL_ENDL; - 		} - -		if (mLogoutMarkerFile.getFileHandle()) -		{ -			mLogoutMarkerFile.close(); -			LLAPRFile::remove( mLogoutMarkerFileName ); -			LL_DEBUGS("MarkerFile") << "removed logout marker '"<<mLogoutMarkerFileName<<"'"<< LL_ENDL; -		} -		else -		{ -			LL_WARNS("MarkerFile") << "logout marker '"<<mLogoutMarkerFileName<<"' not open"<< LL_ENDL; -		} -	} -	else -	{ -		LL_WARNS("MarkerFile") << "leaving markers because this is a second instance" << LL_ENDL; -	} -} - -void LLAppViewer::removeDumpDir() -{ -    //Call this routine only on clean exit.  Crash reporter will clean up -    //its locking table for us. -    if (gDirUtilp->dumpDirExists()) // Check if dump dir was created this run -    { -        std::string dump_dir = gDirUtilp->getExpandedFilename(LL_PATH_DUMP, ""); -        gDirUtilp->deleteDirAndContents(dump_dir); -    } - -    if (mSecondInstance && !isError()) -    { -        std::string log_filename = LLError::logFileName(); -        LLError::logToFile(""); -        LLFile::remove(log_filename); -    } -} - -void LLAppViewer::forceQuit() -{ -	LLApp::setQuitting(); -} - -//TODO: remove -void LLAppViewer::fastQuit(S32 error_code) -{ -	// finish pending transfers -	flushLFSIO(); -	// let sim know we're logging out -	sendLogoutRequest(); -	// flush network buffers by shutting down messaging system -	end_messaging_system(); -	// figure out the error code -	S32 final_error_code = error_code ? error_code : (S32)isError(); -	// this isn't a crash -	removeMarkerFiles(); -	// get outta here -	_exit(final_error_code); -} - -void LLAppViewer::requestQuit() -{ -	LL_INFOS() << "requestQuit" << LL_ENDL; - -	LLViewerRegion* region = gAgent.getRegion(); - -	if( (LLStartUp::getStartupState() < STATE_STARTED) || !region ) -	{ -		// If we have a region, make some attempt to send a logout request first. -		// This prevents the halfway-logged-in avatar from hanging around inworld for a couple minutes. -		if(region) -		{ -			sendLogoutRequest(); -		} - -		// Quit immediately -		forceQuit(); -		return; -	} - -	// Try to send metrics back to the grid -	metricsSend(!gDisconnected); - -	// Try to send last batch of avatar rez metrics. -	if (!gDisconnected && isAgentAvatarValid()) -	{ -		gAgentAvatarp->updateAvatarRezMetrics(true); // force a last packet to be sent. -	} - -	LLHUDEffectSpiral *effectp = (LLHUDEffectSpiral*)LLHUDManager::getInstance()->createViewerEffect(LLHUDObject::LL_HUD_EFFECT_POINT, true); -	effectp->setPositionGlobal(gAgent.getPositionGlobal()); -	effectp->setColor(LLColor4U(gAgent.getEffectColor())); -	LLHUDManager::getInstance()->sendEffects(); -	effectp->markDead() ;//remove it. - -	// Attempt to close all floaters that might be -	// editing things. -	if (gFloaterView) -	{ -		// application is quitting -		gFloaterView->closeAllChildren(true); -	} - -	// Send preferences once, when exiting -	bool include_preferences = true; -	send_viewer_stats(include_preferences); - -	gLogoutTimer.reset(); -	mQuitRequested = true; -} - -static bool finish_quit(const LLSD& notification, const LLSD& response) -{ -	S32 option = LLNotificationsUtil::getSelectedOption(notification, response); - -	if (option == 0) -	{ -		LLAppViewer::instance()->requestQuit(); -	} -	return false; -} -static LLNotificationFunctorRegistration finish_quit_reg("ConfirmQuit", finish_quit); - -void LLAppViewer::userQuit() -{ -	LL_INFOS() << "User requested quit" << LL_ENDL; -	if (gDisconnected -		|| !gViewerWindow -		|| !gViewerWindow->getProgressView() -		|| gViewerWindow->getProgressView()->getVisible()) -	{ -		requestQuit(); -	} -	else -	{ -		LLNotificationsUtil::add("ConfirmQuit"); -	} -} - -static bool finish_early_exit(const LLSD& notification, const LLSD& response) -{ -	LLAppViewer::instance()->forceQuit(); -	return false; -} - -void LLAppViewer::earlyExit(const std::string& name, const LLSD& substitutions) -{ -   	LL_WARNS() << "app_early_exit: " << name << LL_ENDL; -	gDoDisconnect = true; -	LLNotificationsUtil::add(name, substitutions, LLSD(), finish_early_exit); -} - -// 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; -	gDoDisconnect = true; -	finish_early_exit( LLSD(), LLSD() ); -} - -void LLAppViewer::abortQuit() -{ -    LL_INFOS() << "abortQuit()" << LL_ENDL; -	mQuitRequested = false; -} - -void LLAppViewer::migrateCacheDirectory() -{ -#if LL_WINDOWS || LL_DARWIN -	// NOTE: (Nyx) as of 1.21, cache for mac is moving to /library/caches/SecondLife from -	// /library/application support/SecondLife/cache This should clear/delete the old dir. - -	// As of 1.23 the Windows cache moved from -	//   C:\Documents and Settings\James\Application Support\SecondLife\cache -	// to -	//   C:\Documents and Settings\James\Local Settings\Application Support\SecondLife -	// -	// The Windows Vista equivalent is from -	//   C:\Users\James\AppData\Roaming\SecondLife\cache -	// to -	//   C:\Users\James\AppData\Local\SecondLife -	// -	// Note the absence of \cache on the second path.  James. - -	// Only do this once per fresh install of this version. -	if (gSavedSettings.getBOOL("MigrateCacheDirectory")) -	{ -		gSavedSettings.setBOOL("MigrateCacheDirectory", false); - -		std::string old_cache_dir = gDirUtilp->add(gDirUtilp->getOSUserAppDir(), "cache"); -		std::string new_cache_dir = gDirUtilp->getCacheDir(true); - -		if (gDirUtilp->fileExists(old_cache_dir)) -		{ -			LL_INFOS() << "Migrating cache from " << old_cache_dir << " to " << new_cache_dir << LL_ENDL; - -			// Migrate inventory cache to avoid pain to inventory database after mass update -			S32 file_count = 0; -			std::string file_name; -			std::string mask = "*.*"; - -			LLDirIterator iter(old_cache_dir, mask); -			while (iter.next(file_name)) -			{ -				if (file_name == "." || file_name == "..") continue; -				std::string source_path = gDirUtilp->add(old_cache_dir, file_name); -				std::string dest_path = gDirUtilp->add(new_cache_dir, file_name); -				if (!LLFile::rename(source_path, dest_path)) -				{ -					file_count++; -				} -			} -			LL_INFOS() << "Moved " << file_count << " files" << LL_ENDL; - -			// Nuke the old cache -			gDirUtilp->setCacheDir(old_cache_dir); -			purgeCache(); -			gDirUtilp->setCacheDir(new_cache_dir); - -#if LL_DARWIN -			// Clean up Mac files not deleted by removing *.* -			std::string ds_store = old_cache_dir + "/.DS_Store"; -			if (gDirUtilp->fileExists(ds_store)) -			{ -				LLFile::remove(ds_store); -			} -#endif -			if (LLFile::rmdir(old_cache_dir) != 0) -			{ -				LL_WARNS() << "could not delete old cache directory " << old_cache_dir << LL_ENDL; -			} -		} -	} -#endif // LL_WINDOWS || LL_DARWIN -} - -//static -U32 LLAppViewer::getTextureCacheVersion() -{ -	// Viewer texture cache version, change if the texture cache format changes. -	// 2021-03-10 Bumping up by one to help obviate texture cache issues with -	//            Simple Cache Viewer - see SL-14985 for more information -	//const U32 TEXTURE_CACHE_VERSION = 8; -	const U32 TEXTURE_CACHE_VERSION = 9; - -	return TEXTURE_CACHE_VERSION ; -} - -//static -U32 LLAppViewer::getDiskCacheVersion() -{ -    // Viewer disk cache version intorduced in Simple Cache Viewer, change if the cache format changes. -    const U32 DISK_CACHE_VERSION = 1; - -    return DISK_CACHE_VERSION ; -} - -//static -U32 LLAppViewer::getObjectCacheVersion() -{ -	// Viewer object cache version, change if object update -	// format changes. JC -	const U32 INDRA_OBJECT_CACHE_VERSION = 17; - -	return INDRA_OBJECT_CACHE_VERSION; -} - -bool LLAppViewer::initCache() -{ -	mPurgeCache = false; -	bool read_only = mSecondInstance; -	LLAppViewer::getTextureCache()->setReadOnly(read_only) ; -	LLVOCache::initParamSingleton(read_only); - -	// initialize the new disk cache using saved settings -	const std::string cache_dir_name = gSavedSettings.getString("DiskCacheDirName"); - -	const U32 MB = 1024 * 1024; -    const uintmax_t MIN_CACHE_SIZE = 256 * MB; -	const uintmax_t MAX_CACHE_SIZE = 9984ll * MB; -    const uintmax_t setting_cache_total_size = uintmax_t(gSavedSettings.getU32("CacheSize")) * MB; -    const uintmax_t cache_total_size = llclamp(setting_cache_total_size, MIN_CACHE_SIZE, MAX_CACHE_SIZE); -    const F64 disk_cache_percent = gSavedSettings.getF32("DiskCachePercentOfTotal"); -    const F64 texture_cache_percent = 100.0 - disk_cache_percent; - -    // note that the maximum size of this cache is defined as a percentage of the  -    // total cache size - the 'CacheSize' pref - for all caches.  -    const uintmax_t disk_cache_size = uintmax_t(cache_total_size * disk_cache_percent / 100); -	const bool enable_cache_debug_info = gSavedSettings.getBOOL("EnableDiskCacheDebugInfo"); - -	bool texture_cache_mismatch = false; -    bool remove_vfs_files = false; -	if (gSavedSettings.getS32("LocalCacheVersion") != LLAppViewer::getTextureCacheVersion()) -	{ -		texture_cache_mismatch = true; -		if (!read_only) -		{ -			gSavedSettings.setS32("LocalCacheVersion", LLAppViewer::getTextureCacheVersion()); - -            //texture cache version was bumped up in Simple Cache Viewer, and at this point old vfs files are not needed -            remove_vfs_files = true;    -		} -	} - -	if (!read_only) -	{ -		// Purge cache if user requested it -		if (gSavedSettings.getBOOL("PurgeCacheOnStartup") || -			gSavedSettings.getBOOL("PurgeCacheOnNextStartup")) -		{ -			LL_INFOS("AppCache") << "Startup cache purge requested: " << (gSavedSettings.getBOOL("PurgeCacheOnStartup") ? "ALWAYS" : "ONCE") << LL_ENDL; -			gSavedSettings.setBOOL("PurgeCacheOnNextStartup", false); -			mPurgeCache = true; -			// STORM-1141 force purgeAllTextures to get called to prevent a crash here. -brad -			texture_cache_mismatch = true; -		} - -		// We have moved the location of the cache directory over time. -		migrateCacheDirectory(); - -		// Setup and verify the cache location -		std::string cache_location = gSavedSettings.getString("CacheLocation"); -		std::string new_cache_location = gSavedSettings.getString("NewCacheLocation"); -		if (new_cache_location != cache_location) -		{ -			LL_INFOS("AppCache") << "Cache location changed, cache needs purging" << LL_ENDL; -			gDirUtilp->setCacheDir(gSavedSettings.getString("CacheLocation")); -			purgeCache(); // purge old cache -			gDirUtilp->deleteDirAndContents(gDirUtilp->getExpandedFilename(LL_PATH_CACHE, cache_dir_name)); -			gSavedSettings.setString("CacheLocation", new_cache_location); -			gSavedSettings.setString("CacheLocationTopFolder", gDirUtilp->getBaseFileName(new_cache_location)); -		} -	} - -	if (!gDirUtilp->setCacheDir(gSavedSettings.getString("CacheLocation"))) -	{ -		LL_WARNS("AppCache") << "Unable to set cache location" << LL_ENDL; -		gSavedSettings.setString("CacheLocation", ""); -		gSavedSettings.setString("CacheLocationTopFolder", ""); -	} - -	const std::string cache_dir = gDirUtilp->getExpandedFilename(LL_PATH_CACHE, cache_dir_name); -    LLDiskCache::initParamSingleton(cache_dir, disk_cache_size, enable_cache_debug_info); - -	if (!read_only) -	{ -        if (gSavedSettings.getS32("DiskCacheVersion") != LLAppViewer::getDiskCacheVersion()) -        { -            LLDiskCache::getInstance()->clearCache(); -            remove_vfs_files = true; -            gSavedSettings.setS32("DiskCacheVersion", LLAppViewer::getDiskCacheVersion()); -        } - -        if (remove_vfs_files) -        { -            LLDiskCache::getInstance()->removeOldVFSFiles(); -        } -         -        if (mPurgeCache) -		{ -		LLSplashScreen::update(LLTrans::getString("StartupClearingCache")); -		purgeCache(); - -			// clear the new C++ file system based cache -			LLDiskCache::getInstance()->clearCache(); -	} -		else -		{ -			// purge excessive files from the new file system based cache -			LLDiskCache::getInstance()->purge(); -		} -	} -	LLAppViewer::getPurgeDiskCacheThread()->start(); - -	LLSplashScreen::update(LLTrans::getString("StartupInitializingTextureCache")); - -	// Init the texture cache -    // Allocate the remaining percent which is not allocated to the disk cache -    const S64 texture_cache_size = S64(cache_total_size * texture_cache_percent / 100); - -    LLAppViewer::getTextureCache()->initCache(LL_PATH_CACHE, texture_cache_size, texture_cache_mismatch); - -	LLVOCache::getInstance()->initCache(LL_PATH_CACHE, gSavedSettings.getU32("CacheNumberOfRegionsForObjects"), getObjectCacheVersion()); - -    return true; -} - -void LLAppViewer::addOnIdleCallback(const boost::function<void()>& cb) -{ -	gMainloopWork.post(cb); -} - -void LLAppViewer::loadKeyBindings() -{ -	std::string key_bindings_file = gDirUtilp->getExpandedFilename(LL_PATH_USER_SETTINGS, "key_bindings.xml"); -	if (!gDirUtilp->fileExists(key_bindings_file) || !gViewerInput.loadBindingsXML(key_bindings_file)) -	{ -		// Failed to load custom bindings, try default ones -		key_bindings_file = gDirUtilp->getExpandedFilename(LL_PATH_APP_SETTINGS, "key_bindings.xml"); -		if (!gViewerInput.loadBindingsXML(key_bindings_file)) -		{ -            LLError::LLUserWarningMsg::showMissingFiles(); -			LL_ERRS("InitInfo") << "Unable to open default key bindings from " << key_bindings_file << LL_ENDL; -		} -	} -    LLUrlRegistry::instance().setKeybindingHandler(&gViewerInput); -} - -void LLAppViewer::purgeCache() -{ -	LL_INFOS("AppCache") << "Purging Cache and Texture Cache..." << LL_ENDL; -	LLAppViewer::getTextureCache()->purgeCache(LL_PATH_CACHE); -	LLVOCache::getInstance()->removeCache(LL_PATH_CACHE); -	LLViewerShaderMgr::instance()->clearShaderCache(); -	std::string browser_cache = gDirUtilp->getExpandedFilename(LL_PATH_CACHE, "cef_cache"); -	if (LLFile::isdir(browser_cache)) -	{ -		// cef does not support clear_cache and clear_cookies, so clear what we can manually. -		gDirUtilp->deleteDirAndContents(browser_cache); -	} -	gDirUtilp->deleteFilesInDir(gDirUtilp->getExpandedFilename(LL_PATH_CACHE, ""), "*"); -} - -//purge cache immediately, do not wait until the next login. -void LLAppViewer::purgeCacheImmediate() -{ -	LL_INFOS("AppCache") << "Purging Object Cache and Texture Cache immediately..." << LL_ENDL; -	LLAppViewer::getTextureCache()->purgeCache(LL_PATH_CACHE, false); -	LLVOCache::getInstance()->removeCache(LL_PATH_CACHE, true); -} - -std::string LLAppViewer::getSecondLifeTitle() const -{ -	return LLTrans::getString("APP_NAME"); -} - -std::string LLAppViewer::getWindowTitle() const -{ -	return gWindowTitle; -} - -// Callback from a dialog indicating user was logged out. -bool finish_disconnect(const LLSD& notification, const LLSD& response) -{ -	S32 option = LLNotificationsUtil::getSelectedOption(notification, response); - -	if (1 == option) -	{ -        LLAppViewer::instance()->forceQuit(); -	} -	return false; -} - -// Callback from an early disconnect dialog, force an exit -bool finish_forced_disconnect(const LLSD& notification, const LLSD& response) -{ -	LLAppViewer::instance()->forceQuit(); -	return false; -} - - -void LLAppViewer::forceDisconnect(const std::string& mesg) -{ -	if (gDoDisconnect) -    { -		// Already popped up one of these dialogs, don't -		// do this again. -		return; -    } - -	// *TODO: Translate the message if possible -	std::string big_reason = LLAgent::sTeleportErrorMessages[mesg]; -	if ( big_reason.size() == 0 ) -	{ -		big_reason = mesg; -	} - -	LLSD args; -	gDoDisconnect = true; - -	if (LLStartUp::getStartupState() < STATE_STARTED) -	{ -		// Tell users what happened -		args["ERROR_MESSAGE"] = big_reason; -		LLNotificationsUtil::add("ErrorMessage", args, LLSD(), &finish_forced_disconnect); -	} -	else -	{ -		args["MESSAGE"] = big_reason; -		LLNotificationsUtil::add("YouHaveBeenLoggedOut", args, LLSD(), &finish_disconnect ); -	} -} - -void LLAppViewer::badNetworkHandler() -{ -	// Dump the packet -	gMessageSystem->dumpPacketToLog(); - -	// Flush all of our caches on exit in the case of disconnect due to -	// invalid packets. - -	mPurgeCacheOnExit = true; - -	std::ostringstream message; -	message << -		"The viewer has detected mangled network data indicative\n" -		"of a bad upstream network connection or an incomplete\n" -		"local installation of " << LLAppViewer::instance()->getSecondLifeTitle() << ". \n" -		" \n" -		"Try uninstalling and reinstalling to see if this resolves \n" -		"the issue. \n" -		" \n" -		"If the problem continues, see the Tech Support FAQ at: \n" -		"www.secondlife.com/support"; -	forceDisconnect(message.str()); - -	LLApp::instance()->writeMiniDump(); -} - -// This routine may get called more than once during the shutdown process. -// This can happen because we need to get the screenshot before the window -// is destroyed. -void LLAppViewer::saveFinalSnapshot() -{ -	if (!mSavedFinalSnapshot) -	{ -		gSavedSettings.setVector3d("FocusPosOnLogout", gAgentCamera.calcFocusPositionTargetGlobal()); -		gSavedSettings.setVector3d("CameraPosOnLogout", gAgentCamera.calcCameraPositionTargetGlobal()); -		gViewerWindow->setCursor(UI_CURSOR_WAIT); -		gAgentCamera.changeCameraToThirdPerson( false );	// don't animate, need immediate switch -		gSavedSettings.setBOOL("ShowParcelOwners", false); -		idle(); - -		std::string snap_filename = gDirUtilp->getLindenUserDir(); -		snap_filename += gDirUtilp->getDirDelimiter(); -		snap_filename += LLStartUp::getScreenLastFilename(); -		// use full pixel dimensions of viewer window (not post-scale dimensions) -		gViewerWindow->saveSnapshot(snap_filename, -									gViewerWindow->getWindowWidthRaw(), -									gViewerWindow->getWindowHeightRaw(), -									false, -									gSavedSettings.getBOOL("RenderHUDInSnapshot"), -									true, -									LLSnapshotModel::SNAPSHOT_TYPE_COLOR, -									LLSnapshotModel::SNAPSHOT_FORMAT_PNG); -		mSavedFinalSnapshot = true; - -		if (gAgent.isInHomeRegion()) -		{ -			LLVector3d home; -			if (gAgent.getHomePosGlobal(&home) && dist_vec(home, gAgent.getPositionGlobal()) < 10) -			{ -				// We are at home position or close to it, see if we need to create home screenshot -				// Notes: -				// 1. It might be beneficial to also replace home if file is too old -				// 2. This is far from best way/place to update screenshot since location might be not fully loaded, -				// but we don't have many options -				std::string snap_home = gDirUtilp->getLindenUserDir(); -				snap_home += gDirUtilp->getDirDelimiter(); -				snap_home += LLStartUp::getScreenHomeFilename(); -				if (!gDirUtilp->fileExists(snap_home)) -				{ -					// We are at home position yet no home image exist, fix it -					LLFile::copy(snap_filename, snap_home); -				} -			} -		} -	} -} - -void LLAppViewer::loadNameCache() -{ -	// display names cache -	std::string filename = -		gDirUtilp->getExpandedFilename(LL_PATH_CACHE, "avatar_name_cache.xml"); -	LL_INFOS("AvNameCache") << filename << LL_ENDL; -	llifstream name_cache_stream(filename.c_str()); -	if(name_cache_stream.is_open()) -	{ -		if ( ! LLAvatarNameCache::getInstance()->importFile(name_cache_stream)) -        { -            LL_WARNS("AppInit") << "removing invalid '" << filename << "'" << LL_ENDL; -            name_cache_stream.close(); -            LLFile::remove(filename); -        } -	} - -	if (!gCacheName) return; - -	std::string name_cache; -	name_cache = gDirUtilp->getExpandedFilename(LL_PATH_CACHE, "name.cache"); -	llifstream cache_file(name_cache.c_str()); -	if(cache_file.is_open()) -	{ -		if(gCacheName->importFile(cache_file)) return; -	} -} - -void LLAppViewer::saveNameCache() -{ -	// display names cache -	std::string filename = -		gDirUtilp->getExpandedFilename(LL_PATH_CACHE, "avatar_name_cache.xml"); -	llofstream name_cache_stream(filename.c_str()); -	if(name_cache_stream.is_open()) -	{ -		LLAvatarNameCache::getInstance()->exportFile(name_cache_stream); -    } - -    // real names cache -	if (gCacheName) -    { -        std::string name_cache; -        name_cache = gDirUtilp->getExpandedFilename(LL_PATH_CACHE, "name.cache"); -        llofstream cache_file(name_cache.c_str()); -        if(cache_file.is_open()) -        { -            gCacheName->exportFile(cache_file); -        } -	} -} - - -/*!	@brief		This class is an LLFrameTimer that can be created with -				an elapsed time that starts counting up from the given value -				rather than 0.0. - -				Otherwise it behaves the same way as LLFrameTimer. -*/ -class LLFrameStatsTimer : public LLFrameTimer -{ -public: -	LLFrameStatsTimer(F64 elapsed_already = 0.0) -		: LLFrameTimer() -		{ -			mStartTime -= elapsed_already; -		} -}; - -static LLTrace::BlockTimerStatHandle FTM_AUDIO_UPDATE("Update Audio"); -static LLTrace::BlockTimerStatHandle FTM_CLEANUP("Cleanup"); -static LLTrace::BlockTimerStatHandle FTM_CLEANUP_DRAWABLES("Drawables"); -static LLTrace::BlockTimerStatHandle FTM_IDLE_CB("Idle Callbacks"); -static LLTrace::BlockTimerStatHandle FTM_LOD_UPDATE("Update LOD"); -static LLTrace::BlockTimerStatHandle FTM_OBJECTLIST_UPDATE("Update Objectlist"); -static LLTrace::BlockTimerStatHandle FTM_REGION_UPDATE("Update Region"); -static LLTrace::BlockTimerStatHandle FTM_WORLD_UPDATE("Update World"); -static LLTrace::BlockTimerStatHandle FTM_NETWORK("Network"); -static LLTrace::BlockTimerStatHandle FTM_AGENT_NETWORK("Agent Network"); -static LLTrace::BlockTimerStatHandle FTM_VLMANAGER("VL Manager"); -static LLTrace::BlockTimerStatHandle FTM_AGENT_POSITION("Agent Position"); -static LLTrace::BlockTimerStatHandle FTM_HUD_EFFECTS("HUD Effects"); - -/////////////////////////////////////////////////////// -// idle() -// -// Called every time the window is not doing anything. -// Receive packets, update statistics, and schedule a redisplay. -/////////////////////////////////////////////////////// -void LLAppViewer::idle() -{ -    LL_PROFILE_ZONE_SCOPED_CATEGORY_APP; -	pingMainloopTimeout("Main:Idle"); - -	// Update frame timers -	static LLTimer idle_timer; - -	LLFrameTimer::updateFrameTime(); -	LLFrameTimer::updateFrameCount(); -	LLEventTimer::updateClass(); -    LLPerfStats::updateClass(); - -	// LLApp::stepFrame() performs the above three calls plus mRunner.run(). -	// Not sure why we don't call stepFrame() here, except that LLRunner seems -	// completely redundant with LLEventTimer. -	LLNotificationsUI::LLToast::updateClass(); -	LLSmoothInterpolation::updateInterpolants(); -	LLMortician::updateClass(); -	LLFilePickerThread::clearDead();  //calls LLFilePickerThread::notify() -	LLDirPickerThread::clearDead(); -	F32 dt_raw = idle_timer.getElapsedTimeAndResetF32(); - -    LLGLTFMaterialList::flushUpdates(); - -	// Service the WorkQueue we use for replies from worker threads. -	// Use function statics for the timeslice setting so we only have to fetch -	// and convert MainWorkTime once. -	static F32 MainWorkTimeRaw = gSavedSettings.getF32("MainWorkTime"); -	static F32Milliseconds MainWorkTimeMs(MainWorkTimeRaw); -	// MainWorkTime is specified in fractional milliseconds, but std::chrono -	// uses integer representations. What if we want less than a microsecond? -	// Use nanoseconds. We're very sure we will never need to specify a -	// MainWorkTime that would be larger than we could express in -	// std::chrono::nanoseconds. -	static std::chrono::nanoseconds MainWorkTimeNanoSec{ -		std::chrono::nanoseconds::rep(MainWorkTimeMs.value() * 1000000)}; -	gMainloopWork.runFor(MainWorkTimeNanoSec); - -	// Cap out-of-control frame times -	// Too low because in menus, swapping, debugger, etc. -	// Too high because idle called with no objects in view, etc. -	const F32 MIN_FRAME_RATE = 1.f; -	const F32 MAX_FRAME_RATE = 200.f; - -	F32 frame_rate_clamped = 1.f / dt_raw; -	frame_rate_clamped = llclamp(frame_rate_clamped, MIN_FRAME_RATE, MAX_FRAME_RATE); -	gFrameDTClamped = 1.f / frame_rate_clamped; - -	// Global frame timer -	// Smoothly weight toward current frame -	gFPSClamped = (frame_rate_clamped + (4.f * gFPSClamped)) / 5.f; - -	F32 qas = gSavedSettings.getF32("QuitAfterSeconds"); -	if (qas > 0.f) -	{ -		if (gRenderStartTime.getElapsedTimeF32() > qas) -		{ -			LL_INFOS() << "Quitting after " << qas << " seconds. See setting \"QuitAfterSeconds\"." << LL_ENDL; -			LLAppViewer::instance()->forceQuit(); -		} -	} - -	// Must wait until both have avatar object and mute list, so poll -	// here. -	LLIMProcessing::requestOfflineMessages(); - -	/////////////////////////////////// -	// -	// Special case idle if still starting up -	// -	if (LLStartUp::getStartupState() < STATE_STARTED) -	{ -		// Skip rest if idle startup returns false (essentially, no world yet) -		gGLActive = true; -		if (!idle_startup()) -		{ -			gGLActive = false; -			return; -		} -		gGLActive = false; -	} - - -    F32 yaw = 0.f;				// radians - -	if (!gDisconnected) -	{ -		LL_PROFILE_ZONE_NAMED_CATEGORY_NETWORK("network"); //LL_RECORD_BLOCK_TIME(FTM_NETWORK); -		// Update spaceserver timeinfo -	    LLWorld::getInstance()->setSpaceTimeUSec(LLWorld::getInstance()->getSpaceTimeUSec() + LLUnits::Seconds::fromValue(dt_raw)); - - -	    ////////////////////////////////////// -	    // -	    // Update simulator agent state -	    // - -		if (gSavedSettings.getBOOL("RotateRight")) -		{ -			gAgent.moveYaw(-1.f); -		} - -		{ -            LL_PROFILE_ZONE_NAMED_CATEGORY_APP("Autopilot"); -			// Handle automatic walking towards points -			gAgentPilot.updateTarget(); -			gAgent.autoPilot(&yaw); -		} - -		static LLFrameTimer agent_update_timer; - -		// When appropriate, update agent location to the simulator. -		F32 agent_update_time = agent_update_timer.getElapsedTimeF32(); -		F32 agent_force_update_time = mLastAgentForceUpdate + agent_update_time; -        bool timed_out = agent_update_time > (1.0f / (F32)AGENT_UPDATES_PER_SECOND); -        bool force_send = -            // if there is something to send -            (gAgent.controlFlagsDirty() && timed_out) -            // if something changed -            || (mLastAgentControlFlags != gAgent.getControlFlags()) -            // keep alive -            || (agent_force_update_time > (1.0f / (F32) AGENT_FORCE_UPDATES_PER_SECOND)); -        // timing out doesn't warranty that an update will be sent, -        // just that it will be checked. -		if (force_send || timed_out) -		{ -			LL_PROFILE_ZONE_SCOPED_CATEGORY_NETWORK; -			// Send avatar and camera info -			mLastAgentControlFlags = gAgent.getControlFlags(); -			mLastAgentForceUpdate = force_send ? 0 : agent_force_update_time; -			send_agent_update(force_send); -			agent_update_timer.reset(); -		} -	} - -	////////////////////////////////////// -	// -	// Manage statistics -	// -	// -	{ -		// Initialize the viewer_stats_timer with an already elapsed time -		// of SEND_STATS_PERIOD so that the initial stats report will -		// be sent immediately. -		static LLFrameStatsTimer viewer_stats_timer(SEND_STATS_PERIOD); - -		// Update session stats every large chunk of time -		// *FIX: (?) SAMANTHA -		if (viewer_stats_timer.getElapsedTimeF32() >= SEND_STATS_PERIOD && !gDisconnected) -		{ -			LL_INFOS() << "Transmitting sessions stats" << LL_ENDL; -			bool include_preferences = false; -			send_viewer_stats(include_preferences); -			viewer_stats_timer.reset(); -		} - -		// Print the object debugging stats -		static LLFrameTimer object_debug_timer; -		if (object_debug_timer.getElapsedTimeF32() > 5.f) -		{ -			object_debug_timer.reset(); -			if (gObjectList.mNumDeadObjectUpdates) -			{ -				LL_INFOS() << "Dead object updates: " << gObjectList.mNumDeadObjectUpdates << LL_ENDL; -				gObjectList.mNumDeadObjectUpdates = 0; -			} -			if (gObjectList.mNumUnknownUpdates) -			{ -				LL_INFOS() << "Unknown object updates: " << gObjectList.mNumUnknownUpdates << LL_ENDL; -				gObjectList.mNumUnknownUpdates = 0; -			} - -		} -	} - -	if (!gDisconnected) -	{ -        LL_PROFILE_ZONE_NAMED_CATEGORY_DISPLAY("Network"); - -	    //////////////////////////////////////////////// -	    // -	    // Network processing -	    // -	    // NOTE: Starting at this point, we may still have pointers to "dead" objects -	    // floating throughout the various object lists. -	    // -		idleNameCache(); -		idleNetwork(); - - -		// Check for away from keyboard, kick idle agents. -		idle_afk_check(); - -		//  Update statistics for this frame -		update_statistics(); -	} - -	//////////////////////////////////////// -	// -	// Handle the regular UI idle callbacks as well as -	// hover callbacks -	// - -#ifdef LL_DARWIN -	if (!mQuitRequested)  //MAINT-4243 -#endif -	{ -// 		LL_RECORD_BLOCK_TIME(FTM_IDLE_CB); - -		// Do event notifications if necessary.  Yes, we may want to move this elsewhere. -		gEventNotifier.update(); - -		gIdleCallbacks.callFunctions(); -		gInventory.idleNotifyObservers(); -		LLAvatarTracker::instance().idleNotifyObservers(); -	} - -	// Metrics logging (LLViewerAssetStats, etc.) -	{ -		static LLTimer report_interval; - -		// *TODO:  Add configuration controls for this -		F32 seconds = report_interval.getElapsedTimeF32(); -		if (seconds >= app_metrics_interval) -		{ -			metricsSend(! gDisconnected); -			report_interval.reset(); -		} -	} - - -    // Update layonts, handle mouse events, tooltips, e t c -    // updateUI() needs to be called even in case viewer disconected -    // since related notification still needs handling and allows -    // opening chat. -    gViewerWindow->updateUI(); - -	if (gDisconnected) -    { -		return; -    } - -	if (gTeleportDisplay) -    { -		return; -    } - -	/////////////////////////////////////// -	// Agent and camera movement -	// -	LLCoordGL current_mouse = gViewerWindow->getCurrentMouse(); - -	{ -		// After agent and camera moved, figure out if we need to -		// deselect objects. -		LLSelectMgr::getInstance()->deselectAllIfTooFar(); - -	} - -	{ -		// Handle pending gesture processing -		LL_RECORD_BLOCK_TIME(FTM_AGENT_POSITION); -		LLGestureMgr::instance().update(); - -		gAgent.updateAgentPosition(gFrameDTClamped, yaw, current_mouse.mX, current_mouse.mY); -	} - -	{ -		LL_RECORD_BLOCK_TIME(FTM_OBJECTLIST_UPDATE); - -        if (!(logoutRequestSent() && hasSavedFinalSnapshot())) -		{ -			gObjectList.update(gAgent); -		} -	} - -	////////////////////////////////////// -	// -	// Deletes objects... -	// Has to be done after doing idleUpdates (which can kill objects) -	// - -	{ -		LL_RECORD_BLOCK_TIME(FTM_CLEANUP); -		{ -			gObjectList.cleanDeadObjects(); -		} -		{ -			LL_RECORD_BLOCK_TIME(FTM_CLEANUP_DRAWABLES); -			LLDrawable::cleanupDeadDrawables(); -		} -	} - -	// -	// After this point, in theory we should never see a dead object -	// in the various object/drawable lists. -	// - -	////////////////////////////////////// -	// -	// Update/send HUD effects -	// -	// At this point, HUD effects may clean up some references to -	// dead objects. -	// - -	{ -		LL_RECORD_BLOCK_TIME(FTM_HUD_EFFECTS); -		LLSelectMgr::getInstance()->updateEffects(); -		LLHUDManager::getInstance()->cleanupEffects(); -		LLHUDManager::getInstance()->sendEffects(); -	} - -	//////////////////////////////////////// -	// -	// Unpack layer data that we've received -	// - -	{ -		LL_RECORD_BLOCK_TIME(FTM_NETWORK); -		gVLManager.unpackData(); -	} - -	///////////////////////// -	// -	// Update surfaces, and surface textures as well. -	// - -	LLWorld::getInstance()->updateVisibilities(); -	{ -		const F32 max_region_update_time = .001f; // 1ms -		LL_RECORD_BLOCK_TIME(FTM_REGION_UPDATE); -		LLWorld::getInstance()->updateRegions(max_region_update_time); -	} - -	///////////////////////// -	// -	// Update weather effects -	// - -	// Update wind vector -	LLVector3 wind_position_region; -	static LLVector3 average_wind; - -	LLViewerRegion *regionp; -	regionp = LLWorld::getInstance()->resolveRegionGlobal(wind_position_region, gAgent.getPositionGlobal());	// puts agent's local coords into wind_position -	if (regionp) -	{ -		gWindVec = regionp->mWind.getVelocity(wind_position_region); - -		// Compute average wind and use to drive motion of water - -		average_wind = regionp->mWind.getAverage(); -		gSky.setWind(average_wind); -		//LLVOWater::setWind(average_wind); -	} -	else -	{ -		gWindVec.setVec(0.0f, 0.0f, 0.0f); -	} - -	////////////////////////////////////// -	// -	// Sort and cull in the new renderer are moved to pipeline.cpp -	// Here, particles are updated and drawables are moved. -	// - -	{ -		LL_PROFILE_ZONE_NAMED_CATEGORY_APP("world update"); //LL_RECORD_BLOCK_TIME(FTM_WORLD_UPDATE); -		gPipeline.updateMove(); -	} - -	LLWorld::getInstance()->updateParticles(); - -	if (gAgentPilot.isPlaying() && gAgentPilot.getOverrideCamera()) -	{ -		gAgentPilot.moveCamera(); -	} -	else if (LLViewerJoystick::getInstance()->getOverrideCamera()) -	{ -		LLViewerJoystick::getInstance()->moveFlycam(); -	} -	else -	{ -		if (LLToolMgr::getInstance()->inBuildMode()) -		{ -			LLViewerJoystick::getInstance()->moveObjects(); -		} - -		gAgentCamera.updateCamera(); -	} - -	// update media focus -	LLViewerMediaFocus::getInstance()->update(); - -	// Update marketplace -	LLMarketplaceInventoryImporter::update(); -	LLMarketplaceInventoryNotifications::update(); - -	// objects and camera should be in sync, do LOD calculations now -	{ -		LL_RECORD_BLOCK_TIME(FTM_LOD_UPDATE); -		gObjectList.updateApparentAngles(gAgent); -	} - -	// Update AV render info -	LLAvatarRenderInfoAccountant::getInstance()->idle(); - -	{ -		LL_PROFILE_ZONE_NAMED_CATEGORY_APP("audio update"); //LL_RECORD_BLOCK_TIME(FTM_AUDIO_UPDATE); - -		if (gAudiop) -		{ -		    audio_update_volume(false); -			audio_update_listener(); -			audio_update_wind(false); - -			// this line actually commits the changes we've made to source positions, etc. -			gAudiop->idle(); -		} -	} - -	// Handle shutdown process, for example, -	// wait for floaters to close, send quit message, -	// forcibly quit if it has taken too long -	if (mQuitRequested) -	{ -		gGLActive = true; -		idleShutdown(); -	} -} - -void LLAppViewer::idleShutdown() -{ -	// Wait for all modal alerts to get resolved -	if (LLModalDialog::activeCount() > 0) -	{ -		return; -	} - -	// close IM interface -	if(gIMMgr) -	{ -		gIMMgr->disconnectAllSessions(); -	} - -	// Wait for all floaters to get resolved -	if (gFloaterView -		&& !gFloaterView->allChildrenClosed()) -	{ -		return; -	} - - - - -	// ProductEngine: Try moving this code to where we shut down sTextureCache in cleanup() -	// *TODO: ugly -	static bool saved_teleport_history = false; -	if (!saved_teleport_history) -	{ -		saved_teleport_history = true; -		LLTeleportHistory::getInstance()->dump(); -		LLLocationHistory::getInstance()->save(); // *TODO: find a better place for doing this -		return; -	} - -	static bool saved_snapshot = false; -	if (!saved_snapshot) -	{ -		saved_snapshot = true; -		saveFinalSnapshot(); -		return; -	} - -	const F32 SHUTDOWN_UPLOAD_SAVE_TIME = 5.f; - -	S32 pending_uploads = gAssetStorage->getNumPendingUploads(); -	if (pending_uploads > 0 -		&& gLogoutTimer.getElapsedTimeF32() < SHUTDOWN_UPLOAD_SAVE_TIME -		&& !logoutRequestSent()) -	{ -		static S32 total_uploads = 0; -		// Sometimes total upload count can change during logout. -		total_uploads = llmax(total_uploads, pending_uploads); -		gViewerWindow->setShowProgress(true); -		S32 finished_uploads = total_uploads - pending_uploads; -		F32 percent = 100.f * finished_uploads / total_uploads; -		gViewerWindow->setProgressPercent(percent); -		gViewerWindow->setProgressString(LLTrans::getString("SavingSettings")); -		return; -	} - -	if (gPendingMetricsUploads > 0 -		&& gLogoutTimer.getElapsedTimeF32() < SHUTDOWN_UPLOAD_SAVE_TIME -		&& !logoutRequestSent()) -	{ -        gViewerWindow->setShowProgress(true); -        gViewerWindow->setProgressPercent(100.f); -        gViewerWindow->setProgressString(LLTrans::getString("LoggingOut")); -		return; -	} - -	// All floaters are closed.  Tell server we want to quit. -	if( !logoutRequestSent() ) -	{ -		sendLogoutRequest(); - -		// Wait for a LogoutReply message -		gViewerWindow->setShowProgress(true); -		gViewerWindow->setProgressPercent(100.f); -		gViewerWindow->setProgressString(LLTrans::getString("LoggingOut")); -		return; -	} - -	// Make sure that we quit if we haven't received a reply from the server. -	if( logoutRequestSent() -		&& gLogoutTimer.getElapsedTimeF32() > gLogoutMaxTime ) -	{ -		forceQuit(); -		return; -	} -} - -void LLAppViewer::sendLogoutRequest() -{ -	if(!mLogoutRequestSent && gMessageSystem) -	{ -		//Set internal status variables and marker files before actually starting the logout process -		gLogoutInProgress = true; -		if (!mSecondInstance) -		{ -			mLogoutMarkerFileName = gDirUtilp->getExpandedFilename(LL_PATH_LOGS,LOGOUT_MARKER_FILE_NAME); - -			mLogoutMarkerFile.open(mLogoutMarkerFileName, LL_APR_WB); -			if (mLogoutMarkerFile.getFileHandle()) -			{ -				LL_INFOS("MarkerFile") << "Created logout marker file '"<< mLogoutMarkerFileName << "' " << LL_ENDL; -				recordMarkerVersion(mLogoutMarkerFile); -			} -			else -			{ -				LL_WARNS("MarkerFile") << "Cannot create logout marker file " << mLogoutMarkerFileName << LL_ENDL; -			} -		} -		else -		{ -			LL_INFOS("MarkerFile") << "Did not logout marker file because this is a second instance" << LL_ENDL; -		} - -		LLMessageSystem* msg = gMessageSystem; -		msg->newMessageFast(_PREHASH_LogoutRequest); -		msg->nextBlockFast(_PREHASH_AgentData); -		msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID() ); -		msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID()); -		gAgent.sendReliableMessage(); - -		gLogoutTimer.reset(); -		gLogoutMaxTime = LOGOUT_REQUEST_TIME; -		mLogoutRequestSent = true; - -		if(LLVoiceClient::instanceExists()) -		{ -			LLVoiceClient::getInstance()->leaveChannel(); -		} -	} -} - -void LLAppViewer::updateNameLookupUrl(const LLViewerRegion * regionp) -{ -    if (!regionp || !regionp->capabilitiesReceived()) -    { -        return; -    } - -    LLAvatarNameCache *name_cache = LLAvatarNameCache::getInstance(); -    bool had_capability = LLAvatarNameCache::getInstance()->hasNameLookupURL(); -    std::string name_lookup_url; -    name_lookup_url.reserve(128); // avoid a memory allocation below -    name_lookup_url = regionp->getCapability("GetDisplayNames"); -    bool have_capability = !name_lookup_url.empty(); -    if (have_capability) -    { -        // we have support for display names, use it -        U32 url_size = name_lookup_url.size(); -        // capabilities require URLs with slashes before query params: -        // https://<host>:<port>/cap/<uuid>/?ids=<blah> -        // but the caps are granted like: -        // https://<host>:<port>/cap/<uuid> -        if (url_size > 0 && name_lookup_url[url_size - 1] != '/') -        { -            name_lookup_url += '/'; -        } -        name_cache->setNameLookupURL(name_lookup_url); -    } -    else -    { -        // Display names not available on this region -        name_cache->setNameLookupURL(std::string()); -    } - -    // Error recovery - did we change state? -    if (had_capability != have_capability) -    { -        // name tags are persistant on screen, so make sure they refresh -        LLVOAvatar::invalidateNameTags(); -    } -} - -void LLAppViewer::idleNameCache() -{ -	// Neither old nor new name cache can function before agent has a region -	LLViewerRegion* region = gAgent.getRegion(); -    if (!region) -    { -        return; -    } - -	// deal with any queued name requests and replies. -	gCacheName->processPending(); - -	// Can't run the new cache until we have the list of capabilities -	// for the agent region, and can therefore decide whether to use -	// display names or fall back to the old name system. -    if (!region->capabilitiesReceived()) -    { -        return; -    } - -    LLAvatarNameCache::getInstance()->idle(); -} - -// -// Handle messages, and all message related stuff -// - -#define TIME_THROTTLE_MESSAGES - -#ifdef TIME_THROTTLE_MESSAGES -#define CHECK_MESSAGES_DEFAULT_MAX_TIME .020f // 50 ms = 50 fps (just for messages!) -static F32 CheckMessagesMaxTime = CHECK_MESSAGES_DEFAULT_MAX_TIME; -#endif - -static LLTrace::BlockTimerStatHandle FTM_IDLE_NETWORK("Idle Network"); -static LLTrace::BlockTimerStatHandle FTM_MESSAGE_ACKS("Message Acks"); -static LLTrace::BlockTimerStatHandle FTM_RETRANSMIT("Retransmit"); -static LLTrace::BlockTimerStatHandle FTM_TIMEOUT_CHECK("Timeout Check"); -static LLTrace::BlockTimerStatHandle FTM_DYNAMIC_THROTTLE("Dynamic Throttle"); -static LLTrace::BlockTimerStatHandle FTM_CHECK_REGION_CIRCUIT("Check Region Circuit"); - -void LLAppViewer::idleNetwork() -{ -    LL_PROFILE_ZONE_SCOPED_CATEGORY_NETWORK; -	pingMainloopTimeout("idleNetwork"); - -	gObjectList.mNumNewObjects = 0; -	S32 total_decoded = 0; - -	if (!gSavedSettings.getBOOL("SpeedTest")) -	{ -		LL_PROFILE_ZONE_NAMED_CATEGORY_NETWORK("idle network"); //LL_RECORD_BLOCK_TIME(FTM_IDLE_NETWORK); // decode - -		LLTimer check_message_timer; -		//  Read all available packets from network -		const S64 frame_count = gFrameCount;  // U32->S64 -		F32 total_time = 0.0f; - -		{ -			LockMessageChecker lmc(gMessageSystem); -			while (lmc.checkAllMessages(frame_count, gServicePump)) -			{ -				if (gDoDisconnect) -				{ -					// We're disconnecting, don't process any more messages from the server -					// We're usually disconnecting due to either network corruption or a -					// server going down, so this is OK. -					break; -				} - -				total_decoded++; -				gPacketsIn++; - -				if (total_decoded > MESSAGE_MAX_PER_FRAME) -				{ -					break; -				} - -#ifdef TIME_THROTTLE_MESSAGES -				// Prevent slow packets from completely destroying the frame rate. -				// This usually happens due to clumps of avatars taking huge amount -				// of network processing time (which needs to be fixed, but this is -				// a good limit anyway). -				total_time = check_message_timer.getElapsedTimeF32(); -				if (total_time >= CheckMessagesMaxTime) -					break; -#endif -			} - -			// Handle per-frame message system processing. -			lmc.processAcks(gSavedSettings.getF32("AckCollectTime")); -		} - -#ifdef TIME_THROTTLE_MESSAGES -		if (total_time >= CheckMessagesMaxTime) -		{ -			// Increase CheckMessagesMaxTime so that we will eventually catch up -			CheckMessagesMaxTime *= 1.035f; // 3.5% ~= x2 in 20 frames, ~8x in 60 frames -		} -		else -		{ -			// Reset CheckMessagesMaxTime to default value -			CheckMessagesMaxTime = CHECK_MESSAGES_DEFAULT_MAX_TIME; -		} -#endif - - - -		// we want to clear the control after sending out all necessary agent updates -		gAgent.resetControlFlags(); - -		// Decode enqueued messages... -		S32 remaining_possible_decodes = MESSAGE_MAX_PER_FRAME - total_decoded; - -		if( remaining_possible_decodes <= 0 ) -		{ -			LL_INFOS() << "Maxed out number of messages per frame at " << MESSAGE_MAX_PER_FRAME << LL_ENDL; -		} - -		if (gPrintMessagesThisFrame) -		{ -			LL_INFOS() << "Decoded " << total_decoded << " msgs this frame!" << LL_ENDL; -			gPrintMessagesThisFrame = false; -		} -	} -	add(LLStatViewer::NUM_NEW_OBJECTS, gObjectList.mNumNewObjects); - -	// Retransmit unacknowledged packets. -	gXferManager->retransmitUnackedPackets(); -	gAssetStorage->checkForTimeouts(); -	gViewerThrottle.updateDynamicThrottle(); - -	// Check that the circuit between the viewer and the agent's current -	// region is still alive -	LLViewerRegion *agent_region = gAgent.getRegion(); -	if (agent_region && (LLStartUp::getStartupState()==STATE_STARTED)) -	{ -		LLUUID this_region_id = agent_region->getRegionID(); -		bool this_region_alive = agent_region->isAlive(); -		if ((mAgentRegionLastAlive && !this_region_alive) // newly dead -		    && (mAgentRegionLastID == this_region_id)) // same region -		{ -			forceDisconnect(LLTrans::getString("AgentLostConnection")); -		} -		mAgentRegionLastID = this_region_id; -		mAgentRegionLastAlive = this_region_alive; -	} -} - -void LLAppViewer::disconnectViewer() -{ -	if (gDisconnected) -	{ -		return; -	} -	// -	// Cleanup after quitting. -	// -	// Save snapshot for next time, if we made it through initialization - -	LL_INFOS() << "Disconnecting viewer!" << LL_ENDL; - -	// Dump our frame statistics - -	// Remember if we were flying -	gSavedSettings.setBOOL("FlyingAtExit", gAgent.getFlying() ); - -	// Un-minimize all windows so they don't get saved minimized -	if (gFloaterView) -	{ -		gFloaterView->restoreAll(); -	} - -	if (LLSelectMgr::instanceExists()) -	{ -		LLSelectMgr::getInstance()->deselectAll(); -	} - -	// save inventory if appropriate -    if (gInventory.isInventoryUsable() -        && gAgent.getID().notNull()) // Shouldn't be null at this stage -    { -        gInventory.cache(gInventory.getRootFolderID(), gAgent.getID()); -        if (gInventory.getLibraryRootFolderID().notNull() -            && gInventory.getLibraryOwnerID().notNull() -            && !mSecondInstance) // agent is unique, library isn't -        { -            gInventory.cache( -                gInventory.getLibraryRootFolderID(), -                gInventory.getLibraryOwnerID()); -        } -    } - -	saveNameCache(); -	if (LLExperienceCache::instanceExists()) -	{ -		// TODO: LLExperienceCache::cleanup() logic should be moved to -		// cleanupSingleton(). -		LLExperienceCache::instance().cleanup(); -	} - -	// close inventory interface, close all windows -	LLSidepanelInventory::cleanup(); - -	gAgentWearables.cleanup(); -	gAgentCamera.cleanup(); -	// Also writes cached agent settings to gSavedSettings -	gAgent.cleanup(); - -	// This is where we used to call gObjectList.destroy() and then delete gWorldp. -	// Now we just ask the LLWorld singleton to cleanly shut down. -	if(LLWorld::instanceExists()) -	{ -		LLWorld::getInstance()->resetClass(); -	} -	LLVOCache::deleteSingleton(); - -	// call all self-registered classes -	LLDestroyClassList::instance().fireCallbacks(); - -	cleanup_xfer_manager(); -	gDisconnected = true; - -	// Pass the connection state to LLUrlEntryParcel not to attempt -	// parcel info requests while disconnected. -	LLUrlEntryParcel::setDisconnected(gDisconnected); -} - -void LLAppViewer::forceErrorLLError() -{ -   	LL_ERRS() << "This is a deliberate llerror" << LL_ENDL; -} - -void LLAppViewer::forceErrorLLErrorMsg() -{ -    LLError::LLUserWarningMsg::show("Deliberate error"); -    // Note: under debug this will show a message as well, -    // but release won't show anything and will quit silently -    LL_ERRS() << "This is a deliberate llerror with a message" << LL_ENDL; -} - -void LLAppViewer::forceErrorBreakpoint() -{ -   	LL_WARNS() << "Forcing a deliberate breakpoint" << LL_ENDL; -#ifdef LL_WINDOWS -    DebugBreak(); -#else -    asm ("int $3"); -#endif -    return; -} - -void LLAppViewer::forceErrorBadMemoryAccess() -{ -   	LL_WARNS() << "Forcing a deliberate bad memory access" << LL_ENDL; -    S32* crash = NULL; -    *crash = 0xDEADBEEF; -    return; -} - -void LLAppViewer::forceErrorInfiniteLoop() -{ -   	LL_WARNS() << "Forcing a deliberate infinite loop" << LL_ENDL; -    // Loop is intentionally complicated to fool basic loop detection -    LLTimer timer_total; -    LLTimer timer_expiry; -    const S32 report_frequency = 10; -    timer_expiry.setTimerExpirySec(report_frequency); -    while(true) -    { -        if (timer_expiry.hasExpired()) -        { -            LL_INFOS() << "Infinite loop time : " << timer_total.getElapsedSeconds() << LL_ENDL; -            timer_expiry.setTimerExpirySec(report_frequency); -        } -    } -    return; -} - -void LLAppViewer::forceErrorSoftwareException() -{ -   	LL_WARNS() << "Forcing a deliberate exception" << LL_ENDL; -    LLTHROW(LLException("User selected Force Software Exception")); -} - -void LLAppViewer::forceErrorOSSpecificException() -{ -    // Virtual, MacOS only -    const std::string exception_text = "User selected Force OS Exception, Not implemented on this OS"; -    throw std::runtime_error(exception_text); -} - -void LLAppViewer::forceErrorDriverCrash() -{ -   	LL_WARNS() << "Forcing a deliberate driver crash" << LL_ENDL; -	glDeleteTextures(1, NULL); -} - -void LLAppViewer::forceErrorCoroutineCrash() -{ -    LL_WARNS() << "Forcing a crash in LLCoros" << LL_ENDL; -    LLCoros::instance().launch("LLAppViewer::crashyCoro", [] {throw LLException("A deliberate crash from LLCoros"); }); -} - -void LLAppViewer::forceErrorThreadCrash() -{ -    class LLCrashTestThread : public LLThread -    { -    public: - -        LLCrashTestThread() : LLThread("Crash logging test thread") -        { -        } - -        void run() -        { -            LL_ERRS() << "This is a deliberate llerror in thread" << LL_ENDL; -        } -    }; - -    LL_WARNS() << "This is a deliberate crash in a thread" << LL_ENDL; -    LLCrashTestThread *thread = new LLCrashTestThread(); -    thread->start(); -} - -void LLAppViewer::initMainloopTimeout(const std::string& state, F32 secs) -{ -	if(!mMainloopTimeout) -	{ -		mMainloopTimeout = new LLWatchdogTimeout(); -		resumeMainloopTimeout(state, secs); -	} -} - -void LLAppViewer::destroyMainloopTimeout() -{ -	if(mMainloopTimeout) -	{ -		delete mMainloopTimeout; -		mMainloopTimeout = NULL; -	} -} - -void LLAppViewer::resumeMainloopTimeout(const std::string& state, F32 secs) -{ -	if(mMainloopTimeout) -	{ -		if(secs < 0.0f) -		{ -			static LLCachedControl<F32> mainloop_timeout(gSavedSettings, "MainloopTimeoutDefault", 60); -			secs = mainloop_timeout; -		} - -		mMainloopTimeout->setTimeout(secs); -		mMainloopTimeout->start(state); -	} -} - -void LLAppViewer::pauseMainloopTimeout() -{ -	if(mMainloopTimeout) -	{ -		mMainloopTimeout->stop(); -	} -} - -void LLAppViewer::pingMainloopTimeout(const std::string& state, F32 secs) -{ -    LL_PROFILE_ZONE_SCOPED_CATEGORY_APP; - -	if(mMainloopTimeout) -	{ -		if(secs < 0.0f) -		{ -			static LLCachedControl<F32> mainloop_timeout(gSavedSettings, "MainloopTimeoutDefault", 60); -			secs = mainloop_timeout; -		} - -		mMainloopTimeout->setTimeout(secs); -		mMainloopTimeout->ping(state); -	} -} - -void LLAppViewer::handleLoginComplete() -{ -	gLoggedInTime.start(); -	initMainloopTimeout("Mainloop Init"); - -	// Store some data to DebugInfo in case of a freeze. -	gDebugInfo["ClientInfo"]["Name"] = LLVersionInfo::instance().getChannel(); - -	gDebugInfo["ClientInfo"]["MajorVersion"] = LLVersionInfo::instance().getMajor(); -	gDebugInfo["ClientInfo"]["MinorVersion"] = LLVersionInfo::instance().getMinor(); -	gDebugInfo["ClientInfo"]["PatchVersion"] = LLVersionInfo::instance().getPatch(); -	gDebugInfo["ClientInfo"]["BuildVersion"] = std::to_string(LLVersionInfo::instance().getBuild()); - -	LLParcel* parcel = LLViewerParcelMgr::getInstance()->getAgentParcel(); -	if ( parcel && parcel->getMusicURL()[0]) -	{ -		gDebugInfo["ParcelMusicURL"] = parcel->getMusicURL(); -	} -	if ( parcel && parcel->getMediaURL()[0]) -	{ -		gDebugInfo["ParcelMediaURL"] = parcel->getMediaURL(); -	} - -	gDebugInfo["SettingsFilename"] = gSavedSettings.getString("ClientSettingsFile"); -	gDebugInfo["CAFilename"] = gDirUtilp->getCAFile(); -	gDebugInfo["ViewerExePath"] = gDirUtilp->getExecutablePathAndName(); -	gDebugInfo["CurrentPath"] = gDirUtilp->getCurPath(); - -	if(gAgent.getRegion()) -	{ -		gDebugInfo["CurrentSimHost"] = gAgent.getRegion()->getSimHostName(); -		gDebugInfo["CurrentRegion"] = gAgent.getRegion()->getName(); -	} - -	if(LLAppViewer::instance()->mMainloopTimeout) -	{ -		gDebugInfo["MainloopTimeoutState"] = LLAppViewer::instance()->mMainloopTimeout->getState(); -	} - -	mOnLoginCompleted(); - -	writeDebugInfo(); - -	// we logged in successfully, so save settings on logout -	LL_INFOS() << "Login successful, per account settings will be saved on log out." << LL_ENDL; -	mSavePerAccountSettings=true; -} - -//virtual -void LLAppViewer::setMasterSystemAudioMute(bool mute) -{ -	gSavedSettings.setBOOL("MuteAudio", mute); -} - -//virtual -bool LLAppViewer::getMasterSystemAudioMute() -{ -	return gSavedSettings.getBOOL("MuteAudio"); -} - -//---------------------------------------------------------------------------- -// Metrics-related methods (static and otherwise) -//---------------------------------------------------------------------------- - -/** - * LLViewerAssetStats collects data on a per-region (as defined by the agent's - * location) so we need to tell it about region changes which become a kind of - * hidden variable/global state in the collectors.  For collectors not running - * on the main thread, we need to send a message to move the data over safely - * and cheaply (amortized over a run). - */ -void LLAppViewer::metricsUpdateRegion(U64 region_handle) -{ -	if (0 != region_handle) -	{ -		LLViewerAssetStatsFF::set_region(region_handle); -	} -} - -/** - * Attempts to start a multi-threaded metrics report to be sent back to - * the grid for consumption. - */ -void LLAppViewer::metricsSend(bool enable_reporting) -{ -	if (! gViewerAssetStats) -		return; - -	if (LLAppViewer::sTextureFetch) -	{ -		LLViewerRegion * regionp = gAgent.getRegion(); - -		if (enable_reporting && regionp) -		{ -			std::string	caps_url = regionp->getCapability("ViewerMetrics"); - -            LLSD sd = gViewerAssetStats->asLLSD(true); - -			// Send a report request into 'thread1' to get the rest of the data -			// and provide some additional parameters while here. -			LLAppViewer::sTextureFetch->commandSendMetrics(caps_url, -														   gAgentSessionID, -														   gAgentID, -														   sd); -		} -		else -		{ -			LLAppViewer::sTextureFetch->commandDataBreak(); -		} -	} - -	// Reset even if we can't report.  Rather than gather up a huge chunk of -	// data, we'll keep to our sampling interval and retain the data -	// resolution in time. -	gViewerAssetStats->restart(); -} - +/**
 + * @file llappviewer.cpp
 + * @brief The LLAppViewer class definitions
 + *
 + * $LicenseInfo:firstyear=2007&license=viewerlgpl$
 + * Second Life Viewer Source Code
 + * Copyright (C) 2012, 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 "llappviewer.h"
 +
 +// Viewer includes
 +#include "llversioninfo.h"
 +#include "llfeaturemanager.h"
 +#include "lluictrlfactory.h"
 +#include "lltexteditor.h"
 +#include "llenvironment.h"
 +#include "llerrorcontrol.h"
 +#include "lleventtimer.h"
 +#include "llfile.h"
 +#include "llviewertexturelist.h"
 +#include "llgroupmgr.h"
 +#include "llagent.h"
 +#include "llagentcamera.h"
 +#include "llagentlanguage.h"
 +#include "llagentui.h"
 +#include "llagentwearables.h"
 +#include "lldirpicker.h"
 +#include "llfloaterimcontainer.h"
 +#include "llimprocessing.h"
 +#include "llwindow.h"
 +#include "llviewerstats.h"
 +#include "llviewerstatsrecorder.h"
 +#include "llkeyconflict.h" // for legacy keybinding support, remove later
 +#include "llmarketplacefunctions.h"
 +#include "llmarketplacenotifications.h"
 +#include "llmd5.h"
 +#include "llmeshrepository.h"
 +#include "llpumpio.h"
 +#include "llmimetypes.h"
 +#include "llslurl.h"
 +#include "llstartup.h"
 +#include "llfocusmgr.h"
 +#include "llurlfloaterdispatchhandler.h"
 +#include "llviewerjoystick.h"
 +#include "llallocator.h"
 +#include "llcalc.h"
 +#include "llconversationlog.h"
 +#if LL_WINDOWS
 +#include "lldxhardware.h"
 +#endif
 +#include "lltexturestats.h"
 +#include "lltrace.h"
 +#include "lltracethreadrecorder.h"
 +#include "llviewerwindow.h"
 +#include "llviewerdisplay.h"
 +#include "llviewermedia.h"
 +#include "llviewerparcelaskplay.h"
 +#include "llviewerparcelmedia.h"
 +#include "llviewershadermgr.h"
 +#include "llviewermediafocus.h"
 +#include "llviewermessage.h"
 +#include "llviewerobjectlist.h"
 +#include "llworldmap.h"
 +#include "llmutelist.h"
 +#include "llviewerhelp.h"
 +#include "lluicolortable.h"
 +#include "llurldispatcher.h"
 +#include "llurlhistory.h"
 +#include "llrender.h"
 +#include "llteleporthistory.h"
 +#include "lltoast.h"
 +#include "llsdutil_math.h"
 +#include "lllocationhistory.h"
 +#include "llfasttimerview.h"
 +#include "llvector4a.h"
 +#include "llviewermenufile.h"
 +#include "llvoicechannel.h"
 +#include "llvoavatarself.h"
 +#include "llurlmatch.h"
 +#include "lltextutil.h"
 +#include "lllogininstance.h"
 +#include "llprogressview.h"
 +#include "llvocache.h"
 +#include "lldiskcache.h"
 +#include "llvopartgroup.h"
 +#include "llweb.h"
 +#include "llspellcheck.h"
 +#include "llscenemonitor.h"
 +#include "llavatarrenderinfoaccountant.h"
 +#include "lllocalbitmaps.h"
 +#include "llperfstats.h"
 +#include "llgltfmateriallist.h"
 +
 +// Linden library includes
 +#include "llavatarnamecache.h"
 +#include "lldiriterator.h"
 +#include "llexperiencecache.h"
 +#include "llimagej2c.h"
 +#include "llmemory.h"
 +#include "llprimitive.h"
 +#include "llurlaction.h"
 +#include "llurlentry.h"
 +#include "llvolumemgr.h"
 +#include "llxfermanager.h"
 +#include "llphysicsextensions.h"
 +
 +#include "llnotificationmanager.h"
 +#include "llnotifications.h"
 +#include "llnotificationsutil.h"
 +
 +#include "llleap.h"
 +#include "stringize.h"
 +#include "llcoros.h"
 +#include "llexception.h"
 +#if !LL_LINUX
 +#include "cef/dullahan_version.h"
 +#include "vlc/libvlc_version.h"
 +#endif // LL_LINUX
 +
 +#if LL_DARWIN
 +#include "llwindowmacosx.h"
 +#endif
 +
 +// Third party library includes
 +#include <boost/bind.hpp>
 +#include <boost/algorithm/string.hpp>
 +#include <boost/regex.hpp>
 +#include <boost/throw_exception.hpp>
 +
 +#if LL_WINDOWS
 +#   include <share.h> // For _SH_DENYWR in processMarkerFiles
 +#else
 +#   include <sys/file.h> // For processMarkerFiles
 +#endif
 +
 +#include "llapr.h"
 +#include <boost/lexical_cast.hpp>
 +
 +#include "llviewerinput.h"
 +#include "lllfsthread.h"
 +#include "llworkerthread.h"
 +#include "lltexturecache.h"
 +#include "lltexturefetch.h"
 +#include "llimageworker.h"
 +#include "llevents.h"
 +
 +// The files below handle dependencies from cleanup.
 +#include "llkeyframemotion.h"
 +#include "llworldmap.h"
 +#include "llhudmanager.h"
 +#include "lltoolmgr.h"
 +#include "llassetstorage.h"
 +#include "llpolymesh.h"
 +#include "llproxy.h"
 +#include "llaudioengine.h"
 +#include "llstreamingaudio.h"
 +#include "llviewermenu.h"
 +#include "llselectmgr.h"
 +#include "lltrans.h"
 +#include "lltransutil.h"
 +#include "lltracker.h"
 +#include "llviewerparcelmgr.h"
 +#include "llworldmapview.h"
 +#include "llpostprocess.h"
 +
 +#include "lldebugview.h"
 +#include "llconsole.h"
 +#include "llcontainerview.h"
 +#include "lltooltip.h"
 +
 +#include "llsdutil.h"
 +#include "llsdserialize.h"
 +
 +#include "llworld.h"
 +#include "llhudeffecttrail.h"
 +#include "llslurl.h"
 +#include "llurlregistry.h"
 +#include "llwatchdog.h"
 +
 +// Included so that constants/settings might be initialized
 +// in save_settings_to_globals()
 +#include "llbutton.h"
 +#include "llstatusbar.h"
 +#include "llsurface.h"
 +#include "llvosky.h"
 +#include "llvotree.h"
 +#include "llvoavatar.h"
 +#include "llfolderview.h"
 +#include "llagentpilot.h"
 +#include "llvovolume.h"
 +#include "llflexibleobject.h"
 +#include "llvosurfacepatch.h"
 +#include "llviewerfloaterreg.h"
 +#include "llcommandlineparser.h"
 +#include "llfloatermemleak.h"
 +#include "llfloaterreg.h"
 +#include "llfloatersimplesnapshot.h"
 +#include "llfloatersnapshot.h"
 +#include "llsidepanelinventory.h"
 +#include "llatmosphere.h"
 +
 +// includes for idle() idleShutdown()
 +#include "llviewercontrol.h"
 +#include "lleventnotifier.h"
 +#include "llcallbacklist.h"
 +#include "lldeferredsounds.h"
 +#include "pipeline.h"
 +#include "llgesturemgr.h"
 +#include "llsky.h"
 +#include "llvlmanager.h"
 +#include "llviewercamera.h"
 +#include "lldrawpoolbump.h"
 +#include "llvieweraudio.h"
 +#include "llimview.h"
 +#include "llviewerthrottle.h"
 +#include "llparcel.h"
 +#include "llavatariconctrl.h"
 +#include "llgroupiconctrl.h"
 +#include "llviewerassetstats.h"
 +#include "workqueue.h"
 +using namespace LL;
 +
 +// Include for security api initialization
 +#include "llsecapi.h"
 +#include "llmachineid.h"
 +#include "llcleanup.h"
 +
 +#include "llcoproceduremanager.h"
 +#include "llviewereventrecorder.h"
 +
 +// *FIX: These extern globals should be cleaned up.
 +// The globals either represent state/config/resource-storage of either
 +// this app, or another 'component' of the viewer. App globals should be
 +// moved into the app class, where as the other globals should be
 +// moved out of here.
 +// If a global symbol reference seems valid, it will be included
 +// via header files above.
 +
 +//----------------------------------------------------------------------------
 +// llviewernetwork.h
 +#include "llviewernetwork.h"
 +// define a self-registering event API object
 +#include "llappviewerlistener.h"
 +
 +#if LL_LINUX && LL_GTK
 +#include "glib.h"
 +#endif // (LL_LINUX) && LL_GTK
 +
 +#if LL_MSVC
 +// disable boost::lexical_cast warning
 +#pragma warning (disable:4702)
 +#endif
 +
 +static LLAppViewerListener sAppViewerListener(LLAppViewer::instance);
 +
 +////// Windows-specific includes to the bottom - nasty defines in these pollute the preprocessor
 +//
 +//----------------------------------------------------------------------------
 +// viewer.cpp - these are only used in viewer, should be easily moved.
 +
 +#if LL_DARWIN
 +extern void init_apple_menu(const char* product);
 +#endif // LL_DARWIN
 +
 +extern bool gRandomizeFramerate;
 +extern bool gPeriodicSlowFrame;
 +extern bool gDebugGL;
 +
 +#if LL_DARWIN
 +extern bool gHiDPISupport;
 +#endif
 +
 +////////////////////////////////////////////////////////////
 +// All from the last globals push...
 +
 +F32 gSimLastTime; // Used in LLAppViewer::init and send_viewer_stats()
 +F32 gSimFrames;
 +
 +bool gShowObjectUpdates = false;
 +bool gUseQuickTime = true;
 +
 +eLastExecEvent gLastExecEvent = LAST_EXEC_NORMAL;
 +S32 gLastExecDuration = -1; // (<0 indicates unknown)
 +
 +#if LL_WINDOWS
 +#   define LL_PLATFORM_KEY "win"
 +#elif LL_DARWIN
 +#   define LL_PLATFORM_KEY "mac"
 +#elif LL_LINUX
 +#   define LL_PLATFORM_KEY "lnx"
 +#else
 +#   error "Unknown Platform"
 +#endif
 +const char* gPlatform = LL_PLATFORM_KEY;
 +
 +LLSD gDebugInfo;
 +
 +U32 gFrameCount = 0;
 +U32 gForegroundFrameCount = 0; // number of frames that app window was in foreground
 +LLPumpIO* gServicePump = NULL;
 +
 +U64MicrosecondsImplicit gFrameTime = 0;
 +F32SecondsImplicit gFrameTimeSeconds = 0.f;
 +F32SecondsImplicit gFrameIntervalSeconds = 0.f;
 +F32 gFPSClamped = 10.f;                     // Pretend we start at target rate.
 +F32 gFrameDTClamped = 0.f;                  // Time between adjacent checks to network for packets
 +U64MicrosecondsImplicit gStartTime = 0; // gStartTime is "private", used only to calculate gFrameTimeSeconds
 +
 +LLTimer gRenderStartTime;
 +LLFrameTimer gForegroundTime;
 +LLFrameTimer gLoggedInTime;
 +LLTimer gLogoutTimer;
 +static const F32 LOGOUT_REQUEST_TIME = 6.f;  // this will be cut short by the LogoutReply msg.
 +F32 gLogoutMaxTime = LOGOUT_REQUEST_TIME;
 +
 +
 +S32 gPendingMetricsUploads = 0;
 +
 +
 +bool                gDisconnected = false;
 +
 +// used to restore texture state after a mode switch
 +LLFrameTimer    gRestoreGLTimer;
 +bool            gRestoreGL = false;
 +bool            gUseWireframe = false;
 +
 +LLMemoryInfo gSysMemory;
 +U64Bytes gMemoryAllocated(0); // updated in display_stats() in llviewerdisplay.cpp
 +
 +std::string gLastVersionChannel;
 +
 +LLVector3           gWindVec(3.0, 3.0, 0.0);
 +LLVector3           gRelativeWindVec(0.0, 0.0, 0.0);
 +
 +U32     gPacketsIn = 0;
 +
 +bool                gPrintMessagesThisFrame = false;
 +
 +bool gRandomizeFramerate = false;
 +bool gPeriodicSlowFrame = false;
 +
 +bool gCrashOnStartup = false;
 +bool gLLErrorActivated = false;
 +bool gLogoutInProgress = false;
 +
 +bool gSimulateMemLeak = false;
 +
 +// We don't want anyone, especially threads working on the graphics pipeline,
 +// to have to block due to this WorkQueue being full.
 +WorkQueue gMainloopWork("mainloop", 1024*1024);
 +
 +////////////////////////////////////////////////////////////
 +// Internal globals... that should be removed.
 +static std::string gArgs;
 +const int MAX_MARKER_LENGTH = 1024;
 +const std::string MARKER_FILE_NAME("SecondLife.exec_marker");
 +const std::string START_MARKER_FILE_NAME("SecondLife.start_marker");
 +const std::string ERROR_MARKER_FILE_NAME("SecondLife.error_marker");
 +const std::string LLERROR_MARKER_FILE_NAME("SecondLife.llerror_marker");
 +const std::string LOGOUT_MARKER_FILE_NAME("SecondLife.logout_marker");
 +static bool gDoDisconnect = false;
 +static std::string gLaunchFileOnQuit;
 +
 +// Used on Win32 for other apps to identify our window (eg, win_setup)
 +const char* const VIEWER_WINDOW_CLASSNAME = "Second Life";
 +
 +//----------------------------------------------------------------------------
 +
 +// List of entries from strings.xml to always replace
 +static std::set<std::string> default_trans_args;
 +void init_default_trans_args()
 +{
 +    default_trans_args.insert("SECOND_LIFE"); // World
 +    default_trans_args.insert("APP_NAME");
 +    default_trans_args.insert("CAPITALIZED_APP_NAME");
 +    default_trans_args.insert("SECOND_LIFE_GRID");
 +    default_trans_args.insert("SUPPORT_SITE");
 +    // This URL shows up in a surprising number of places in various skin
 +    // files. We really only want to have to maintain a single copy of it.
 +    default_trans_args.insert("create_account_url");
 +}
 +
 +struct SettingsFile : public LLInitParam::Block<SettingsFile>
 +{
 +    Mandatory<std::string>  name;
 +    Optional<std::string>   file_name;
 +    Optional<bool>          required,
 +                            persistent;
 +    Optional<std::string>   file_name_setting;
 +
 +    SettingsFile()
 +    :   name("name"),
 +        file_name("file_name"),
 +        required("required", false),
 +        persistent("persistent", true),
 +        file_name_setting("file_name_setting")
 +    {}
 +};
 +
 +struct SettingsGroup : public LLInitParam::Block<SettingsGroup>
 +{
 +    Mandatory<std::string>  name;
 +    Mandatory<S32>          path_index;
 +    Multiple<SettingsFile>  files;
 +
 +    SettingsGroup()
 +    :   name("name"),
 +        path_index("path_index"),
 +        files("file")
 +    {}
 +};
 +
 +struct SettingsFiles : public LLInitParam::Block<SettingsFiles>
 +{
 +    Multiple<SettingsGroup> groups;
 +
 +    SettingsFiles()
 +    : groups("group")
 +    {}
 +};
 +
 +static std::string gWindowTitle;
 +
 +//----------------------------------------------------------------------------
 +// Metrics logging control constants
 +//----------------------------------------------------------------------------
 +static const F32 METRICS_INTERVAL_DEFAULT = 600.0;
 +static const F32 METRICS_INTERVAL_QA = 30.0;
 +static F32 app_metrics_interval = METRICS_INTERVAL_DEFAULT;
 +static bool app_metrics_qa_mode = false;
 +
 +void idle_afk_check()
 +{
 +    // check idle timers
 +    F32 current_idle = gAwayTriggerTimer.getElapsedTimeF32();
 +    F32 afk_timeout  = gSavedSettings.getS32("AFKTimeout");
 +    if (afk_timeout && (current_idle > afk_timeout) && ! gAgent.getAFK())
 +    {
 +        LL_INFOS("IdleAway") << "Idle more than " << afk_timeout << " seconds: automatically changing to Away status" << LL_ENDL;
 +        gAgent.setAFK();
 +    }
 +}
 +
 +// A callback set in LLAppViewer::init()
 +static void ui_audio_callback(const LLUUID& uuid)
 +{
 +    if (gAudiop)
 +    {
 +        SoundData soundData(uuid, gAgent.getID(), 1.0f, LLAudioEngine::AUDIO_TYPE_UI);
 +        gAudiop->triggerSound(soundData);
 +    }
 +}
 +
 +// A callback set in LLAppViewer::init()
 +static void deferred_ui_audio_callback(const LLUUID& uuid)
 +{
 +    if (gAudiop)
 +    {
 +        SoundData soundData(uuid, gAgent.getID(), 1.0f, LLAudioEngine::AUDIO_TYPE_UI);
 +        LLDeferredSounds::instance().deferSound(soundData);
 +    }
 +}
 +
 +bool    create_text_segment_icon_from_url_match(LLUrlMatch* match,LLTextBase* base)
 +{
 +    if(!match || !base || base->getPlainText())
 +        return false;
 +
 +    LLUUID match_id = match->getID();
 +
 +    LLIconCtrl* icon;
 +
 +    if( match->getMenuName() == "menu_url_group.xml" // See LLUrlEntryGroup constructor
 +        || gAgent.isInGroup(match_id, true)) //This check seems unfiting, urls are either /agent or /group
 +    {
 +        LLGroupIconCtrl::Params icon_params;
 +        icon_params.group_id = match_id;
 +        icon_params.rect = LLRect(0, 16, 16, 0);
 +        icon_params.visible = true;
 +        icon = LLUICtrlFactory::instance().create<LLGroupIconCtrl>(icon_params);
 +    }
 +    else
 +    {
 +        LLAvatarIconCtrl::Params icon_params;
 +        icon_params.avatar_id = match_id;
 +        icon_params.rect = LLRect(0, 16, 16, 0);
 +        icon_params.visible = true;
 +        icon = LLUICtrlFactory::instance().create<LLAvatarIconCtrl>(icon_params);
 +    }
 +
 +    LLInlineViewSegment::Params params;
 +    params.force_newline = false;
 +    params.view = icon;
 +    params.left_pad = 4;
 +    params.right_pad = 4;
 +    params.top_pad = -2;
 +    params.bottom_pad = 2;
 +
 +    base->appendWidget(params," ",false);
 +
 +    return true;
 +}
 +
 +// Use these strictly for things that are constructed at startup,
 +// or for things that are performance critical.  JC
 +static void settings_to_globals()
 +{
 +    LLSurface::setTextureSize(gSavedSettings.getU32("RegionTextureSize"));
 +
 +#if LL_DARWIN
 +    LLRender::sGLCoreProfile = true;
 +#else
 +    LLRender::sGLCoreProfile = gSavedSettings.getBOOL("RenderGLContextCoreProfile");
 +#endif
 +    LLRender::sNsightDebugSupport = gSavedSettings.getBOOL("RenderNsightDebugSupport");
 +    LLImageGL::sGlobalUseAnisotropic    = gSavedSettings.getBOOL("RenderAnisotropic");
 +    LLImageGL::sCompressTextures        = gSavedSettings.getBOOL("RenderCompressTextures");
 +    LLVOVolume::sLODFactor              = llclamp(gSavedSettings.getF32("RenderVolumeLODFactor"), 0.01f, MAX_LOD_FACTOR);
 +    LLVOVolume::sDistanceFactor         = 1.f-LLVOVolume::sLODFactor * 0.1f;
 +    LLVolumeImplFlexible::sUpdateFactor = gSavedSettings.getF32("RenderFlexTimeFactor");
 +    LLVOTree::sTreeFactor               = gSavedSettings.getF32("RenderTreeLODFactor");
 +    LLVOAvatar::sLODFactor              = llclamp(gSavedSettings.getF32("RenderAvatarLODFactor"), 0.f, MAX_AVATAR_LOD_FACTOR);
 +    LLVOAvatar::sPhysicsLODFactor       = llclamp(gSavedSettings.getF32("RenderAvatarPhysicsLODFactor"), 0.f, MAX_AVATAR_LOD_FACTOR);
 +    LLVOAvatar::updateImpostorRendering(gSavedSettings.getU32("RenderAvatarMaxNonImpostors"));
 +    LLVOAvatar::sVisibleInFirstPerson   = gSavedSettings.getBOOL("FirstPersonAvatarVisible");
 +    // clamp auto-open time to some minimum usable value
 +    LLFolderView::sAutoOpenTime         = llmax(0.25f, gSavedSettings.getF32("FolderAutoOpenDelay"));
 +    LLSelectMgr::sRectSelectInclusive   = gSavedSettings.getBOOL("RectangleSelectInclusive");
 +    LLSelectMgr::sRenderHiddenSelections = gSavedSettings.getBOOL("RenderHiddenSelections");
 +    LLSelectMgr::sRenderLightRadius = gSavedSettings.getBOOL("RenderLightRadius");
 +
 +    gAgentPilot.setNumRuns(gSavedSettings.getS32("StatsNumRuns"));
 +    gAgentPilot.setQuitAfterRuns(gSavedSettings.getBOOL("StatsQuitAfterRuns"));
 +    gAgent.setHideGroupTitle(gSavedSettings.getBOOL("RenderHideGroupTitle"));
 +
 +    gDebugWindowProc = gSavedSettings.getBOOL("DebugWindowProc");
 +    gShowObjectUpdates = gSavedSettings.getBOOL("ShowObjectUpdates");
 +    LLWorldMapView::setScaleSetting(gSavedSettings.getF32("MapScale"));
 +
 +#if LL_DARWIN
 +    LLWindowMacOSX::sUseMultGL = gSavedSettings.getBOOL("RenderAppleUseMultGL");
 +    gHiDPISupport = gSavedSettings.getBOOL("RenderHiDPI");
 +#endif
 +}
 +
 +static void settings_modify()
 +{
 +    LLPipeline::sRenderTransparentWater = gSavedSettings.getBOOL("RenderTransparentWater");
 +    LLPipeline::sRenderDeferred = true; // false is deprecated
 +    LLRenderTarget::sUseFBO             = LLPipeline::sRenderDeferred;
 +    LLVOSurfacePatch::sLODFactor        = gSavedSettings.getF32("RenderTerrainLODFactor");
 +    LLVOSurfacePatch::sLODFactor *= LLVOSurfacePatch::sLODFactor; //square lod factor to get exponential range of [1,4]
 +    gDebugGL       = gDebugGLSession || gDebugSession;
 +    gDebugPipeline = gSavedSettings.getBOOL("RenderDebugPipeline");
 +}
 +
 +class LLFastTimerLogThread : public LLThread
 +{
 +public:
 +    std::string mFile;
 +
 +    LLFastTimerLogThread(std::string& test_name) : LLThread("fast timer log")
 +    {
 +        std::string file_name = test_name + std::string(".slp");
 +        mFile = gDirUtilp->getExpandedFilename(LL_PATH_LOGS, file_name);
 +    }
 +
 +    void run()
 +    {
 +        llofstream os(mFile.c_str());
 +
 +        while (!LLAppViewer::instance()->isQuitting())
 +        {
 +            LLTrace::BlockTimer::writeLog(os);
 +            os.flush();
 +            ms_sleep(32);
 +        }
 +
 +        os.close();
 +    }
 +};
 +
 +//virtual
 +bool LLAppViewer::initSLURLHandler()
 +{
 +    // does nothing unless subclassed
 +    return false;
 +}
 +
 +//virtual
 +bool LLAppViewer::sendURLToOtherInstance(const std::string& url)
 +{
 +    // does nothing unless subclassed
 +    return false;
 +}
 +
 +//----------------------------------------------------------------------------
 +// LLAppViewer definition
 +
 +// Static members.
 +// The single viewer app.
 +LLAppViewer* LLAppViewer::sInstance = NULL;
 +LLTextureCache* LLAppViewer::sTextureCache = NULL;
 +LLImageDecodeThread* LLAppViewer::sImageDecodeThread = NULL;
 +LLTextureFetch* LLAppViewer::sTextureFetch = NULL;
 +LLPurgeDiskCacheThread* LLAppViewer::sPurgeDiskCacheThread = NULL;
 +
 +std::string getRuntime()
 +{
 +    return llformat("%.4f", (F32)LLTimer::getElapsedSeconds().value());
 +}
 +
 +LLAppViewer::LLAppViewer()
 +:   mMarkerFile(),
 +    mLogoutMarkerFile(),
 +    mReportedCrash(false),
 +    mNumSessions(0),
 +    mGeneralThreadPool(nullptr),
 +    mPurgeCache(false),
 +    mPurgeCacheOnExit(false),
 +    mPurgeUserDataOnExit(false),
 +    mSecondInstance(false),
 +    mUpdaterNotFound(false),
 +    mSavedFinalSnapshot(false),
 +    mSavePerAccountSettings(false),     // don't save settings on logout unless login succeeded.
 +    mQuitRequested(false),
 +    mLogoutRequestSent(false),
 +    mLastAgentControlFlags(0),
 +    mLastAgentForceUpdate(0),
 +    mMainloopTimeout(NULL),
 +    mAgentRegionLastAlive(false),
 +    mRandomizeFramerate(LLCachedControl<bool>(gSavedSettings,"Randomize Framerate", false)),
 +    mPeriodicSlowFrame(LLCachedControl<bool>(gSavedSettings,"Periodic Slow Frame", false)),
 +    mFastTimerLogThread(NULL),
 +    mSettingsLocationList(NULL),
 +    mIsFirstRun(false)
 +{
 +    if(NULL != sInstance)
 +    {
 +        LL_ERRS() << "Oh no! An instance of LLAppViewer already exists! LLAppViewer is sort of like a singleton." << LL_ENDL;
 +    }
 +
 +    mDumpPath ="";
 +
 +    // Need to do this initialization before we do anything else, since anything
 +    // that touches files should really go through the lldir API
 +    gDirUtilp->initAppDirs("SecondLife");
 +    //
 +    // IMPORTANT! Do NOT put anything that will write
 +    // into the log files during normal startup until AFTER
 +    // we run the "program crashed last time" error handler below.
 +    //
 +    sInstance = this;
 +
 +    gLoggedInTime.stop();
 +
 +    processMarkerFiles();
 +    //
 +    // OK to write stuff to logs now, we've now crash reported if necessary
 +    //
 +
 +    LLLoginInstance::instance().setPlatformInfo(gPlatform, LLOSInfo::instance().getOSVersionString(), LLOSInfo::instance().getOSStringSimple());
 +
 +    // Under some circumstances we want to read the static_debug_info.log file
 +    // from the previous viewer run between this constructor call and the
 +    // init() call, which will overwrite the static_debug_info.log file for
 +    // THIS run. So setDebugFileNames() early.
 +#   ifdef LL_BUGSPLAT
 +    // MAINT-8917: don't create a dump directory just for the
 +    // static_debug_info.log file
 +    std::string logdir = gDirUtilp->getExpandedFilename(LL_PATH_LOGS, "");
 +#   else // ! LL_BUGSPLAT
 +    // write Google Breakpad minidump files to a per-run dump directory to avoid multiple viewer issues.
 +    std::string logdir = gDirUtilp->getExpandedFilename(LL_PATH_DUMP, "");
 +#   endif // ! LL_BUGSPLAT
 +    mDumpPath = logdir;
 +
 +    setDebugFileNames(logdir);
 +}
 +
 +LLAppViewer::~LLAppViewer()
 +{
 +    delete mSettingsLocationList;
 +
 +    destroyMainloopTimeout();
 +
 +    // If we got to this destructor somehow, the app didn't hang.
 +    removeMarkerFiles();
 +}
 +
 +class LLUITranslationBridge : public LLTranslationBridge
 +{
 +public:
 +    virtual std::string getString(const std::string &xml_desc)
 +    {
 +        return LLTrans::getString(xml_desc);
 +    }
 +};
 +
 +
 +bool LLAppViewer::init()
 +{
 +    setupErrorHandling(mSecondInstance);
 +
 +    //
 +    // Start of the application
 +    //
 +
 +    // initialize the LLSettingsType translation bridge.
 +    LLTranslationBridge::ptr_t trans = std::make_shared<LLUITranslationBridge>();
 +    LLSettingsType::initParamSingleton(trans);
 +
 +    // initialize SSE options
 +    LLVector4a::initClass();
 +
 +    //initialize particle index pool
 +    LLVOPartGroup::initClass();
 +
 +    // set skin search path to default, will be overridden later
 +    // this allows simple skinned file lookups to work
 +    gDirUtilp->setSkinFolder("default", "en");
 +
 +//  initLoggingAndGetLastDuration();
 +
 +    //
 +    // OK to write stuff to logs now, we've now crash reported if necessary
 +    //
 +    init_default_trans_args();
 +
 +    // inits from settings.xml and from strings.xml
 +    if (!initConfiguration())
 +        return false;
 +
 +    LL_INFOS("InitInfo") << "Configuration initialized." << LL_ENDL ;
 +
 +    //set the max heap size.
 +    initMaxHeapSize() ;
 +    LLCoros::instance().setStackSize(gSavedSettings.getS32("CoroutineStackSize"));
 +
 +
 +    // Although initLoggingAndGetLastDuration() is the right place to mess with
 +    // setFatalFunction(), we can't query gSavedSettings until after
 +    // initConfiguration().
 +    S32 rc(gSavedSettings.getS32("QAModeTermCode"));
 +    if (rc >= 0)
 +    {
 +        // QAModeTermCode set, terminate with that rc on LL_ERRS. Use
 +        // _exit() rather than exit() because normal cleanup depends too
 +        // much on successful startup!
 +        LLError::setFatalFunction([rc](const std::string&){ _exit(rc); });
 +    }
 +
 +    mAlloc.setProfilingEnabled(gSavedSettings.getBOOL("MemProfiling"));
 +
 +    // Initialize the non-LLCurl libcurl library.  Should be called
 +    // before consumers (LLTextureFetch).
 +    mAppCoreHttp.init();
 +
 +    LL_INFOS("InitInfo") << "LLCore::Http initialized." << LL_ENDL ;
 +
 +    LLMachineID::init();
 +
 +    {
 +        if (gSavedSettings.getBOOL("QAModeMetrics"))
 +        {
 +            app_metrics_qa_mode = true;
 +            app_metrics_interval = METRICS_INTERVAL_QA;
 +        }
 +        LLViewerAssetStatsFF::init();
 +    }
 +
 +    initThreads();
 +    LL_INFOS("InitInfo") << "Threads initialized." << LL_ENDL ;
 +
 +    // Initialize settings early so that the defaults for ignorable dialogs are
 +    // picked up and then correctly re-saved after launching the updater (STORM-1268).
 +    LLUI::settings_map_t settings_map;
 +    settings_map["config"] = &gSavedSettings;
 +    settings_map["ignores"] = &gWarningSettings;
 +    settings_map["floater"] = &gSavedSettings; // *TODO: New settings file
 +    settings_map["account"] = &gSavedPerAccountSettings;
 +
 +    LLUI::initParamSingleton(settings_map,
 +        LLUIImageList::getInstance(),
 +        ui_audio_callback,
 +        deferred_ui_audio_callback);
 +    LL_INFOS("InitInfo") << "UI initialized." << LL_ENDL ;
 +
 +    // NOW LLUI::getLanguage() should work. gDirUtilp must know the language
 +    // for this session ASAP so all the file-loading commands that follow,
 +    // that use findSkinnedFilenames(), will include the localized files.
 +    gDirUtilp->setSkinFolder(gDirUtilp->getSkinFolder(), LLUI::getLanguage());
 +
 +    // Setup LLTrans after LLUI::initClass has been called.
 +    initStrings();
 +
 +    // initialize LLWearableType translation bridge.
 +    // Will immediately use LLTranslationBridge to init LLWearableDictionary
 +    LLWearableType::initParamSingleton(trans);
 +
 +    // Setup notifications after LLUI::initClass() has been called.
 +    LLNotifications::instance();
 +    LL_INFOS("InitInfo") << "Notifications initialized." << LL_ENDL ;
 +
 +    //////////////////////////////////////////////////////////////////////////////
 +    //////////////////////////////////////////////////////////////////////////////
 +    //////////////////////////////////////////////////////////////////////////////
 +    //////////////////////////////////////////////////////////////////////////////
 +    // *FIX: The following code isn't grouped into functions yet.
 +
 +    //
 +    // Various introspection concerning the libs we're using - particularly
 +    // the libs involved in getting to a full login screen.
 +    //
 +    LL_INFOS("InitInfo") << "J2C Engine is: " << LLImageJ2C::getEngineInfo() << LL_ENDL;
 +    LL_INFOS("InitInfo") << "libcurl version is: " << LLCore::LLHttp::getCURLVersion() << LL_ENDL;
 +
 +    /////////////////////////////////////////////////
 +    // OS-specific login dialogs
 +    /////////////////////////////////////////////////
 +
 +    //test_cached_control();
 +
 +    // track number of times that app has run
 +    mNumSessions = gSavedSettings.getS32("NumSessions");
 +    mNumSessions++;
 +    gSavedSettings.setS32("NumSessions", mNumSessions);
 +
 +    // LLKeyboard relies on LLUI to know what some accelerator keys are called.
 +    LLKeyboard::setStringTranslatorFunc( LLTrans::getKeyboardString );
 +
 +    // Provide the text fields with callbacks for opening Urls
 +    LLUrlAction::setOpenURLCallback(boost::bind(&LLWeb::loadURL, _1, LLStringUtil::null, LLStringUtil::null));
 +    LLUrlAction::setOpenURLInternalCallback(boost::bind(&LLWeb::loadURLInternal, _1, LLStringUtil::null, LLStringUtil::null, false));
 +    LLUrlAction::setOpenURLExternalCallback(boost::bind(&LLWeb::loadURLExternal, _1, true, LLStringUtil::null));
 +    LLUrlAction::setExecuteSLURLCallback(&LLURLDispatcher::dispatchFromTextEditor);
 +
 +    // Let code in llui access the viewer help floater
 +    LLUI::getInstance()->mHelpImpl = LLViewerHelp::getInstance();
 +
 +    LL_INFOS("InitInfo") << "UI initialization is done." << LL_ENDL ;
 +
 +    // Load translations for tooltips
 +    LLFloater::initClass();
 +    LLUrlFloaterDispatchHandler::registerInDispatcher();
 +
 +    /////////////////////////////////////////////////
 +
 +    LLToolMgr::getInstance(); // Initialize tool manager if not already instantiated
 +
 +    LLViewerFloaterReg::registerFloaters();
 +
 +    /////////////////////////////////////////////////
 +    //
 +    // Load settings files
 +    //
 +    //
 +    LLGroupMgr::parseRoleActions("role_actions.xml");
 +
 +    LLAgent::parseTeleportMessages("teleport_strings.xml");
 +
 +    // load MIME type -> media impl mappings
 +    std::string mime_types_name;
 +#if LL_DARWIN
 +    mime_types_name = "mime_types_mac.xml";
 +#elif LL_LINUX
 +    mime_types_name = "mime_types_linux.xml";
 +#else
 +    mime_types_name = "mime_types.xml";
 +#endif
 +    LLMIMETypes::parseMIMETypes( mime_types_name );
 +
 +    // Copy settings to globals. *TODO: Remove or move to appropriage class initializers
 +    settings_to_globals();
 +    // Setup settings listeners
 +    settings_setup_listeners();
 +    // Modify settings based on system configuration and compile options
 +    settings_modify();
 +
 +    // Find partition serial number (Windows) or hardware serial (Mac)
 +    mSerialNumber = generateSerialNumber();
 +
 +    // do any necessary set-up for accepting incoming SLURLs from apps
 +    initSLURLHandler();
 +
 +    if(false == initHardwareTest())
 +    {
 +        // Early out from user choice.
 +        return false;
 +    }
 +    LL_INFOS("InitInfo") << "Hardware test initialization done." << LL_ENDL ;
 +
 +    // Prepare for out-of-memory situations, during which we will crash on
 +    // purpose and save a dump.
 +#if LL_WINDOWS && LL_RELEASE_FOR_DOWNLOAD && LL_USE_SMARTHEAP
 +    MemSetErrorHandler(first_mem_error_handler);
 +#endif // LL_WINDOWS && LL_RELEASE_FOR_DOWNLOAD && LL_USE_SMARTHEAP
 +
 +    // *Note: this is where gViewerStats used to be created.
 +
 +    if (!initCache())
 +    {
 +        LL_WARNS("InitInfo") << "Failed to init cache" << LL_ENDL;
 +        std::ostringstream msg;
 +        msg << LLTrans::getString("MBUnableToAccessFile");
 +        OSMessageBox(msg.str(),LLStringUtil::null,OSMB_OK);
 +        return 0;
 +    }
 +    LL_INFOS("InitInfo") << "Cache initialization is done." << LL_ENDL ;
 +
 +    // Initialize event recorder
 +    LLViewerEventRecorder::createInstance();
 +
 +    //
 +    // Initialize the window
 +    //
 +    gGLActive = true;
 +    initWindow();
 +    LL_INFOS("InitInfo") << "Window is initialized." << LL_ENDL ;
 +
 +    // writeSystemInfo can be called after window is initialized (gViewerWindow non-null)
 +    writeSystemInfo();
 +
 +    // initWindow also initializes the Feature List, so now we can initialize this global.
 +    LLCubeMap::sUseCubeMaps = LLFeatureManager::getInstance()->isFeatureAvailable("RenderCubeMap");
 +
 +    // call all self-registered classes
 +    LLInitClassList::instance().fireCallbacks();
 +
 +    LLFolderViewItem::initClass(); // SJB: Needs to happen after initWindow(), not sure why but related to fonts
 +
 +    gGLManager.getGLInfo(gDebugInfo);
 +    gGLManager.printGLInfoString();
 +
 +    // If we don't have the right GL requirements, exit.
 +    if (!gGLManager.mHasRequirements)
 +    {
 +        // already handled with a MBVideoDrvErr
 +        return 0;
 +    }
 +
 +    // Without SSE2 support we will crash almost immediately, warn here.
 +    if (!gSysCPU.hasSSE2())
 +    {
 +        // can't use an alert here since we're exiting and
 +        // all hell breaks lose.
 +        OSMessageBox(
 +            LLNotifications::instance().getGlobalString("UnsupportedCPUSSE2"),
 +            LLStringUtil::null,
 +            OSMB_OK);
 +        return 0;
 +    }
 +
 +    // alert the user if they are using unsupported hardware
 +    if(!gSavedSettings.getBOOL("AlertedUnsupportedHardware"))
 +    {
 +        bool unsupported = false;
 +        LLSD args;
 +        std::string minSpecs;
 +
 +        // get cpu data from xml
 +        std::stringstream minCPUString(LLNotifications::instance().getGlobalString("UnsupportedCPUAmount"));
 +        S32 minCPU = 0;
 +        minCPUString >> minCPU;
 +
 +        // get RAM data from XML
 +        std::stringstream minRAMString(LLNotifications::instance().getGlobalString("UnsupportedRAMAmount"));
 +        U64Bytes minRAM;
 +        minRAMString >> minRAM;
 +
 +        if(!LLFeatureManager::getInstance()->isGPUSupported() && LLFeatureManager::getInstance()->getGPUClass() != GPU_CLASS_UNKNOWN)
 +        {
 +            minSpecs += LLNotifications::instance().getGlobalString("UnsupportedGPU");
 +            minSpecs += "\n";
 +            unsupported = true;
 +        }
 +        if(gSysCPU.getMHz() < minCPU)
 +        {
 +            minSpecs += LLNotifications::instance().getGlobalString("UnsupportedCPU");
 +            minSpecs += "\n";
 +            unsupported = true;
 +        }
 +        if(gSysMemory.getPhysicalMemoryKB() < minRAM)
 +        {
 +            minSpecs += LLNotifications::instance().getGlobalString("UnsupportedRAM");
 +            minSpecs += "\n";
 +            unsupported = true;
 +        }
 +
 +        if (LLFeatureManager::getInstance()->getGPUClass() == GPU_CLASS_UNKNOWN)
 +        {
 +            LLNotificationsUtil::add("UnknownGPU");
 +        }
 +
 +        if(unsupported)
 +        {
 +            if(!gSavedSettings.controlExists("WarnUnsupportedHardware")
 +                || gSavedSettings.getBOOL("WarnUnsupportedHardware"))
 +            {
 +                args["MINSPECS"] = minSpecs;
 +                LLNotificationsUtil::add("UnsupportedHardware", args );
 +            }
 +
 +        }
 +    }
 +
 +#if LL_WINDOWS && ADDRESS_SIZE == 64
 +    if (gGLManager.mIsIntel)
 +    {
 +        // Check intel driver's version
 +        // Ex: "3.1.0 - Build 8.15.10.2559";
 +        std::string version = ll_safe_string((const char *)glGetString(GL_VERSION));
 +
 +        const boost::regex is_intel_string("[0-9].[0-9].[0-9] - Build [0-9]{1,2}.[0-9]{2}.[0-9]{2}.[0-9]{4}");
 +
 +        if (boost::regex_search(version, is_intel_string))
 +        {
 +            // Valid string, extract driver version
 +            std::size_t found = version.find("Build ");
 +            std::string driver = version.substr(found + 6);
 +            S32 v1, v2, v3, v4;
 +            S32 count = sscanf(driver.c_str(), "%d.%d.%d.%d", &v1, &v2, &v3, &v4);
 +            if (count > 0 && v1 <= 10)
 +            {
 +                LL_INFOS("AppInit") << "Detected obsolete intel driver: " << driver << LL_ENDL;
 +
 +                if (!gViewerWindow->getInitAlert().empty() // graphic initialization crashed on last run
 +                    || LLVersionInfo::getInstance()->getChannelAndVersion() != gLastRunVersion // viewer was updated
 +                    || mNumSessions % 20 == 0 //periodically remind user to update driver
 +                    )
 +                {
 +                    LLUIString details = LLNotifications::instance().getGlobalString("UnsupportedIntelDriver");
 +                    std::string gpu_name = ll_safe_string((const char *)glGetString(GL_RENDERER));
 +                    LL_INFOS("AppInit") << "Notifying user about obsolete intel driver for " << gpu_name << LL_ENDL;
 +                    details.setArg("[VERSION]", driver);
 +                    details.setArg("[GPUNAME]", gpu_name);
 +                    S32 button = OSMessageBox(details.getString(),
 +                        LLStringUtil::null,
 +                        OSMB_YESNO);
 +                    if (OSBTN_YES == button && gViewerWindow)
 +                    {
 +                        std::string url = LLWeb::escapeURL(LLTrans::getString("IntelDriverPage"));
 +                        if (gViewerWindow->getWindow())
 +                        {
 +                            gViewerWindow->getWindow()->spawnWebBrowser(url, false);
 +                        }
 +                    }
 +                }
 +            }
 +        }
 +    }
 +#endif
 +
 +    // Obsolete? mExpectedGLVersion is always zero
 +#if LL_WINDOWS
 +    if (gGLManager.mGLVersion < LLFeatureManager::getInstance()->getExpectedGLVersion())
 +    {
 +        std::string url;
 +        if (gGLManager.mIsIntel)
 +        {
 +            url = LLTrans::getString("IntelDriverPage");
 +        }
 +        else if (gGLManager.mIsNVIDIA)
 +        {
 +            url = LLTrans::getString("NvidiaDriverPage");
 +        }
 +        else if (gGLManager.mIsAMD)
 +        {
 +            url = LLTrans::getString("AMDDriverPage");
 +        }
 +
 +        if (!url.empty())
 +        {
 +            LLNotificationsUtil::add("OldGPUDriver", LLSD().with("URL", url));
 +        }
 +    }
 +#endif
 +
 +
 +    // save the graphics card
 +    gDebugInfo["GraphicsCard"] = LLFeatureManager::getInstance()->getGPUString();
 +
 +    // Save the current version to the prefs file
 +    gSavedSettings.setString("LastRunVersion",
 +                             LLVersionInfo::instance().getChannelAndVersion());
 +
 +    gSimLastTime = gRenderStartTime.getElapsedTimeF32();
 +    gSimFrames = (F32)gFrameCount;
 +
 +    if (gSavedSettings.getBOOL("JoystickEnabled"))
 +    {
 +        LLViewerJoystick::getInstance()->init(false);
 +    }
 +
 +    try {
 +        initializeSecHandler();
 +    }
 +    catch (LLProtectedDataException&)
 +    {
 +      LLNotificationsUtil::add("CorruptedProtectedDataStore");
 +    }
 +
 +    gGLActive = false;
 +
 +#if LL_RELEASE_FOR_DOWNLOAD
 +    // Skip updater if this is a non-interactive instance
 +    if (!gSavedSettings.getBOOL("CmdLineSkipUpdater") && !gNonInteractive)
 +    {
 +        LLProcess::Params updater;
 +        updater.desc = "updater process";
 +        // Because it's the updater, it MUST persist beyond the lifespan of the
 +        // viewer itself.
 +        updater.autokill = false;
 +        std::string updater_file;
 +#if LL_WINDOWS
 +        updater_file = "SLVersionChecker.exe";
 +        updater.executable = gDirUtilp->getExpandedFilename(LL_PATH_EXECUTABLE, updater_file);
 +#elif LL_DARWIN
 +        updater_file = "SLVersionChecker";
 +        updater.executable = gDirUtilp->add(gDirUtilp->getAppRODataDir(), "updater", updater_file);
 +#else
 +        updater_file = "SLVersionChecker";
 +        updater.executable = gDirUtilp->getExpandedFilename(LL_PATH_EXECUTABLE, updater_file);
 +#endif
 +        // add LEAP mode command-line argument to whichever of these we selected
 +        updater.args.add("leap");
 +        // UpdaterServiceSettings
 +        if (gSavedSettings.getBOOL("FirstLoginThisInstall"))
 +        {
 +            // Befor first login, treat this as 'manual' updates,
 +            // updater won't install anything, but required updates
 +            updater.args.add("0");
 +        }
 +        else
 +        {
 +            updater.args.add(stringize(gSavedSettings.getU32("UpdaterServiceSetting")));
 +        }
 +        // channel
 +        updater.args.add(LLVersionInfo::instance().getChannel());
 +        // testok
 +        updater.args.add(stringize(gSavedSettings.getBOOL("UpdaterWillingToTest")));
 +        // ForceAddressSize
 +        updater.args.add(stringize(gSavedSettings.getU32("ForceAddressSize")));
 +
 +        try
 +        {
 +            // Run the updater. An exception from launching the updater should bother us.
 +            LLLeap::create(updater, true);
 +            mUpdaterNotFound = false;
 +        }
 +        catch (...)
 +        {
 +            LLUIString details = LLNotifications::instance().getGlobalString("LLLeapUpdaterFailure");
 +            details.setArg("[UPDATER_APP]", updater_file);
 +            OSMessageBox(
 +                details.getString(),
 +                LLStringUtil::null,
 +                OSMB_OK);
 +            mUpdaterNotFound = true;
 +        }
 +    }
 +    else
 +    {
 +        LL_WARNS("InitInfo") << "Skipping updater check." << LL_ENDL;
 +    }
 +#endif //LL_RELEASE_FOR_DOWNLOAD
 +
 +    {
 +        // Iterate over --leap command-line options. But this is a bit tricky: if
 +        // there's only one, it won't be an array at all.
 +        LLSD LeapCommand(gSavedSettings.getLLSD("LeapCommand"));
 +        LL_DEBUGS("InitInfo") << "LeapCommand: " << LeapCommand << LL_ENDL;
 +        if (LeapCommand.isDefined() && !LeapCommand.isArray())
 +        {
 +            // If LeapCommand is actually a scalar value, make an array of it.
 +            // Have to do it in two steps because LeapCommand.append(LeapCommand)
 +            // trashes content! :-P
 +            LLSD item(LeapCommand);
 +            LeapCommand.append(item);
 +        }
 +        for (const auto& leap : llsd::inArray(LeapCommand))
 +        {
 +            LL_INFOS("InitInfo") << "processing --leap \"" << leap << '"' << LL_ENDL;
 +            // We don't have any better description of this plugin than the
 +            // user-specified command line. Passing "" causes LLLeap to derive a
 +            // description from the command line itself.
 +            // Suppress LLLeap::Error exception: trust LLLeap's own logging. We
 +            // don't consider any one --leap command mission-critical, so if one
 +            // fails, log it, shrug and carry on.
 +            LLLeap::create("", leap, false); // exception=false
 +        }
 +    }
 +
 +    if (gSavedSettings.getBOOL("QAMode") && gSavedSettings.getS32("QAModeEventHostPort") > 0)
 +    {
 +        LL_WARNS("InitInfo") << "QAModeEventHostPort DEPRECATED: "
 +                             << "lleventhost no longer supported as a dynamic library"
 +                             << LL_ENDL;
 +    }
 +
 +    LLTextUtil::TextHelpers::iconCallbackCreationFunction = create_text_segment_icon_from_url_match;
 +
 +    //EXT-7013 - On windows for some locale (Japanese) standard
 +    //datetime formatting functions didn't support some parameters such as "weekday".
 +    //Names for days and months localized in xml are also useful for Polish locale(STORM-107).
 +    std::string language = gSavedSettings.getString("Language");
 +    if(language == "ja" || language == "pl")
 +    {
 +        LLStringOps::setupWeekDaysNames(LLTrans::getString("dateTimeWeekdaysNames"));
 +        LLStringOps::setupWeekDaysShortNames(LLTrans::getString("dateTimeWeekdaysShortNames"));
 +        LLStringOps::setupMonthNames(LLTrans::getString("dateTimeMonthNames"));
 +        LLStringOps::setupMonthShortNames(LLTrans::getString("dateTimeMonthShortNames"));
 +        LLStringOps::setupDayFormat(LLTrans::getString("dateTimeDayFormat"));
 +
 +        LLStringOps::sAM = LLTrans::getString("dateTimeAM");
 +        LLStringOps::sPM = LLTrans::getString("dateTimePM");
 +    }
 +
 +    LLAgentLanguage::init();
 +
 +    /// Tell the Coprocedure manager how to discover and store the pool sizes
 +    // what I wanted
 +    LLCoprocedureManager::getInstance()->setPropertyMethods(
 +        boost::bind(&LLControlGroup::getU32, boost::ref(gSavedSettings), _1),
 +        boost::bind(&LLControlGroup::declareU32, boost::ref(gSavedSettings), _1, _2, _3, LLControlVariable::PERSIST_ALWAYS));
 +
 +    // TODO: consider moving proxy initialization here or LLCopocedureManager after proxy initialization, may be implement
 +    // some other protection to make sure we don't use network before initializng proxy
 +
 +    /*----------------------------------------------------------------------*/
 +    // nat 2016-06-29 moved the following here from the former mainLoop().
 +    mMainloopTimeout = new LLWatchdogTimeout();
 +
 +    // Create IO Pump to use for HTTP Requests.
 +    gServicePump = new LLPumpIO(gAPRPoolp);
 +
 +    // Note: this is where gLocalSpeakerMgr and gActiveSpeakerMgr used to be instantiated.
 +
 +    LLVoiceChannel::initClass();
 +    LLVoiceClient::initParamSingleton(gServicePump);
 +    LLVoiceChannel::setCurrentVoiceChannelChangedCallback(boost::bind(&LLFloaterIMContainer::onCurrentChannelChanged, _1), true);
 +
 +    joystick = LLViewerJoystick::getInstance();
 +    joystick->setNeedsReset(true);
 +    /*----------------------------------------------------------------------*/
 +    // Load User's bindings
 +    loadKeyBindings();
 +
 +    //LLSimpleton creations
 +    LLEnvironment::createInstance();
 +    LLWorld::createInstance();
 +    LLSelectMgr::createInstance();
 +    LLViewerCamera::createInstance();
 +
 +#if LL_WINDOWS
 +    if (!mSecondInstance)
 +    {
 +        gDirUtilp->deleteDirAndContents(gDirUtilp->getDumpLogsDirPath());
 +    }
 +#endif
 +
 +    return true;
 +}
 +
 +void LLAppViewer::initMaxHeapSize()
 +{
 +    //set the max heap size.
 +    //here is some info regarding to the max heap size:
 +    //------------------------------------------------------------------------------------------
 +    // OS       | setting | SL address bits | max manageable memory space | max heap size
 +    // Win 32   | default | 32-bit          | 2GB                         | < 1.7GB
 +    // Win 32   | /3G     | 32-bit          | 3GB                         | < 1.7GB or 2.7GB
 +    //Linux 32  | default | 32-bit          | 3GB                         | < 2.7GB
 +    //Linux 32  |HUGEMEM  | 32-bit          | 4GB                         | < 3.7GB
 +    //64-bit OS |default  | 32-bit          | 4GB                         | < 3.7GB
 +    //64-bit OS |default  | 64-bit          | N/A (> 4GB)                 | N/A (> 4GB)
 +    //------------------------------------------------------------------------------------------
 +    //currently SL is built under 32-bit setting, we set its max heap size no more than 1.6 GB.
 +
 + #ifndef LL_X86_64
 +    F32Gigabytes max_heap_size_gb = (F32Gigabytes)gSavedSettings.getF32("MaxHeapSize") ;
 +#else
 +    F32Gigabytes max_heap_size_gb = (F32Gigabytes)gSavedSettings.getF32("MaxHeapSize64");
 +#endif
 +
 +    LLMemory::initMaxHeapSizeGB(max_heap_size_gb);
 +}
 +
 +
 +// externally visible timers
 +LLTrace::BlockTimerStatHandle FTM_FRAME("Frame");
 +
 +bool LLAppViewer::frame()
 +{
 +    bool ret = false;
 +
 +    if (gSimulateMemLeak)
 +    {
 +        try
 +        {
 +            ret = doFrame();
 +        }
 +        catch (const LLContinueError&)
 +        {
 +            LOG_UNHANDLED_EXCEPTION("");
 +        }
 +        catch (std::bad_alloc&)
 +        {
 +            LLMemory::logMemoryInfo(true);
 +            LLFloaterMemLeak* mem_leak_instance = LLFloaterReg::findTypedInstance<LLFloaterMemLeak>("mem_leaking");
 +            if (mem_leak_instance)
 +            {
 +                mem_leak_instance->stop();
 +            }
 +            LL_WARNS() << "Bad memory allocation in LLAppViewer::frame()!" << LL_ENDL;
 +        }
 +    }
 +    else
 +    {
 +        try
 +        {
 +            ret = doFrame();
 +        }
 +        catch (const LLContinueError&)
 +        {
 +            LOG_UNHANDLED_EXCEPTION("");
 +        }
 +    }
 +
 +    return ret;
 +}
 +
 +bool LLAppViewer::doFrame()
 +{
 +    LL_RECORD_BLOCK_TIME(FTM_FRAME);
 +    {
 +    // and now adjust the visuals from previous frame.
 +    if(LLPerfStats::tunables.userAutoTuneEnabled && LLPerfStats::tunables.tuningFlag != LLPerfStats::Tunables::Nothing)
 +    {
 +        LLPerfStats::tunables.applyUpdates();
 +    }
 +
 +    LLPerfStats::RecordSceneTime T (LLPerfStats::StatType_t::RENDER_FRAME);
 +    if (!LLWorld::instanceExists())
 +    {
 +        LLWorld::createInstance();
 +    }
 +
 +    LLEventPump& mainloop(LLEventPumps::instance().obtain("mainloop"));
 +    LLSD newFrame;
 +    {
 +        LLPerfStats::RecordSceneTime T (LLPerfStats::StatType_t::RENDER_IDLE); // perf stats
 +        {
 +            LL_PROFILE_ZONE_NAMED_CATEGORY_APP("df LLTrace");
 +            if (LLFloaterReg::instanceVisible("block_timers"))
 +            {
 +                LLTrace::BlockTimer::processTimes();
 +            }
 +
 +            LLTrace::get_frame_recording().nextPeriod();
 +            LLTrace::BlockTimer::logStats();
 +        }
 +
 +        LLTrace::get_thread_recorder()->pullFromChildren();
 +
 +        //clear call stack records
 +        LL_CLEAR_CALLSTACKS();
 +    }
 +    {
 +        {
 +            LLPerfStats::RecordSceneTime T(LLPerfStats::StatType_t::RENDER_IDLE); // ensure we have the entire top scope of frame covered (input event and coro)
 +            LL_PROFILE_ZONE_NAMED_CATEGORY_APP("df processMiscNativeEvents")
 +            pingMainloopTimeout("Main:MiscNativeWindowEvents");
 +
 +            if (gViewerWindow)
 +            {
 +                LL_PROFILE_ZONE_NAMED_CATEGORY_APP("System Messages");
 +                gViewerWindow->getWindow()->processMiscNativeEvents();
 +            }
 +
 +            {
 +                LL_PROFILE_ZONE_NAMED_CATEGORY_APP("df gatherInput")
 +                pingMainloopTimeout("Main:GatherInput");
 +            }
 +
 +            if (gViewerWindow)
 +            {
 +                LL_PROFILE_ZONE_NAMED_CATEGORY_APP("System Messages");
 +                if (!restoreErrorTrap())
 +                {
 +                    LL_WARNS() << " Someone took over my signal/exception handler (post messagehandling)!" << LL_ENDL;
 +                }
 +
 +                gViewerWindow->getWindow()->gatherInput();
 +            }
 +
 +            //memory leaking simulation
 +            if (gSimulateMemLeak)
 +            {
 +                LLFloaterMemLeak* mem_leak_instance =
 +                    LLFloaterReg::findTypedInstance<LLFloaterMemLeak>("mem_leaking");
 +                if (mem_leak_instance)
 +                {
 +                    mem_leak_instance->idle();
 +                }
 +            }
 +
 +            {
 +                LL_PROFILE_ZONE_NAMED_CATEGORY_APP("df mainloop")
 +                // canonical per-frame event
 +                mainloop.post(newFrame);
 +            }
 +
 +            {
 +                LL_PROFILE_ZONE_NAMED_CATEGORY_APP("df suspend")
 +                // give listeners a chance to run
 +                llcoro::suspend();
 +                // if one of our coroutines threw an uncaught exception, rethrow it now
 +                LLCoros::instance().rethrow();
 +            }
 +        }
 +
 +        if (!LLApp::isExiting())
 +        {
 +            LL_PROFILE_ZONE_NAMED_CATEGORY_APP( "df JoystickKeyboard" )
 +            pingMainloopTimeout("Main:JoystickKeyboard");
 +
 +            // Scan keyboard for movement keys.  Command keys and typing
 +            // are handled by windows callbacks.  Don't do this until we're
 +            // done initializing.  JC
 +            if (gViewerWindow
 +                && (gHeadlessClient || gViewerWindow->getWindow()->getVisible())
 +                && gViewerWindow->getActive()
 +                && !gViewerWindow->getWindow()->getMinimized()
 +                && LLStartUp::getStartupState() == STATE_STARTED
 +                && (gHeadlessClient || !gViewerWindow->getShowProgress())
 +                && !gFocusMgr.focusLocked())
 +            {
 +                LLPerfStats::RecordSceneTime T (LLPerfStats::StatType_t::RENDER_IDLE);
 +                joystick->scanJoystick();
 +                gKeyboard->scanKeyboard();
 +                gViewerInput.scanMouse();
 +            }
 +
 +            // Update state based on messages, user input, object idle.
 +            {
 +                {
 +                    LL_PROFILE_ZONE_NAMED_CATEGORY_APP( "df pauseMainloopTimeout" )
 +                    pauseMainloopTimeout(); // *TODO: Remove. Messages shouldn't be stalling for 20+ seconds!
 +                }
 +
 +                {
 +                    LLPerfStats::RecordSceneTime T (LLPerfStats::StatType_t::RENDER_IDLE);
 +                    LL_PROFILE_ZONE_NAMED_CATEGORY_APP("df idle");
 +                    idle();
 +                }
 +
 +                {
 +                    LL_PROFILE_ZONE_NAMED_CATEGORY_APP( "df resumeMainloopTimeout" )
 +                    resumeMainloopTimeout();
 +                }
 +            }
 +
 +            if (gDoDisconnect && (LLStartUp::getStartupState() == STATE_STARTED))
 +            {
 +                pauseMainloopTimeout();
 +                saveFinalSnapshot();
 +
 +                if (LLVoiceClient::instanceExists())
 +                {
 +                    LLVoiceClient::getInstance()->terminate();
 +                }
 +
 +                disconnectViewer();
 +                resumeMainloopTimeout();
 +            }
 +
 +            // Render scene.
 +            // *TODO: Should we run display() even during gHeadlessClient?  DK 2011-02-18
 +            if (!LLApp::isExiting() && !gHeadlessClient && gViewerWindow)
 +            {
 +                LL_PROFILE_ZONE_NAMED_CATEGORY_APP("df Display");
 +                pingMainloopTimeout("Main:Display");
 +                gGLActive = true;
 +
 +                display();
 +
 +                {
 +                    LLPerfStats::RecordSceneTime T(LLPerfStats::StatType_t::RENDER_IDLE);
 +                    LL_PROFILE_ZONE_NAMED_CATEGORY_APP("df Snapshot");
 +                    pingMainloopTimeout("Main:Snapshot");
 +                    gPipeline.mReflectionMapManager.update();
 +                    LLFloaterSnapshot::update(); // take snapshots
 +                    LLFloaterSimpleSnapshot::update();
 +                    gGLActive = false;
 +                }
 +
 +                if (LLViewerStatsRecorder::instanceExists())
 +                {
 +                    LLViewerStatsRecorder::instance().idle();
 +                }
 +            }
 +        }
 +
 +        {
 +            LL_PROFILE_ZONE_NAMED_CATEGORY_APP( "df pauseMainloopTimeout" )
 +        pingMainloopTimeout("Main:Sleep");
 +
 +        pauseMainloopTimeout();
 +        }
 +
 +        // Sleep and run background threads
 +        {
 +            //LL_RECORD_BLOCK_TIME(SLEEP2);
 +            LL_PROFILE_ZONE_WARN( "Sleep2" )
 +
 +            // yield some time to the os based on command line option
 +            static LLCachedControl<S32> yield_time(gSavedSettings, "YieldTime", -1);
 +            if(yield_time >= 0)
 +            {
 +                LL_PROFILE_ZONE_NAMED_CATEGORY_APP("Yield");
 +                LL_PROFILE_ZONE_NUM( yield_time )
 +                ms_sleep(yield_time);
 +            }
 +
 +            if (gNonInteractive)
 +            {
 +                S32 non_interactive_ms_sleep_time = 100;
 +                LLAppViewer::getTextureCache()->pause();
 +                ms_sleep(non_interactive_ms_sleep_time);
 +            }
 +
 +            // yield cooperatively when not running as foreground window
 +            // and when not quiting (causes trouble at mac's cleanup stage)
 +            if (!LLApp::isExiting()
 +                && ((gViewerWindow && !gViewerWindow->getWindow()->getVisible())
 +                    || !gFocusMgr.getAppHasFocus()))
 +            {
 +                // Sleep if we're not rendering, or the window is minimized.
 +                static LLCachedControl<S32> s_background_yield_time(gSavedSettings, "BackgroundYieldTime", 40);
 +                S32 milliseconds_to_sleep = llclamp((S32)s_background_yield_time, 0, 1000);
 +                // don't sleep when BackgroundYieldTime set to 0, since this will still yield to other threads
 +                // of equal priority on Windows
 +                if (milliseconds_to_sleep > 0)
 +                {
 +                    LLPerfStats::RecordSceneTime T ( LLPerfStats::StatType_t::RENDER_SLEEP );
 +                    ms_sleep(milliseconds_to_sleep);
 +                    // also pause worker threads during this wait period
 +                    LLAppViewer::getTextureCache()->pause();
 +                }
 +            }
 +
 +            if (mRandomizeFramerate)
 +            {
 +                ms_sleep(rand() % 200);
 +            }
 +
 +            if (mPeriodicSlowFrame
 +                && (gFrameCount % 10 == 0))
 +            {
 +                LL_INFOS() << "Periodic slow frame - sleeping 500 ms" << LL_ENDL;
 +                ms_sleep(500);
 +            }
 +
 +            S32 total_work_pending = 0;
 +            S32 total_io_pending = 0;
 +            {
 +                S32 work_pending = 0;
 +                S32 io_pending = 0;
 +                F32 max_time = llmin(gFrameIntervalSeconds.value() *10.f, 1.f);
 +
 +                work_pending += updateTextureThreads(max_time);
 +
 +                {
 +                    LL_PROFILE_ZONE_NAMED_CATEGORY_APP("LFS Thread");
 +                    io_pending += LLLFSThread::updateClass(1);
 +                }
 +
 +                if (io_pending > 1000)
 +                {
 +                    ms_sleep(llmin(io_pending/100,100)); // give the lfs some time to catch up
 +                }
 +
 +                total_work_pending += work_pending ;
 +                total_io_pending += io_pending ;
 +
 +            }
 +
 +            {
 +                LL_PROFILE_ZONE_NAMED_CATEGORY_APP( "df gMeshRepo" )
 +            gMeshRepo.update() ;
 +            }
 +
 +            if(!total_work_pending) //pause texture fetching threads if nothing to process.
 +            {
 +                LL_PROFILE_ZONE_NAMED_CATEGORY_APP( "df getTextureCache" )
 +                LLAppViewer::getTextureCache()->pause();
 +                LLAppViewer::getTextureFetch()->pause();
 +            }
 +            if(!total_io_pending) //pause file threads if nothing to process.
 +            {
 +                LL_PROFILE_ZONE_NAMED_CATEGORY_APP( "df LLVFSThread" )
 +                LLLFSThread::sLocal->pause();
 +            }
 +
 +            {
 +                LL_PROFILE_ZONE_NAMED_CATEGORY_APP( "df resumeMainloopTimeout" )
 +                resumeMainloopTimeout();
 +            }
 +            pingMainloopTimeout("Main:End");
 +        }
 +    }
 +
 +    if (LLApp::isExiting())
 +    {
 +        // Save snapshot for next time, if we made it through initialization
 +        if (STATE_STARTED == LLStartUp::getStartupState())
 +        {
 +            saveFinalSnapshot();
 +        }
 +
 +        if (LLVoiceClient::instanceExists())
 +        {
 +            LLVoiceClient::getInstance()->terminate();
 +        }
 +
 +        delete gServicePump;
 +        gServicePump = NULL;
 +
 +        destroyMainloopTimeout();
 +
 +        LL_INFOS() << "Exiting main_loop" << LL_ENDL;
 +    }
 +    }LLPerfStats::StatsRecorder::endFrame();
 +    LL_PROFILER_FRAME_END
 +
 +    return ! LLApp::isRunning();
 +}
 +
 +S32 LLAppViewer::updateTextureThreads(F32 max_time)
 +{
 +    S32 work_pending = 0;
 +    {
 +        LL_PROFILE_ZONE_NAMED_CATEGORY_APP("Texture Cache");
 +        work_pending += LLAppViewer::getTextureCache()->update(max_time); // unpauses the texture cache thread
 +    }
 +    {
 +        LL_PROFILE_ZONE_NAMED_CATEGORY_APP("Image Decode");
 +        work_pending += LLAppViewer::getImageDecodeThread()->update(max_time); // unpauses the image thread
 +    }
 +    {
 +        LL_PROFILE_ZONE_NAMED_CATEGORY_APP("Image Fetch");
 +        work_pending += LLAppViewer::getTextureFetch()->update(max_time); // unpauses the texture fetch thread
 +    }
 +    return work_pending;
 +}
 +
 +void LLAppViewer::flushLFSIO()
 +{
 +    S32 pending = LLLFSThread::updateClass(0);
 +    if (pending > 0)
 +    {
 +        LL_INFOS() << "Waiting for pending IO to finish: " << pending << LL_ENDL;
 +        while (1)
 +        {
 +            pending = LLLFSThread::updateClass(0);
 +            if (!pending)
 +            {
 +                break;
 +            }
 +            ms_sleep(100);
 +        }
 +    }
 +}
 +
 +bool LLAppViewer::cleanup()
 +{
 +    LLAtmosphere::cleanupClass();
 +
 +    //ditch LLVOAvatarSelf instance
 +    gAgentAvatarp = NULL;
 +
 +    LLNotifications::instance().clear();
 +
 +    // workaround for DEV-35406 crash on shutdown
 +    LLEventPumps::instance().reset(true);
 +
 +    //dump scene loading monitor results
 +    if (LLSceneMonitor::instanceExists())
 +    {
 +        if (!isSecondInstance())
 +        {
 +            std::string dump_path = gDirUtilp->getExpandedFilename(LL_PATH_LOGS, "scene_monitor_results.csv");
 +            LLSceneMonitor::instance().dumpToFile(dump_path);
 +        }
 +        LLSceneMonitor::deleteSingleton();
 +    }
 +
 +    // There used to be an 'if (LLFastTimerView::sAnalyzePerformance)' block
 +    // here, completely redundant with the one that occurs later in this same
 +    // function. Presumably the duplication was due to an automated merge gone
 +    // bad. Not knowing which instance to prefer, we chose to retain the later
 +    // one because it happens just after mFastTimerLogThread is deleted. This
 +    // comment is in case we guessed wrong, so we can move it here instead.
 +
 +#if LL_LINUX
 +    // remove any old breakpad minidump files from the log directory
 +    if (! isError())
 +    {
 +        std::string logdir = gDirUtilp->getExpandedFilename(LL_PATH_LOGS, "");
 +        gDirUtilp->deleteFilesInDir(logdir, "*-*-*-*-*.dmp");
 +    }
 +#endif
 +
 +    // Kill off LLLeap objects. We can find them all because LLLeap is derived
 +    // from LLInstanceTracker.
 +    LLLeap::instance_snapshot().deleteAll();
 +
 +    //flag all elements as needing to be destroyed immediately
 +    // to ensure shutdown order
 +    LLMortician::setZealous(true);
 +
 +    // Give any remaining SLPlugin instances a chance to exit cleanly.
 +    LLPluginProcessParent::shutdown();
 +
 +    disconnectViewer();
 +    LLViewerCamera::deleteSingleton();
 +
 +    LL_INFOS() << "Viewer disconnected" << LL_ENDL;
 +
 +    if (gKeyboard)
 +    {
 +        gKeyboard->resetKeys();
 +    }
 +
 +    display_cleanup();
 +
 +    release_start_screen(); // just in case
 +
 +    LLError::logToFixedBuffer(NULL); // stop the fixed buffer recorder
 +
 +    LL_INFOS() << "Cleaning Up" << LL_ENDL;
 +
 +    // shut down mesh streamer
 +    gMeshRepo.shutdown();
 +
 +    // shut down Havok
 +    LLPhysicsExtensions::quitSystem();
 +
 +    // Must clean up texture references before viewer window is destroyed.
 +    if(LLHUDManager::instanceExists())
 +    {
 +        LLHUDManager::getInstance()->updateEffects();
 +        LLHUDObject::updateAll();
 +        LLHUDManager::getInstance()->cleanupEffects();
 +        LLHUDObject::cleanupHUDObjects();
 +        LL_INFOS() << "HUD Objects cleaned up" << LL_ENDL;
 +    }
 +
 +    LLKeyframeDataCache::clear();
 +
 +    // End TransferManager before deleting systems it depends on (Audio, AssetStorage)
 +#if 0 // this seems to get us stuck in an infinite loop...
 +    gTransferManager.cleanup();
 +#endif
 +
 +    // Note: this is where gWorldMap used to be deleted.
 +
 +    // Note: this is where gHUDManager used to be deleted.
 +    if(LLHUDManager::instanceExists())
 +    {
 +        LLHUDManager::getInstance()->shutdownClass();
 +    }
 +
 +    delete gAssetStorage;
 +    gAssetStorage = NULL;
 +
 +    LLPolyMesh::freeAllMeshes();
 +
 +    LLStartUp::cleanupNameCache();
 +
 +    // Note: this is where gLocalSpeakerMgr and gActiveSpeakerMgr used to be deleted.
 +
 +    if (LLWorldMap::instanceExists())
 +    {
 +        LLWorldMap::getInstance()->reset(); // release any images
 +    }
 +
 +    LLCalc::cleanUp();
 +
 +    LL_INFOS() << "Global stuff deleted" << LL_ENDL;
 +
 +    if (gAudiop)
 +    {
 +        LL_INFOS() << "Shutting down audio" << LL_ENDL;
 +
 +        // be sure to stop the internet stream cleanly BEFORE destroying the interface to stop it.
 +        gAudiop->stopInternetStream();
 +        // shut down the streaming audio sub-subsystem first, in case it relies on not outliving the general audio subsystem.
 +        LLStreamingAudioInterface *sai = gAudiop->getStreamingAudioImpl();
 +        delete sai;
 +        gAudiop->setStreamingAudioImpl(NULL);
 +
 +        // shut down the audio subsystem
 +        gAudiop->shutdown();
 +
 +        delete gAudiop;
 +        gAudiop = NULL;
 +    }
 +
 +    // Note: this is where LLFeatureManager::getInstance()-> used to be deleted.
 +
 +    // Patch up settings for next time
 +    // Must do this before we delete the viewer window,
 +    // such that we can suck rectangle information out of
 +    // it.
 +    cleanupSavedSettings();
 +    LL_INFOS() << "Settings patched up" << LL_ENDL;
 +
 +    // delete some of the files left around in the cache.
 +    removeCacheFiles("*.wav");
 +    removeCacheFiles("*.tmp");
 +    removeCacheFiles("*.lso");
 +    removeCacheFiles("*.out");
 +    removeCacheFiles("*.dsf");
 +    removeCacheFiles("*.bodypart");
 +    removeCacheFiles("*.clothing");
 +
 +    LL_INFOS() << "Cache files removed" << LL_ENDL;
 +
 +    LL_INFOS() << "Shutting down Views" << LL_ENDL;
 +
 +    // Destroy the UI
 +    if( gViewerWindow)
 +        gViewerWindow->shutdownViews();
 +
 +    LL_INFOS() << "Cleaning up Inventory" << LL_ENDL;
 +
 +    // Cleanup Inventory after the UI since it will delete any remaining observers
 +    // (Deleted observers should have already removed themselves)
 +    gInventory.cleanupInventory();
 +
 +    LLCoros::getInstance()->printActiveCoroutines();
 +
 +    LL_INFOS() << "Cleaning up Selections" << LL_ENDL;
 +
 +    // Clean up selection managers after UI is destroyed, as UI may be observing them.
 +    // Clean up before GL is shut down because we might be holding on to objects with texture references
 +    LLSelectMgr::cleanupGlobals();
 +
 +    LL_INFOS() << "Shutting down OpenGL" << LL_ENDL;
 +
 +    // Shut down OpenGL
 +    if( gViewerWindow)
 +    {
 +        gViewerWindow->shutdownGL();
 +
 +        // Destroy window, and make sure we're not fullscreen
 +        // This may generate window reshape and activation events.
 +        // Therefore must do this before destroying the message system.
 +        delete gViewerWindow;
 +        gViewerWindow = NULL;
 +        LL_INFOS() << "ViewerWindow deleted" << LL_ENDL;
 +    }
 +
 +    LLSplashScreen::show();
 +    LLSplashScreen::update(LLTrans::getString("ShuttingDown"));
 +
 +    LL_INFOS() << "Cleaning up Keyboard & Joystick" << LL_ENDL;
 +
 +    // viewer UI relies on keyboard so keep it aound until viewer UI isa gone
 +    delete gKeyboard;
 +    gKeyboard = NULL;
 +
 +    if (LLViewerJoystick::instanceExists())
 +    {
 +        // Turn off Space Navigator and similar devices
 +        LLViewerJoystick::getInstance()->terminate();
 +    }
 +
 +    LL_INFOS() << "Cleaning up Objects" << LL_ENDL;
 +
 +    LLViewerObject::cleanupVOClasses();
 +
 +    SUBSYSTEM_CLEANUP(LLAvatarAppearance);
 +
 +    SUBSYSTEM_CLEANUP(LLPostProcess);
 +
 +    LLTracker::cleanupInstance();
 +
 +    // *FIX: This is handled in LLAppViewerWin32::cleanup().
 +    // I'm keeping the comment to remember its order in cleanup,
 +    // in case of unforseen dependency.
 +    //#if LL_WINDOWS
 +    //  gDXHardware.cleanup();
 +    //#endif // LL_WINDOWS
 +
 +    LLVolumeMgr* volume_manager = LLPrimitive::getVolumeManager();
 +    if (!volume_manager->cleanup())
 +    {
 +        LL_WARNS() << "Remaining references in the volume manager!" << LL_ENDL;
 +    }
 +    LLPrimitive::cleanupVolumeManager();
 +
 +    LL_INFOS() << "Additional Cleanup..." << LL_ENDL;
 +
 +    LLViewerParcelMgr::cleanupGlobals();
 +
 +    // *Note: this is where gViewerStats used to be deleted.
 +
 +    //end_messaging_system();
 +
 +    LLPrimitive::cleanupVolumeManager();
 +    SUBSYSTEM_CLEANUP(LLWorldMapView);
 +    SUBSYSTEM_CLEANUP(LLFolderViewItem);
 +
 +    LL_INFOS() << "Saving Data" << LL_ENDL;
 +
 +    // Store the time of our current logoff
 +    gSavedPerAccountSettings.setU32("LastLogoff", time_corrected());
 +
 +    if (LLEnvironment::instanceExists())
 +    {
 +        //Store environment settings if necessary
 +        LLEnvironment::getInstance()->saveToSettings();
 +    }
 +
 +    // Must do this after all panels have been deleted because panels that have persistent rects
 +    // save their rects on delete.
 +    gSavedSettings.saveToFile(gSavedSettings.getString("ClientSettingsFile"), true);
 +
 +    LLUIColorTable::instance().saveUserSettings();
 +
 +    // PerAccountSettingsFile should be empty if no user has been logged on.
 +    // *FIX:Mani This should get really saved in a "logoff" mode.
 +    if (gSavedSettings.getString("PerAccountSettingsFile").empty())
 +    {
 +        LL_INFOS() << "Not saving per-account settings; don't know the account name yet." << LL_ENDL;
 +    }
 +    // Only save per account settings if the previous login succeeded, otherwise
 +    // we might end up with a cleared out settings file in case a previous login
 +    // failed after loading per account settings.
 +    else if (!mSavePerAccountSettings)
 +    {
 +        LL_INFOS() << "Not saving per-account settings; last login was not successful." << LL_ENDL;
 +    }
 +    else
 +    {
 +        gSavedPerAccountSettings.saveToFile(gSavedSettings.getString("PerAccountSettingsFile"), true);
 +        LL_INFOS() << "Saved settings" << LL_ENDL;
 +
 +        if (LLViewerParcelAskPlay::instanceExists())
 +        {
 +            LLViewerParcelAskPlay::getInstance()->saveSettings();
 +        }
 +    }
 +
 +    std::string warnings_settings_filename = gDirUtilp->getExpandedFilename(LL_PATH_USER_SETTINGS, getSettingsFilename("Default", "Warnings"));
 +    gWarningSettings.saveToFile(warnings_settings_filename, true);
 +
 +    // Save URL history file
 +    LLURLHistory::saveFile("url_history.xml");
 +
 +    // save mute list. gMuteList used to also be deleted here too.
 +    if (gAgent.isInitialized() && LLMuteList::instanceExists())
 +    {
 +        LLMuteList::getInstance()->cache(gAgent.getID());
 +    }
 +
 +    //save call log list
 +    if (LLConversationLog::instanceExists())
 +    {
 +        LLConversationLog::instance().cache();
 +    }
 +
 +    clearSecHandler();
 +
 +    if (mPurgeCacheOnExit)
 +    {
 +        LL_INFOS() << "Purging all cache files on exit" << LL_ENDL;
 +        gDirUtilp->deleteFilesInDir(gDirUtilp->getExpandedFilename(LL_PATH_CACHE,""), "*.*");
 +    }
 +
 +    writeDebugInfo();
 +
 +    LLLocationHistory::getInstance()->save();
 +
 +    LLAvatarIconIDCache::getInstance()->save();
 +
 +    // Stop the plugin read thread if it's running.
 +    LLPluginProcessParent::setUseReadThread(false);
 +
 +    LL_INFOS() << "Shutting down Threads" << LL_ENDL;
 +
 +    // Let threads finish
 +    LLTimer idleTimer;
 +    idleTimer.reset();
 +    const F64 max_idle_time = 5.f; // 5 seconds
 +    while(1)
 +    {
 +        S32 pending = 0;
 +        pending += LLAppViewer::getTextureCache()->update(1); // unpauses the worker thread
 +        pending += LLAppViewer::getImageDecodeThread()->update(1); // unpauses the image thread
 +        pending += LLAppViewer::getTextureFetch()->update(1); // unpauses the texture fetch thread
 +        pending += LLLFSThread::updateClass(0);
 +        F64 idle_time = idleTimer.getElapsedTimeF64();
 +        if(!pending)
 +        {
 +            break ; //done
 +        }
 +        else if(idle_time >= max_idle_time)
 +        {
 +            LL_WARNS() << "Quitting with pending background tasks." << LL_ENDL;
 +            break;
 +        }
 +    }
 +
 +    if (mPurgeUserDataOnExit)
 +    {
 +        // Ideally we should not save anything from this session since it is going to be purged now,
 +        // but this is a very 'rare' case (user deleting himself), not worth overcomplicating 'save&cleanup' code
 +        std::string user_path = gDirUtilp->getOSUserAppDir() + gDirUtilp->getDirDelimiter() + LLStartUp::getUserId();
 +        gDirUtilp->deleteDirAndContents(user_path);
 +    }
 +
 +    // Delete workers first
 +    // shotdown all worker threads before deleting them in case of co-dependencies
 +    mAppCoreHttp.requestStop();
 +    sTextureFetch->shutdown();
 +    sTextureCache->shutdown();
 +    sImageDecodeThread->shutdown();
 +    sPurgeDiskCacheThread->shutdown();
 +    if (mGeneralThreadPool)
 +    {
 +        mGeneralThreadPool->close();
 +    }
 +
 +    sTextureFetch->shutDownTextureCacheThread() ;
 +    LLLFSThread::sLocal->shutdown();
 +
 +    LL_INFOS() << "Shutting down message system" << LL_ENDL;
 +    end_messaging_system();
 +
 +    // Non-LLCurl libcurl library
 +    mAppCoreHttp.cleanup();
 +
 +    SUBSYSTEM_CLEANUP(LLFilePickerThread);
 +    SUBSYSTEM_CLEANUP(LLDirPickerThread);
 +
 +    //MUST happen AFTER SUBSYSTEM_CLEANUP(LLCurl)
 +    delete sTextureCache;
 +    sTextureCache = NULL;
 +    if (sTextureFetch)
 +    {
 +        sTextureFetch->shutdown();
 +        sTextureFetch->waitOnPending();
 +        delete sTextureFetch;
 +        sTextureFetch = NULL;
 +    }
 +    delete sImageDecodeThread;
 +    sImageDecodeThread = NULL;
 +    delete mFastTimerLogThread;
 +    mFastTimerLogThread = NULL;
 +    delete sPurgeDiskCacheThread;
 +    sPurgeDiskCacheThread = NULL;
 +    delete mGeneralThreadPool;
 +    mGeneralThreadPool = NULL;
 +
 +    if (LLFastTimerView::sAnalyzePerformance)
 +    {
 +        LL_INFOS() << "Analyzing performance" << LL_ENDL;
 +
 +        std::string baseline_name = LLTrace::BlockTimer::sLogName + "_baseline.slp";
 +        std::string current_name  = LLTrace::BlockTimer::sLogName + ".slp";
 +        std::string report_name   = LLTrace::BlockTimer::sLogName + "_report.csv";
 +
 +        LLFastTimerView::doAnalysis(
 +            gDirUtilp->getExpandedFilename(LL_PATH_LOGS, baseline_name),
 +            gDirUtilp->getExpandedFilename(LL_PATH_LOGS, current_name),
 +            gDirUtilp->getExpandedFilename(LL_PATH_LOGS, report_name));
 +    }
 +
 +    SUBSYSTEM_CLEANUP(LLMetricPerformanceTesterBasic) ;
 +
 +    LL_INFOS() << "Cleaning up Media and Textures" << LL_ENDL;
 +
 +    //Note:
 +    //SUBSYSTEM_CLEANUP(LLViewerMedia) has to be put before gTextureList.shutdown()
 +    //because some new image might be generated during cleaning up media. --bao
 +    gTextureList.shutdown(); // shutdown again in case a callback added something
 +    LLUIImageList::getInstance()->cleanUp();
 +
 +    SUBSYSTEM_CLEANUP(LLImage);
 +    SUBSYSTEM_CLEANUP(LLLFSThread);
 +
 +    LL_INFOS() << "Misc Cleanup" << LL_ENDL;
 +
 +    gSavedSettings.cleanup();
 +    LLUIColorTable::instance().clear();
 +
 +    LLWatchdog::getInstance()->cleanup();
 +
 +    LLViewerAssetStatsFF::cleanup();
 +
 +    // If we're exiting to launch an URL, do that here so the screen
 +    // is at the right resolution before we launch IE.
 +    if (!gLaunchFileOnQuit.empty())
 +    {
 +        LL_INFOS() << "Launch file on quit." << LL_ENDL;
 +#if LL_WINDOWS
 +        // Indicate an application is starting.
 +        SetCursor(LoadCursor(NULL, IDC_WAIT));
 +#endif
 +
 +        // HACK: Attempt to wait until the screen res. switch is complete.
 +        ms_sleep(1000);
 +
 +        LLWeb::loadURLExternal( gLaunchFileOnQuit, false );
 +        LL_INFOS() << "File launched." << LL_ENDL;
 +    }
 +    // make sure nothing uses applyProxySettings by this point.
 +    LL_INFOS() << "Cleaning up LLProxy." << LL_ENDL;
 +    SUBSYSTEM_CLEANUP(LLProxy);
 +    LLCore::LLHttp::cleanup();
 +
 +    ll_close_fail_log();
 +
 +    LLError::LLCallStacks::cleanup();
 +
 +    LLEnvironment::deleteSingleton();
 +    LLSelectMgr::deleteSingleton();
 +    LLViewerEventRecorder::deleteSingleton();
 +    LLWorld::deleteSingleton();
 +    LLVoiceClient::deleteSingleton();
 +
 +    // It's not at first obvious where, in this long sequence, a generic cleanup
 +    // call OUGHT to go. So let's say this: as we migrate cleanup from
 +    // explicit hand-placed calls into the generic mechanism, eventually
 +    // all cleanup will get subsumed into the generic call. So the calls you
 +    // still see above are calls that MUST happen before the generic cleanup
 +    // kicks in.
 +
 +    // This calls every remaining LLSingleton's cleanupSingleton() and
 +    // deleteSingleton() methods.
 +    LLSingletonBase::deleteAll();
 +
 +    LLSplashScreen::hide();
 +
 +    LL_INFOS() << "Goodbye!" << LL_ENDL;
 +
 +    removeDumpDir();
 +
 +    // return 0;
 +    return true;
 +}
 +
 +void LLAppViewer::initGeneralThread()
 +{
 +    if (mGeneralThreadPool)
 +    {
 +        return;
 +    }
 +
 +    mGeneralThreadPool = new LL::ThreadPool("General", 3);
 +    mGeneralThreadPool->start();
 +}
 +
 +bool LLAppViewer::initThreads()
 +{
 +    static const bool enable_threads = true;
 +
 +    LLImage::initClass(gSavedSettings.getBOOL("TextureNewByteRange"),gSavedSettings.getS32("TextureReverseByteRange"));
 +
 +    LLLFSThread::initClass(enable_threads && true); // TODO: fix crashes associated with this shutdo
 +
 +    //auto configure thread count
 +    LLSD threadCounts = gSavedSettings.getLLSD("ThreadPoolSizes");
 +
 +    // get the number of concurrent threads that can run
 +    S32 cores = std::thread::hardware_concurrency();
 +
 +    U32 max_cores = gSavedSettings.getU32("EmulateCoreCount");
 +    if (max_cores != 0)
 +    {
 +        cores = llmin(cores, (S32) max_cores);
 +    }
 +
 +    // The only configurable thread count right now is ImageDecode
 +    // The viewer typically starts around 8 threads not including image decode,
 +    // so try to leave at least one core free
 +    S32 image_decode_count = llclamp(cores - 9, 1, 8);
 +    threadCounts["ImageDecode"] = image_decode_count;
 +    gSavedSettings.setLLSD("ThreadPoolSizes", threadCounts);
 +
 +    // Image decoding
 +    LLAppViewer::sImageDecodeThread = new LLImageDecodeThread(enable_threads && true);
 +    LLAppViewer::sTextureCache = new LLTextureCache(enable_threads && true);
 +    LLAppViewer::sTextureFetch = new LLTextureFetch(LLAppViewer::getTextureCache(),
 +                                                    enable_threads && true,
 +                                                    app_metrics_qa_mode);
 +
 +    // general task background thread (LLPerfStats, etc)
 +    LLAppViewer::instance()->initGeneralThread();
 +
 +    LLAppViewer::sPurgeDiskCacheThread = new LLPurgeDiskCacheThread();
 +
 +    if (LLTrace::BlockTimer::sLog || LLTrace::BlockTimer::sMetricLog)
 +    {
 +        LLTrace::BlockTimer::setLogLock(new LLMutex());
 +        mFastTimerLogThread = new LLFastTimerLogThread(LLTrace::BlockTimer::sLogName);
 +        mFastTimerLogThread->start();
 +    }
 +
 +    // Mesh streaming and caching
 +    gMeshRepo.init();
 +
 +    LLFilePickerThread::initClass();
 +    LLDirPickerThread::initClass();
 +
 +    // *FIX: no error handling here!
 +    return true;
 +}
 +
 +void errorCallback(LLError::ELevel level, const std::string &error_string)
 +{
 +    if (level == LLError::LEVEL_ERROR)
 +    {
 +#ifndef LL_RELEASE_FOR_DOWNLOAD
 +        OSMessageBox(error_string, LLTrans::getString("MBFatalError"), OSMB_OK);
 +#endif
 +
 +        gDebugInfo["FatalMessage"] = error_string;
 +        // We're not already crashing -- we simply *intend* to crash. Since we
 +        // haven't actually trashed anything yet, we can afford to write the whole
 +        // static info file.
 +        LLAppViewer::instance()->writeDebugInfo();
 +    }
 +}
 +
 +void errorMSG(const std::string& title_string, const std::string& message_string)
 +{
 +    if (!message_string.empty())
 +    {
 +        OSMessageBox(message_string, title_string.empty() ? LLTrans::getString("MBFatalError") : title_string, OSMB_OK);
 +    }
 +}
 +
 +void LLAppViewer::initLoggingAndGetLastDuration()
 +{
 +    //
 +    // Set up logging defaults for the viewer
 +    //
 +    LLError::initForApplication( gDirUtilp->getExpandedFilename(LL_PATH_USER_SETTINGS, "")
 +                                ,gDirUtilp->getExpandedFilename(LL_PATH_APP_SETTINGS, "")
 +                                );
 +    LLError::addGenericRecorder(&errorCallback);
 +    //LLError::setTimeFunction(getRuntime);
 +
 +    LLError::LLUserWarningMsg::setHandler(errorMSG);
 +
 +
 +    if (mSecondInstance)
 +    {
 +        LLFile::mkdir(gDirUtilp->getDumpLogsDirPath());
 +
 +        LLUUID uid;
 +        uid.generate();
 +        LLError::logToFile(gDirUtilp->getDumpLogsDirPath(uid.asString() + ".log"));
 +    }
 +    else
 +    {
 +        // Remove the last ".old" log file.
 +        std::string old_log_file = gDirUtilp->getExpandedFilename(LL_PATH_LOGS,
 +            "SecondLife.old");
 +        LLFile::remove(old_log_file);
 +
 +        // Get name of the log file
 +        std::string log_file = gDirUtilp->getExpandedFilename(LL_PATH_LOGS,
 +            "SecondLife.log");
 +        /*
 +        * Before touching any log files, compute the duration of the last run
 +        * by comparing the ctime of the previous start marker file with the ctime
 +        * of the last log file.
 +        */
 +        std::string start_marker_file_name = gDirUtilp->getExpandedFilename(LL_PATH_LOGS, START_MARKER_FILE_NAME);
 +        llstat start_marker_stat;
 +        llstat log_file_stat;
 +        std::ostringstream duration_log_stream; // can't log yet, so save any message for when we can below
 +        int start_stat_result = LLFile::stat(start_marker_file_name, &start_marker_stat);
 +        int log_stat_result = LLFile::stat(log_file, &log_file_stat);
 +        if (0 == start_stat_result && 0 == log_stat_result)
 +        {
 +            int elapsed_seconds = log_file_stat.st_ctime - start_marker_stat.st_ctime;
 +            // only report a last run time if the last viewer was the same version
 +            // because this stat will be counted against this version
 +            if (markerIsSameVersion(start_marker_file_name))
 +            {
 +                gLastExecDuration = elapsed_seconds;
 +            }
 +            else
 +            {
 +                duration_log_stream << "start marker from some other version; duration is not reported";
 +                gLastExecDuration = -1;
 +            }
 +        }
 +        else
 +        {
 +            // at least one of the LLFile::stat calls failed, so we can't compute the run time
 +            duration_log_stream << "duration stat failure; start: " << start_stat_result << " log: " << log_stat_result;
 +            gLastExecDuration = -1; // unknown
 +        }
 +        std::string duration_log_msg(duration_log_stream.str());
 +
 +        // Create a new start marker file for comparison with log file time for the next run
 +        LLAPRFile start_marker_file;
 +        start_marker_file.open(start_marker_file_name, LL_APR_WB);
 +        if (start_marker_file.getFileHandle())
 +        {
 +            recordMarkerVersion(start_marker_file);
 +            start_marker_file.close();
 +        }
 +
 +        // Rename current log file to ".old"
 +        LLFile::rename(log_file, old_log_file);
 +
 +        // Set the log file to SecondLife.log
 +        LLError::logToFile(log_file);
 +        LL_INFOS() << "Started logging to " << log_file << LL_ENDL;
 +        if (!duration_log_msg.empty())
 +        {
 +            LL_WARNS("MarkerFile") << duration_log_msg << LL_ENDL;
 +        }
 +    }
 +}
 +
 +bool LLAppViewer::loadSettingsFromDirectory(const std::string& location_key,
 +                        bool set_defaults)
 +{
 +    if (!mSettingsLocationList)
 +    {
 +        LL_ERRS() << "Invalid settings location list" << LL_ENDL;
 +    }
 +
 +    for (const SettingsGroup& group : mSettingsLocationList->groups)
 +    {
 +        // skip settings groups that aren't the one we requested
 +        if (group.name() != location_key) continue;
 +
 +        ELLPath path_index = (ELLPath)group.path_index();
 +        if(path_index <= LL_PATH_NONE || path_index >= LL_PATH_LAST)
 +        {
 +            LL_ERRS() << "Out of range path index in app_settings/settings_files.xml" << LL_ENDL;
 +            return false;
 +        }
 +
 +        for (const SettingsFile& file : group.files)
 +        {
 +            LL_INFOS("Settings") << "Attempting to load settings for the group " << file.name()
 +                << " - from location " << location_key << LL_ENDL;
 +
 +            auto settings_group = LLControlGroup::getInstance(file.name);
 +            if(!settings_group)
 +            {
 +                LL_WARNS("Settings") << "No matching settings group for name " << file.name() << LL_ENDL;
 +                continue;
 +            }
 +
 +            std::string full_settings_path;
 +
 +            if (file.file_name_setting.isProvided()
 +                && gSavedSettings.controlExists(file.file_name_setting))
 +            {
 +                // try to find filename stored in file_name_setting control
 +                full_settings_path = gSavedSettings.getString(file.file_name_setting());
 +                if (full_settings_path.empty())
 +                {
 +                    continue;
 +                }
 +                else if (!gDirUtilp->fileExists(full_settings_path))
 +                {
 +                    // search in default path
 +                    full_settings_path = gDirUtilp->getExpandedFilename((ELLPath)path_index, full_settings_path);
 +                }
 +            }
 +            else
 +            {
 +                // by default, use specified file name
 +                full_settings_path = gDirUtilp->getExpandedFilename((ELLPath)path_index, file.file_name());
 +            }
 +
 +            if(settings_group->loadFromFile(full_settings_path, set_defaults, file.persistent))
 +            {   // success!
 +                LL_INFOS("Settings") << "Loaded settings file " << full_settings_path << LL_ENDL;
 +            }
 +            else
 +            {   // failed to load
 +                if(file.required)
 +                {
 +                    LLError::LLUserWarningMsg::showMissingFiles();
 +                    LL_ERRS() << "Error: Cannot load required settings file from: " << full_settings_path << LL_ENDL;
 +                    return false;
 +                }
 +                else
 +                {
 +                    // only complain if we actually have a filename at this point
 +                    if (!full_settings_path.empty())
 +                    {
 +                        LL_INFOS("Settings") << "Cannot load " << full_settings_path << " - No settings found." << LL_ENDL;
 +                    }
 +                }
 +            }
 +        }
 +    }
 +
 +    return true;
 +}
 +
 +std::string LLAppViewer::getSettingsFilename(const std::string& location_key,
 +                                             const std::string& file)
 +{
 +    for (const SettingsGroup& group : mSettingsLocationList->groups)
 +    {
 +        if (group.name() == location_key)
 +        {
 +            for (const SettingsFile& settings_file : group.files)
 +            {
 +                if (settings_file.name() == file)
 +                {
 +                    return settings_file.file_name;
 +                }
 +            }
 +        }
 +    }
 +
 +    return std::string();
 +}
 +
 +void LLAppViewer::loadColorSettings()
 +{
 +    LLUIColorTable::instance().loadFromSettings();
 +}
 +
 +namespace
 +{
 +    void handleCommandLineError(LLControlGroupCLP& clp)
 +    {
 +        LL_WARNS() << "Error parsing command line options. Command Line options ignored."  << LL_ENDL;
 +
 +        LL_INFOS() << "Command line usage:\n" << clp << LL_ENDL;
 +
 +        OSMessageBox(STRINGIZE(LLTrans::getString("MBCmdLineError") << clp.getErrorMessage()),
 +                     LLStringUtil::null,
 +                     OSMB_OK);
 +    }
 +} // anonymous namespace
 +
 +// Set a named control temporarily for this session, as when set via the command line --set option.
 +// Name can be specified as "<control_group>.<control_name>", with default group being Global.
 +bool tempSetControl(const std::string& name, const std::string& value)
 +{
 +    std::string name_part;
 +    std::string group_part;
 +    LLControlVariable* control = NULL;
 +
 +    // Name can be further split into ControlGroup.Name, with the default control group being Global
 +    size_t pos = name.find('.');
 +    if (pos != std::string::npos)
 +    {
 +        group_part = name.substr(0, pos);
 +        name_part = name.substr(pos+1);
 +        LL_INFOS() << "Setting " << group_part << "." << name_part << " to " << value << LL_ENDL;
 +        auto g = LLControlGroup::getInstance(group_part);
 +        if (g) control = g->getControl(name_part);
 +    }
 +    else
 +    {
 +        LL_INFOS() << "Setting Global." << name << " to " << value << LL_ENDL;
 +        control = gSavedSettings.getControl(name);
 +    }
 +
 +    if (control)
 +    {
 +        control->setValue(value, false);
 +        return true;
 +    }
 +    return false;
 +}
 +
 +bool LLAppViewer::initConfiguration()
 +{
 +    //Load settings files list
 +    std::string settings_file_list = gDirUtilp->getExpandedFilename(LL_PATH_APP_SETTINGS, "settings_files.xml");
 +    LLXMLNodePtr root;
 +    bool success = LLXMLNode::parseFile(settings_file_list, root, NULL);
 +    if (!success)
 +    {
 +        LL_WARNS() << "Cannot load default configuration file " << settings_file_list << LL_ENDL;
 +        LLError::LLUserWarningMsg::showMissingFiles();
 +        if (gDirUtilp->fileExists(settings_file_list))
 +        {
 +            LL_ERRS() << "Cannot load default configuration file settings_files.xml. "
 +                << "Please reinstall viewer from https://secondlife.com/support/downloads/ "
 +                << "and contact https://support.secondlife.com if issue persists after reinstall."
 +                << LL_ENDL;
 +        }
 +        else
 +        {
 +            LL_ERRS() << "Default configuration file settings_files.xml not found. "
 +                << "Please reinstall viewer from https://secondlife.com/support/downloads/ "
 +                << "and contact https://support.secondlife.com if issue persists after reinstall."
 +                << LL_ENDL;
 +        }
 +    }
 +
 +    mSettingsLocationList = new SettingsFiles();
 +
 +    LLXUIParser parser;
 +    parser.readXUI(root, *mSettingsLocationList, settings_file_list);
 +
 +    if (!mSettingsLocationList->validateBlock())
 +    {
 +        LLError::LLUserWarningMsg::showMissingFiles();
 +        LL_ERRS() << "Invalid settings file list " << settings_file_list << LL_ENDL;
 +    }
 +
 +    // The settings and command line parsing have a fragile
 +    // order-of-operation:
 +    // - load defaults from app_settings
 +    // - set procedural settings values
 +    // - read command line settings
 +    // - selectively apply settings needed to load user settings.
 +    // - load overrides from user_settings
 +    // - apply command line settings (to override the overrides)
 +    // - load per account settings (happens in llstartup
 +
 +    // - load defaults
 +    bool set_defaults = true;
 +    if(!loadSettingsFromDirectory("Default", set_defaults))
 +    {
 +        OSMessageBox(
 +            "Unable to load default settings file. The installation may be corrupted.",
 +            LLStringUtil::null,OSMB_OK);
 +        return false;
 +    }
 +
 +    initStrings(); // setup paths for LLTrans based on settings files only
 +    // - set procedural settings
 +    // Note: can't use LL_PATH_PER_SL_ACCOUNT for any of these since we haven't logged in yet
 +    gSavedSettings.setString("ClientSettingsFile",
 +        gDirUtilp->getExpandedFilename(LL_PATH_USER_SETTINGS, getSettingsFilename("Default", "Global")));
 +
 +#ifndef LL_RELEASE_FOR_DOWNLOAD
 +    // provide developer build only overrides for these control variables that are not
 +    // persisted to settings.xml
 +    LLControlVariable* c = gSavedSettings.getControl("AllowMultipleViewers");
 +    if (c)
 +    {
 +        c->setValue(true, false);
 +    }
 +
 +    gSavedSettings.setBOOL("QAMode", true );
 +    gSavedSettings.setS32("WatchdogEnabled", 0);
 +#endif
 +
 +    // These are warnings that appear on the first experience of that condition.
 +    // They are already set in the settings_default.xml file, but still need to be added to LLFirstUse
 +    // for disable/reset ability
 +//  LLFirstUse::addConfigVariable("FirstBalanceIncrease");
 +//  LLFirstUse::addConfigVariable("FirstBalanceDecrease");
 +//  LLFirstUse::addConfigVariable("FirstSit");
 +//  LLFirstUse::addConfigVariable("FirstMap");
 +//  LLFirstUse::addConfigVariable("FirstGoTo");
 +//  LLFirstUse::addConfigVariable("FirstBuild");
 +//  LLFirstUse::addConfigVariable("FirstLeftClickNoHit");
 +//  LLFirstUse::addConfigVariable("FirstTeleport");
 +//  LLFirstUse::addConfigVariable("FirstOverrideKeys");
 +//  LLFirstUse::addConfigVariable("FirstAttach");
 +//  LLFirstUse::addConfigVariable("FirstAppearance");
 +//  LLFirstUse::addConfigVariable("FirstInventory");
 +//  LLFirstUse::addConfigVariable("FirstSandbox");
 +//  LLFirstUse::addConfigVariable("FirstFlexible");
 +//  LLFirstUse::addConfigVariable("FirstDebugMenus");
 +//  LLFirstUse::addConfigVariable("FirstSculptedPrim");
 +//  LLFirstUse::addConfigVariable("FirstVoice");
 +//  LLFirstUse::addConfigVariable("FirstMedia");
 +
 +    // - read command line settings.
 +    LLControlGroupCLP clp;
 +    std::string cmd_line_config = gDirUtilp->getExpandedFilename(LL_PATH_APP_SETTINGS,
 +                                                          "cmd_line.xml");
 +
 +    clp.configure(cmd_line_config, &gSavedSettings);
 +
 +    if(!initParseCommandLine(clp))
 +    {
 +        handleCommandLineError(clp);
 +        return false;
 +    }
 +
 +    // - selectively apply settings
 +
 +    // If the user has specified a alternate settings file name.
 +    // Load it now before loading the user_settings/settings.xml
 +    if(clp.hasOption("settings"))
 +    {
 +        std::string user_settings_filename =
 +            gDirUtilp->getExpandedFilename(LL_PATH_USER_SETTINGS,
 +                                           clp.getOption("settings")[0]);
 +        gSavedSettings.setString("ClientSettingsFile", user_settings_filename);
 +        LL_INFOS("Settings")    << "Using command line specified settings filename: "
 +            << user_settings_filename << LL_ENDL;
 +    }
 +
 +    // - load overrides from user_settings
 +    loadSettingsFromDirectory("User");
 +
 +    if (gSavedSettings.getBOOL("FirstRunThisInstall"))
 +    {
 +        // Set firstrun flag to indicate that some further init actiona should be taken
 +        // like determining screen DPI value and so on
 +        mIsFirstRun = true;
 +
 +        gSavedSettings.setBOOL("FirstRunThisInstall", false);
 +    }
 +
 +    if (clp.hasOption("sessionsettings"))
 +    {
 +        std::string session_settings_filename = clp.getOption("sessionsettings")[0];
 +        gSavedSettings.setString("SessionSettingsFile", session_settings_filename);
 +        LL_INFOS("Settings")    << "Using session settings filename: "
 +            << session_settings_filename << LL_ENDL;
 +    }
 +    loadSettingsFromDirectory("Session");
 +
 +    if (clp.hasOption("usersessionsettings"))
 +    {
 +        std::string user_session_settings_filename = clp.getOption("usersessionsettings")[0];
 +        gSavedSettings.setString("UserSessionSettingsFile", user_session_settings_filename);
 +        LL_INFOS("Settings") << "Using user session settings filename: "
 +            << user_session_settings_filename << LL_ENDL;
 +
 +    }
 +    loadSettingsFromDirectory("UserSession");
 +
 +    // - apply command line settings
 +    if (! clp.notify())
 +    {
 +        handleCommandLineError(clp);
 +        return false;
 +    }
 +
 +    // Register the core crash option as soon as we can
 +    // if we want gdb post-mortem on cores we need to be up and running
 +    // ASAP or we might miss init issue etc.
 +    if(gSavedSettings.getBOOL("DisableCrashLogger"))
 +    {
 +        LL_WARNS() << "Crashes will be handled by system, stack trace logs and crash logger are both disabled" << LL_ENDL;
 +        disableCrashlogger();
 +    }
 +
 +    gNonInteractive = gSavedSettings.getBOOL("NonInteractive");
 +    // Handle initialization from settings.
 +    // Start up the debugging console before handling other options.
 +    if (gSavedSettings.getBOOL("ShowConsoleWindow") && !gNonInteractive)
 +    {
 +        initConsole();
 +    }
 +
 +    if(clp.hasOption("help"))
 +    {
 +        std::ostringstream msg;
 +        msg << LLTrans::getString("MBCmdLineUsg") << "\n" << clp;
 +        LL_INFOS()  << msg.str() << LL_ENDL;
 +
 +        OSMessageBox(
 +            msg.str(),
 +            LLStringUtil::null,
 +            OSMB_OK);
 +
 +        return false;
 +    }
 +
 +    if(clp.hasOption("set"))
 +    {
 +        const LLCommandLineParser::token_vector_t& set_values = clp.getOption("set");
 +        if(0x1 & set_values.size())
 +        {
 +            LL_WARNS() << "Invalid '--set' parameter count." << LL_ENDL;
 +        }
 +        else
 +        {
 +            LLCommandLineParser::token_vector_t::const_iterator itr = set_values.begin();
 +            for(; itr != set_values.end(); ++itr)
 +            {
 +                const std::string& name = *itr;
 +                const std::string& value = *(++itr);
 +                if (!tempSetControl(name,value))
 +                {
 +                    LL_WARNS() << "Failed --set " << name << ": setting name unknown." << LL_ENDL;
 +                }
 +            }
 +        }
 +    }
 +
 +    if  (clp.hasOption("logevents")) {
 +        LLViewerEventRecorder::instance().setEventLoggingOn();
 +    }
 +
 +    std::string CmdLineChannel(gSavedSettings.getString("CmdLineChannel"));
 +    if(! CmdLineChannel.empty())
 +    {
 +        LLVersionInfo::instance().resetChannel(CmdLineChannel);
 +    }
 +
 +    // If we have specified crash on startup, set the global so we'll trigger the crash at the right time
 +    gCrashOnStartup = gSavedSettings.getBOOL("CrashOnStartup");
 +
 +    if (gSavedSettings.getBOOL("LogPerformance"))
 +    {
 +        LLTrace::BlockTimer::sLog = true;
 +        LLTrace::BlockTimer::sLogName = std::string("performance");
 +    }
 +
 +    std::string test_name(gSavedSettings.getString("LogMetrics"));
 +    if (! test_name.empty())
 +    {
 +        LLTrace::BlockTimer::sMetricLog = true;
 +        // '--logmetrics' is specified with a named test metric argument so the data gathering is done only on that test
 +        // In the absence of argument, every metric would be gathered (makes for a rather slow run and hard to decipher report...)
 +        LL_INFOS() << "'--logmetrics' argument : " << test_name << LL_ENDL;
 +        LLTrace::BlockTimer::sLogName = test_name;
 +    }
 +
 +    if (clp.hasOption("graphicslevel"))
 +    {
 +        // User explicitly requested --graphicslevel on the command line. We
 +        // expect this switch has already set RenderQualityPerformance. Check
 +        // that value for validity later.
 +        // Capture the requested value separately from the settings variable
 +        // because, if this is the first run, LLViewerWindow's constructor
 +        // will call LLFeatureManager::applyRecommendedSettings(), which
 +        // overwrites this settings variable!
 +        mForceGraphicsLevel = gSavedSettings.getU32("RenderQualityPerformance");
 +    }
 +
 +    LLFastTimerView::sAnalyzePerformance = gSavedSettings.getBOOL("AnalyzePerformance");
 +    gAgentPilot.setReplaySession(gSavedSettings.getBOOL("ReplaySession"));
 +
 +    if (gSavedSettings.getBOOL("DebugSession"))
 +    {
 +        gDebugSession = true;
 +        gDebugGL = true;
 +
 +        ll_init_fail_log(gDirUtilp->getExpandedFilename(LL_PATH_LOGS, "test_failures.log"));
 +    }
 +
 +    if (gSavedSettings.getBOOL("RenderDebugGLSession"))
 +    {
 +        gDebugGLSession = true;
 +        gDebugGL = true;
 +        // gDebugGL can cause excessive logging
 +        // so it's limited to a single session
 +        gSavedSettings.setBOOL("RenderDebugGLSession", false);
 +    }
 +
 +    const LLControlVariable* skinfolder = gSavedSettings.getControl("SkinCurrent");
 +    if(skinfolder && LLStringUtil::null != skinfolder->getValue().asString())
 +    {
 +        // Examining "Language" may not suffice -- see LLUI::getLanguage()
 +        // logic. Unfortunately LLUI::getLanguage() doesn't yet do us much
 +        // good because we haven't yet called LLUI::initClass().
 +        gDirUtilp->setSkinFolder(skinfolder->getValue().asString(),
 +                                 gSavedSettings.getString("Language"));
 +    }
 +
 +    if (gSavedSettings.getBOOL("SpellCheck"))
 +    {
 +        std::list<std::string> dict_list;
 +        std::string dict_setting = gSavedSettings.getString("SpellCheckDictionary");
 +        boost::split(dict_list, dict_setting, boost::is_any_of(std::string(",")));
 +        if (!dict_list.empty())
 +        {
 +            LLSpellChecker::setUseSpellCheck(dict_list.front());
 +            dict_list.pop_front();
 +            LLSpellChecker::instance().setSecondaryDictionaries(dict_list);
 +        }
 +    }
 +
 +    if (gNonInteractive)
 +    {
 +        tempSetControl("AllowMultipleViewers", "true");
 +        tempSetControl("SLURLPassToOtherInstance", "false");
 +        tempSetControl("RenderWater", "false");
 +        tempSetControl("FlyingAtExit", "false");
 +        tempSetControl("WindowWidth", "1024");
 +        tempSetControl("WindowHeight", "200");
 +        LLError::setEnabledLogTypesMask(0);
 +        llassert_always(!gSavedSettings.getBOOL("SLURLPassToOtherInstance"));
 +    }
 +
 +
 +    // Handle slurl use. NOTE: Don't let SL-55321 reappear.
 +    // This initial-SLURL logic, up through the call to
 +    // sendURLToOtherInstance(), must precede LLSplashScreen::show() --
 +    // because if sendURLToOtherInstance() succeeds, we take a fast exit,
 +    // SKIPPING the splash screen and everything else.
 +
 +    // *FIX: This init code should be made more robust to prevent
 +    // the issue SL-55321 from returning. One thought is to allow
 +    // only select options to be set from command line when a slurl
 +    // is specified. More work on the settings system is needed to
 +    // achieve this. For now...
 +
 +    // *NOTE:Mani The command line parser parses tokens and is
 +    // setup to bail after parsing the '--url' option or the
 +    // first option specified without a '--option' flag (or
 +    // any other option that uses the 'last_option' setting -
 +    // see LLControlGroupCLP::configure())
 +
 +    // What can happen is that someone can use IE (or potentially
 +    // other browsers) and do the rough equivalent of command
 +    // injection and steal passwords. Phoenix. SL-55321
 +
 +    std::string starting_location;
 +
 +    std::string cmd_line_login_location(gSavedSettings.getString("CmdLineLoginLocation"));
 +    if(! cmd_line_login_location.empty())
 +    {
 +        starting_location = cmd_line_login_location;
 +    }
 +    else
 +    {
 +        std::string default_login_location(gSavedSettings.getString("DefaultLoginLocation"));
 +        if (! default_login_location.empty())
 +        {
 +            starting_location = default_login_location;
 +        }
 +    }
 +
 +    LLSLURL start_slurl;
 +    if (! starting_location.empty())
 +    {
 +        start_slurl = starting_location;
 +        LLStartUp::setStartSLURL(start_slurl);
 +        if(start_slurl.getType() == LLSLURL::LOCATION)
 +        {
 +            LLGridManager::getInstance()->setGridChoice(start_slurl.getGrid());
 +        }
 +    }
 +
 +    // NextLoginLocation is set as a side effect of LLStartUp::setStartSLURL()
 +    std::string nextLoginLocation = gSavedSettings.getString( "NextLoginLocation" );
 +    if ( !nextLoginLocation.empty() )
 +    {
 +        LL_DEBUGS("AppInit")<<"set start from NextLoginLocation: "<<nextLoginLocation<<LL_ENDL;
 +        LLStartUp::setStartSLURL(LLSLURL(nextLoginLocation));
 +    }
 +    else if (   (   clp.hasOption("login") || clp.hasOption("autologin"))
 +             && gSavedSettings.getString("CmdLineLoginLocation").empty())
 +    {
 +        // If automatic login from command line with --login switch
 +        // init StartSLURL location.
 +        std::string start_slurl_setting = gSavedSettings.getString("LoginLocation");
 +        LL_DEBUGS("AppInit") << "start slurl setting '" << start_slurl_setting << "'" << LL_ENDL;
 +        LLStartUp::setStartSLURL(LLSLURL(start_slurl_setting));
 +    }
 +    else
 +    {
 +        // the login location will be set by the login panel (see LLPanelLogin)
 +    }
 +
 +    //RN: if we received a URL, hand it off to the existing instance.
 +    // don't call anotherInstanceRunning() when doing URL handoff, as
 +    // it relies on checking a marker file which will not work when running
 +    // out of different directories
 +
 +    if (start_slurl.isValid() &&
 +        (gSavedSettings.getBOOL("SLURLPassToOtherInstance")))
 +    {
 +        if (sendURLToOtherInstance(start_slurl.getSLURLString()))
 +        {
 +            // successfully handed off URL to existing instance, exit
 +            return false;
 +        }
 +    }
 +
 +    // Display splash screen.  Must be after above check for previous
 +    // crash as this dialog is always frontmost.
 +    std::string splash_msg;
 +    LLStringUtil::format_map_t args;
 +    args["[APP_NAME]"] = LLTrans::getString("SECOND_LIFE");
 +    splash_msg = LLTrans::getString("StartupLoading", args);
 +    LLSplashScreen::show();
 +    LLSplashScreen::update(splash_msg);
 +
 +    //LLVolumeMgr::initClass();
 +    LLVolumeMgr* volume_manager = new LLVolumeMgr();
 +    volume_manager->useMutex(); // LLApp and LLMutex magic must be manually enabled
 +    LLPrimitive::setVolumeManager(volume_manager);
 +
 +    // Note: this is where we used to initialize gFeatureManagerp.
 +
 +    gStartTime = totalTime();
 +
 +    //
 +    // Set the name of the window
 +    //
 +    gWindowTitle = LLTrans::getString("APP_NAME");
 +#if LL_DEBUG
 +    gWindowTitle += std::string(" [DEBUG]");
 +#endif
 +    if (!gArgs.empty())
 +    {
 +    gWindowTitle += std::string(" ") + gArgs;
 +    }
 +    LLStringUtil::truncate(gWindowTitle, 255);
 +
 +    //
 +    // Check for another instance of the app running
 +    // This happens AFTER LLSplashScreen::show(). That may or may not be
 +    // important.
 +    //
 +    if (mSecondInstance && !gSavedSettings.getBOOL("AllowMultipleViewers"))
 +    {
 +        OSMessageBox(
 +            LLTrans::getString("MBAlreadyRunning"),
 +            LLStringUtil::null,
 +            OSMB_OK);
 +        return false;
 +    }
 +
 +    if (mSecondInstance)
 +    {
 +        // This is the second instance of SL. Mute voice,
 +        // but make sure the setting is *not* persisted.
 +        // Also see LLVivoxVoiceClient::voiceEnabled()
 +        LLControlVariable* enable_voice = gSavedSettings.getControl("EnableVoiceChat");
 +        if(enable_voice)
 +        {
 +            const bool DO_NOT_PERSIST = false;
 +            enable_voice->setValue(LLSD(false), DO_NOT_PERSIST);
 +        }
 +    }
 +
 +    gLastRunVersion = gSavedSettings.getString("LastRunVersion");
 +
 +    loadColorSettings();
 +
 +    // Let anyone else who cares know that we've populated our settings
 +    // variables.
 +    for (const auto& key : LLControlGroup::key_snapshot())
 +    {
 +        // For each named instance of LLControlGroup, send an event saying
 +        // we've initialized an LLControlGroup instance by that name.
 +        LLEventPumps::instance().obtain("LLControlGroup").post(LLSDMap("init", key));
 +    }
 +
 +    LLError::LLUserWarningMsg::setOutOfMemoryStrings(LLTrans::getString("MBOutOfMemoryTitle"), LLTrans::getString("MBOutOfMemoryErr"));
 +
 +    return true; // Config was successful.
 +}
 +
 +// The following logic is replicated in initConfiguration() (to be able to get
 +// some initial strings before we've finished initializing enough to know the
 +// current language) and also in init() (to initialize for real). Somehow it
 +// keeps growing, necessitating a method all its own.
 +void LLAppViewer::initStrings()
 +{
 +    std::string strings_file = "strings.xml";
 +    std::string strings_path_full = gDirUtilp->findSkinnedFilenameBaseLang(LLDir::XUI, strings_file);
 +    if (strings_path_full.empty() || !LLFile::isfile(strings_path_full))
 +    {
 +        if (strings_path_full.empty())
 +        {
 +            LL_WARNS() << "The file '" << strings_file << "' is not found" << LL_ENDL;
 +        }
 +        else
 +        {
 +            llstat st;
 +            int rc = LLFile::stat(strings_path_full, &st);
 +            if (rc != 0)
 +            {
 +                LL_WARNS() << "The file '" << strings_path_full << "' failed to get status. Error code: " << rc << LL_ENDL;
 +            }
 +            else if (S_ISDIR(st.st_mode))
 +            {
 +                LL_WARNS() << "The filename '" << strings_path_full << "' is a directory name" << LL_ENDL;
 +            }
 +            else
 +            {
 +                LL_WARNS() << "The filename '" << strings_path_full << "' doesn't seem to be a regular file name" << LL_ENDL;
 +            }
 +        }
 +
 +        // initial check to make sure files are there failed
 +        gDirUtilp->dumpCurrentDirectories(LLError::LEVEL_WARN);
 +        LLError::LLUserWarningMsg::showMissingFiles();
 +        LL_ERRS() << "Viewer failed to find localization and UI files."
 +            << " Please reinstall viewer from https://secondlife.com/support/downloads"
 +            << " and contact https://support.secondlife.com if issue persists after reinstall." << LL_ENDL;
 +    }
 +    LLTransUtil::parseStrings(strings_file, default_trans_args);
 +    LLTransUtil::parseLanguageStrings("language_settings.xml");
 +
 +    // parseStrings() sets up the LLTrans substitution table. Add this one item.
 +    LLTrans::setDefaultArg("[sourceid]", gSavedSettings.getString("sourceid"));
 +
 +    // Now that we've set "[sourceid]", have to go back through
 +    // default_trans_args and reinitialize all those other keys because some
 +    // of them, in turn, reference "[sourceid]".
 +    for (const std::string& key : default_trans_args)
 +    {
 +        std::string brackets(key), nobrackets(key);
 +        // Invalid to inspect key[0] if key is empty(). But then, the entire
 +        // body of this loop is pointless if key is empty().
 +        if (key.empty())
 +            continue;
 +
 +        if (key[0] != '[')
 +        {
 +            // key was passed without brackets. That means that 'nobrackets'
 +            // is correct but 'brackets' is not.
 +            brackets = STRINGIZE('[' << brackets << ']');
 +        }
 +        else
 +        {
 +            // key was passed with brackets. That means that 'brackets' is
 +            // correct but 'nobrackets' is not. Erase the left bracket.
 +            nobrackets.erase(0, 1);
 +            std::string::size_type length(nobrackets.length());
 +            if (length && nobrackets[length - 1] == ']')
 +            {
 +                nobrackets.erase(length - 1);
 +            }
 +        }
 +        // Calling LLTrans::getString() is what embeds the other default
 +        // translation strings into this one.
 +        LLTrans::setDefaultArg(brackets, LLTrans::getString(nobrackets));
 +    }
 +}
 +
 +bool LLAppViewer::meetsRequirementsForMaximizedStart()
 +{
 +    bool maximizedOk = (gSysMemory.getPhysicalMemoryKB() >= U32Gigabytes(1));
 +
 +    return maximizedOk;
 +}
 +
 +bool LLAppViewer::initWindow()
 +{
 +    LL_INFOS("AppInit") << "Initializing window..." << LL_ENDL;
 +
 +    // store setting in a global for easy access and modification
 +    gHeadlessClient = gSavedSettings.getBOOL("HeadlessClient");
 +
 +    // always start windowed
 +    bool ignorePixelDepth = gSavedSettings.getBOOL("IgnorePixelDepth");
 +
 +    LLViewerWindow::Params window_params;
 +    window_params
 +        .title(gWindowTitle)
 +        .name(VIEWER_WINDOW_CLASSNAME)
 +        .x(gSavedSettings.getS32("WindowX"))
 +        .y(gSavedSettings.getS32("WindowY"))
 +        .width(gSavedSettings.getU32("WindowWidth"))
 +        .height(gSavedSettings.getU32("WindowHeight"))
 +        .min_width(gSavedSettings.getU32("MinWindowWidth"))
 +        .min_height(gSavedSettings.getU32("MinWindowHeight"))
 +        .fullscreen(gSavedSettings.getBOOL("FullScreen"))
 +        .ignore_pixel_depth(ignorePixelDepth)
 +        .first_run(mIsFirstRun);
 +
 +    gViewerWindow = new LLViewerWindow(window_params);
 +
 +    LL_INFOS("AppInit") << "gViewerwindow created." << LL_ENDL;
 +
 +    // Need to load feature table before cheking to start watchdog.
 +    bool use_watchdog = false;
 +    int watchdog_enabled_setting = gSavedSettings.getS32("WatchdogEnabled");
 +    if (watchdog_enabled_setting == -1)
 +    {
 +        use_watchdog = !LLFeatureManager::getInstance()->isFeatureAvailable("WatchdogDisabled");
 +    }
 +    else
 +    {
 +        // The user has explicitly set this setting; always use that value.
 +        use_watchdog = bool(watchdog_enabled_setting);
 +    }
 +
 +    LL_INFOS("AppInit") << "watchdog"
 +                        << (use_watchdog ? " " : " NOT ")
 +                        << "enabled"
 +                        << " (setting = " << watchdog_enabled_setting << ")"
 +                        << LL_ENDL;
 +
 +    if (use_watchdog)
 +    {
 +        LLWatchdog::getInstance()->init();
 +    }
 +
 +    LLNotificationsUI::LLNotificationManager::getInstance();
 +
 +
 +#ifdef LL_DARWIN
 +    //Satisfy both MAINT-3135 (OSX 10.6 and earlier) MAINT-3288 (OSX 10.7 and later)
 +    LLOSInfo& os_info = LLOSInfo::instance();
 +    if (os_info.mMajorVer == 10 && os_info.mMinorVer < 7)
 +    {
 +        if ( os_info.mMinorVer == 6 && os_info.mBuild < 8 )
 +            gViewerWindow->getWindow()->setOldResize(true);
 +    }
 +#endif
 +
 +    if (gSavedSettings.getBOOL("WindowMaximized"))
 +    {
 +        gViewerWindow->getWindow()->maximize();
 +    }
 +
 +    //
 +    // Initialize GL stuff
 +    //
 +
 +    if (mForceGraphicsLevel && (LLFeatureManager::instance().isValidGraphicsLevel(*mForceGraphicsLevel)))
 +    {
 +        LLFeatureManager::getInstance()->setGraphicsLevel(*mForceGraphicsLevel, false);
 +        gSavedSettings.setU32("RenderQualityPerformance", *mForceGraphicsLevel);
 +    }
 +
 +    // Set this flag in case we crash while initializing GL
 +    gSavedSettings.setBOOL("RenderInitError", true);
 +    gSavedSettings.saveToFile( gSavedSettings.getString("ClientSettingsFile"), true );
 +
 +    gPipeline.init();
 +    LL_INFOS("AppInit") << "gPipeline Initialized" << LL_ENDL;
 +
 +    stop_glerror();
 +    gViewerWindow->initGLDefaults();
 +
 +    gSavedSettings.setBOOL("RenderInitError", false);
 +    gSavedSettings.saveToFile( gSavedSettings.getString("ClientSettingsFile"), true );
 +
 +    //If we have a startup crash, it's usually near GL initialization, so simulate that.
 +    if(gCrashOnStartup)
 +    {
 +        LLAppViewer::instance()->forceErrorLLError();
 +    }
 +
 +    //
 +    // Determine if the window should start maximized on initial run based
 +    // on graphics capability
 +    //
 +    if (gSavedSettings.getBOOL("FirstLoginThisInstall") && meetsRequirementsForMaximizedStart())
 +    {
 +        LL_INFOS("AppInit") << "This client met the requirements for a maximized initial screen." << LL_ENDL;
 +        gSavedSettings.setBOOL("WindowMaximized", true);
 +    }
 +
 +    if (gSavedSettings.getBOOL("WindowMaximized"))
 +    {
 +        gViewerWindow->getWindow()->maximize();
 +    }
 +
 +    LLUI::getInstance()->mWindow = gViewerWindow->getWindow();
 +
 +    // Show watch cursor
 +    gViewerWindow->setCursor(UI_CURSOR_WAIT);
 +
 +    // Finish view initialization
 +    gViewerWindow->initBase();
 +
 +    // show viewer window
 +    //gViewerWindow->getWindow()->show();
 +
 +    LL_INFOS("AppInit") << "Window initialization done." << LL_ENDL;
 +
 +    return true;
 +}
 +
 +bool LLAppViewer::isUpdaterMissing()
 +{
 +    return mUpdaterNotFound;
 +}
 +
 +bool LLAppViewer::waitForUpdater()
 +{
 +    return !gSavedSettings.getBOOL("CmdLineSkipUpdater") && !mUpdaterNotFound && !gNonInteractive;
 +}
 +
 +void LLAppViewer::writeDebugInfo(bool isStatic)
 +{
 +#if LL_WINDOWS && LL_BUGSPLAT
 +    // bugsplat does not create dump folder and debug logs are written directly
 +    // to logs folder, so it conflicts with main instance
 +    if (mSecondInstance)
 +    {
 +        return;
 +    }
 +#endif
 +
 +    //Try to do the minimum when writing data during a crash.
 +    std::string* debug_filename;
 +    debug_filename = ( isStatic
 +        ? getStaticDebugFile()
 +        : getDynamicDebugFile() );
 +
 +    LL_INFOS() << "Writing debug file " << *debug_filename << LL_ENDL;
 +    llofstream out_file(debug_filename->c_str());
 +
 +    isStatic ?  LLSDSerialize::toPrettyXML(gDebugInfo, out_file)
 +             :  LLSDSerialize::toPrettyXML(gDebugInfo["Dynamic"], out_file);
 +}
 +
 +LLSD LLAppViewer::getViewerInfo() const
 +{
 +    // The point of having one method build an LLSD info block and the other
 +    // construct the user-visible About string is to ensure that the same info
 +    // is available to a getInfo() caller as to the user opening
 +    // LLFloaterAbout.
 +    LLSD info;
 +    auto& versionInfo(LLVersionInfo::instance());
 +    // With GitHub builds, the build number is too big to fit in a 32-bit int,
 +    // and LLSD doesn't deal with integers wider than int. Use string.
 +    info["VIEWER_VERSION"] = llsd::array(versionInfo.getMajor(), versionInfo.getMinor(),
 +                                         versionInfo.getPatch(), stringize(versionInfo.getBuild()));
 +    info["VIEWER_VERSION_STR"] = versionInfo.getVersion();
 +    info["CHANNEL"] = versionInfo.getChannel();
 +    info["ADDRESS_SIZE"] = ADDRESS_SIZE;
 +    std::string build_config = versionInfo.getBuildConfig();
 +    if (build_config != "Release")
 +    {
 +        info["BUILD_CONFIG"] = build_config;
 +    }
 +
 +    // return a URL to the release notes for this viewer, such as:
 +    // https://releasenotes.secondlife.com/viewer/2.1.0.123456.html
 +    std::string url = versionInfo.getReleaseNotes(); // VVM supplied
 +    if (url.empty())
 +    {
 +        url = LLTrans::getString("RELEASE_NOTES_BASE_URL");
 +        if (!LLStringUtil::endsWith(url, "/"))
 +            url += "/";
 +        url += LLURI::escape(versionInfo.getVersion()) + ".html";
 +    }
 +    info["VIEWER_RELEASE_NOTES_URL"] = url;
 +
 +    // Position
 +    LLViewerRegion* region = gAgent.getRegion();
 +    if (region)
 +    {
 +        LLVector3d pos = gAgent.getPositionGlobal();
 +        info["POSITION"] = ll_sd_from_vector3d(pos);
 +        info["POSITION_LOCAL"] = ll_sd_from_vector3(gAgent.getPosAgentFromGlobal(pos));
 +        info["REGION"] = gAgent.getRegion()->getName();
 +
 +        boost::regex regex("\\.(secondlife|lindenlab)\\..*");
 +        info["HOSTNAME"] = boost::regex_replace(gAgent.getRegion()->getSimHostName(), regex, "");
 +        info["SERVER_VERSION"] = gLastVersionChannel;
 +        LLSLURL slurl;
 +        LLAgentUI::buildSLURL(slurl);
 +        info["SLURL"] = slurl.getSLURLString();
 +    }
 +
 +    // CPU
 +    info["CPU"] = gSysCPU.getCPUString();
 +    info["MEMORY_MB"] = LLSD::Integer(gSysMemory.getPhysicalMemoryKB().valueInUnits<LLUnits::Megabytes>());
 +    // Moved hack adjustment to Windows memory size into llsys.cpp
 +    info["OS_VERSION"] = LLOSInfo::instance().getOSString();
 +    info["GRAPHICS_CARD_VENDOR"] = ll_safe_string((const char*)(glGetString(GL_VENDOR)));
 +    info["GRAPHICS_CARD"] = ll_safe_string((const char*)(glGetString(GL_RENDERER)));
 +
 +#if LL_WINDOWS
 +    std::string drvinfo;
 +
 +    if (gGLManager.mIsIntel)
 +    {
 +        drvinfo = gDXHardware.getDriverVersionWMI(LLDXHardware::GPU_INTEL);
 +    }
 +    else if (gGLManager.mIsNVIDIA)
 +    {
 +        drvinfo = gDXHardware.getDriverVersionWMI(LLDXHardware::GPU_NVIDIA);
 +    }
 +    else if (gGLManager.mIsAMD)
 +    {
 +        drvinfo = gDXHardware.getDriverVersionWMI(LLDXHardware::GPU_AMD);
 +    }
 +
 +    if (drvinfo.empty())
 +    {
 +        // Generic/substitute windows driver? Unknown vendor?
 +        LL_WARNS("DriverVersion") << "Vendor based driver search failed, searching for any driver" << LL_ENDL;
 +        drvinfo = gDXHardware.getDriverVersionWMI(LLDXHardware::GPU_ANY);
 +    }
 +
 +    if (!drvinfo.empty())
 +    {
 +        info["GRAPHICS_DRIVER_VERSION"] = drvinfo;
 +    }
 +    else
 +    {
 +        LL_WARNS("DriverVersion")<< "Cannot get driver version from getDriverVersionWMI" << LL_ENDL;
 +        LLSD driver_info = gDXHardware.getDisplayInfo();
 +        if (driver_info.has("DriverVersion"))
 +        {
 +            info["GRAPHICS_DRIVER_VERSION"] = driver_info["DriverVersion"];
 +        }
 +    }
 +#endif
 +
 +    info["OPENGL_VERSION"] = ll_safe_string((const char*)(glGetString(GL_VERSION)));
 +
 +    // Settings
 +
 +    LLRect window_rect = gViewerWindow->getWindowRectRaw();
 +    info["WINDOW_WIDTH"] = window_rect.getWidth();
 +    info["WINDOW_HEIGHT"] = window_rect.getHeight();
 +    info["FONT_SIZE_ADJUSTMENT"] = gSavedSettings.getF32("FontScreenDPI");
 +    info["UI_SCALE"] = gSavedSettings.getF32("UIScaleFactor");
 +    info["DRAW_DISTANCE"] = gSavedSettings.getF32("RenderFarClip");
 +    info["NET_BANDWITH"] = gSavedSettings.getF32("ThrottleBandwidthKBPS");
 +    info["LOD_FACTOR"] = gSavedSettings.getF32("RenderVolumeLODFactor");
 +    info["RENDER_QUALITY"] = (F32)gSavedSettings.getU32("RenderQualityPerformance");
 +    info["TEXTURE_MEMORY"] = gGLManager.mVRAM;
 +
 +#if LL_DARWIN
 +    info["HIDPI"] = gHiDPISupport;
 +#endif
 +
 +    // Libraries
 +
 +    info["J2C_VERSION"] = LLImageJ2C::getEngineInfo();
 +    bool want_fullname = true;
 +    info["AUDIO_DRIVER_VERSION"] = gAudiop ? LLSD(gAudiop->getDriverName(want_fullname)) : "Undefined";
 +    if(LLVoiceClient::getInstance()->voiceEnabled())
 +    {
 +        LLVoiceVersionInfo version = LLVoiceClient::getInstance()->getVersion();
 +        const std::string build_version = version.mBuildVersion;
 +        std::ostringstream version_string;
 +        if (std::equal(build_version.begin(), build_version.begin() + version.serverVersion.size(),
 +                       version.serverVersion.begin()))
 +        {  // Normal case: Show type and build version.
 +            version_string << version.serverType << " " << build_version << std::endl;
 +        }
 +        else
 +        {  // Mismatch: Show both versions.
 +            version_string << version.serverVersion << "/" << build_version << std::endl;
 +        }
 +        info["VOICE_VERSION"] = version_string.str();
 +    }
 +    else
 +    {
 +        info["VOICE_VERSION"] = LLTrans::getString("NotConnected");
 +    }
 +
 +#if !LL_LINUX
 +    std::ostringstream cef_ver_codec;
 +    cef_ver_codec << "Dullahan: ";
 +    cef_ver_codec << DULLAHAN_VERSION_MAJOR;
 +    cef_ver_codec << ".";
 +    cef_ver_codec << DULLAHAN_VERSION_MINOR;
 +    cef_ver_codec << ".";
 +    cef_ver_codec << DULLAHAN_VERSION_POINT;
 +    cef_ver_codec << ".";
 +    cef_ver_codec << DULLAHAN_VERSION_BUILD;
 +
 +    cef_ver_codec << std::endl;
 +    cef_ver_codec << "  CEF: ";
 +    cef_ver_codec << CEF_VERSION;
 +
 +    cef_ver_codec << std::endl;
 +    cef_ver_codec << "  Chromium: ";
 +    cef_ver_codec << CHROME_VERSION_MAJOR;
 +    cef_ver_codec << ".";
 +    cef_ver_codec << CHROME_VERSION_MINOR;
 +    cef_ver_codec << ".";
 +    cef_ver_codec << CHROME_VERSION_BUILD;
 +    cef_ver_codec << ".";
 +    cef_ver_codec << CHROME_VERSION_PATCH;
 +
 +    info["LIBCEF_VERSION"] = cef_ver_codec.str();
 +#else
 +    info["LIBCEF_VERSION"] = "Undefined";
 +#endif
 +
 +#if !LL_LINUX
 +    std::ostringstream vlc_ver_codec;
 +    vlc_ver_codec << LIBVLC_VERSION_MAJOR;
 +    vlc_ver_codec << ".";
 +    vlc_ver_codec << LIBVLC_VERSION_MINOR;
 +    vlc_ver_codec << ".";
 +    vlc_ver_codec << LIBVLC_VERSION_REVISION;
 +    info["LIBVLC_VERSION"] = vlc_ver_codec.str();
 +#else
 +    info["LIBVLC_VERSION"] = "Undefined";
 +#endif
 +
 +    S32 packets_in = LLViewerStats::instance().getRecording().getSum(LLStatViewer::PACKETS_IN);
 +    if (packets_in > 0)
 +    {
 +        info["PACKETS_LOST"] = LLViewerStats::instance().getRecording().getSum(LLStatViewer::PACKETS_LOST);
 +        info["PACKETS_IN"] = packets_in;
 +        info["PACKETS_PCT"] = 100.f*info["PACKETS_LOST"].asReal() / info["PACKETS_IN"].asReal();
 +    }
 +
 +    if (mServerReleaseNotesURL.empty())
 +    {
 +        if (gAgent.getRegion())
 +        {
 +            info["SERVER_RELEASE_NOTES_URL"] = LLTrans::getString("RetrievingData");
 +        }
 +        else
 +        {
 +            info["SERVER_RELEASE_NOTES_URL"] = LLTrans::getString("NotConnected");
 +        }
 +    }
 +    else if (LLStringUtil::startsWith(mServerReleaseNotesURL, "http")) // it's an URL
 +    {
 +        info["SERVER_RELEASE_NOTES_URL"] = "[" + LLWeb::escapeURL(mServerReleaseNotesURL) + " " + LLTrans::getString("ReleaseNotes") + "]";
 +    }
 +    else
 +    {
 +        info["SERVER_RELEASE_NOTES_URL"] = mServerReleaseNotesURL;
 +    }
 +
 +    // populate field for new local disk cache with some details
 +    info["DISK_CACHE_INFO"] = LLDiskCache::getInstance()->getCacheInfo();
 +
 +    return info;
 +}
 +
 +std::string LLAppViewer::getViewerInfoString(bool default_string) const
 +{
 +    std::ostringstream support;
 +
 +    LLSD info(getViewerInfo());
 +
 +    // Render the LLSD from getInfo() as a format_map_t
 +    LLStringUtil::format_map_t args;
 +
 +    // allow the "Release Notes" URL label to be localized
 +    args["ReleaseNotes"] = LLTrans::getString("ReleaseNotes", default_string);
 +
 +    for (LLSD::map_const_iterator ii(info.beginMap()), iend(info.endMap());
 +        ii != iend; ++ii)
 +    {
 +        if (! ii->second.isArray())
 +        {
 +            // Scalar value
 +            if (ii->second.isUndefined())
 +            {
 +                args[ii->first] = LLTrans::getString("none_text", default_string);
 +            }
 +            else
 +            {
 +                // don't forget to render value asString()
 +                args[ii->first] = ii->second.asString();
 +            }
 +        }
 +        else
 +        {
 +            // array value: build KEY_0, KEY_1 etc. entries
 +            for (LLSD::Integer n(0), size(ii->second.size()); n < size; ++n)
 +            {
 +                args[STRINGIZE(ii->first << '_' << n)] = ii->second[n].asString();
 +            }
 +        }
 +    }
 +
 +    // Now build the various pieces
 +    support << LLTrans::getString("AboutHeader", args, default_string);
 +    if (info.has("BUILD_CONFIG"))
 +    {
 +        support << "\n" << LLTrans::getString("BuildConfig", args, default_string);
 +    }
 +    if (info.has("REGION"))
 +    {
 +        support << "\n\n" << LLTrans::getString("AboutPosition", args, default_string);
 +    }
 +    support << "\n\n" << LLTrans::getString("AboutSystem", args, default_string);
 +    support << "\n";
 +    if (info.has("GRAPHICS_DRIVER_VERSION"))
 +    {
 +        support << "\n" << LLTrans::getString("AboutDriver", args, default_string);
 +    }
 +    support << "\n" << LLTrans::getString("AboutOGL", args, default_string);
 +    support << "\n\n" << LLTrans::getString("AboutSettings", args, default_string);
 +#if LL_DARWIN
 +    support << "\n" << LLTrans::getString("AboutOSXHiDPI", args, default_string);
 +#endif
 +    support << "\n\n" << LLTrans::getString("AboutLibs", args, default_string);
 +    if (info.has("COMPILER"))
 +    {
 +        support << "\n" << LLTrans::getString("AboutCompiler", args, default_string);
 +    }
 +    if (info.has("PACKETS_IN"))
 +    {
 +        support << '\n' << LLTrans::getString("AboutTraffic", args, default_string);
 +    }
 +
 +    // SLT timestamp
 +    LLSD substitution;
 +    substitution["datetime"] = (S32)time(NULL);//(S32)time_corrected();
 +    support << "\n" << LLTrans::getString("AboutTime", substitution, default_string);
 +
 +    return support.str();
 +}
 +
 +void LLAppViewer::cleanupSavedSettings()
 +{
 +    gSavedSettings.setBOOL("MouseSun", false);
 +
 +    gSavedSettings.setBOOL("UseEnergy", true);              // force toggle to turn off, since sends message to simulator
 +
 +    gSavedSettings.setBOOL("DebugWindowProc", gDebugWindowProc);
 +
 +    gSavedSettings.setBOOL("ShowObjectUpdates", gShowObjectUpdates);
 +
 +    if (gDebugView)
 +    {
 +        gSavedSettings.setBOOL("ShowDebugConsole", gDebugView->mDebugConsolep->getVisible());
 +    }
 +
 +    // save window position if not maximized
 +    // as we don't track it in callbacks
 +    if(NULL != gViewerWindow)
 +    {
 +        bool maximized = gViewerWindow->getWindow()->getMaximized();
 +        if (!maximized)
 +        {
 +            LLCoordScreen window_pos;
 +
 +            if (gViewerWindow->getWindow()->getPosition(&window_pos))
 +            {
 +                gSavedSettings.setS32("WindowX", window_pos.mX);
 +                gSavedSettings.setS32("WindowY", window_pos.mY);
 +            }
 +        }
 +    }
 +
 +    gSavedSettings.setF32("MapScale", LLWorldMapView::getScaleSetting());
 +
 +    // Some things are cached in LLAgent.
 +    if (gAgent.isInitialized())
 +    {
 +        gSavedSettings.setF32("RenderFarClip", gAgentCamera.mDrawDistance);
 +    }
 +}
 +
 +void LLAppViewer::removeCacheFiles(const std::string& file_mask)
 +{
 +    gDirUtilp->deleteFilesInDir(gDirUtilp->getExpandedFilename(LL_PATH_CACHE, ""), file_mask);
 +}
 +
 +void LLAppViewer::writeSystemInfo()
 +{
 +
 +    if (! gDebugInfo.has("Dynamic") )
 +        gDebugInfo["Dynamic"] = LLSD::emptyMap();
 +
 +#if LL_WINDOWS && !LL_BUGSPLAT
 +    gDebugInfo["SLLog"] = gDirUtilp->getExpandedFilename(LL_PATH_DUMP,"SecondLife.log");
 +#else
 +    //Not ideal but sufficient for good reporting.
 +    gDebugInfo["SLLog"] = gDirUtilp->getExpandedFilename(LL_PATH_LOGS,"SecondLife.old");  //LLError::logFileName();
 +#endif
 +
 +    gDebugInfo["ClientInfo"]["Name"] = LLVersionInfo::instance().getChannel();
 +    gDebugInfo["ClientInfo"]["MajorVersion"] = LLVersionInfo::instance().getMajor();
 +    gDebugInfo["ClientInfo"]["MinorVersion"] = LLVersionInfo::instance().getMinor();
 +    gDebugInfo["ClientInfo"]["PatchVersion"] = LLVersionInfo::instance().getPatch();
 +    gDebugInfo["ClientInfo"]["BuildVersion"] = std::to_string(LLVersionInfo::instance().getBuild());
 +    gDebugInfo["ClientInfo"]["AddressSize"] = LLVersionInfo::instance().getAddressSize();
 +
 +    gDebugInfo["CAFilename"] = gDirUtilp->getCAFile();
 +
 +    gDebugInfo["CPUInfo"]["CPUString"] = gSysCPU.getCPUString();
 +    gDebugInfo["CPUInfo"]["CPUFamily"] = gSysCPU.getFamily();
 +    gDebugInfo["CPUInfo"]["CPUMhz"] = (S32)gSysCPU.getMHz();
 +    gDebugInfo["CPUInfo"]["CPUAltivec"] = gSysCPU.hasAltivec();
 +    gDebugInfo["CPUInfo"]["CPUSSE"] = gSysCPU.hasSSE();
 +    gDebugInfo["CPUInfo"]["CPUSSE2"] = gSysCPU.hasSSE2();
 +
 +    gDebugInfo["RAMInfo"]["Physical"] = LLSD::Integer(gSysMemory.getPhysicalMemoryKB().value());
 +    gDebugInfo["RAMInfo"]["Allocated"] = LLSD::Integer(gMemoryAllocated.valueInUnits<LLUnits::Kilobytes>());
 +    gDebugInfo["OSInfo"] = LLOSInfo::instance().getOSStringSimple();
 +
 +    // The user is not logged on yet, but record the current grid choice login url
 +    // which may have been the intended grid.
 +    gDebugInfo["GridName"] = LLGridManager::getInstance()->getGridId();
 +
 +    // *FIX:Mani - move this down in llappviewerwin32
 +#ifdef LL_WINDOWS
 +    DWORD thread_id = GetCurrentThreadId();
 +    gDebugInfo["MainloopThreadID"] = (S32)thread_id;
 +#endif
 +
 +#ifndef LL_BUGSPLAT
 +    // "CrashNotHandled" is set here, while things are running well,
 +    // in case of a freeze. If there is a freeze, the crash logger will be launched
 +    // and can read this value from the debug_info.log.
 +    gDebugInfo["CrashNotHandled"] = LLSD::Boolean(true);
 +#else // LL_BUGSPLAT
 +    // "CrashNotHandled" is obsolete; it used (not very successsfully)
 +    // to try to distinguish crashes from freezes - the intent here to to avoid calling it a freeze
 +    gDebugInfo["CrashNotHandled"] = LLSD::Boolean(false);
 +#endif // ! LL_BUGSPLAT
 +
 +    // Insert crash host url (url to post crash log to) if configured. This insures
 +    // that the crash report will go to the proper location in the case of a
 +    // prior freeze.
 +    std::string crashHostUrl = gSavedSettings.get<std::string>("CrashHostUrl");
 +    if(crashHostUrl != "")
 +    {
 +        gDebugInfo["CrashHostUrl"] = crashHostUrl;
 +    }
 +
 +    // Dump some debugging info
 +    LL_INFOS("SystemInfo") << "Application: " << LLTrans::getString("APP_NAME") << LL_ENDL;
 +    LL_INFOS("SystemInfo") << "Version: " << LLVersionInfo::instance().getChannelAndVersion() << LL_ENDL;
 +
 +    // Dump the local time and time zone
 +    time_t now;
 +    time(&now);
 +    char tbuffer[256];      /* Flawfinder: ignore */
 +    strftime(tbuffer, 256, "%Y-%m-%dT%H:%M:%S %Z", localtime(&now));
 +    LL_INFOS("SystemInfo") << "Local time: " << tbuffer << LL_ENDL;
 +
 +    // query some system information
 +    LL_INFOS("SystemInfo") << "CPU info:\n" << gSysCPU << LL_ENDL;
 +    LL_INFOS("SystemInfo") << "Memory info:\n" << gSysMemory << LL_ENDL;
 +    LL_INFOS("SystemInfo") << "OS: " << LLOSInfo::instance().getOSStringSimple() << LL_ENDL;
 +    LL_INFOS("SystemInfo") << "OS info: " << LLOSInfo::instance() << LL_ENDL;
 +
 +    gDebugInfo["SettingsFilename"] = gSavedSettings.getString("ClientSettingsFile");
 +    gDebugInfo["ViewerExePath"] = gDirUtilp->getExecutablePathAndName();
 +    gDebugInfo["CurrentPath"] = gDirUtilp->getCurPath();
 +    gDebugInfo["FirstLogin"] = LLSD::Boolean(gAgent.isFirstLogin());
 +    gDebugInfo["FirstRunThisInstall"] = gSavedSettings.getBOOL("FirstRunThisInstall");
 +    gDebugInfo["StartupState"] = LLStartUp::getStartupStateString();
 +
 +    if (gViewerWindow)
 +    {
 +    std::vector<std::string> resolutions = gViewerWindow->getWindow()->getDisplaysResolutionList();
 +    for (auto res_iter : resolutions)
 +    {
 +        gDebugInfo["DisplayInfo"].append(res_iter);
 +    }
 +    }
 +
 +    writeDebugInfo(); // Save out debug_info.log early, in case of crash.
 +}
 +
 +#ifdef LL_WINDOWS
 +//For whatever reason, in Windows when using OOP server for breakpad, the callback to get the
 +//name of the dump file is not getting triggered by the breakpad library.   Unfortunately they
 +//also didn't see fit to provide a simple query request across the pipe to get this name either.
 +//Since we are putting our output in a runtime generated directory and we know the header data in
 +//the dump format, we can however use the following hack to identify our file.
 +// TODO make this a member function.
 +void getFileList()
 +{
 +    std::stringstream filenames;
 +
 +    typedef std::vector<std::string> vec;
 +    std::string pathname = gDirUtilp->getExpandedFilename(LL_PATH_DUMP,"");
 +    vec file_vec = gDirUtilp->getFilesInDir(pathname);
 +    for(vec::const_iterator iter=file_vec.begin(); iter!=file_vec.end(); ++iter)
 +    {
 +        filenames << *iter << " ";
 +        if ( ( iter->length() > 30 ) && (iter->rfind(".dmp") == (iter->length()-4) ) )
 +        {
 +            std::string fullname = pathname + *iter;
 +            llifstream fdat( fullname.c_str(), std::ifstream::binary);
 +            if (fdat)
 +            {
 +                char buf[5];
 +                fdat.read(buf,4);
 +                fdat.close();
 +                if (!strncmp(buf,"MDMP",4))
 +                {
 +                    gDebugInfo["Dynamic"]["MinidumpPath"] = fullname;
 +                    break;
 +                }
 +            }
 +        }
 +    }
 +    filenames << std::endl;
 +    gDebugInfo["Dynamic"]["DumpDirContents"] = filenames.str();
 +}
 +#endif
 +
 +// static
 +void LLAppViewer::recordMarkerVersion(LLAPRFile& marker_file)
 +{
 +    std::string marker_version(LLVersionInfo::instance().getChannelAndVersion());
 +    if ( marker_version.length() > MAX_MARKER_LENGTH )
 +    {
 +        LL_WARNS_ONCE("MarkerFile") << "Version length ("<< marker_version.length()<< ")"
 +                                    << " greater than maximum (" << MAX_MARKER_LENGTH << ")"
 +                                    << ": marker matching may be incorrect"
 +                                    << LL_ENDL;
 +    }
 +
 +    // record the viewer version in the marker file
 +    marker_file.write(marker_version.data(), marker_version.length());
 +}
 +
 +bool LLAppViewer::markerIsSameVersion(const std::string& marker_name) const
 +{
 +    bool sameVersion = false;
 +
 +    std::string my_version(LLVersionInfo::instance().getChannelAndVersion());
 +    char marker_version[MAX_MARKER_LENGTH];
 +    S32  marker_version_length;
 +
 +    LLAPRFile marker_file;
 +    marker_file.open(marker_name, LL_APR_RB);
 +    if (marker_file.getFileHandle())
 +    {
 +        marker_version_length = marker_file.read(marker_version, sizeof(marker_version));
 +        std::string marker_string(marker_version, marker_version_length);
 +        if ( 0 == my_version.compare( 0, my_version.length(), marker_version, 0, marker_version_length ) )
 +        {
 +            sameVersion = true;
 +        }
 +        LL_DEBUGS("MarkerFile") << "Compare markers for '" << marker_name << "': "
 +                                << "\n   mine '" << my_version    << "'"
 +                                << "\n marker '" << marker_string << "'"
 +                                << "\n " << ( sameVersion ? "same" : "different" ) << " version"
 +                                << LL_ENDL;
 +        marker_file.close();
 +    }
 +    return sameVersion;
 +}
 +
 +void LLAppViewer::processMarkerFiles()
 +{
 +    //We've got 4 things to test for here
 +    // - Other Process Running (SecondLife.exec_marker present, locked)
 +    // - Freeze (SecondLife.exec_marker present, not locked)
 +    // - LLError Crash (SecondLife.llerror_marker present)
 +    // - Other Crash (SecondLife.error_marker present)
 +    // These checks should also remove these files for the last 2 cases if they currently exist
 +
 +    std::ostringstream marker_log_stream;
 +    bool marker_is_same_version = true;
 +    // first, look for the marker created at startup and deleted on a clean exit
 +    mMarkerFileName = gDirUtilp->getExpandedFilename(LL_PATH_LOGS,MARKER_FILE_NAME);
 +    if (LLAPRFile::isExist(mMarkerFileName, NULL, LL_APR_RB))
 +    {
 +        // File exists...
 +        // first, read it to see if it was created by the same version (we need this later)
 +        marker_is_same_version = markerIsSameVersion(mMarkerFileName);
 +
 +        // now test to see if this file is locked by a running process (try to open for write)
 +        marker_log_stream << "Checking exec marker file for lock...";
 +        mMarkerFile.open(mMarkerFileName, LL_APR_WB);
 +        apr_file_t* fMarker = mMarkerFile.getFileHandle() ;
 +        if (!fMarker)
 +        {
 +            marker_log_stream << "Exec marker file open failed - assume it is locked.";
 +            mSecondInstance = true; // lock means that instance is running.
 +        }
 +        else
 +        {
 +            // We were able to open it, now try to lock it ourselves...
 +            if (apr_file_lock(fMarker, APR_FLOCK_NONBLOCK | APR_FLOCK_EXCLUSIVE) != APR_SUCCESS)
 +            {
 +                marker_log_stream << "Locking exec marker failed.";
 +                mSecondInstance = true; // lost a race? be conservative
 +            }
 +            else
 +            {
 +                // No other instances; we've locked this file now, so record our version; delete on quit.
 +                recordMarkerVersion(mMarkerFile);
 +                marker_log_stream << "Exec marker file existed but was not locked; rewritten.";
 +            }
 +        }
 +        initLoggingAndGetLastDuration();
 +
 +        std::string marker_log_msg(marker_log_stream.str());
 +        LL_INFOS("MarkerFile") << marker_log_msg << LL_ENDL;
 +
 +        if (mSecondInstance)
 +        {
 +            LL_INFOS("MarkerFile") << "Exec marker '"<< mMarkerFileName << "' owned by another instance" << LL_ENDL;
 +        }
 +        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;
 +            gLastExecEvent = LAST_EXEC_OTHER_CRASH;
 +        }
 +        else
 +        {
 +            LL_INFOS("MarkerFile") << "Exec marker '"<< mMarkerFileName << "' found, but versions did not match" << LL_ENDL;
 +        }
 +    }
 +    else // marker did not exist... last exec (if any) did not freeze
 +    {
 +        initLoggingAndGetLastDuration();
 +        // Create the marker file for this execution & lock it; it will be deleted on a clean exit
 +        apr_status_t s;
 +        s = mMarkerFile.open(mMarkerFileName, LL_APR_WB, true);
 +
 +        if (s == APR_SUCCESS && mMarkerFile.getFileHandle())
 +        {
 +            LL_DEBUGS("MarkerFile") << "Exec marker file '"<< mMarkerFileName << "' created." << LL_ENDL;
 +            if (APR_SUCCESS == apr_file_lock(mMarkerFile.getFileHandle(), APR_FLOCK_NONBLOCK | APR_FLOCK_EXCLUSIVE))
 +            {
 +                recordMarkerVersion(mMarkerFile);
 +                LL_DEBUGS("MarkerFile") << "Exec marker file locked." << LL_ENDL;
 +            }
 +            else
 +            {
 +                LL_WARNS("MarkerFile") << "Exec marker file cannot be locked." << LL_ENDL;
 +            }
 +        }
 +        else
 +        {
 +            LL_WARNS("MarkerFile") << "Failed to create exec marker file '"<< mMarkerFileName << "'." << LL_ENDL;
 +        }
 +    }
 +
 +    // now check for cases in which the exec marker may have been cleaned up by crash handlers
 +
 +    // check for any last exec event report based on whether or not it happened during logout
 +    // (the logout marker is created when logout begins)
 +    std::string logout_marker_file =  gDirUtilp->getExpandedFilename(LL_PATH_LOGS, LOGOUT_MARKER_FILE_NAME);
 +    if(LLAPRFile::isExist(logout_marker_file, NULL, LL_APR_RB))
 +    {
 +        if (markerIsSameVersion(logout_marker_file))
 +        {
 +            gLastExecEvent = LAST_EXEC_LOGOUT_FROZE;
 +            LL_INFOS("MarkerFile") << "Logout crash marker '"<< logout_marker_file << "', changing LastExecEvent to LOGOUT_FROZE" << LL_ENDL;
 +        }
 +        else
 +        {
 +            LL_INFOS("MarkerFile") << "Logout crash marker '"<< logout_marker_file << "' found, but versions did not match" << LL_ENDL;
 +        }
 +        LLAPRFile::remove(logout_marker_file);
 +    }
 +    // further refine based on whether or not a marker created during an llerr crash is found
 +    std::string llerror_marker_file = gDirUtilp->getExpandedFilename(LL_PATH_LOGS, LLERROR_MARKER_FILE_NAME);
 +    if(LLAPRFile::isExist(llerror_marker_file, NULL, LL_APR_RB))
 +    {
 +        if (markerIsSameVersion(llerror_marker_file))
 +        {
 +            if ( gLastExecEvent == LAST_EXEC_LOGOUT_FROZE )
 +            {
 +                gLastExecEvent = LAST_EXEC_LOGOUT_CRASH;
 +                LL_INFOS("MarkerFile") << "LLError marker '"<< llerror_marker_file << "' crashed, setting LastExecEvent to LOGOUT_CRASH" << LL_ENDL;
 +            }
 +            else
 +            {
 +                gLastExecEvent = LAST_EXEC_LLERROR_CRASH;
 +                LL_INFOS("MarkerFile") << "LLError marker '"<< llerror_marker_file << "' crashed, setting LastExecEvent to LLERROR_CRASH" << LL_ENDL;
 +            }
 +        }
 +        else
 +        {
 +            LL_INFOS("MarkerFile") << "LLError marker '"<< llerror_marker_file << "' found, but versions did not match" << LL_ENDL;
 +        }
 +        LLAPRFile::remove(llerror_marker_file);
 +    }
 +    // and last refine based on whether or not a marker created during a non-llerr crash is found
 +    std::string error_marker_file = gDirUtilp->getExpandedFilename(LL_PATH_LOGS, ERROR_MARKER_FILE_NAME);
 +    if(LLAPRFile::isExist(error_marker_file, NULL, LL_APR_RB))
 +    {
 +        if (markerIsSameVersion(error_marker_file))
 +        {
 +            if (gLastExecEvent == LAST_EXEC_LOGOUT_FROZE)
 +            {
 +                gLastExecEvent = LAST_EXEC_LOGOUT_CRASH;
 +                LL_INFOS("MarkerFile") << "Error marker '"<< error_marker_file << "' crashed, setting LastExecEvent to LOGOUT_CRASH" << LL_ENDL;
 +            }
 +            else
 +            {
 +                gLastExecEvent = LAST_EXEC_OTHER_CRASH;
 +                LL_INFOS("MarkerFile") << "Error marker '"<< error_marker_file << "' crashed, setting LastExecEvent to " << gLastExecEvent << LL_ENDL;
 +            }
 +        }
 +        else
 +        {
 +            LL_INFOS("MarkerFile") << "Error marker '"<< error_marker_file << "' marker found, but versions did not match" << LL_ENDL;
 +        }
 +        LLAPRFile::remove(error_marker_file);
 +    }
 +}
 +
 +void LLAppViewer::removeMarkerFiles()
 +{
 +    if (!mSecondInstance)
 +    {
 +        if (mMarkerFile.getFileHandle())
 +        {
 +            mMarkerFile.close() ;
 +            LLAPRFile::remove( mMarkerFileName );
 +            LL_DEBUGS("MarkerFile") << "removed exec marker '"<<mMarkerFileName<<"'"<< LL_ENDL;
 +        }
 +        else
 +        {
 +            LL_WARNS("MarkerFile") << "marker '"<<mMarkerFileName<<"' not open"<< LL_ENDL;
 +        }
 +
 +        if (mLogoutMarkerFile.getFileHandle())
 +        {
 +            mLogoutMarkerFile.close();
 +            LLAPRFile::remove( mLogoutMarkerFileName );
 +            LL_DEBUGS("MarkerFile") << "removed logout marker '"<<mLogoutMarkerFileName<<"'"<< LL_ENDL;
 +        }
 +        else
 +        {
 +            LL_WARNS("MarkerFile") << "logout marker '"<<mLogoutMarkerFileName<<"' not open"<< LL_ENDL;
 +        }
 +    }
 +    else
 +    {
 +        LL_WARNS("MarkerFile") << "leaving markers because this is a second instance" << LL_ENDL;
 +    }
 +}
 +
 +void LLAppViewer::removeDumpDir()
 +{
 +    //Call this routine only on clean exit.  Crash reporter will clean up
 +    //its locking table for us.
 +    if (gDirUtilp->dumpDirExists()) // Check if dump dir was created this run
 +    {
 +        std::string dump_dir = gDirUtilp->getExpandedFilename(LL_PATH_DUMP, "");
 +        gDirUtilp->deleteDirAndContents(dump_dir);
 +    }
 +
 +    if (mSecondInstance && !isError())
 +    {
 +        std::string log_filename = LLError::logFileName();
 +        LLError::logToFile("");
 +        LLFile::remove(log_filename);
 +    }
 +}
 +
 +void LLAppViewer::forceQuit()
 +{
 +    LLApp::setQuitting();
 +}
 +
 +//TODO: remove
 +void LLAppViewer::fastQuit(S32 error_code)
 +{
 +    // finish pending transfers
 +    flushLFSIO();
 +    // let sim know we're logging out
 +    sendLogoutRequest();
 +    // flush network buffers by shutting down messaging system
 +    end_messaging_system();
 +    // figure out the error code
 +    S32 final_error_code = error_code ? error_code : (S32)isError();
 +    // this isn't a crash
 +    removeMarkerFiles();
 +    // get outta here
 +    _exit(final_error_code);
 +}
 +
 +void LLAppViewer::requestQuit()
 +{
 +    LL_INFOS() << "requestQuit" << LL_ENDL;
 +
 +    LLViewerRegion* region = gAgent.getRegion();
 +
 +    if( (LLStartUp::getStartupState() < STATE_STARTED) || !region )
 +    {
 +        // If we have a region, make some attempt to send a logout request first.
 +        // This prevents the halfway-logged-in avatar from hanging around inworld for a couple minutes.
 +        if(region)
 +        {
 +            sendLogoutRequest();
 +        }
 +
 +        // Quit immediately
 +        forceQuit();
 +        return;
 +    }
 +
 +    // Try to send metrics back to the grid
 +    metricsSend(!gDisconnected);
 +
 +    // Try to send last batch of avatar rez metrics.
 +    if (!gDisconnected && isAgentAvatarValid())
 +    {
 +        gAgentAvatarp->updateAvatarRezMetrics(true); // force a last packet to be sent.
 +    }
 +
 +    LLHUDEffectSpiral *effectp = (LLHUDEffectSpiral*)LLHUDManager::getInstance()->createViewerEffect(LLHUDObject::LL_HUD_EFFECT_POINT, true);
 +    effectp->setPositionGlobal(gAgent.getPositionGlobal());
 +    effectp->setColor(LLColor4U(gAgent.getEffectColor()));
 +    LLHUDManager::getInstance()->sendEffects();
 +    effectp->markDead() ;//remove it.
 +
 +    // Attempt to close all floaters that might be
 +    // editing things.
 +    if (gFloaterView)
 +    {
 +        // application is quitting
 +        gFloaterView->closeAllChildren(true);
 +    }
 +
 +    // Send preferences once, when exiting
 +    bool include_preferences = true;
 +    send_viewer_stats(include_preferences);
 +
 +    gLogoutTimer.reset();
 +    mQuitRequested = true;
 +}
 +
 +static bool finish_quit(const LLSD& notification, const LLSD& response)
 +{
 +    S32 option = LLNotificationsUtil::getSelectedOption(notification, response);
 +
 +    if (option == 0)
 +    {
 +        LLAppViewer::instance()->requestQuit();
 +    }
 +    return false;
 +}
 +static LLNotificationFunctorRegistration finish_quit_reg("ConfirmQuit", finish_quit);
 +
 +void LLAppViewer::userQuit()
 +{
 +    LL_INFOS() << "User requested quit" << LL_ENDL;
 +    if (gDisconnected
 +        || !gViewerWindow
 +        || !gViewerWindow->getProgressView()
 +        || gViewerWindow->getProgressView()->getVisible())
 +    {
 +        requestQuit();
 +    }
 +    else
 +    {
 +        LLNotificationsUtil::add("ConfirmQuit");
 +    }
 +}
 +
 +static bool finish_early_exit(const LLSD& notification, const LLSD& response)
 +{
 +    LLAppViewer::instance()->forceQuit();
 +    return false;
 +}
 +
 +void LLAppViewer::earlyExit(const std::string& name, const LLSD& substitutions)
 +{
 +    LL_WARNS() << "app_early_exit: " << name << LL_ENDL;
 +    gDoDisconnect = true;
 +    LLNotificationsUtil::add(name, substitutions, LLSD(), finish_early_exit);
 +}
 +
 +// 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;
 +    gDoDisconnect = true;
 +    finish_early_exit( LLSD(), LLSD() );
 +}
 +
 +void LLAppViewer::abortQuit()
 +{
 +    LL_INFOS() << "abortQuit()" << LL_ENDL;
 +    mQuitRequested = false;
 +}
 +
 +void LLAppViewer::migrateCacheDirectory()
 +{
 +#if LL_WINDOWS || LL_DARWIN
 +    // NOTE: (Nyx) as of 1.21, cache for mac is moving to /library/caches/SecondLife from
 +    // /library/application support/SecondLife/cache This should clear/delete the old dir.
 +
 +    // As of 1.23 the Windows cache moved from
 +    //   C:\Documents and Settings\James\Application Support\SecondLife\cache
 +    // to
 +    //   C:\Documents and Settings\James\Local Settings\Application Support\SecondLife
 +    //
 +    // The Windows Vista equivalent is from
 +    //   C:\Users\James\AppData\Roaming\SecondLife\cache
 +    // to
 +    //   C:\Users\James\AppData\Local\SecondLife
 +    //
 +    // Note the absence of \cache on the second path.  James.
 +
 +    // Only do this once per fresh install of this version.
 +    if (gSavedSettings.getBOOL("MigrateCacheDirectory"))
 +    {
 +        gSavedSettings.setBOOL("MigrateCacheDirectory", false);
 +
 +        std::string old_cache_dir = gDirUtilp->add(gDirUtilp->getOSUserAppDir(), "cache");
 +        std::string new_cache_dir = gDirUtilp->getCacheDir(true);
 +
 +        if (gDirUtilp->fileExists(old_cache_dir))
 +        {
 +            LL_INFOS() << "Migrating cache from " << old_cache_dir << " to " << new_cache_dir << LL_ENDL;
 +
 +            // Migrate inventory cache to avoid pain to inventory database after mass update
 +            S32 file_count = 0;
 +            std::string file_name;
 +            std::string mask = "*.*";
 +
 +            LLDirIterator iter(old_cache_dir, mask);
 +            while (iter.next(file_name))
 +            {
 +                if (file_name == "." || file_name == "..") continue;
 +                std::string source_path = gDirUtilp->add(old_cache_dir, file_name);
 +                std::string dest_path = gDirUtilp->add(new_cache_dir, file_name);
 +                if (!LLFile::rename(source_path, dest_path))
 +                {
 +                    file_count++;
 +                }
 +            }
 +            LL_INFOS() << "Moved " << file_count << " files" << LL_ENDL;
 +
 +            // Nuke the old cache
 +            gDirUtilp->setCacheDir(old_cache_dir);
 +            purgeCache();
 +            gDirUtilp->setCacheDir(new_cache_dir);
 +
 +#if LL_DARWIN
 +            // Clean up Mac files not deleted by removing *.*
 +            std::string ds_store = old_cache_dir + "/.DS_Store";
 +            if (gDirUtilp->fileExists(ds_store))
 +            {
 +                LLFile::remove(ds_store);
 +            }
 +#endif
 +            if (LLFile::rmdir(old_cache_dir) != 0)
 +            {
 +                LL_WARNS() << "could not delete old cache directory " << old_cache_dir << LL_ENDL;
 +            }
 +        }
 +    }
 +#endif // LL_WINDOWS || LL_DARWIN
 +}
 +
 +//static
 +U32 LLAppViewer::getTextureCacheVersion()
 +{
 +    // Viewer texture cache version, change if the texture cache format changes.
 +    // 2021-03-10 Bumping up by one to help obviate texture cache issues with
 +    //            Simple Cache Viewer - see SL-14985 for more information
 +    //const U32 TEXTURE_CACHE_VERSION = 8;
 +    const U32 TEXTURE_CACHE_VERSION = 9;
 +
 +    return TEXTURE_CACHE_VERSION ;
 +}
 +
 +//static
 +U32 LLAppViewer::getDiskCacheVersion()
 +{
 +    // Viewer disk cache version intorduced in Simple Cache Viewer, change if the cache format changes.
 +    const U32 DISK_CACHE_VERSION = 1;
 +
 +    return DISK_CACHE_VERSION ;
 +}
 +
 +//static
 +U32 LLAppViewer::getObjectCacheVersion()
 +{
 +    // Viewer object cache version, change if object update
 +    // format changes. JC
 +    const U32 INDRA_OBJECT_CACHE_VERSION = 17;
 +
 +    return INDRA_OBJECT_CACHE_VERSION;
 +}
 +
 +bool LLAppViewer::initCache()
 +{
 +    mPurgeCache = false;
 +    bool read_only = mSecondInstance;
 +    LLAppViewer::getTextureCache()->setReadOnly(read_only) ;
 +    LLVOCache::initParamSingleton(read_only);
 +
 +    // initialize the new disk cache using saved settings
 +    const std::string cache_dir_name = gSavedSettings.getString("DiskCacheDirName");
 +
 +    const U32 MB = 1024 * 1024;
 +    const uintmax_t MIN_CACHE_SIZE = 256 * MB;
 +    const uintmax_t MAX_CACHE_SIZE = 9984ll * MB;
 +    const uintmax_t setting_cache_total_size = uintmax_t(gSavedSettings.getU32("CacheSize")) * MB;
 +    const uintmax_t cache_total_size = llclamp(setting_cache_total_size, MIN_CACHE_SIZE, MAX_CACHE_SIZE);
 +    const F64 disk_cache_percent = gSavedSettings.getF32("DiskCachePercentOfTotal");
 +    const F64 texture_cache_percent = 100.0 - disk_cache_percent;
 +
 +    // note that the maximum size of this cache is defined as a percentage of the
 +    // total cache size - the 'CacheSize' pref - for all caches.
 +    const uintmax_t disk_cache_size = uintmax_t(cache_total_size * disk_cache_percent / 100);
 +    const bool enable_cache_debug_info = gSavedSettings.getBOOL("EnableDiskCacheDebugInfo");
 +
 +    bool texture_cache_mismatch = false;
 +    bool remove_vfs_files = false;
 +    if (gSavedSettings.getS32("LocalCacheVersion") != LLAppViewer::getTextureCacheVersion())
 +    {
 +        texture_cache_mismatch = true;
 +        if (!read_only)
 +        {
 +            gSavedSettings.setS32("LocalCacheVersion", LLAppViewer::getTextureCacheVersion());
 +
 +            //texture cache version was bumped up in Simple Cache Viewer, and at this point old vfs files are not needed
 +            remove_vfs_files = true;
 +        }
 +    }
 +
 +    if (!read_only)
 +    {
 +        // Purge cache if user requested it
 +        if (gSavedSettings.getBOOL("PurgeCacheOnStartup") ||
 +            gSavedSettings.getBOOL("PurgeCacheOnNextStartup"))
 +        {
 +            LL_INFOS("AppCache") << "Startup cache purge requested: " << (gSavedSettings.getBOOL("PurgeCacheOnStartup") ? "ALWAYS" : "ONCE") << LL_ENDL;
 +            gSavedSettings.setBOOL("PurgeCacheOnNextStartup", false);
 +            mPurgeCache = true;
 +            // STORM-1141 force purgeAllTextures to get called to prevent a crash here. -brad
 +            texture_cache_mismatch = true;
 +        }
 +
 +        // We have moved the location of the cache directory over time.
 +        migrateCacheDirectory();
 +
 +        // Setup and verify the cache location
 +        std::string cache_location = gSavedSettings.getString("CacheLocation");
 +        std::string new_cache_location = gSavedSettings.getString("NewCacheLocation");
 +        if (new_cache_location != cache_location)
 +        {
 +            LL_INFOS("AppCache") << "Cache location changed, cache needs purging" << LL_ENDL;
 +            gDirUtilp->setCacheDir(gSavedSettings.getString("CacheLocation"));
 +            purgeCache(); // purge old cache
 +            gDirUtilp->deleteDirAndContents(gDirUtilp->getExpandedFilename(LL_PATH_CACHE, cache_dir_name));
 +            gSavedSettings.setString("CacheLocation", new_cache_location);
 +            gSavedSettings.setString("CacheLocationTopFolder", gDirUtilp->getBaseFileName(new_cache_location));
 +        }
 +    }
 +
 +    if (!gDirUtilp->setCacheDir(gSavedSettings.getString("CacheLocation")))
 +    {
 +        LL_WARNS("AppCache") << "Unable to set cache location" << LL_ENDL;
 +        gSavedSettings.setString("CacheLocation", "");
 +        gSavedSettings.setString("CacheLocationTopFolder", "");
 +    }
 +
 +    const std::string cache_dir = gDirUtilp->getExpandedFilename(LL_PATH_CACHE, cache_dir_name);
 +    LLDiskCache::initParamSingleton(cache_dir, disk_cache_size, enable_cache_debug_info);
 +
 +    if (!read_only)
 +    {
 +        if (gSavedSettings.getS32("DiskCacheVersion") != LLAppViewer::getDiskCacheVersion())
 +        {
 +            LLDiskCache::getInstance()->clearCache();
 +            remove_vfs_files = true;
 +            gSavedSettings.setS32("DiskCacheVersion", LLAppViewer::getDiskCacheVersion());
 +        }
 +
 +        if (remove_vfs_files)
 +        {
 +            LLDiskCache::getInstance()->removeOldVFSFiles();
 +        }
 +
 +        if (mPurgeCache)
 +        {
 +        LLSplashScreen::update(LLTrans::getString("StartupClearingCache"));
 +        purgeCache();
 +
 +            // clear the new C++ file system based cache
 +            LLDiskCache::getInstance()->clearCache();
 +    }
 +        else
 +        {
 +            // purge excessive files from the new file system based cache
 +            LLDiskCache::getInstance()->purge();
 +        }
 +    }
 +    LLAppViewer::getPurgeDiskCacheThread()->start();
 +
 +    LLSplashScreen::update(LLTrans::getString("StartupInitializingTextureCache"));
 +
 +    // Init the texture cache
 +    // Allocate the remaining percent which is not allocated to the disk cache
 +    const S64 texture_cache_size = S64(cache_total_size * texture_cache_percent / 100);
 +
 +    LLAppViewer::getTextureCache()->initCache(LL_PATH_CACHE, texture_cache_size, texture_cache_mismatch);
 +
 +    const U32 CACHE_NUMBER_OF_REGIONS_FOR_OBJECTS = 128;
 +    LLVOCache::getInstance()->initCache(LL_PATH_CACHE, CACHE_NUMBER_OF_REGIONS_FOR_OBJECTS, getObjectCacheVersion());
 +
 +    return true;
 +}
 +
 +void LLAppViewer::addOnIdleCallback(const boost::function<void()>& cb)
 +{
 +    gMainloopWork.post(cb);
 +}
 +
 +void LLAppViewer::loadKeyBindings()
 +{
 +    std::string key_bindings_file = gDirUtilp->getExpandedFilename(LL_PATH_USER_SETTINGS, "key_bindings.xml");
 +    if (!gDirUtilp->fileExists(key_bindings_file) || !gViewerInput.loadBindingsXML(key_bindings_file))
 +    {
 +        // Failed to load custom bindings, try default ones
 +        key_bindings_file = gDirUtilp->getExpandedFilename(LL_PATH_APP_SETTINGS, "key_bindings.xml");
 +        if (!gViewerInput.loadBindingsXML(key_bindings_file))
 +        {
 +            LLError::LLUserWarningMsg::showMissingFiles();
 +            LL_ERRS("InitInfo") << "Unable to open default key bindings from " << key_bindings_file << LL_ENDL;
 +        }
 +    }
 +    LLUrlRegistry::instance().setKeybindingHandler(&gViewerInput);
 +}
 +
 +void LLAppViewer::purgeCache()
 +{
 +    LL_INFOS("AppCache") << "Purging Cache and Texture Cache..." << LL_ENDL;
 +    LLAppViewer::getTextureCache()->purgeCache(LL_PATH_CACHE);
 +    LLVOCache::getInstance()->removeCache(LL_PATH_CACHE);
 +    LLViewerShaderMgr::instance()->clearShaderCache();
 +    std::string browser_cache = gDirUtilp->getExpandedFilename(LL_PATH_CACHE, "cef_cache");
 +    if (LLFile::isdir(browser_cache))
 +    {
 +        // cef does not support clear_cache and clear_cookies, so clear what we can manually.
 +        gDirUtilp->deleteDirAndContents(browser_cache);
 +    }
 +    gDirUtilp->deleteFilesInDir(gDirUtilp->getExpandedFilename(LL_PATH_CACHE, ""), "*");
 +}
 +
 +//purge cache immediately, do not wait until the next login.
 +void LLAppViewer::purgeCacheImmediate()
 +{
 +    LL_INFOS("AppCache") << "Purging Object Cache and Texture Cache immediately..." << LL_ENDL;
 +    LLAppViewer::getTextureCache()->purgeCache(LL_PATH_CACHE, false);
 +    LLVOCache::getInstance()->removeCache(LL_PATH_CACHE, true);
 +}
 +
 +std::string LLAppViewer::getSecondLifeTitle() const
 +{
 +    return LLTrans::getString("APP_NAME");
 +}
 +
 +std::string LLAppViewer::getWindowTitle() const
 +{
 +    return gWindowTitle;
 +}
 +
 +// Callback from a dialog indicating user was logged out.
 +bool finish_disconnect(const LLSD& notification, const LLSD& response)
 +{
 +    S32 option = LLNotificationsUtil::getSelectedOption(notification, response);
 +
 +    if (1 == option)
 +    {
 +        LLAppViewer::instance()->forceQuit();
 +    }
 +    return false;
 +}
 +
 +// Callback from an early disconnect dialog, force an exit
 +bool finish_forced_disconnect(const LLSD& notification, const LLSD& response)
 +{
 +    LLAppViewer::instance()->forceQuit();
 +    return false;
 +}
 +
 +
 +void LLAppViewer::forceDisconnect(const std::string& mesg)
 +{
 +    if (gDoDisconnect)
 +    {
 +        // Already popped up one of these dialogs, don't
 +        // do this again.
 +        return;
 +    }
 +
 +    // *TODO: Translate the message if possible
 +    std::string big_reason = LLAgent::sTeleportErrorMessages[mesg];
 +    if ( big_reason.size() == 0 )
 +    {
 +        big_reason = mesg;
 +    }
 +
 +    LLSD args;
 +    gDoDisconnect = true;
 +
 +    if (LLStartUp::getStartupState() < STATE_STARTED)
 +    {
 +        // Tell users what happened
 +        args["ERROR_MESSAGE"] = big_reason;
 +        LLNotificationsUtil::add("ErrorMessage", args, LLSD(), &finish_forced_disconnect);
 +    }
 +    else
 +    {
 +        args["MESSAGE"] = big_reason;
 +        LLNotificationsUtil::add("YouHaveBeenLoggedOut", args, LLSD(), &finish_disconnect );
 +    }
 +}
 +
 +void LLAppViewer::badNetworkHandler()
 +{
 +    // Dump the packet
 +    gMessageSystem->dumpPacketToLog();
 +
 +    // Flush all of our caches on exit in the case of disconnect due to
 +    // invalid packets.
 +
 +    mPurgeCacheOnExit = true;
 +
 +    std::ostringstream message;
 +    message <<
 +        "The viewer has detected mangled network data indicative\n"
 +        "of a bad upstream network connection or an incomplete\n"
 +        "local installation of " << LLAppViewer::instance()->getSecondLifeTitle() << ". \n"
 +        " \n"
 +        "Try uninstalling and reinstalling to see if this resolves \n"
 +        "the issue. \n"
 +        " \n"
 +        "If the problem continues, see the Tech Support FAQ at: \n"
 +        "www.secondlife.com/support";
 +    forceDisconnect(message.str());
 +
 +    LLApp::instance()->writeMiniDump();
 +}
 +
 +// This routine may get called more than once during the shutdown process.
 +// This can happen because we need to get the screenshot before the window
 +// is destroyed.
 +void LLAppViewer::saveFinalSnapshot()
 +{
 +    if (!mSavedFinalSnapshot)
 +    {
 +        gSavedSettings.setVector3d("FocusPosOnLogout", gAgentCamera.calcFocusPositionTargetGlobal());
 +        gSavedSettings.setVector3d("CameraPosOnLogout", gAgentCamera.calcCameraPositionTargetGlobal());
 +        gViewerWindow->setCursor(UI_CURSOR_WAIT);
 +        gAgentCamera.changeCameraToThirdPerson( false );    // don't animate, need immediate switch
 +        gSavedSettings.setBOOL("ShowParcelOwners", false);
 +        idle();
 +
 +        std::string snap_filename = gDirUtilp->getLindenUserDir();
 +        snap_filename += gDirUtilp->getDirDelimiter();
 +        snap_filename += LLStartUp::getScreenLastFilename();
 +        // use full pixel dimensions of viewer window (not post-scale dimensions)
 +        gViewerWindow->saveSnapshot(snap_filename,
 +                                    gViewerWindow->getWindowWidthRaw(),
 +                                    gViewerWindow->getWindowHeightRaw(),
 +                                    false,
 +                                    gSavedSettings.getBOOL("RenderHUDInSnapshot"),
 +                                    true,
 +                                    LLSnapshotModel::SNAPSHOT_TYPE_COLOR,
 +                                    LLSnapshotModel::SNAPSHOT_FORMAT_PNG);
 +        mSavedFinalSnapshot = true;
 +
 +        if (gAgent.isInHomeRegion())
 +        {
 +            LLVector3d home;
 +            if (gAgent.getHomePosGlobal(&home) && dist_vec(home, gAgent.getPositionGlobal()) < 10)
 +            {
 +                // We are at home position or close to it, see if we need to create home screenshot
 +                // Notes:
 +                // 1. It might be beneficial to also replace home if file is too old
 +                // 2. This is far from best way/place to update screenshot since location might be not fully loaded,
 +                // but we don't have many options
 +                std::string snap_home = gDirUtilp->getLindenUserDir();
 +                snap_home += gDirUtilp->getDirDelimiter();
 +                snap_home += LLStartUp::getScreenHomeFilename();
 +                if (!gDirUtilp->fileExists(snap_home))
 +                {
 +                    // We are at home position yet no home image exist, fix it
 +                    LLFile::copy(snap_filename, snap_home);
 +                }
 +            }
 +        }
 +    }
 +}
 +
 +void LLAppViewer::loadNameCache()
 +{
 +    // display names cache
 +    std::string filename =
 +        gDirUtilp->getExpandedFilename(LL_PATH_CACHE, "avatar_name_cache.xml");
 +    LL_INFOS("AvNameCache") << filename << LL_ENDL;
 +    llifstream name_cache_stream(filename.c_str());
 +    if(name_cache_stream.is_open())
 +    {
 +        if ( ! LLAvatarNameCache::getInstance()->importFile(name_cache_stream))
 +        {
 +            LL_WARNS("AppInit") << "removing invalid '" << filename << "'" << LL_ENDL;
 +            name_cache_stream.close();
 +            LLFile::remove(filename);
 +        }
 +    }
 +
 +    if (!gCacheName) return;
 +
 +    std::string name_cache;
 +    name_cache = gDirUtilp->getExpandedFilename(LL_PATH_CACHE, "name.cache");
 +    llifstream cache_file(name_cache.c_str());
 +    if(cache_file.is_open())
 +    {
 +        if(gCacheName->importFile(cache_file)) return;
 +    }
 +}
 +
 +void LLAppViewer::saveNameCache()
 +{
 +    // display names cache
 +    std::string filename =
 +        gDirUtilp->getExpandedFilename(LL_PATH_CACHE, "avatar_name_cache.xml");
 +    llofstream name_cache_stream(filename.c_str());
 +    if(name_cache_stream.is_open())
 +    {
 +        LLAvatarNameCache::getInstance()->exportFile(name_cache_stream);
 +    }
 +
 +    // real names cache
 +    if (gCacheName)
 +    {
 +        std::string name_cache;
 +        name_cache = gDirUtilp->getExpandedFilename(LL_PATH_CACHE, "name.cache");
 +        llofstream cache_file(name_cache.c_str());
 +        if(cache_file.is_open())
 +        {
 +            gCacheName->exportFile(cache_file);
 +        }
 +    }
 +}
 +
 +
 +/*! @brief      This class is an LLFrameTimer that can be created with
 +                an elapsed time that starts counting up from the given value
 +                rather than 0.0.
 +
 +                Otherwise it behaves the same way as LLFrameTimer.
 +*/
 +class LLFrameStatsTimer : public LLFrameTimer
 +{
 +public:
 +    LLFrameStatsTimer(F64 elapsed_already = 0.0)
 +        : LLFrameTimer()
 +        {
 +            mStartTime -= elapsed_already;
 +        }
 +};
 +
 +static LLTrace::BlockTimerStatHandle FTM_AUDIO_UPDATE("Update Audio");
 +static LLTrace::BlockTimerStatHandle FTM_CLEANUP("Cleanup");
 +static LLTrace::BlockTimerStatHandle FTM_CLEANUP_DRAWABLES("Drawables");
 +static LLTrace::BlockTimerStatHandle FTM_IDLE_CB("Idle Callbacks");
 +static LLTrace::BlockTimerStatHandle FTM_LOD_UPDATE("Update LOD");
 +static LLTrace::BlockTimerStatHandle FTM_OBJECTLIST_UPDATE("Update Objectlist");
 +static LLTrace::BlockTimerStatHandle FTM_REGION_UPDATE("Update Region");
 +static LLTrace::BlockTimerStatHandle FTM_WORLD_UPDATE("Update World");
 +static LLTrace::BlockTimerStatHandle FTM_NETWORK("Network");
 +static LLTrace::BlockTimerStatHandle FTM_AGENT_NETWORK("Agent Network");
 +static LLTrace::BlockTimerStatHandle FTM_VLMANAGER("VL Manager");
 +static LLTrace::BlockTimerStatHandle FTM_AGENT_POSITION("Agent Position");
 +static LLTrace::BlockTimerStatHandle FTM_HUD_EFFECTS("HUD Effects");
 +
 +///////////////////////////////////////////////////////
 +// idle()
 +//
 +// Called every time the window is not doing anything.
 +// Receive packets, update statistics, and schedule a redisplay.
 +///////////////////////////////////////////////////////
 +void LLAppViewer::idle()
 +{
 +    LL_PROFILE_ZONE_SCOPED_CATEGORY_APP;
 +    pingMainloopTimeout("Main:Idle");
 +
 +    // Update frame timers
 +    static LLTimer idle_timer;
 +
 +    LLFrameTimer::updateFrameTime();
 +    LLFrameTimer::updateFrameCount();
 +    LLEventTimer::updateClass();
 +    LLPerfStats::updateClass();
 +
 +    // LLApp::stepFrame() performs the above three calls plus mRunner.run().
 +    // Not sure why we don't call stepFrame() here, except that LLRunner seems
 +    // completely redundant with LLEventTimer.
 +    LLNotificationsUI::LLToast::updateClass();
 +    LLSmoothInterpolation::updateInterpolants();
 +    LLMortician::updateClass();
 +    LLFilePickerThread::clearDead();  //calls LLFilePickerThread::notify()
 +    LLDirPickerThread::clearDead();
 +    F32 dt_raw = idle_timer.getElapsedTimeAndResetF32();
 +
 +    LLGLTFMaterialList::flushUpdates();
 +
 +    // Service the WorkQueue we use for replies from worker threads.
 +    // Use function statics for the timeslice setting so we only have to fetch
 +    // and convert MainWorkTime once.
 +    static F32 MainWorkTimeRaw = gSavedSettings.getF32("MainWorkTime");
 +    static F32Milliseconds MainWorkTimeMs(MainWorkTimeRaw);
 +    // MainWorkTime is specified in fractional milliseconds, but std::chrono
 +    // uses integer representations. What if we want less than a microsecond?
 +    // Use nanoseconds. We're very sure we will never need to specify a
 +    // MainWorkTime that would be larger than we could express in
 +    // std::chrono::nanoseconds.
 +    static std::chrono::nanoseconds MainWorkTimeNanoSec{
 +        std::chrono::nanoseconds::rep(MainWorkTimeMs.value() * 1000000)};
 +    gMainloopWork.runFor(MainWorkTimeNanoSec);
 +
 +    // Cap out-of-control frame times
 +    // Too low because in menus, swapping, debugger, etc.
 +    // Too high because idle called with no objects in view, etc.
 +    const F32 MIN_FRAME_RATE = 1.f;
 +    const F32 MAX_FRAME_RATE = 200.f;
 +
 +    F32 frame_rate_clamped = 1.f / dt_raw;
 +    frame_rate_clamped = llclamp(frame_rate_clamped, MIN_FRAME_RATE, MAX_FRAME_RATE);
 +    gFrameDTClamped = 1.f / frame_rate_clamped;
 +
 +    // Global frame timer
 +    // Smoothly weight toward current frame
 +    gFPSClamped = (frame_rate_clamped + (4.f * gFPSClamped)) / 5.f;
 +
 +    F32 qas = gSavedSettings.getF32("QuitAfterSeconds");
 +    if (qas > 0.f)
 +    {
 +        if (gRenderStartTime.getElapsedTimeF32() > qas)
 +        {
 +            LL_INFOS() << "Quitting after " << qas << " seconds. See setting \"QuitAfterSeconds\"." << LL_ENDL;
 +            LLAppViewer::instance()->forceQuit();
 +        }
 +    }
 +
 +    // Must wait until both have avatar object and mute list, so poll
 +    // here.
 +    LLIMProcessing::requestOfflineMessages();
 +
 +    ///////////////////////////////////
 +    //
 +    // Special case idle if still starting up
 +    //
 +    if (LLStartUp::getStartupState() < STATE_STARTED)
 +    {
 +        // Skip rest if idle startup returns false (essentially, no world yet)
 +        gGLActive = true;
 +        if (!idle_startup())
 +        {
 +            gGLActive = false;
 +            return;
 +        }
 +        gGLActive = false;
 +    }
 +
 +
 +    F32 yaw = 0.f;              // radians
 +
 +    if (!gDisconnected)
 +    {
 +        LL_PROFILE_ZONE_NAMED_CATEGORY_NETWORK("network"); //LL_RECORD_BLOCK_TIME(FTM_NETWORK);
 +        // Update spaceserver timeinfo
 +        LLWorld::getInstance()->setSpaceTimeUSec(LLWorld::getInstance()->getSpaceTimeUSec() + LLUnits::Seconds::fromValue(dt_raw));
 +
 +
 +        //////////////////////////////////////
 +        //
 +        // Update simulator agent state
 +        //
 +
 +        if (gSavedSettings.getBOOL("RotateRight"))
 +        {
 +            gAgent.moveYaw(-1.f);
 +        }
 +
 +        {
 +            LL_PROFILE_ZONE_NAMED_CATEGORY_APP("Autopilot");
 +            // Handle automatic walking towards points
 +            gAgentPilot.updateTarget();
 +            gAgent.autoPilot(&yaw);
 +        }
 +
 +        static LLFrameTimer agent_update_timer;
 +
 +        // When appropriate, update agent location to the simulator.
 +        F32 agent_update_time = agent_update_timer.getElapsedTimeF32();
 +        F32 agent_force_update_time = mLastAgentForceUpdate + agent_update_time;
 +        bool timed_out = agent_update_time > (1.0f / (F32)AGENT_UPDATES_PER_SECOND);
 +        bool force_send =
 +            // if there is something to send
 +            (gAgent.controlFlagsDirty() && timed_out)
 +            // if something changed
 +            || (mLastAgentControlFlags != gAgent.getControlFlags())
 +            // keep alive
 +            || (agent_force_update_time > (1.0f / (F32) AGENT_FORCE_UPDATES_PER_SECOND));
 +        // timing out doesn't warranty that an update will be sent,
 +        // just that it will be checked.
 +        if (force_send || timed_out)
 +        {
 +            LL_PROFILE_ZONE_SCOPED_CATEGORY_NETWORK;
 +            // Send avatar and camera info
 +            mLastAgentControlFlags = gAgent.getControlFlags();
 +            mLastAgentForceUpdate = force_send ? 0 : agent_force_update_time;
 +            send_agent_update(force_send);
 +            agent_update_timer.reset();
 +        }
 +    }
 +
 +    //////////////////////////////////////
 +    //
 +    // Manage statistics
 +    //
 +    //
 +    {
 +        // Initialize the viewer_stats_timer with an already elapsed time
 +        // of SEND_STATS_PERIOD so that the initial stats report will
 +        // be sent immediately.
 +        static LLFrameStatsTimer viewer_stats_timer(SEND_STATS_PERIOD);
 +
 +        // Update session stats every large chunk of time
 +        // *FIX: (?) SAMANTHA
 +        if (viewer_stats_timer.getElapsedTimeF32() >= SEND_STATS_PERIOD && !gDisconnected)
 +        {
 +            LL_INFOS() << "Transmitting sessions stats" << LL_ENDL;
 +            bool include_preferences = false;
 +            send_viewer_stats(include_preferences);
 +            viewer_stats_timer.reset();
 +        }
 +
 +        // Print the object debugging stats
 +        static LLFrameTimer object_debug_timer;
 +        if (object_debug_timer.getElapsedTimeF32() > 5.f)
 +        {
 +            object_debug_timer.reset();
 +            if (gObjectList.mNumDeadObjectUpdates)
 +            {
 +                LL_INFOS() << "Dead object updates: " << gObjectList.mNumDeadObjectUpdates << LL_ENDL;
 +                gObjectList.mNumDeadObjectUpdates = 0;
 +            }
 +            if (gObjectList.mNumUnknownUpdates)
 +            {
 +                LL_INFOS() << "Unknown object updates: " << gObjectList.mNumUnknownUpdates << LL_ENDL;
 +                gObjectList.mNumUnknownUpdates = 0;
 +            }
 +
 +        }
 +    }
 +
 +    if (!gDisconnected)
 +    {
 +        LL_PROFILE_ZONE_NAMED_CATEGORY_DISPLAY("Network");
 +
 +        ////////////////////////////////////////////////
 +        //
 +        // Network processing
 +        //
 +        // NOTE: Starting at this point, we may still have pointers to "dead" objects
 +        // floating throughout the various object lists.
 +        //
 +        idleNameCache();
 +        idleNetwork();
 +
 +
 +        // Check for away from keyboard, kick idle agents.
 +        idle_afk_check();
 +
 +        //  Update statistics for this frame
 +        update_statistics();
 +    }
 +
 +    ////////////////////////////////////////
 +    //
 +    // Handle the regular UI idle callbacks as well as
 +    // hover callbacks
 +    //
 +
 +#ifdef LL_DARWIN
 +    if (!mQuitRequested)  //MAINT-4243
 +#endif
 +    {
 +//      LL_RECORD_BLOCK_TIME(FTM_IDLE_CB);
 +
 +        // Do event notifications if necessary.  Yes, we may want to move this elsewhere.
 +        gEventNotifier.update();
 +
 +        gIdleCallbacks.callFunctions();
 +        gInventory.idleNotifyObservers();
 +        LLAvatarTracker::instance().idleNotifyObservers();
 +    }
 +
 +    // Metrics logging (LLViewerAssetStats, etc.)
 +    {
 +        static LLTimer report_interval;
 +
 +        // *TODO:  Add configuration controls for this
 +        F32 seconds = report_interval.getElapsedTimeF32();
 +        if (seconds >= app_metrics_interval)
 +        {
 +            metricsSend(! gDisconnected);
 +            report_interval.reset();
 +        }
 +    }
 +
 +
 +    // Update layonts, handle mouse events, tooltips, e t c
 +    // updateUI() needs to be called even in case viewer disconected
 +    // since related notification still needs handling and allows
 +    // opening chat.
 +    gViewerWindow->updateUI();
 +
 +    if (gDisconnected)
 +    {
 +        return;
 +    }
 +
 +    if (gTeleportDisplay)
 +    {
 +        return;
 +    }
 +
 +    ///////////////////////////////////////
 +    // Agent and camera movement
 +    //
 +    LLCoordGL current_mouse = gViewerWindow->getCurrentMouse();
 +
 +    {
 +        // After agent and camera moved, figure out if we need to
 +        // deselect objects.
 +        LLSelectMgr::getInstance()->deselectAllIfTooFar();
 +
 +    }
 +
 +    {
 +        // Handle pending gesture processing
 +        LL_RECORD_BLOCK_TIME(FTM_AGENT_POSITION);
 +        LLGestureMgr::instance().update();
 +
 +        gAgent.updateAgentPosition(gFrameDTClamped, yaw, current_mouse.mX, current_mouse.mY);
 +    }
 +
 +    {
 +        LL_RECORD_BLOCK_TIME(FTM_OBJECTLIST_UPDATE);
 +
 +        if (!(logoutRequestSent() && hasSavedFinalSnapshot()))
 +        {
 +            gObjectList.update(gAgent);
 +        }
 +    }
 +
 +    //////////////////////////////////////
 +    //
 +    // Deletes objects...
 +    // Has to be done after doing idleUpdates (which can kill objects)
 +    //
 +
 +    {
 +        LL_RECORD_BLOCK_TIME(FTM_CLEANUP);
 +        {
 +            gObjectList.cleanDeadObjects();
 +        }
 +        {
 +            LL_RECORD_BLOCK_TIME(FTM_CLEANUP_DRAWABLES);
 +            LLDrawable::cleanupDeadDrawables();
 +        }
 +    }
 +
 +    //
 +    // After this point, in theory we should never see a dead object
 +    // in the various object/drawable lists.
 +    //
 +
 +    //////////////////////////////////////
 +    //
 +    // Update/send HUD effects
 +    //
 +    // At this point, HUD effects may clean up some references to
 +    // dead objects.
 +    //
 +
 +    {
 +        LL_RECORD_BLOCK_TIME(FTM_HUD_EFFECTS);
 +        LLSelectMgr::getInstance()->updateEffects();
 +        LLHUDManager::getInstance()->cleanupEffects();
 +        LLHUDManager::getInstance()->sendEffects();
 +    }
 +
 +    ////////////////////////////////////////
 +    //
 +    // Unpack layer data that we've received
 +    //
 +
 +    {
 +        LL_RECORD_BLOCK_TIME(FTM_NETWORK);
 +        gVLManager.unpackData();
 +    }
 +
 +    /////////////////////////
 +    //
 +    // Update surfaces, and surface textures as well.
 +    //
 +
 +    LLWorld::getInstance()->updateVisibilities();
 +    {
 +        const F32 max_region_update_time = .001f; // 1ms
 +        LL_RECORD_BLOCK_TIME(FTM_REGION_UPDATE);
 +        LLWorld::getInstance()->updateRegions(max_region_update_time);
 +    }
 +
 +    /////////////////////////
 +    //
 +    // Update weather effects
 +    //
 +
 +    // Update wind vector
 +    LLVector3 wind_position_region;
 +    static LLVector3 average_wind;
 +
 +    LLViewerRegion *regionp;
 +    regionp = LLWorld::getInstance()->resolveRegionGlobal(wind_position_region, gAgent.getPositionGlobal());    // puts agent's local coords into wind_position
 +    if (regionp)
 +    {
 +        gWindVec = regionp->mWind.getVelocity(wind_position_region);
 +
 +        // Compute average wind and use to drive motion of water
 +
 +        average_wind = regionp->mWind.getAverage();
 +        gSky.setWind(average_wind);
 +        //LLVOWater::setWind(average_wind);
 +    }
 +    else
 +    {
 +        gWindVec.setVec(0.0f, 0.0f, 0.0f);
 +    }
 +
 +    //////////////////////////////////////
 +    //
 +    // Sort and cull in the new renderer are moved to pipeline.cpp
 +    // Here, particles are updated and drawables are moved.
 +    //
 +
 +    {
 +        LL_PROFILE_ZONE_NAMED_CATEGORY_APP("world update"); //LL_RECORD_BLOCK_TIME(FTM_WORLD_UPDATE);
 +        gPipeline.updateMove();
 +    }
 +
 +    LLWorld::getInstance()->updateParticles();
 +
 +    if (gAgentPilot.isPlaying() && gAgentPilot.getOverrideCamera())
 +    {
 +        gAgentPilot.moveCamera();
 +    }
 +    else if (LLViewerJoystick::getInstance()->getOverrideCamera())
 +    {
 +        LLViewerJoystick::getInstance()->moveFlycam();
 +    }
 +    else
 +    {
 +        if (LLToolMgr::getInstance()->inBuildMode())
 +        {
 +            LLViewerJoystick::getInstance()->moveObjects();
 +        }
 +
 +        gAgentCamera.updateCamera();
 +    }
 +
 +    // update media focus
 +    LLViewerMediaFocus::getInstance()->update();
 +
 +    // Update marketplace
 +    LLMarketplaceInventoryImporter::update();
 +    LLMarketplaceInventoryNotifications::update();
 +
 +    // objects and camera should be in sync, do LOD calculations now
 +    {
 +        LL_RECORD_BLOCK_TIME(FTM_LOD_UPDATE);
 +        gObjectList.updateApparentAngles(gAgent);
 +    }
 +
 +    // Update AV render info
 +    LLAvatarRenderInfoAccountant::getInstance()->idle();
 +
 +    {
 +        LL_PROFILE_ZONE_NAMED_CATEGORY_APP("audio update"); //LL_RECORD_BLOCK_TIME(FTM_AUDIO_UPDATE);
 +
 +        if (gAudiop)
 +        {
 +            audio_update_volume(false);
 +            audio_update_listener();
 +            audio_update_wind(false);
 +
 +            // this line actually commits the changes we've made to source positions, etc.
 +            gAudiop->idle();
 +        }
 +    }
 +
 +    // Handle shutdown process, for example,
 +    // wait for floaters to close, send quit message,
 +    // forcibly quit if it has taken too long
 +    if (mQuitRequested)
 +    {
 +        gGLActive = true;
 +        idleShutdown();
 +    }
 +}
 +
 +void LLAppViewer::idleShutdown()
 +{
 +    // Wait for all modal alerts to get resolved
 +    if (LLModalDialog::activeCount() > 0)
 +    {
 +        return;
 +    }
 +
 +    // close IM interface
 +    if(gIMMgr)
 +    {
 +        gIMMgr->disconnectAllSessions();
 +    }
 +
 +    // Wait for all floaters to get resolved
 +    if (gFloaterView
 +        && !gFloaterView->allChildrenClosed())
 +    {
 +        return;
 +    }
 +
 +
 +
 +
 +    // ProductEngine: Try moving this code to where we shut down sTextureCache in cleanup()
 +    // *TODO: ugly
 +    static bool saved_teleport_history = false;
 +    if (!saved_teleport_history)
 +    {
 +        saved_teleport_history = true;
 +        LLTeleportHistory::getInstance()->dump();
 +        LLLocationHistory::getInstance()->save(); // *TODO: find a better place for doing this
 +        return;
 +    }
 +
 +    static bool saved_snapshot = false;
 +    if (!saved_snapshot)
 +    {
 +        saved_snapshot = true;
 +        saveFinalSnapshot();
 +        return;
 +    }
 +
 +    const F32 SHUTDOWN_UPLOAD_SAVE_TIME = 5.f;
 +
 +    S32 pending_uploads = gAssetStorage->getNumPendingUploads();
 +    if (pending_uploads > 0
 +        && gLogoutTimer.getElapsedTimeF32() < SHUTDOWN_UPLOAD_SAVE_TIME
 +        && !logoutRequestSent())
 +    {
 +        static S32 total_uploads = 0;
 +        // Sometimes total upload count can change during logout.
 +        total_uploads = llmax(total_uploads, pending_uploads);
 +        gViewerWindow->setShowProgress(true);
 +        S32 finished_uploads = total_uploads - pending_uploads;
 +        F32 percent = 100.f * finished_uploads / total_uploads;
 +        gViewerWindow->setProgressPercent(percent);
 +        gViewerWindow->setProgressString(LLTrans::getString("SavingSettings"));
 +        return;
 +    }
 +
 +    if (gPendingMetricsUploads > 0
 +        && gLogoutTimer.getElapsedTimeF32() < SHUTDOWN_UPLOAD_SAVE_TIME
 +        && !logoutRequestSent())
 +    {
 +        gViewerWindow->setShowProgress(true);
 +        gViewerWindow->setProgressPercent(100.f);
 +        gViewerWindow->setProgressString(LLTrans::getString("LoggingOut"));
 +        return;
 +    }
 +
 +    // All floaters are closed.  Tell server we want to quit.
 +    if( !logoutRequestSent() )
 +    {
 +        sendLogoutRequest();
 +
 +        // Wait for a LogoutReply message
 +        gViewerWindow->setShowProgress(true);
 +        gViewerWindow->setProgressPercent(100.f);
 +        gViewerWindow->setProgressString(LLTrans::getString("LoggingOut"));
 +        return;
 +    }
 +
 +    // Make sure that we quit if we haven't received a reply from the server.
 +    if( logoutRequestSent()
 +        && gLogoutTimer.getElapsedTimeF32() > gLogoutMaxTime )
 +    {
 +        forceQuit();
 +        return;
 +    }
 +}
 +
 +void LLAppViewer::sendLogoutRequest()
 +{
 +    if(!mLogoutRequestSent && gMessageSystem)
 +    {
 +        //Set internal status variables and marker files before actually starting the logout process
 +        gLogoutInProgress = true;
 +        if (!mSecondInstance)
 +        {
 +            mLogoutMarkerFileName = gDirUtilp->getExpandedFilename(LL_PATH_LOGS,LOGOUT_MARKER_FILE_NAME);
 +
 +            mLogoutMarkerFile.open(mLogoutMarkerFileName, LL_APR_WB);
 +            if (mLogoutMarkerFile.getFileHandle())
 +            {
 +                LL_INFOS("MarkerFile") << "Created logout marker file '"<< mLogoutMarkerFileName << "' " << LL_ENDL;
 +                recordMarkerVersion(mLogoutMarkerFile);
 +            }
 +            else
 +            {
 +                LL_WARNS("MarkerFile") << "Cannot create logout marker file " << mLogoutMarkerFileName << LL_ENDL;
 +            }
 +        }
 +        else
 +        {
 +            LL_INFOS("MarkerFile") << "Did not logout marker file because this is a second instance" << LL_ENDL;
 +        }
 +
 +        LLMessageSystem* msg = gMessageSystem;
 +        msg->newMessageFast(_PREHASH_LogoutRequest);
 +        msg->nextBlockFast(_PREHASH_AgentData);
 +        msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID() );
 +        msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
 +        gAgent.sendReliableMessage();
 +
 +        gLogoutTimer.reset();
 +        gLogoutMaxTime = LOGOUT_REQUEST_TIME;
 +        mLogoutRequestSent = true;
 +
 +        if(LLVoiceClient::instanceExists())
 +        {
 +            LLVoiceClient::getInstance()->leaveChannel();
 +        }
 +    }
 +}
 +
 +void LLAppViewer::updateNameLookupUrl(const LLViewerRegion * regionp)
 +{
 +    if (!regionp || !regionp->capabilitiesReceived())
 +    {
 +        return;
 +    }
 +
 +    LLAvatarNameCache *name_cache = LLAvatarNameCache::getInstance();
 +    bool had_capability = LLAvatarNameCache::getInstance()->hasNameLookupURL();
 +    std::string name_lookup_url;
 +    name_lookup_url.reserve(128); // avoid a memory allocation below
 +    name_lookup_url = regionp->getCapability("GetDisplayNames");
 +    bool have_capability = !name_lookup_url.empty();
 +    if (have_capability)
 +    {
 +        // we have support for display names, use it
 +        U32 url_size = name_lookup_url.size();
 +        // capabilities require URLs with slashes before query params:
 +        // https://<host>:<port>/cap/<uuid>/?ids=<blah>
 +        // but the caps are granted like:
 +        // https://<host>:<port>/cap/<uuid>
 +        if (url_size > 0 && name_lookup_url[url_size - 1] != '/')
 +        {
 +            name_lookup_url += '/';
 +        }
 +        name_cache->setNameLookupURL(name_lookup_url);
 +    }
 +    else
 +    {
 +        // Display names not available on this region
 +        name_cache->setNameLookupURL(std::string());
 +    }
 +
 +    // Error recovery - did we change state?
 +    if (had_capability != have_capability)
 +    {
 +        // name tags are persistant on screen, so make sure they refresh
 +        LLVOAvatar::invalidateNameTags();
 +    }
 +}
 +
 +void LLAppViewer::idleNameCache()
 +{
 +    // Neither old nor new name cache can function before agent has a region
 +    LLViewerRegion* region = gAgent.getRegion();
 +    if (!region)
 +    {
 +        return;
 +    }
 +
 +    // deal with any queued name requests and replies.
 +    gCacheName->processPending();
 +
 +    // Can't run the new cache until we have the list of capabilities
 +    // for the agent region, and can therefore decide whether to use
 +    // display names or fall back to the old name system.
 +    if (!region->capabilitiesReceived())
 +    {
 +        return;
 +    }
 +
 +    LLAvatarNameCache::getInstance()->idle();
 +}
 +
 +//
 +// Handle messages, and all message related stuff
 +//
 +
 +#define TIME_THROTTLE_MESSAGES
 +
 +#ifdef TIME_THROTTLE_MESSAGES
 +#define CHECK_MESSAGES_DEFAULT_MAX_TIME .020f // 50 ms = 50 fps (just for messages!)
 +static F32 CheckMessagesMaxTime = CHECK_MESSAGES_DEFAULT_MAX_TIME;
 +#endif
 +
 +static LLTrace::BlockTimerStatHandle FTM_IDLE_NETWORK("Idle Network");
 +static LLTrace::BlockTimerStatHandle FTM_MESSAGE_ACKS("Message Acks");
 +static LLTrace::BlockTimerStatHandle FTM_RETRANSMIT("Retransmit");
 +static LLTrace::BlockTimerStatHandle FTM_TIMEOUT_CHECK("Timeout Check");
 +static LLTrace::BlockTimerStatHandle FTM_DYNAMIC_THROTTLE("Dynamic Throttle");
 +static LLTrace::BlockTimerStatHandle FTM_CHECK_REGION_CIRCUIT("Check Region Circuit");
 +
 +void LLAppViewer::idleNetwork()
 +{
 +    LL_PROFILE_ZONE_SCOPED_CATEGORY_NETWORK;
 +    pingMainloopTimeout("idleNetwork");
 +
 +    gObjectList.mNumNewObjects = 0;
 +    S32 total_decoded = 0;
 +
 +    if (!gSavedSettings.getBOOL("SpeedTest"))
 +    {
 +        LL_PROFILE_ZONE_NAMED_CATEGORY_NETWORK("idle network"); //LL_RECORD_BLOCK_TIME(FTM_IDLE_NETWORK); // decode
 +
 +        LLTimer check_message_timer;
 +        //  Read all available packets from network
 +        const S64 frame_count = gFrameCount;  // U32->S64
 +        F32 total_time = 0.0f;
 +
 +        {
 +            LockMessageChecker lmc(gMessageSystem);
 +            while (lmc.checkAllMessages(frame_count, gServicePump))
 +            {
 +                if (gDoDisconnect)
 +                {
 +                    // We're disconnecting, don't process any more messages from the server
 +                    // We're usually disconnecting due to either network corruption or a
 +                    // server going down, so this is OK.
 +                    break;
 +                }
 +
 +                total_decoded++;
 +                gPacketsIn++;
 +
 +                if (total_decoded > MESSAGE_MAX_PER_FRAME)
 +                {
 +                    break;
 +                }
 +
 +#ifdef TIME_THROTTLE_MESSAGES
 +                // Prevent slow packets from completely destroying the frame rate.
 +                // This usually happens due to clumps of avatars taking huge amount
 +                // of network processing time (which needs to be fixed, but this is
 +                // a good limit anyway).
 +                total_time = check_message_timer.getElapsedTimeF32();
 +                if (total_time >= CheckMessagesMaxTime)
 +                    break;
 +#endif
 +            }
 +
 +            // Handle per-frame message system processing.
 +            lmc.processAcks(gSavedSettings.getF32("AckCollectTime"));
 +        }
 +
 +#ifdef TIME_THROTTLE_MESSAGES
 +        if (total_time >= CheckMessagesMaxTime)
 +        {
 +            // Increase CheckMessagesMaxTime so that we will eventually catch up
 +            CheckMessagesMaxTime *= 1.035f; // 3.5% ~= x2 in 20 frames, ~8x in 60 frames
 +        }
 +        else
 +        {
 +            // Reset CheckMessagesMaxTime to default value
 +            CheckMessagesMaxTime = CHECK_MESSAGES_DEFAULT_MAX_TIME;
 +        }
 +#endif
 +
 +
 +
 +        // we want to clear the control after sending out all necessary agent updates
 +        gAgent.resetControlFlags();
 +
 +        // Decode enqueued messages...
 +        S32 remaining_possible_decodes = MESSAGE_MAX_PER_FRAME - total_decoded;
 +
 +        if( remaining_possible_decodes <= 0 )
 +        {
 +            LL_INFOS() << "Maxed out number of messages per frame at " << MESSAGE_MAX_PER_FRAME << LL_ENDL;
 +        }
 +
 +        if (gPrintMessagesThisFrame)
 +        {
 +            LL_INFOS() << "Decoded " << total_decoded << " msgs this frame!" << LL_ENDL;
 +            gPrintMessagesThisFrame = false;
 +        }
 +    }
 +    add(LLStatViewer::NUM_NEW_OBJECTS, gObjectList.mNumNewObjects);
 +
 +    // Retransmit unacknowledged packets.
 +    gXferManager->retransmitUnackedPackets();
 +    gAssetStorage->checkForTimeouts();
 +    gViewerThrottle.updateDynamicThrottle();
 +
 +    // Check that the circuit between the viewer and the agent's current
 +    // region is still alive
 +    LLViewerRegion *agent_region = gAgent.getRegion();
 +    if (agent_region && (LLStartUp::getStartupState()==STATE_STARTED))
 +    {
 +        LLUUID this_region_id = agent_region->getRegionID();
 +        bool this_region_alive = agent_region->isAlive();
 +        if ((mAgentRegionLastAlive && !this_region_alive) // newly dead
 +            && (mAgentRegionLastID == this_region_id)) // same region
 +        {
 +            forceDisconnect(LLTrans::getString("AgentLostConnection"));
 +        }
 +        mAgentRegionLastID = this_region_id;
 +        mAgentRegionLastAlive = this_region_alive;
 +    }
 +}
 +
 +void LLAppViewer::disconnectViewer()
 +{
 +    if (gDisconnected)
 +    {
 +        return;
 +    }
 +    //
 +    // Cleanup after quitting.
 +    //
 +    // Save snapshot for next time, if we made it through initialization
 +
 +    LL_INFOS() << "Disconnecting viewer!" << LL_ENDL;
 +
 +    // Dump our frame statistics
 +
 +    // Remember if we were flying
 +    gSavedSettings.setBOOL("FlyingAtExit", gAgent.getFlying() );
 +
 +    // Un-minimize all windows so they don't get saved minimized
 +    if (gFloaterView)
 +    {
 +        gFloaterView->restoreAll();
 +    }
 +
 +    if (LLSelectMgr::instanceExists())
 +    {
 +        LLSelectMgr::getInstance()->deselectAll();
 +    }
 +
 +    // save inventory if appropriate
 +    if (gInventory.isInventoryUsable()
 +        && gAgent.getID().notNull()) // Shouldn't be null at this stage
 +    {
 +        gInventory.cache(gInventory.getRootFolderID(), gAgent.getID());
 +        if (gInventory.getLibraryRootFolderID().notNull()
 +            && gInventory.getLibraryOwnerID().notNull()
 +            && !mSecondInstance) // agent is unique, library isn't
 +        {
 +            gInventory.cache(
 +                gInventory.getLibraryRootFolderID(),
 +                gInventory.getLibraryOwnerID());
 +        }
 +    }
 +
 +    saveNameCache();
 +    if (LLExperienceCache::instanceExists())
 +    {
 +        // TODO: LLExperienceCache::cleanup() logic should be moved to
 +        // cleanupSingleton().
 +        LLExperienceCache::instance().cleanup();
 +    }
 +
 +    // close inventory interface, close all windows
 +    LLSidepanelInventory::cleanup();
 +
 +    gAgentWearables.cleanup();
 +    gAgentCamera.cleanup();
 +    // Also writes cached agent settings to gSavedSettings
 +    gAgent.cleanup();
 +
 +    // This is where we used to call gObjectList.destroy() and then delete gWorldp.
 +    // Now we just ask the LLWorld singleton to cleanly shut down.
 +    if(LLWorld::instanceExists())
 +    {
 +        LLWorld::getInstance()->resetClass();
 +    }
 +    LLVOCache::deleteSingleton();
 +
 +    // call all self-registered classes
 +    LLDestroyClassList::instance().fireCallbacks();
 +
 +    cleanup_xfer_manager();
 +    gDisconnected = true;
 +
 +    // Pass the connection state to LLUrlEntryParcel not to attempt
 +    // parcel info requests while disconnected.
 +    LLUrlEntryParcel::setDisconnected(gDisconnected);
 +}
 +
 +void LLAppViewer::forceErrorLLError()
 +{
 +    LL_ERRS() << "This is a deliberate llerror" << LL_ENDL;
 +}
 +
 +void LLAppViewer::forceErrorLLErrorMsg()
 +{
 +    LLError::LLUserWarningMsg::show("Deliberate error");
 +    // Note: under debug this will show a message as well,
 +    // but release won't show anything and will quit silently
 +    LL_ERRS() << "This is a deliberate llerror with a message" << LL_ENDL;
 +}
 +
 +void LLAppViewer::forceErrorBreakpoint()
 +{
 +    LL_WARNS() << "Forcing a deliberate breakpoint" << LL_ENDL;
 +#ifdef LL_WINDOWS
 +    DebugBreak();
 +#else
 +    asm ("int $3");
 +#endif
 +    return;
 +}
 +
 +void LLAppViewer::forceErrorBadMemoryAccess()
 +{
 +    LL_WARNS() << "Forcing a deliberate bad memory access" << LL_ENDL;
 +    S32* crash = NULL;
 +    *crash = 0xDEADBEEF;
 +    return;
 +}
 +
 +void LLAppViewer::forceErrorInfiniteLoop()
 +{
 +    LL_WARNS() << "Forcing a deliberate infinite loop" << LL_ENDL;
 +    // Loop is intentionally complicated to fool basic loop detection
 +    LLTimer timer_total;
 +    LLTimer timer_expiry;
 +    const S32 report_frequency = 10;
 +    timer_expiry.setTimerExpirySec(report_frequency);
 +    while(true)
 +    {
 +        if (timer_expiry.hasExpired())
 +        {
 +            LL_INFOS() << "Infinite loop time : " << timer_total.getElapsedSeconds() << LL_ENDL;
 +            timer_expiry.setTimerExpirySec(report_frequency);
 +        }
 +    }
 +    return;
 +}
 +
 +void LLAppViewer::forceErrorSoftwareException()
 +{
 +    LL_WARNS() << "Forcing a deliberate exception" << LL_ENDL;
 +    LLTHROW(LLException("User selected Force Software Exception"));
 +}
 +
 +void LLAppViewer::forceErrorOSSpecificException()
 +{
 +    // Virtual, MacOS only
 +    const std::string exception_text = "User selected Force OS Exception, Not implemented on this OS";
 +    throw std::runtime_error(exception_text);
 +}
 +
 +void LLAppViewer::forceErrorDriverCrash()
 +{
 +    LL_WARNS() << "Forcing a deliberate driver crash" << LL_ENDL;
 +    glDeleteTextures(1, NULL);
 +}
 +
 +void LLAppViewer::forceErrorCoroutineCrash()
 +{
 +    LL_WARNS() << "Forcing a crash in LLCoros" << LL_ENDL;
 +    LLCoros::instance().launch("LLAppViewer::crashyCoro", [] {throw LLException("A deliberate crash from LLCoros"); });
 +}
 +
 +void LLAppViewer::forceErrorThreadCrash()
 +{
 +    class LLCrashTestThread : public LLThread
 +    {
 +    public:
 +
 +        LLCrashTestThread() : LLThread("Crash logging test thread")
 +        {
 +        }
 +
 +        void run()
 +        {
 +            LL_ERRS() << "This is a deliberate llerror in thread" << LL_ENDL;
 +        }
 +    };
 +
 +    LL_WARNS() << "This is a deliberate crash in a thread" << LL_ENDL;
 +    LLCrashTestThread *thread = new LLCrashTestThread();
 +    thread->start();
 +}
 +
 +void LLAppViewer::initMainloopTimeout(const std::string& state, F32 secs)
 +{
 +    if(!mMainloopTimeout)
 +    {
 +        mMainloopTimeout = new LLWatchdogTimeout();
 +        resumeMainloopTimeout(state, secs);
 +    }
 +}
 +
 +void LLAppViewer::destroyMainloopTimeout()
 +{
 +    if(mMainloopTimeout)
 +    {
 +        delete mMainloopTimeout;
 +        mMainloopTimeout = NULL;
 +    }
 +}
 +
 +void LLAppViewer::resumeMainloopTimeout(const std::string& state, F32 secs)
 +{
 +    if(mMainloopTimeout)
 +    {
 +        if(secs < 0.0f)
 +        {
 +            static LLCachedControl<F32> mainloop_timeout(gSavedSettings, "MainloopTimeoutDefault", 60);
 +            secs = mainloop_timeout;
 +        }
 +
 +        mMainloopTimeout->setTimeout(secs);
 +        mMainloopTimeout->start(state);
 +    }
 +}
 +
 +void LLAppViewer::pauseMainloopTimeout()
 +{
 +    if(mMainloopTimeout)
 +    {
 +        mMainloopTimeout->stop();
 +    }
 +}
 +
 +void LLAppViewer::pingMainloopTimeout(const std::string& state, F32 secs)
 +{
 +    LL_PROFILE_ZONE_SCOPED_CATEGORY_APP;
 +
 +    if(mMainloopTimeout)
 +    {
 +        if(secs < 0.0f)
 +        {
 +            static LLCachedControl<F32> mainloop_timeout(gSavedSettings, "MainloopTimeoutDefault", 60);
 +            secs = mainloop_timeout;
 +        }
 +
 +        mMainloopTimeout->setTimeout(secs);
 +        mMainloopTimeout->ping(state);
 +    }
 +}
 +
 +void LLAppViewer::handleLoginComplete()
 +{
 +    gLoggedInTime.start();
 +    initMainloopTimeout("Mainloop Init");
 +
 +    // Store some data to DebugInfo in case of a freeze.
 +    gDebugInfo["ClientInfo"]["Name"] = LLVersionInfo::instance().getChannel();
 +
 +    gDebugInfo["ClientInfo"]["MajorVersion"] = LLVersionInfo::instance().getMajor();
 +    gDebugInfo["ClientInfo"]["MinorVersion"] = LLVersionInfo::instance().getMinor();
 +    gDebugInfo["ClientInfo"]["PatchVersion"] = LLVersionInfo::instance().getPatch();
 +    gDebugInfo["ClientInfo"]["BuildVersion"] = std::to_string(LLVersionInfo::instance().getBuild());
 +
 +    LLParcel* parcel = LLViewerParcelMgr::getInstance()->getAgentParcel();
 +    if ( parcel && parcel->getMusicURL()[0])
 +    {
 +        gDebugInfo["ParcelMusicURL"] = parcel->getMusicURL();
 +    }
 +    if ( parcel && parcel->getMediaURL()[0])
 +    {
 +        gDebugInfo["ParcelMediaURL"] = parcel->getMediaURL();
 +    }
 +
 +    gDebugInfo["SettingsFilename"] = gSavedSettings.getString("ClientSettingsFile");
 +    gDebugInfo["CAFilename"] = gDirUtilp->getCAFile();
 +    gDebugInfo["ViewerExePath"] = gDirUtilp->getExecutablePathAndName();
 +    gDebugInfo["CurrentPath"] = gDirUtilp->getCurPath();
 +
 +    if(gAgent.getRegion())
 +    {
 +        gDebugInfo["CurrentSimHost"] = gAgent.getRegion()->getSimHostName();
 +        gDebugInfo["CurrentRegion"] = gAgent.getRegion()->getName();
 +    }
 +
 +    if(LLAppViewer::instance()->mMainloopTimeout)
 +    {
 +        gDebugInfo["MainloopTimeoutState"] = LLAppViewer::instance()->mMainloopTimeout->getState();
 +    }
 +
 +    mOnLoginCompleted();
 +
 +    writeDebugInfo();
 +
 +    // we logged in successfully, so save settings on logout
 +    LL_INFOS() << "Login successful, per account settings will be saved on log out." << LL_ENDL;
 +    mSavePerAccountSettings=true;
 +}
 +
 +//virtual
 +void LLAppViewer::setMasterSystemAudioMute(bool mute)
 +{
 +    gSavedSettings.setBOOL("MuteAudio", mute);
 +}
 +
 +//virtual
 +bool LLAppViewer::getMasterSystemAudioMute()
 +{
 +    return gSavedSettings.getBOOL("MuteAudio");
 +}
 +
 +//----------------------------------------------------------------------------
 +// Metrics-related methods (static and otherwise)
 +//----------------------------------------------------------------------------
 +
 +/**
 + * LLViewerAssetStats collects data on a per-region (as defined by the agent's
 + * location) so we need to tell it about region changes which become a kind of
 + * hidden variable/global state in the collectors.  For collectors not running
 + * on the main thread, we need to send a message to move the data over safely
 + * and cheaply (amortized over a run).
 + */
 +void LLAppViewer::metricsUpdateRegion(U64 region_handle)
 +{
 +    if (0 != region_handle)
 +    {
 +        LLViewerAssetStatsFF::set_region(region_handle);
 +    }
 +}
 +
 +/**
 + * Attempts to start a multi-threaded metrics report to be sent back to
 + * the grid for consumption.
 + */
 +void LLAppViewer::metricsSend(bool enable_reporting)
 +{
 +    if (! gViewerAssetStats)
 +        return;
 +
 +    if (LLAppViewer::sTextureFetch)
 +    {
 +        LLViewerRegion * regionp = gAgent.getRegion();
 +
 +        if (enable_reporting && regionp)
 +        {
 +            std::string caps_url = regionp->getCapability("ViewerMetrics");
 +
 +            LLSD sd = gViewerAssetStats->asLLSD(true);
 +
 +            // Send a report request into 'thread1' to get the rest of the data
 +            // and provide some additional parameters while here.
 +            LLAppViewer::sTextureFetch->commandSendMetrics(caps_url,
 +                                                           gAgentSessionID,
 +                                                           gAgentID,
 +                                                           sd);
 +        }
 +        else
 +        {
 +            LLAppViewer::sTextureFetch->commandDataBreak();
 +        }
 +    }
 +
 +    // Reset even if we can't report.  Rather than gather up a huge chunk of
 +    // data, we'll keep to our sampling interval and retain the data
 +    // resolution in time.
 +    gViewerAssetStats->restart();
 +}
 +
  | 
