diff options
author | Steven Bennetts <steve@lindenlab.com> | 2007-11-07 22:55:27 +0000 |
---|---|---|
committer | Steven Bennetts <steve@lindenlab.com> | 2007-11-07 22:55:27 +0000 |
commit | 050dad0ce35207a4ac1562175e853590ad9b7681 (patch) | |
tree | be5dc291d2313112e5733d8c004edfe67da6fc54 /indra/newview/llappviewer.cpp | |
parent | 6fd04521d720a3a4904069d10e8ed970d870ba7f (diff) |
merge svn+ssh://steve@svn/svn/linden/branches/viewer-cleanup-3 -r 73026:73079
Diffstat (limited to 'indra/newview/llappviewer.cpp')
-rw-r--r-- | indra/newview/llappviewer.cpp | 3894 |
1 files changed, 3894 insertions, 0 deletions
diff --git a/indra/newview/llappviewer.cpp b/indra/newview/llappviewer.cpp new file mode 100644 index 0000000000..c763ff928c --- /dev/null +++ b/indra/newview/llappviewer.cpp @@ -0,0 +1,3894 @@ +/** + * @file llappviewer.cpp + * @brief The LLAppViewer class definitions + * + * $LicenseInfo:firstyear=2007&license=viewergpl$ + * + * Copyright (c) 2007, Linden Research, Inc. + * + * Second Life Viewer Source Code + * The source code in this file ("Source Code") is provided by Linden Lab + * to you under the terms of the GNU General Public License, version 2.0 + * ("GPL"), unless you have obtained a separate licensing agreement + * ("Other License"), formally executed by you and Linden Lab. Terms of + * the GPL can be found in doc/GPL-license.txt in this distribution, or + * online at http://secondlife.com/developers/opensource/gplv2 + * + * There are special exceptions to the terms and conditions of the GPL as + * it is applied to this Source Code. View the full text of the exception + * in the file doc/FLOSS-exception.txt in this software distribution, or + * online at http://secondlife.com/developers/opensource/flossexception + * + * By copying, modifying or distributing this software, you acknowledge + * that you have read and understood your obligations described above, + * and agree to abide by those obligations. + * + * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO + * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, + * COMPLETENESS OR PERFORMANCE. + * $/LicenseInfo$ + */ + + +#include "llviewerprecompiledheaders.h" +#include "llappviewer.h" + +#include "llversionviewer.h" +#include "llfeaturemanager.h" +#include "llvieweruictrlfactory.h" +#include "llalertdialog.h" +#include "llerrorcontrol.h" +#include "llviewerimagelist.h" +#include "llgroupmgr.h" +#include "llagent.h" +#include "llwindow.h" +#include "llviewerstats.h" +#include "llmd5.h" +#include "llpumpio.h" +#include "llfloateractivespeakers.h" +#include "llimpanel.h" +#include "llstartup.h" +#include "llfocusmgr.h" +#include "llviewerjoystick.h" +#include "llcurl.h" +#include "llfloatersnapshot.h" +#include "llviewerwindow.h" +#include "llviewerdisplay.h" +#include "llviewermessage.h" +#include "llviewerobjectlist.h" +#include "llworldmap.h" +#include "llmutelist.h" + +#include "llweb.h" +#include "llsecondlifeurls.h" + +#if LL_WINDOWS + #include "llwindebug.h" +#endif + +#if LL_WINDOWS +# include <share.h> // For _SH_DENYWR in initMarkerFile +#else +# include <sys/file.h> // For initMarkerFile support +#endif + + + +#include "llnotify.h" +#include "llmediaengine.h" +#include "llviewerkeyboard.h" +#include "lllfsthread.h" +#include "llworkerthread.h" +#include "lltexturecache.h" +#include "lltexturefetch.h" +#include "llimageworker.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 "lleconomy.h" +#include "llcachename.h" +#include "audioengine.h" +#include "llviewermenu.h" +#include "llselectmgr.h" +#include "lltracker.h" +#include "llmozlib.h" +#include "llviewerparcelmgr.h" +#include "llworldmapview.h" + +#include "lldebugview.h" +#include "llconsole.h" +#include "llcontainerview.h" +#include "llhoverview.h" + +#if LL_WINDOWS && LL_LCD_COMPILE + #include "lllcd.h" +#endif + +#if LL_QUICKTIME_ENABLED + #if LL_DARWIN + #include <QuickTime/QuickTime.h> + #else + // quicktime specific includes + #include "MacTypes.h" + #include "QTML.h" + #include "Movies.h" + #include "FixMath.h" + #endif +#endif + +#include "llworld.h" +#include "llhudeffecttrail.h" +#include "llvectorperfoptions.h" +#include "llurlsimstring.h" + +// Included so that constants/settings might be initialized +// in save_settings_to_globals() +#include "llbutton.h" +#include "llcombobox.h" +#include "llstatusbar.h" +#include "llsurface.h" +#include "llvosky.h" +#include "llvotree.h" +#include "llvoavatar.h" +#include "llfolderview.h" +#include "lltoolbar.h" +#include "llframestats.h" +#include "llagentpilot.h" +#include "llsrv.h" + +// includes for idle() idleShutdown() +#include "llviewercontrol.h" +#include "lleventnotifier.h" +#include "llcallbacklist.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 "llinventoryview.h" + +// *FIX: Remove these once the command line params thing is figured out. +// Yuck! +static int gTempArgC = 0; +static char** gTempArgV; + +// *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" +// extern EGridInfo gGridChoice; + +//---------------------------------------------------------------------------- +// viewer.cpp - these are only used in viewer, should be easily moved. +extern void disable_win_error_reporting(); + +//#define APPLE_PREVIEW // Define this if you're doing a preview build on the Mac +#if LL_RELEASE_FOR_DOWNLOAD +// Default userserver for production builds is agni +#ifndef APPLE_PREVIEW +static EGridInfo GridDefaultChoice = GRID_INFO_AGNI; +#else +static EGridInfo GridDefaultChoice = GRID_INFO_ADITI; +#endif +#else +// Default userserver for development builds is dmz +static EGridInfo GridDefaultChoice = GRID_INFO_DMZ; +#endif + +#if LL_WINDOWS +extern void create_console(); +#endif + + +#if LL_DARWIN +#include <Carbon/Carbon.h> +extern void init_apple_menu(const char* product); +extern OSErr AEGURLHandler(const AppleEvent *messagein, AppleEvent *reply, long refIn); +extern OSErr AEQuitHandler(const AppleEvent *messagein, AppleEvent *reply, long refIn); +extern OSStatus simpleDialogHandler(EventHandlerCallRef handler, EventRef event, void *userdata); +extern OSStatus DisplayReleaseNotes(void); +#include <boost/tokenizer.hpp> +#endif // LL_DARWIN + + +#include "moviemaker.h" +extern BOOL gbCapturing; + +#if !LL_SOLARIS + extern MovieMaker gMovieMaker; +#endif + +extern BOOL gRandomizeFramerate; +extern BOOL gPeriodicSlowFrame; + +#if LL_GSTREAMER_ENABLED +void UnloadGStreamer(); +#endif + +extern void send_stats(); +//////////////////////////////////////////////////////////// +// All from the last globals push... +bool gVerifySSLCert = true; +BOOL gHandleKeysAsync = FALSE; + +BOOL gProbeHardware = TRUE; // Use DirectX 9 to probe for hardware + +S32 gYieldMS = 0; // set in parse_args, used in mainLoop +BOOL gYieldTime = FALSE; + +const F32 DEFAULT_AFK_TIMEOUT = 5.f * 60.f; // time with no input before user flagged as Away From Keyboard + +F32 gSimLastTime; // Used in LLAppViewer::init and send_stats() +F32 gSimFrames; + +LLString gDisabledMessage; // Set in LLAppViewer::initConfiguration used in idle_startup + +BOOL gHideLinks = FALSE; // Set in LLAppViewer::initConfiguration, used externally + +BOOL gInProductionGrid = FALSE; + +BOOL gAllowIdleAFK = TRUE; +F32 gAFKTimeout = DEFAULT_AFK_TIMEOUT; +BOOL gShowObjectUpdates = FALSE; +BOOL gLogMessages = FALSE; +std::string gChannelName = LL_CHANNEL; +BOOL gUseAudio = TRUE; +LLString gCmdLineFirstName; +LLString gCmdLineLastName; +LLString gCmdLinePassword; + +BOOL gAutoLogin = FALSE; + +const char* DEFAULT_SETTINGS_FILE = "settings.xml"; +BOOL gRequestInventoryLibrary = TRUE; +BOOL gGodConnect = FALSE; +BOOL gAcceptTOS = FALSE; +BOOL gAcceptCriticalMessage = FALSE; + +LLUUID gViewerDigest; // MD5 digest of the viewer's executable file. +BOOL gLastExecFroze = FALSE; + +U32 gFrameCount = 0; +U32 gForegroundFrameCount = 0; // number of frames that app window was in foreground +LLPumpIO* gServicePump = NULL; + +BOOL gPacificDaylightTime = FALSE; + +U64 gFrameTime = 0; +F32 gFrameTimeSeconds = 0.f; +F32 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 +U64 gStartTime = 0; // gStartTime is "private", used only to calculate gFrameTimeSeconds + +LLTimer gRenderStartTime; +LLFrameTimer gForegroundTime; +LLTimer gLogoutTimer; +static const F32 LOGOUT_REQUEST_TIME = 6.f; // this will be cut short by the LogoutReply msg. +F32 gLogoutMaxTime = LOGOUT_REQUEST_TIME; + +LLUUID gInventoryLibraryOwner; +LLUUID gInventoryLibraryRoot; + +BOOL gDisableVoice = FALSE; +BOOL gDisconnected = FALSE; + +// Map scale in pixels per region +F32 gMapScale = 128.f; +F32 gMiniMapScale = 128.f; + +// used to restore texture state after a mode switch +LLFrameTimer gRestoreGLTimer; +BOOL gRestoreGL = FALSE; +BOOL gUseWireframe = FALSE; + +F32 gMouseSensitivity = 3.f; +BOOL gInvertMouse = FALSE; + +// VFS globals - see llappviewer.h +LLVFS* gStaticVFS = NULL; + +LLMemoryInfo gSysMemory; + +bool gPreloadImages = true; +bool gPreloadSounds = true; + +LLString gLastVersionChannel; + +LLVector3 gWindVec(3.0, 3.0, 0.0); +LLVector3 gRelativeWindVec(0.0, 0.0, 0.0); + +U32 gPacketsIn = 0; + +BOOL gPrintMessagesThisFrame = FALSE; + +BOOL gUseConsole = TRUE; + +BOOL gRandomizeFramerate = FALSE; +BOOL gPeriodicSlowFrame = FALSE; + +BOOL gQAMode = FALSE; + +//////////////////////////////////////////////////////////// +// Internal globals... that should be removed. +static F32 gQuitAfterSeconds = 0.f; +static BOOL gRotateRight = FALSE; +static BOOL gIgnorePixelDepth = FALSE; + +// Allow multiple viewers in ReleaseForDownload +#if LL_RELEASE_FOR_DOWNLOAD +static BOOL gMultipleViewersOK = FALSE; +#else +static BOOL gMultipleViewersOK = TRUE; +#endif + +static std::map<std::string, std::string> gCommandLineSettings; +static std::map<std::string, std::string> gCommandLineForcedSettings; + +static LLString gArgs; + +static LLString gOldSettingsFileName; +static const char* LEGACY_DEFAULT_SETTINGS_FILE = "settings.ini"; +static BOOL gDoDisconnect = FALSE; +static LLString gLaunchFileOnQuit; + +//---------------------------------------------------------------------------- +// File scope definitons +const char *VFS_DATA_FILE_BASE = "data.db2.x."; +const char *VFS_INDEX_FILE_BASE = "index.db2.x."; + +static LLString gSecondLife; +static LLString gWindowTitle; +#ifdef LL_WINDOWS + static char sWindowClass[] = "Second Life"; +#endif + +std::vector<std::string> gLoginURIs; +static std::string gHelperURI; + +static const char USAGE[] = "\n" +"usage:\tviewer [options]\n" +"options:\n" +" -login <first> <last> <password> log in as a user\n" +" -autologin log in as last saved user\n" +" -loginuri <URI> login server and CGI script to use\n" +" -helperuri <URI> helper web CGI prefix to use\n" +" -settings <filename> specify the filename of a\n" +" configuration file\n" +" default is settings.xml\n" +" -setdefault <variable> <value> specify the value of a particular\n" +" configuration variable which can be\n" +" overridden by settings.xml\n" +" -set <variable> <value> specify the value of a particular\n" +" configuration variable that\n" +" overrides all other settings\n" +" -user <user_server_ip> specify userserver in dotted quad\n" +#if !LL_RELEASE_FOR_DOWNLOAD +" -sim <simulator_ip> specify the simulator ip address\n" +#endif +" -god log in as god if you have god access\n" +" -purge delete files in cache\n" +" -safe reset preferences, run in safe mode\n" +" -noutc logs in local time, not UTC\n" +" -nothread run vfs in single thread\n" +" -noinvlib Do not request inventory library\n" +" -multiple allow multiple viewers\n" +" -nomultiple block multiple viewers\n" +" -novoice disable voice\n" +" -ignorepixeldepth ignore pixel depth settings\n" +" -cooperative [ms] yield some idle time to local host\n" +" -skin ui/branding skin folder to use\n" +#if LL_WINDOWS +" -noprobe disable hardware probe\n" +#endif +" -noquicktime disable QuickTime movies, speeds startup\n" +" -nopreload don't preload UI images or sounds, speeds startup\n" +// these seem to be unused +//" -noenv turn off environmental effects\n" +//" -proxy <proxy_ip> specify the proxy ip address\n" +"\n"; + +void idle_afk_check() +{ + // check idle timers + if (gAllowIdleAFK && (gAwayTriggerTimer.getElapsedTimeF32() > gAFKTimeout)) + { + gAgent.setAFK(); + } +} + +// A callback set in LLAppViewer::init() +static void ui_audio_callback(const LLUUID& uuid) +{ + if (gAudiop) + { + F32 volume = gSavedSettings.getF32("AudioLevelUI"); + gAudiop->triggerSound(uuid, gAgent.getID(), volume); + } +} + +void request_initial_instant_messages() +{ + static BOOL requested = FALSE; + if (!requested + && gMuteListp + && gMuteListp->isLoaded() + && gAgent.getAvatarObject()) + { + // Auto-accepted inventory items may require the avatar object + // to build a correct name. Likewise, inventory offers from + // muted avatars require the mute list to properly mute. + LLMessageSystem* msg = gMessageSystem; + msg->newMessageFast(_PREHASH_RetrieveInstantMessages); + msg->nextBlockFast(_PREHASH_AgentData); + msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID()); + msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID()); + gAgent.sendReliableMessage(); + requested = TRUE; + } +} + +// Use these strictly for things that are constructed at startup, +// or for things that are performance critical. JC +static void saved_settings_to_globals() +{ + LLBUTTON_H_PAD = gSavedSettings.getS32("ButtonHPad"); + LLBUTTON_V_PAD = gSavedSettings.getS32("ButtonVPad"); + BTN_HEIGHT_SMALL = gSavedSettings.getS32("ButtonHeightSmall"); + BTN_HEIGHT = gSavedSettings.getS32("ButtonHeight"); + + MENU_BAR_HEIGHT = gSavedSettings.getS32("MenuBarHeight"); + MENU_BAR_WIDTH = gSavedSettings.getS32("MenuBarWidth"); + STATUS_BAR_HEIGHT = gSavedSettings.getS32("StatusBarHeight"); + + LLCOMBOBOX_HEIGHT = BTN_HEIGHT - 2; + LLCOMBOBOX_WIDTH = 128; + + LLSurface::setTextureSize(gSavedSettings.getU32("RegionTextureSize")); + + LLVOSky::sNighttimeBrightness = gSavedSettings.getF32("RenderNightBrightness"); + + LLImageGL::sGlobalUseAnisotropic = gSavedSettings.getBOOL("RenderAnisotropic"); + LLVOVolume::sLODFactor = gSavedSettings.getF32("RenderVolumeLODFactor"); + LLVOVolume::sDistanceFactor = 1.f-LLVOVolume::sLODFactor * 0.1f; + LLVolumeImplFlexible::sUpdateFactor = gSavedSettings.getF32("RenderFlexTimeFactor"); + LLVOTree::sTreeFactor = gSavedSettings.getF32("RenderTreeLODFactor"); + LLVOAvatar::sLODFactor = gSavedSettings.getF32("RenderAvatarLODFactor"); + LLVOAvatar::sMaxVisible = gSavedSettings.getS32("RenderAvatarMaxVisible"); + LLVOAvatar::sVisibleInFirstPerson = gSavedSettings.getBOOL("FirstPersonAvatarVisible"); + // clamp auto-open time to some minimum usable value + LLFolderView::sAutoOpenTime = llmax(0.25f, gSavedSettings.getF32("FolderAutoOpenDelay")); + LLToolBar::sInventoryAutoOpenTime = gSavedSettings.getF32("InventoryAutoOpenDelay"); + LLSelectMgr::sRectSelectInclusive = gSavedSettings.getBOOL("RectangleSelectInclusive"); + LLSelectMgr::sRenderHiddenSelections = gSavedSettings.getBOOL("RenderHiddenSelections"); + LLSelectMgr::sRenderLightRadius = gSavedSettings.getBOOL("RenderLightRadius"); + + gFrameStats.setTrackStats(gSavedSettings.getBOOL("StatsSessionTrackFrameStats")); + gAgentPilot.mNumRuns = gSavedSettings.getS32("StatsNumRuns"); + gAgentPilot.mQuitAfterRuns = gSavedSettings.getBOOL("StatsQuitAfterRuns"); + gAgent.mHideGroupTitle = gSavedSettings.getBOOL("RenderHideGroupTitle"); + + gDebugWindowProc = gSavedSettings.getBOOL("DebugWindowProc"); + gAllowIdleAFK = gSavedSettings.getBOOL("AllowIdleAFK"); + gAFKTimeout = gSavedSettings.getF32("AFKTimeout"); + gMouseSensitivity = gSavedSettings.getF32("MouseSensitivity"); + gInvertMouse = gSavedSettings.getBOOL("InvertMouse"); + gShowObjectUpdates = gSavedSettings.getBOOL("ShowObjectUpdates"); + gMapScale = gSavedSettings.getF32("MapScale"); + gMiniMapScale = gSavedSettings.getF32("MiniMapScale"); + gHandleKeysAsync = gSavedSettings.getBOOL("AsyncKeyboard"); + LLHoverView::sShowHoverTips = gSavedSettings.getBOOL("ShowHoverTips"); + +#if LL_VECTORIZE + if (gSysCPU.hasAltivec()) + { + gSavedSettings.setBOOL("VectorizeEnable", TRUE ); + gSavedSettings.setU32("VectorizeProcessor", 0 ); + } + else + if (gSysCPU.hasSSE2()) + { + gSavedSettings.setBOOL("VectorizeEnable", TRUE ); + gSavedSettings.setU32("VectorizeProcessor", 2 ); + } + else + if (gSysCPU.hasSSE()) + { + gSavedSettings.setBOOL("VectorizeEnable", TRUE ); + gSavedSettings.setU32("VectorizeProcessor", 1 ); + } + else + { + // Don't bother testing or running if CPU doesn't support it. JC + gSavedSettings.setBOOL("VectorizePerfTest", FALSE ); + gSavedSettings.setBOOL("VectorizeEnable", FALSE ); + gSavedSettings.setU32("VectorizeProcessor", 0 ); + gSavedSettings.setBOOL("VectorizeSkin", FALSE); + } +#else + // This build target doesn't support SSE, don't test/run. + gSavedSettings.setBOOL("VectorizePerfTest", FALSE ); + gSavedSettings.setBOOL("VectorizeEnable", FALSE ); + gSavedSettings.setU32("VectorizeProcessor", 0 ); + gSavedSettings.setBOOL("VectorizeSkin", FALSE); +#endif + + // propagate push to talk preference to current status + gSavedSettings.setBOOL("PTTCurrentlyEnabled", gSavedSettings.getBOOL("EnablePushToTalk")); + + settings_setup_listeners(); + + // gAgent.init() also loads from saved settings. +} + +int parse_args(int argc, char **argv) +{ + // Sometimes IP addresses passed in on the command line have leading + // or trailing white space. Use LLString to clean that up. + LLString ip_string; + S32 j; + + for (j = 1; j < argc; j++) + { + gArgs += argv[j]; + gArgs += " "; + + LLString argument = argv[j]; + if ((!strcmp(argv[j], "-port")) && (++j < argc)) + { + sscanf(argv[j], "%u", &(gAgent.mViewerPort)); + } + else if ((!strcmp(argv[j], "-drop")) && (++j < argc)) + { + sscanf(argv[j], "%f", &gPacketDropPercentage); + } + else if ((!strcmp(argv[j], "-inbw")) && (++j < argc)) + { + sscanf(argv[j], "%f", &gInBandwidth); + } + else if ((!strcmp(argv[j], "-outbw")) && (++j < argc)) + { + sscanf(argv[j], "%f", &gOutBandwidth); + } + else if (!strcmp(argv[j], "--aditi")) + { + gGridChoice = GRID_INFO_ADITI; + snprintf(gGridName, MAX_STRING, "%s", gGridInfo[gGridChoice].mName); // Flawfinder: ignore + } + else if (!strcmp(argv[j], "--agni")) + { + gGridChoice = GRID_INFO_AGNI; + snprintf(gGridName, MAX_STRING, "%s", gGridInfo[gGridChoice].mName); // Flawfinder: ignore + } + else if (!strcmp(argv[j], "--dmz")) + { + gGridChoice = GRID_INFO_DMZ; + snprintf(gGridName, MAX_STRING, "%s", gGridInfo[gGridChoice].mName); // Flawfinder: ignore + } + else if (!strcmp(argv[j], "--siva")) + { + gGridChoice = GRID_INFO_SIVA; + snprintf(gGridName, MAX_STRING, "%s", gGridInfo[gGridChoice].mName); // Flawfinder: ignore + } + else if (!strcmp(argv[j], "--shakti")) + { + gGridChoice = GRID_INFO_SHAKTI; + snprintf(gGridName, MAX_STRING, "%s", gGridInfo[gGridChoice].mName); // Flawfinder: ignore + } + else if (!strcmp(argv[j], "--durga")) + { + gGridChoice = GRID_INFO_DURGA; + snprintf(gGridName, MAX_STRING, "%s", gGridInfo[gGridChoice].mName); // Flawfinder: ignore + } + else if (!strcmp(argv[j], "--soma")) + { + gGridChoice = GRID_INFO_SOMA; + snprintf(gGridName, MAX_STRING, "%s", gGridInfo[gGridChoice].mName); // Flawfinder: ignore + } + else if (!strcmp(argv[j], "--ganga")) + { + gGridChoice = GRID_INFO_GANGA; + sprintf(gGridName,"%s", gGridInfo[gGridChoice].mName); + } + else if (!strcmp(argv[j], "--vaak")) + { + gGridChoice = GRID_INFO_VAAK; + sprintf(gGridName,"%s", gGridInfo[gGridChoice].mName); + } + else if (!strcmp(argv[j], "--uma")) + { + gGridChoice = GRID_INFO_UMA; + sprintf(gGridName,"%s", gGridInfo[gGridChoice].mName); + } + else if (!strcmp(argv[j], "-user") && (++j < argc)) + { + if (!strcmp(argv[j], "-")) + { + gGridChoice = GRID_INFO_LOCAL; + snprintf(gGridName, MAX_STRING, "%s", LOOPBACK_ADDRESS_STRING); // Flawfinder: ignore + } + else + { + gGridChoice = GRID_INFO_OTHER; + ip_string.assign( argv[j] ); + LLString::trim(ip_string); + snprintf(gGridName, MAX_STRING, "%s", ip_string.c_str()); // Flawfinder: ignore + } + } + else if (!strcmp(argv[j], "-loginuri") && (++j < argc)) + { + LLAppViewer::instance()->addLoginURI(utf8str_trim(argv[j])); + } + else if (!strcmp(argv[j], "-helperuri") && (++j < argc)) + { + LLAppViewer::instance()->setHelperURI(utf8str_trim(argv[j])); + } + else if (!strcmp(argv[j], "-debugviews")) + { + LLView::sDebugRects = TRUE; + } + else if (!strcmp(argv[j], "-skin") && (++j < argc)) + { + std::string folder(argv[j]); + gDirUtilp->setSkinFolder(folder); + } + else if (!strcmp(argv[j], "-autologin") || !strcmp(argv[j], "--autologin")) // keep --autologin for compatibility + { + gAutoLogin = TRUE; + } + else if (!strcmp(argv[j], "-quitafter") && (++j < argc)) + { + gQuitAfterSeconds = (F32)atof(argv[j]); + } + else if (!strcmp(argv[j], "-rotate")) + { + gRotateRight = TRUE; + } +// else if (!strcmp(argv[j], "-noenv")) +// { + //turn OFF environmental effects for slow machines/video cards +// gRequestParaboloidMap = FALSE; +// } + else if (!strcmp(argv[j], "-noaudio")) + { + gUseAudio = FALSE; + } + else if (!strcmp(argv[j], "-nosound")) // tends to be popular cmdline on Linux. + { + gUseAudio = FALSE; + } + else if (!strcmp(argv[j], "-noprobe")) + { + gProbeHardware = FALSE; + } + else if (!strcmp(argv[j], "-noquicktime")) + { + // Developers can log in faster if they don't load all the + // quicktime dlls. + gUseQuickTime = false; + } + else if (!strcmp(argv[j], "-nopreload")) + { + // Developers can log in faster if they don't decode sounds + // or images on startup, ~5 seconds faster. + gPreloadSounds = false; + gPreloadImages = false; + } + else if (!strcmp(argv[j], "-purge")) + { + LLAppViewer::instance()->purgeCache(); + } + else if(!strcmp(argv[j], "-noinvlib")) + { + gRequestInventoryLibrary = FALSE; + } + else if (!strcmp(argv[j], "-log")) + { + gLogMessages = TRUE; + continue; + } + else if (!strcmp(argv[j], "-logfile") && (++j < argc)) + { + // *NOTE: This buffer size is hard coded into scanf() below. + char logfile[256]; // Flawfinder: ignore + sscanf(argv[j], "%255s", logfile); // Flawfinder: ignore + llinfos << "Setting log file to " << logfile << llendl; + LLFile::remove(logfile); + LLError::logToFile(logfile); + } + else if (!strcmp(argv[j], "-settings") && (++j < argc)) + { + gSettingsFileName = gDirUtilp->getExpandedFilename(LL_PATH_USER_SETTINGS, argv[j]); + } + else if (!strcmp(argv[j], "-setdefault") && (j + 2 < argc)) + { + std::string control_name; + std::string control_value; + + j++; + if (argv[j]) control_name = std::string(argv[j]); + + j++; + if (argv[j]) control_value = std::string(argv[j]); + + // grab control name and value + if (!control_name.empty()) + { + gCommandLineSettings[control_name] = control_value; + } + } + else if (!strcmp(argv[j], "-set") && (j + 2 < argc)) + { + std::string control_name; + std::string control_value; + + j++; + if (argv[j]) control_name = std::string(argv[j]); + + j++; + if (argv[j]) control_value = std::string(argv[j]); + + // grab control name and value + if (!control_name.empty()) + { + gCommandLineForcedSettings[control_name] = control_value; + } + } + else if (!strcmp(argv[j], "-login")) + { + if (j + 3 < argc) + { + j++; + gCmdLineFirstName = argv[j]; + j++; + gCmdLineLastName = argv[j]; + j++; + gCmdLinePassword = argv[j]; + } + else + { + // only works if -login is last parameter on command line + llerrs << "Not enough parameters to -login. Did you mean -loginuri?" << llendl; + } + } + else if (!strcmp(argv[j], "-god")) + { + gGodConnect = TRUE; + } + else if (!strcmp(argv[j], "-noconsole")) + { + gUseConsole = FALSE; + } + else if (!strcmp(argv[j], "-safe")) + { + llinfos << "Setting viewer feature table to run in safe mode, resetting prefs" << llendl; + gFeatureManagerp->setSafe(TRUE); + } + else if (!strcmp(argv[j], "-multiple")) + { + gMultipleViewersOK = TRUE; + } + else if (!strcmp(argv[j], "-nomultiple")) + { + gMultipleViewersOK = FALSE; + } + else if (!strcmp(argv[j], "-novoice")) + { + gDisableVoice = TRUE; + } + else if (!strcmp(argv[j], "-nothread")) + { + LLVFile::ALLOW_ASYNC = FALSE; + llinfos << "Running VFS in nothread mode" << llendl; + } + // some programs don't respect the command line options in protocol handlers (I'm looking at you, Opera) + // so this allows us to parse the URL straight off the command line without a "-url" paramater + else if (!argument.compare(0, std::string( "secondlife://" ).length(), std::string("secondlife://"))) + { + // *NOTE: After setting the url, bail. 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 + LLURLSimString::setString(argv[j]); + gArgs += argv[j]; + return 0; + } + else if (!strcmp(argv[j], "-url") && (++j < argc)) + { + // *NOTE: After setting the url, bail. 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 + LLURLSimString::setString(argv[j]); + gArgs += argv[j]; + return 0; + } + else if (!strcmp(argv[j], "-ignorepixeldepth")) + { + gIgnorePixelDepth = TRUE; + } + else if (!strcmp(argv[j], "-cooperative")) + { + S32 ms_to_yield = 0; + if(++j < argc) + { + S32 rv = sscanf(argv[j], "%d", &ms_to_yield); + if(0 == rv) + { + --j; + } + } + else + { + --j; + } + gYieldMS = ms_to_yield; + gYieldTime = TRUE; + } + else if (!strcmp(argv[j], "-no-verify-ssl-cert")) + { + gVerifySSLCert = false; + } + else if ( (!strcmp(argv[j], "--channel") || !strcmp(argv[j], "-channel")) && (++j < argc)) + { + gChannelName = argv[j]; + } +#if LL_DARWIN + else if (!strncmp(argv[j], "-psn_", 5)) + { + // this is the Finder passing the process session number + // we ignore this + } +#endif + else if(!strncmp(argv[j], "-qa", 3)) + { + gQAMode = TRUE; + } + else + { + + // DBC - Mac OS X passes some stuff by default on the command line (e.g. psn). + // Second Life URLs are passed this way as well? + llwarns << "Possible unknown keyword " << argv[j] << llendl; + + // print usage information + llinfos << USAGE << llendl; + // return 1; + } + } + return 0; +} + +bool send_url_to_other_instance(const std::string& url) +{ +#if LL_WINDOWS + wchar_t window_class[256]; /* Flawfinder: ignore */ // Assume max length < 255 chars. + mbstowcs(window_class, sWindowClass, 255); + window_class[255] = 0; + // Use the class instead of the window name. + HWND other_window = FindWindow(window_class, NULL); + if (other_window != NULL) + { + lldebugs << "Found other window with the name '" << gWindowTitle << "'" << llendl; + COPYDATASTRUCT cds; + const S32 SLURL_MESSAGE_TYPE = 0; + cds.dwData = SLURL_MESSAGE_TYPE; + cds.cbData = url.length() + 1; + cds.lpData = (void*)url.c_str(); + + LRESULT msg_result = SendMessage(other_window, WM_COPYDATA, NULL, (LPARAM)&cds); + lldebugs << "SendMessage(WM_COPYDATA) to other window '" + << gWindowTitle << "' returned " << msg_result << llendl; + return true; + } +#endif + return false; +} + +//---------------------------------------------------------------------------- +// LLAppViewer definition + +// Static members. +// The single viewer app. +LLAppViewer* LLAppViewer::sInstance = NULL; + +LLTextureCache* LLAppViewer::sTextureCache = NULL; +LLWorkerThread* LLAppViewer::sImageDecodeThread = NULL; +LLTextureFetch* LLAppViewer::sTextureFetch = NULL; + +LLAppViewer::LLAppViewer() : + mMarkerFile(NULL), + mLastExecFroze(false), + mDebugFile(NULL), + mCrashBehavior(CRASH_BEHAVIOR_ASK), + mReportedCrash(false), + mNumSessions(0), + mPurgeCache(false), + mPurgeOnExit(false), + mSecondInstance(false), + mSavedFinalSnapshot(false), + mQuitRequested(false), + mLogoutRequestSent(false) +{ + if(NULL != sInstance) + { + llerrs << "Oh no! An instance of LLAppViewer already exists! LLAppViewer is sort of like a singleton." << llendl; + } + + sInstance = this; +} + +LLAppViewer::~LLAppViewer() +{ + // If we got to this destructor somehow, the app didn't hang. + removeMarkerFile(); +} + +bool LLAppViewer::tempStoreCommandOptions(int argc, char** argv) +{ + gTempArgC = argc; + gTempArgV = argv; + return true; +} + +bool LLAppViewer::init() +{ + // *NOTE:Mani - LLCurl::initClass is not thread safe. + // Called before threads are created. + LLCurl::initClass(); + + initThreads(); + + initEarlyConfiguration(); + + // + // Start of the application + // + // 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. + // + + // 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"); + + + initLogging(); + + // + // OK to write stuff to logs now, we've now crash reported if necessary + // + + // Set up some defaults... + gSettingsFileName = gDirUtilp->getExpandedFilename(LL_PATH_USER_SETTINGS, DEFAULT_SETTINGS_FILE); + gOldSettingsFileName = gDirUtilp->getExpandedFilename(LL_PATH_USER_SETTINGS, LEGACY_DEFAULT_SETTINGS_FILE); + + initConfiguration(); + + ////////////////////////////////////////////////////////////////////////////// + ////////////////////////////////////////////////////////////////////////////// + ////////////////////////////////////////////////////////////////////////////// + ////////////////////////////////////////////////////////////////////////////// + // *FIX: The following code isn't grouped into functions yet. + + // + // Write system information into the debug log (CPU, OS, etc.) + // + writeSystemInfo(); + + // Build a string representing the current version number. + gCurrentVersion = llformat("%d.%d.%d", LL_VERSION_MAJOR, LL_VERSION_MINOR, LL_VERSION_PATCH ); + + // + // Load the feature tables + // + llinfos << "Loading feature tables." << llendl; + + gFeatureManagerp->loadFeatureTables(); + gFeatureManagerp->initCPUFeatureMasks(); + + // Merge with the command line overrides + gSavedSettings.applyOverrides(gCommandLineSettings); + + // Need to do this before calling parseAlerts + gUICtrlFactory = new LLViewerUICtrlFactory(); + + // Pre-load alerts.xml to define the warnings settings (always loads from skins/xui/en-us/) + // Do this *before* loading the settings file + LLAlertDialog::parseAlerts("alerts.xml", &gSavedSettings, TRUE); + + // Overwrite default settings with user settings + llinfos << "Loading configuration file " << gSettingsFileName << llendl; + if (0 == gSavedSettings.loadFromFile(gSettingsFileName)) + { + llinfos << "Failed to load settings from " << gSettingsFileName << llendl; + llinfos << "Loading legacy settings from " << gOldSettingsFileName << llendl; + gSavedSettings.loadFromFileLegacy(gOldSettingsFileName); + } + + // need to do this here - need to have initialized global settings first + LLString nextLoginLocation = gSavedSettings.getString( "NextLoginLocation" ); + if ( nextLoginLocation.length() ) + { + LLURLSimString::setString( nextLoginLocation.c_str() ); + }; + + // Merge with the command line overrides + gSavedSettings.applyOverrides(gCommandLineForcedSettings); + + gLastRunVersion = gSavedSettings.getString("LastRunVersion"); + + fixup_settings(); + + // Get the single value from the crash settings file, if it exists + std::string crash_settings_filename = gDirUtilp->getExpandedFilename(LL_PATH_USER_SETTINGS, CRASH_SETTINGS_FILE); + gCrashSettings.loadFromFile(crash_settings_filename.c_str()); + + ///////////////////////////////////////////////// + // OS-specific login dialogs + ///////////////////////////////////////////////// +#if LL_WINDOWS + /* + // Display initial login screen, comes up quickly. JC + { + LLSplashScreen::hide(); + + INT_PTR result = DialogBox(hInstance, L"CONNECTBOX", NULL, login_dialog_func); + if (result < 0) + { + llwarns << "Connect dialog box failed, returned " << result << llendl; + return 1; + } + // success, result contains which button user clicked + llinfos << "Connect dialog box clicked " << result << llendl; + + LLSplashScreen::show(); + } + */ +#endif + + // track number of times that app has run + mNumSessions = gSavedSettings.getS32("NumSessions"); + mNumSessions++; + gSavedSettings.setS32("NumSessions", mNumSessions); + + gSavedSettings.setString("HelpLastVisitedURL",gSavedSettings.getString("HelpHomeURL")); + + if (gSavedSettings.getBOOL("VerboseLogs")) + { + LLError::setPrintLocation(true); + } + +#if !LL_RELEASE_FOR_DOWNLOAD + if (gGridChoice == GRID_INFO_NONE) + { + // Development version: load last server choice by default (overridden by cmd line args) + + S32 server = gSavedSettings.getS32("ServerChoice"); + if (server != 0) + gGridChoice = (EGridInfo)llclamp(server, 0, (S32)GRID_INFO_COUNT - 1); + if (server == GRID_INFO_OTHER) + { + LLString custom_server = gSavedSettings.getString("CustomServer"); + if (custom_server.empty()) + { + snprintf(gGridName, MAX_STRING, "none"); /* Flawfinder: ignore */ + } + else + { + snprintf(gGridName, MAX_STRING, "%s", custom_server.c_str()); /* Flawfinder: ignore */ + } + } + } +#endif + + if (gGridChoice == GRID_INFO_NONE) + { + gGridChoice = GridDefaultChoice; + } + + // Load art UUID information, don't require these strings to be declared in code. + LLString viewer_art_filename = gDirUtilp->getExpandedFilename(LL_PATH_APP_SETTINGS,"viewerart.xml"); + llinfos << "Loading art table from " << viewer_art_filename << llendl; + gViewerArt.loadFromFile(viewer_art_filename.c_str(), FALSE); + LLString textures_filename = gDirUtilp->getExpandedFilename(LL_PATH_SKINS, "textures", "textures.xml"); + llinfos << "Loading art table from " << textures_filename << llendl; + gViewerArt.loadFromFile(textures_filename.c_str(), FALSE); + + LLString colors_base_filename = gDirUtilp->getExpandedFilename(LL_PATH_APP_SETTINGS, "colors_base.xml"); + llinfos << "Loading base colors from " << colors_base_filename << llendl; + gColors.loadFromFile(colors_base_filename.c_str(), FALSE, TYPE_COL4U); + + // Load overrides from user colors file + LLString user_colors_filename = gDirUtilp->getExpandedFilename(LL_PATH_APP_SETTINGS, "colors.xml"); + llinfos << "Loading user colors from " << user_colors_filename << llendl; + if (gColors.loadFromFile(user_colors_filename.c_str(), FALSE, TYPE_COL4U) == 0) + { + llinfos << "Failed to load user colors from " << user_colors_filename << llendl; + LLString user_legacy_colors_filename = gDirUtilp->getExpandedFilename(LL_PATH_APP_SETTINGS, "colors.ini"); + llinfos << "Loading legacy colors from " << user_legacy_colors_filename << llendl; + gColors.loadFromFileLegacy(user_legacy_colors_filename.c_str(), FALSE, TYPE_COL4U); + } + + // Widget construction depends on LLUI being initialized + LLUI::initClass(&gSavedSettings, + &gColors, + &gViewerArt, + &gImageList, + ui_audio_callback, + &LLUI::sGLScaleFactor); + + gUICtrlFactory->setupPaths(); // update paths with correct language set + + ///////////////////////////////////////////////// + // + // Load settings files + // + // + LLGroupMgr::parseRoleActions("role_actions.xml"); + + LLAgent::parseTeleportMessages("teleport_strings.xml"); + + mCrashBehavior = gCrashSettings.getS32(CRASH_BEHAVIOR_SETTING); + + LLVectorPerformanceOptions::initClass(); + + // Move certain saved settings into global variables for speed + saved_settings_to_globals(); + + + // Find partition serial number (Windows) or hardware serial (Mac) + mSerialNumber = generateSerialNumber(); + + if(false == initHardwareTest()) + { + // Early out from user choice. + return false; + } + + // Always fetch the Ethernet MAC address, needed both for login + // and password load. + LLUUID::getNodeID(gMACAddress); + + // 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 + + gViewerStats = new LLViewerStats(); + + // + // Initialize the VFS, and gracefully handle initialization errors + // + + if (!initCache()) + { + std::ostringstream msg; + msg << + gSecondLife << " is unable to access a file that it needs.\n" + "\n" + "This can be because you somehow have multiple copies running, " + "or your system incorrectly thinks a file is open. " + "If this message persists, restart your computer and try again. " + "If it continues to persist, you may need to completely uninstall " << + gSecondLife << " and reinstall it."; + OSMessageBox( + msg.str().c_str(), + NULL, + OSMB_OK); + return 1; + } + +#if LL_DARWIN + // Display the release notes for the current version + if(!gHideLinks && gCurrentVersion != gLastRunVersion) + { + // Current version and last run version don't match exactly. Display the release notes. + DisplayReleaseNotes(); + } +#endif + + // + // Initialize the window + // + initWindow(); + + #if LL_WINDOWS && LL_LCD_COMPILE + // start up an LCD window on a logitech keyboard, if there is one + HINSTANCE hInstance = GetModuleHandle(NULL); + gLcdScreen = new llLCD(hInstance); + CreateLCDDebugWindows(); + #endif + + writeDebug(gGLManager.getGLInfoString()); + llinfos << gGLManager.getGLInfoString() << llendl; + + //load key settings + bind_keyboard_functions(); + + // Load Default bindings + if (!gViewerKeyboard.loadBindings(gDirUtilp->getExpandedFilename(LL_PATH_APP_SETTINGS,"keys.ini").c_str())) + { + llerrs << "Unable to open keys.ini" << llendl; + } + // Load Custom bindings (override defaults) + gViewerKeyboard.loadBindings(gDirUtilp->getExpandedFilename(LL_PATH_APP_SETTINGS,"custom_keys.ini").c_str()); + + // Calculate the digest for the executable (takes < 90ms on a fast machine). + FILE* app_file = LLFile::fopen( gDirUtilp->getExecutablePathAndName().c_str(), "rb" ); /* Flawfinder: ignore */ + if( app_file ) + { + LLMD5 app_md5; + app_md5.update( app_file ); // Automatically closes the file + app_md5.finalize(); + app_md5.raw_digest( gViewerDigest.mData ); + } + llinfos << "Viewer Digest: " << gViewerDigest << llendl; + + // If we don't have the right GL requirements, exit. + // BUG: This should just be changed to a generic GL "Not good enough" flag + if (!gGLManager.mHasMultitexture && !gNoRender) + { + std::ostringstream msg; + msg << + "You do not appear to have the proper hardware requirements " + "for " << gSecondLife << ". " << gSecondLife << " requires an OpenGL graphics " + "card that has multitexture support. If this is the case, " + "you may want to make sure that you have the latest drivers for " + "your graphics card, and service packs and patches for your " + "operating system.\n" + "If you continue to have problems, please go to: " + "www.secondlife.com/support "; + OSMessageBox( + msg.str().c_str(), + NULL, + OSMB_OK); + return 0; + } + + // Save the current version to the prefs file + gSavedSettings.setString("LastRunVersion", gCurrentVersion); + + gSimLastTime = gRenderStartTime.getElapsedTimeF32(); + gSimFrames = (F32)gFrameCount; + + return true; +} + +bool LLAppViewer::mainLoop() +{ + //------------------------------------------- + // Run main loop until time to quit + //------------------------------------------- + + // Create IO Pump to use for HTTP Requests. + gServicePump = new LLPumpIO(gAPRPoolp); + LLHTTPClient::setPump(*gServicePump); + LLHTTPClient::setCABundle(gDirUtilp->getCAFile()); + + // initialize voice stuff here + gLocalSpeakerMgr = new LLLocalSpeakerMgr(); + gActiveChannelSpeakerMgr = new LLActiveSpeakerMgr(); + + LLVoiceChannel::initClass(); + LLVoiceClient::init(gServicePump); + + LLMemType mt1(LLMemType::MTYPE_MAIN); + LLTimer frameTimer,idleTimer; + LLTimer debugTime; + + // Handle messages + while (!LLApp::isExiting()) + { + LLFastTimer::reset(); // Should be outside of any timer instances + { + LLFastTimer t(LLFastTimer::FTM_FRAME); + + { + LLFastTimer t2(LLFastTimer::FTM_MESSAGES); + #if LL_WINDOWS + if (!LLWinDebug::setupExceptionHandler()) + { + llwarns << " Someone took over my exception handler (post messagehandling)!" << llendl; + } + #endif + + gViewerWindow->mWindow->gatherInput(); + } + +#if 1 && !RELEASE_FOR_DOWNLOAD + // once per second debug info + if (debugTime.getElapsedTimeF32() > 1.f) + { + debugTime.reset(); + } +#endif + if (!LLApp::isExiting()) + { + // 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->mWindow->getVisible() + && gViewerWindow->getActive() + && !gViewerWindow->mWindow->getMinimized() + && LLStartUp::getStartupState() == STATE_STARTED + && !gViewerWindow->getShowProgress() + && !gFocusMgr.focusLocked()) + { + gKeyboard->scanKeyboard(); + LLViewerJoystick::scanJoystick(); + } + + // Update state based on messages, user input, object idle. + { + LLFastTimer t3(LLFastTimer::FTM_IDLE); + idle(); + LLCurl::process(); + // this pump is necessary to make the login screen show up + gServicePump->pump(); + gServicePump->callback(); + } + + if (gDoDisconnect && (LLStartUp::getStartupState() == STATE_STARTED)) + { + saveFinalSnapshot(); + disconnectViewer(); + } + + // Render scene. + if (!LLApp::isExiting()) + { + display(); + + LLFloaterSnapshot::update(); // take snapshots + +#if !LL_SOLARIS + if (gbCapturing) + { + gMovieMaker.Snap(); + } +#endif +#if LL_WINDOWS && LL_LCD_COMPILE + // update LCD Screen + gLcdScreen->UpdateDisplay(); +#endif + } + + } + + // Sleep and run background threads + { + LLFastTimer t2(LLFastTimer::FTM_SLEEP); + bool run_multiple_threads = gSavedSettings.getBOOL("RunMultipleThreads"); + + // yield some time to the os based on command line option + if(gYieldTime) + { + ms_sleep(gYieldMS); + } + + // yield cooperatively when not running as foreground window + if ( gNoRender + || !gViewerWindow->mWindow->getVisible() + || !gFocusMgr.getAppHasFocus()) + { + // Sleep if we're not rendering, or the window is minimized. + S32 milliseconds_to_sleep = llclamp(gSavedSettings.getS32("BackgroundYieldTime"), 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) + { + ms_sleep(milliseconds_to_sleep); + // also pause worker threads during this wait period + LLAppViewer::getTextureCache()->pause(); + LLAppViewer::getImageDecodeThread()->pause(); + } + } + + if (gRandomizeFramerate) + { + ms_sleep(rand() % 200); + } + + if (gPeriodicSlowFrame + && (gFrameCount % 10 == 0)) + { + llinfos << "Periodic slow frame - sleeping 500 ms" << llendl; + ms_sleep(500); + } + + + const F64 min_frame_time = 0.0; //(.0333 - .0010); // max video frame rate = 30 fps + const F64 min_idle_time = 0.0; //(.0010); // min idle time = 1 ms + const F64 max_idle_time = run_multiple_threads ? min_idle_time : .005; // 5 ms + idleTimer.reset(); + while(1) + { + S32 work_pending = 0; + S32 io_pending = 0; + work_pending += LLAppViewer::getTextureCache()->update(1); // unpauses the texture cache thread + work_pending += LLAppViewer::getImageDecodeThread()->update(1); // unpauses the image thread + work_pending += LLAppViewer::getTextureFetch()->update(1); // unpauses the texture fetch thread + io_pending += LLVFSThread::updateClass(1); + io_pending += LLLFSThread::updateClass(1); + if (io_pending > 1000) + { + ms_sleep(llmin(io_pending/100,100)); // give the vfs some time to catch up + } + + F64 frame_time = frameTimer.getElapsedTimeF64(); + F64 idle_time = idleTimer.getElapsedTimeF64(); + if (frame_time >= min_frame_time && + idle_time >= min_idle_time && + (!work_pending || idle_time >= max_idle_time)) + { + break; + } + } + frameTimer.reset(); + + // Prevent the worker threads from running while rendering. + // if (LLThread::processorCount()==1) //pause() should only be required when on a single processor client... + if (run_multiple_threads == FALSE) + { + LLAppViewer::getTextureCache()->pause(); + LLAppViewer::getImageDecodeThread()->pause(); + // LLAppViewer::getTextureFetch()->pause(); // Don't pause the fetch (IO) thread + } + //LLVFSThread::sLocal->pause(); // Prevent the VFS thread from running while rendering. + //LLLFSThread::sLocal->pause(); // Prevent the LFS thread from running while rendering. + } + + } + } + + // Save snapshot for next time, if we made it through initialization + if (STATE_STARTED == LLStartUp::getStartupState()) + { + saveFinalSnapshot(); + } + + delete gServicePump; + + llinfos << "Exiting main_loop" << llendflush; + + return true; +} + +bool LLAppViewer::cleanup() +{ + //flag all elements as needing to be destroyed immediately + // to ensure shutdown order + LLMortician::setZealous(TRUE); + + LLVoiceClient::terminate(); + + disconnectViewer(); + + llinfos << "Viewer disconnected" << llendflush; + + display_cleanup(); + + release_start_screen(); // just in case + + LLError::logToFixedBuffer(NULL); + + llinfos << "Cleaning Up" << llendflush; + + LLKeyframeDataCache::clear(); + + // Must clean up texture references before viewer window is destroyed. + LLHUDObject::cleanupHUDObjects(); + llinfos << "HUD Objects cleaned up" << llendflush; + + // End TransferManager before deleting systems it depends on (Audio, VFS, AssetStorage) +#if 0 // this seems to get us stuck in an infinite loop... + gTransferManager.cleanup(); +#endif + + // Clean up map data storage + delete gWorldMap; + gWorldMap = NULL; + + delete gHUDManager; + gHUDManager = NULL; + + delete gToolMgr; + gToolMgr = NULL; + + delete gAssetStorage; + gAssetStorage = NULL; + + LLPolyMesh::freeAllMeshes(); + + delete gCacheName; + gCacheName = NULL; + + delete gGlobalEconomy; + gGlobalEconomy = NULL; + + delete gLocalSpeakerMgr; + gLocalSpeakerMgr = NULL; + + LLNotifyBox::cleanup(); + + llinfos << "Global stuff deleted" << llendflush; + +#if !LL_RELEASE_FOR_DOWNLOAD + if (gAudiop) + { + gAudiop->shutdown(); + } +#else + // This hack exists because fmod likes to occasionally hang forever + // when shutting down for no apparent reason. + llwarns << "Hack, skipping audio engine cleanup" << llendflush; +#endif + + + // moved to main application shutdown for now because it's non-trivial and only needs to be done once + // (even though it goes against the media framework design) + + LLMediaEngine::cleanupClass(); + +#if LL_QUICKTIME_ENABLED + if (gQuickTimeInitialized) + { + // clean up media stuff + llinfos << "Cleaning up QuickTime" << llendl; + ExitMovies (); + #if LL_WINDOWS + // Only necessary/available on Windows. + TerminateQTML (); + #endif + } + llinfos << "Quicktime cleaned up" << llendflush; +#endif + +#if LL_GSTREAMER_ENABLED + llinfos << "Cleaning up GStreamer" << llendl; + UnloadGStreamer(); + llinfos << "GStreamer cleaned up" << llendflush; +#endif + + llinfos << "Cleaning up feature manager" << llendflush; + delete gFeatureManagerp; + gFeatureManagerp = NULL; + + // 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(); + llinfos << "Settings patched up" << llendflush; + + delete gAudiop; + gAudiop = NULL; + + // delete some of the files left around in the cache. + removeCacheFiles("*.wav"); + removeCacheFiles("*.tmp"); + removeCacheFiles("*.lso"); + removeCacheFiles("*.out"); + removeCacheFiles("*.dsf"); + removeCacheFiles("*.bodypart"); + removeCacheFiles("*.clothing"); + + llinfos << "Cache files removed" << llendflush; + + + cleanup_menus(); + + // Wait for any pending VFS IO + while (1) + { + S32 pending = LLVFSThread::updateClass(0); + pending += LLLFSThread::updateClass(0); + if (!pending) + { + break; + } + llinfos << "Waiting for pending IO to finish: " << pending << llendflush; + ms_sleep(100); + } + llinfos << "Shutting down." << llendflush; + + // Destroy Windows(R) 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; + llinfos << "ViewerWindow deleted" << llendflush; + + // viewer UI relies on keyboard so keep it aound until viewer UI isa gone + delete gKeyboard; + gKeyboard = NULL; + + // Clean up selection managers after UI is destroyed, as UI + // may be observing them. + LLSelectMgr::cleanupGlobals(); + + LLViewerObject::cleanupVOClasses(); + + LLTracker::cleanupInstance(); + +#if LL_LIBXUL_ENABLED + // this must be done after floater cleanup (delete gViewerWindow) since + // floaters potentially need the manager to destroy their contents. + LLMozLib::getInstance()->reset(); +#endif + + // *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 + +#if LL_WINDOWS && LL_LCD_COMPILE + // shut down the LCD window on a logitech keyboard, if there is one + delete gLcdScreen; + gLcdScreen = NULL; +#endif + + if (!gVolumeMgr->cleanup()) + { + llwarns << "Remaining references in the volume manager!" << llendflush; + } + + LLViewerParcelMgr::cleanupGlobals(); + + delete gViewerStats; + gViewerStats = NULL; + + //end_messaging_system(); + + LLFollowCamMgr::cleanupClass(); + LLVolumeMgr::cleanupClass(); + LLWorldMapView::cleanupClass(); + LLUI::cleanupClass(); + + // + // Shut down the VFS's AFTER the decode manager cleans up (since it cleans up vfiles). + // Also after viewerwindow is deleted, since it may have image pointers (which have vfiles) + // Also after shutting down the messaging system since it has VFS dependencies + // + LLVFile::cleanupClass(); + llinfos << "VFS cleaned up" << llendflush; + + // Store the time of our current logoff + gSavedPerAccountSettings.setU32("LastLogoff", time_corrected()); + + // Must do this after all panels have been deleted because panels that have persistent rects + // save their rects on delete. + gSavedSettings.saveToFile(gSettingsFileName, TRUE); + if (!gPerAccountSettingsFileName.empty()) + { + gSavedPerAccountSettings.saveToFile(gPerAccountSettingsFileName, TRUE); + } + llinfos << "Saved settings" << llendflush; + + std::string crash_settings_filename = gDirUtilp->getExpandedFilename(LL_PATH_USER_SETTINGS, CRASH_SETTINGS_FILE); + // save all settings, even if equals defaults + gCrashSettings.saveToFile(crash_settings_filename.c_str(), FALSE); + + delete gUICtrlFactory; + gUICtrlFactory = NULL; + + gSavedSettings.cleanup(); + gViewerArt.cleanup(); + gColors.cleanup(); + gCrashSettings.cleanup(); + + if (gMuteListp) + { + // save mute list + gMuteListp->cache(gAgent.getID()); + + delete gMuteListp; + gMuteListp = NULL; + } + + if (mPurgeOnExit) + { + llinfos << "Purging all cache files on exit" << llendflush; + char mask[LL_MAX_PATH]; /* Flawfinder: ignore */ + snprintf(mask, LL_MAX_PATH, "%s*.*", gDirUtilp->getDirDelimiter().c_str()); /* Flawfinder: ignore */ + gDirUtilp->deleteFilesInDir(gDirUtilp->getExpandedFilename(LL_PATH_CACHE,"").c_str(),mask); + } + + removeMarkerFile(); // Any crashes from here on we'll just have to ignore + + closeDebug(); + + // 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 += LLVFSThread::updateClass(0); + pending += LLLFSThread::updateClass(0); + F64 idle_time = idleTimer.getElapsedTimeF64(); + if (!pending || idle_time >= max_idle_time) + { + llwarns << "Quitting with pending background tasks." << llendl; + break; + } + } + + // Delete workers first + // shotdown all worker threads before deleting them in case of co-dependencies + sTextureCache->shutdown(); + sTextureFetch->shutdown(); + sImageDecodeThread->shutdown(); + delete sTextureCache; + sTextureCache = NULL; + delete sTextureFetch; + sTextureFetch = NULL; + delete sImageDecodeThread; + sImageDecodeThread = NULL; + + gImageList.shutdown(); // shutdown again in case a callback added something + + // This should eventually be done in LLAppViewer + LLImageJ2C::closeDSO(); + LLImageFormatted::cleanupClass(); + LLVFSThread::cleanupClass(); + LLLFSThread::cleanupClass(); + + llinfos << "VFS Thread finished" << llendflush; + +#ifndef LL_RELEASE_FOR_DOWNLOAD + llinfos << "Auditing VFS" << llendl; + gVFS->audit(); +#endif + + // For safety, the LLVFS has to be deleted *after* LLVFSThread. This should be cleaned up. + // (LLVFS doesn't know about LLVFSThread so can't kill pending requests) -Steve + delete gStaticVFS; + gStaticVFS = NULL; + delete gVFS; + gVFS = NULL; + + end_messaging_system(); + + // *NOTE:Mani - The following call is not thread safe. + LLCurl::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()) + { +#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 ); + } + + + llinfos << "Goodbye" << llendflush; + // return 0; + return true; +} + +bool LLAppViewer::initEarlyConfiguration() +{ + // *FIX: globals - This method sets a bunch of globals early in the init process. + int argc = gTempArgC; + char** argv = gTempArgV; + + // HACK! We REALLY want to know what grid they were trying to connect to if they + // crashed hard. + // So we walk through the command line args ONLY looking for the + // userserver arguments first. And we don't do ANYTHING but set + // the gGridName (which gets passed to the crash reporter). + // We're assuming that they're trying to log into the same grid as last + // time, which seems fairly reasonable. + snprintf(gGridName, MAX_STRING, "%s", gGridInfo[GridDefaultChoice].mName); // Flawfinder: ignore + S32 j; + for (j = 1; j < argc; j++) + { + if (!strcmp(argv[j], "--aditi")) + { + snprintf(gGridName, MAX_STRING, "%s", gGridInfo[GRID_INFO_ADITI].mName); // Flawfinder: ignore + } + else if (!strcmp(argv[j], "--agni")) + { + snprintf(gGridName, MAX_STRING, "%s", gGridInfo[GRID_INFO_AGNI].mName); // Flawfinder: ignore + } + else if (!strcmp(argv[j], "--dmz")) + { + snprintf(gGridName, MAX_STRING, "%s", gGridInfo[GRID_INFO_DMZ].mName); // Flawfinder: ignore + } + else if (!strcmp(argv[j], "--siva")) + { + snprintf(gGridName, MAX_STRING, "%s", gGridInfo[GRID_INFO_SIVA].mName); // Flawfinder: ignore + } + else if (!strcmp(argv[j], "--shakti")) + { + sprintf(gGridName,"%s", gGridInfo[GRID_INFO_SHAKTI].mName); + } + else if (!strcmp(argv[j], "--durga")) + { + snprintf(gGridName, MAX_STRING, "%s", gGridInfo[GRID_INFO_DURGA].mName); // Flawfinder: ignore + } + else if (!strcmp(argv[j], "--soma")) + { + snprintf(gGridName, MAX_STRING, "%s", gGridInfo[GRID_INFO_SOMA].mName); // Flawfinder: ignore + } + else if (!strcmp(argv[j], "--ganga")) + { + snprintf(gGridName, MAX_STRING, "%s", gGridInfo[GRID_INFO_GANGA].mName); // Flawfinder: ignore + } + else if (!strcmp(argv[j], "--vaak")) + { + sprintf(gGridName,"%s", gGridInfo[GRID_INFO_VAAK].mName); + } + else if (!strcmp(argv[j], "--uma")) + { + sprintf(gGridName,"%s", gGridInfo[GRID_INFO_UMA].mName); + } + else if (!strcmp(argv[j], "-user") && (++j < argc)) + { + if (!strcmp(argv[j], "-")) + { + snprintf(gGridName, MAX_STRING, "%s", LOOPBACK_ADDRESS_STRING); // Flawfinder: ignore + } + else + { + snprintf(gGridName, MAX_STRING, "%s", argv[j]); // Flawfinder: ignore + } + } + else if (!strcmp(argv[j], "-multiple")) + { + // Hack to detect -multiple so we can disable the marker file check (which will always fail) + gMultipleViewersOK = TRUE; + } + else if (!strcmp(argv[j], "-novoice")) + { + // May need to know this early also + gDisableVoice = TRUE; + } + else if (!strcmp(argv[j], "-url") && (++j < argc)) + { + LLURLSimString::setString(argv[j]); + } + } + + return true; +} + +bool LLAppViewer::initThreads() +{ +#if MEM_TRACK_MEM + static const bool enable_threads = false; +#else + static const bool enable_threads = true; +#endif + LLVFSThread::initClass(enable_threads && true); + LLLFSThread::initClass(enable_threads && true); + + // Image decoding + LLAppViewer::sImageDecodeThread = new LLWorkerThread("ImageDecode", enable_threads && true); + LLAppViewer::sTextureCache = new LLTextureCache(enable_threads && true); + LLAppViewer::sTextureFetch = new LLTextureFetch(LLAppViewer::getTextureCache(), enable_threads && false); + LLImageWorker::initClass(LLAppViewer::getImageDecodeThread()); + LLImageJ2C::openDSO(); + + // *FIX: no error handling here! + return true; +} + +void errorCallback(const std::string &error_string) +{ +#ifndef LL_RELEASE_FOR_DOWNLOAD + OSMessageBox(error_string.c_str(), "Fatal Error", OSMB_OK); +#endif + LLError::crashAndLoop(error_string); +} + +bool LLAppViewer::initLogging() +{ + // + // Set up logging defaults for the viewer + // + LLError::initForApplication( + gDirUtilp->getExpandedFilename(LL_PATH_APP_SETTINGS, "")); + LLError::setFatalFunction(errorCallback); + + // Remove the last ".old" log file. + std::string old_log_file = gDirUtilp->getExpandedFilename(LL_PATH_LOGS, + "SecondLife.old"); + LLFile::remove(old_log_file.c_str()); + + // Rename current log file to ".old" + std::string log_file = gDirUtilp->getExpandedFilename(LL_PATH_LOGS, + "SecondLife.log"); + LLFile::rename(log_file.c_str(), old_log_file.c_str()); + + // Set the log file to SecondLife.log + + LLError::logToFile(log_file); + + // *FIX:Mani no error handling here! + return true; +} + +bool LLAppViewer::initConfiguration() +{ + // Ye olde parse_args()... + if(!doConfigFromCommandLine()) + { + return false; + } + + // XUI:translate + gSecondLife = "Second Life"; + + // Read skin/branding settings if specified. + if (! gDirUtilp->getSkinDir().empty() ) + { + std::string skin_def_file = gDirUtilp->getExpandedFilename(LL_PATH_TOP_SKIN, "skin.xml"); + LLXmlTree skin_def_tree; + + if (!skin_def_tree.parseFile(skin_def_file)) + { + llerrs << "Failed to parse skin definition." << llendl; + } + + LLXmlTreeNode* rootp = skin_def_tree.getRoot(); + LLXmlTreeNode* disabled_message_node = rootp->getChildByName("disabled_message"); + if (disabled_message_node) + { + gDisabledMessage = disabled_message_node->getContents(); + } + + static LLStdStringHandle hide_links_string = LLXmlTree::addAttributeString("hide_links"); + rootp->getFastAttributeBOOL(hide_links_string, gHideLinks); + + // Legacy string. This flag really meant we didn't want to expose references to "Second Life". + // Just set gHideLinks instead. + static LLStdStringHandle silent_string = LLXmlTree::addAttributeString("silent_update"); + BOOL silent_update; + rootp->getFastAttributeBOOL(silent_string, silent_update); + gHideLinks = (gHideLinks || silent_update); + } + +#if LL_DARWIN + // Initialize apple menubar and various callbacks + init_apple_menu(gSecondLife.c_str()); + +#if __ppc__ + // If the CPU doesn't have Altivec (i.e. it's not at least a G4), don't go any further. + // Only test PowerPC - all Intel Macs have SSE. + if(!gSysCPU.hasAltivec()) + { + std::ostringstream msg; + msg << gSecondLife << " requires a processor with AltiVec (G4 or later)."; + OSMessageBox( + msg.str().c_str(), + NULL, + OSMB_OK); + removeMarkerFile(); + return false; + } +#endif + +#endif // LL_DARWIN + + // Display splash screen. Must be after above check for previous + // crash as this dialog is always frontmost. + std::ostringstream splash_msg; + splash_msg << "Loading " << gSecondLife << "..."; + LLSplashScreen::show(); + LLSplashScreen::update(splash_msg.str().c_str()); + + LLVolumeMgr::initClass(); + + // Initialize the feature manager + // The feature manager is responsible for determining what features + // are turned on/off in the app. + gFeatureManagerp = new LLFeatureManager; + + gStartTime = totalTime(); + + //////////////////////////////////////// + // + // Process ini files + // + + // declare all possible setting variables + declare_settings(); + +#if !LL_RELEASE_FOR_DOWNLOAD +// only write the defaults for non-release builds! + gSavedSettings.saveToFile(gDirUtilp->getExpandedFilename(LL_PATH_USER_SETTINGS,"settings_default.xml").c_str(), FALSE); +#endif + + // + // Set the name of the window + // +#if LL_RELEASE_FOR_DOWNLOAD + gWindowTitle = gSecondLife; +#elif LL_DEBUG + gWindowTitle = gSecondLife + LLString(" [DEBUG] ") + gArgs; +#else + gWindowTitle = gSecondLife + LLString(" ") + gArgs; +#endif + LLString::truncate(gWindowTitle, 255); + + if (!gMultipleViewersOK) + { + // + // Check for another instance of the app running + // + //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 + std::string slurl; + if (!LLStartUp::sSLURLCommand.empty()) + { + slurl = LLStartUp::sSLURLCommand; + } + else if (LLURLSimString::parse()) + { + slurl = LLURLSimString::getURL(); + } + if (!slurl.empty()) + { + if (send_url_to_other_instance(slurl)) + { + // successfully handed off URL to existing instance, exit + return 1; + } + } + + mSecondInstance = anotherInstanceRunning(); + + if (mSecondInstance) + { + std::ostringstream msg; + msg << + gSecondLife << " is already running.\n" + "\n" + "Check your task bar for a minimized copy of the program.\n" + "If this message persists, restart your computer.", + OSMessageBox( + msg.str().c_str(), + NULL, + OSMB_OK); + return 1; + } + + initMarkerFile(); + +#if LL_SEND_CRASH_REPORTS + if (gLastExecFroze) + { + llinfos << "Last execution froze, requesting to send crash report." << llendl; + // + // Pop up a freeze or crash warning dialog + // + std::ostringstream msg; + msg << gSecondLife + << " appears to have frozen or crashed on the previous run.\n" + << "Would you like to send a crash report?"; + std::string alert; + alert = gSecondLife; + alert += " Alert"; + S32 choice = OSMessageBox(msg.str().c_str(), + alert.c_str(), + OSMB_YESNO); + if (OSBTN_YES == choice) + { + llinfos << "Sending crash report." << llendl; + + removeMarkerFile(); +#if LL_WINDOWS + std::string exe_path = gDirUtilp->getAppRODataDir(); + exe_path += gDirUtilp->getDirDelimiter(); + exe_path += "win_crash_logger.exe"; + + std::string arg_string = "-previous -user "; + arg_string += gGridName; + arg_string += " -name \""; + arg_string += gSecondLife; + arg_string += "\""; + // Spawn crash logger. + // NEEDS to wait until completion, otherwise log files will get smashed. + _spawnl(_P_WAIT, exe_path.c_str(), exe_path.c_str(), arg_string.c_str(), NULL); +#elif LL_DARWIN + std::string command_str; + command_str = "crashreporter.app/Contents/MacOS/crashreporter "; + command_str += "-previous -user "; + command_str += gGridName; + // XXX -- We need to exit fullscreen mode for this to work. + // XXX -- system() also doesn't wait for completion. Hmm... + system(command_str.c_str()); /* Flawfinder: Ignore */ +#elif LL_LINUX || LL_SOLARIS + std::string cmd =gDirUtilp->getAppRODataDir(); + cmd += gDirUtilp->getDirDelimiter(); +#if LL_LINUX + cmd += "linux-crash-logger.bin"; +#else // LL_SOLARIS + cmd += "bin/solaris-crash-logger"; +#endif + char* const cmdargv[] = + {(char*)cmd.c_str(), + (char*)"-previous", + (char*)"-user", + (char*)gGridName, + (char*)"-name", + (char*)gSecondLife.c_str(), + NULL}; + pid_t pid = fork(); + if (pid == 0) + { // child + execv(cmd.c_str(), cmdargv); /* Flawfinder: Ignore */ + llwarns << "execv failure when trying to start " << cmd << llendl; + _exit(1); // avoid atexit() + } else { + if (pid > 0) + { + // wait for child proc to die + int childExitStatus; + waitpid(pid, &childExitStatus, 0); + } else { + llwarns << "fork failure." << llendl; + } + } +#endif + } + else + { + llinfos << "Not sending crash report." << llendl; + } + } +#endif // #if LL_SEND_CRASH_REPORTS + } + else + { + mSecondInstance = anotherInstanceRunning(); + + if (mSecondInstance) + { + gDisableVoice = TRUE; + /* Don't start another instance if using -multiple + //RN: if we received a URL, hand it off to the existing instance + if (LLURLSimString::parse()) + { + LLURLSimString::send_to_other_instance(); + return 1; + } + */ + } + + initMarkerFile(); + } + + return true; // Config was successful. +} + +bool LLAppViewer::doConfigFromCommandLine() +{ + // *FIX: This is what parse args used to do, minus the arg reading part. + // Now the arg parsing is handled by LLApp::parseCommandOptions() and this + // method need only interpret settings. Perhaps some day interested parties + // can ask an app about a setting rather than have the app set + // a gazzillion globals. + + ///////////////////////////////////////// + // + // Process command line arguments + // + S32 args_result = 0; + +#if LL_DARWIN + { + // On the Mac, read in arguments.txt (if it exists) and process it for additional arguments. + LLString args; + if(_read_file_into_string(args, "arguments.txt")) /* Flawfinder: ignore*/ + { + // The arguments file exists. + // It should consist of command line arguments separated by newlines. + // Split it into individual arguments and build a fake argv[] to pass to parse_args. + std::vector<std::string> arglist; + + arglist.push_back("newview"); + + llinfos << "Reading additional command line arguments from arguments.txt..." << llendl; + + typedef boost::tokenizer<boost::escaped_list_separator<char> > tokenizer; + boost::escaped_list_separator<char> sep("\\", "\r\n ", "\"'"); + tokenizer tokens(args, sep); + tokenizer::iterator token_iter; + + for(token_iter = tokens.begin(); token_iter != tokens.end(); ++token_iter) + { + llinfos << "argument: '" << (token_iter->c_str()) << "'" << llendl; + + arglist.push_back(*token_iter); + } + + char **fakeargv = new char*[arglist.size()]; + int i; + for(i=0; i < arglist.size(); i++) + fakeargv[i] = const_cast<char*>(arglist[i].c_str()); + + args_result = parse_args(arglist.size(), fakeargv); + delete[] fakeargv; + } + + // Get the user's preferred language string based on the Mac OS localization mechanism. + // To add a new localization: + // go to the "Resources" section of the project + // get info on "language.txt" + // in the "General" tab, click the "Add Localization" button + // create a new localization for the language you're adding + // set the contents of the new localization of the file to the string corresponding to our localization + // (i.e. "en-us", "ja", etc. Use the existing ones as a guide.) + CFURLRef url = CFBundleCopyResourceURL(CFBundleGetMainBundle(), CFSTR("language"), CFSTR("txt"), NULL); + char path[MAX_PATH]; + if(CFURLGetFileSystemRepresentation(url, false, (UInt8 *)path, sizeof(path))) + { + LLString lang; + if(_read_file_into_string(lang, path)) /* Flawfinder: ignore*/ + { + gCommandLineForcedSettings["SystemLanguage"] = lang; + } + } + CFRelease(url); + } +#endif + + int argc = gTempArgC; + char** argv = gTempArgV; + + // + // Parse the command line arguments + // + args_result |= parse_args(argc, argv); + if (args_result) + { + removeMarkerFile(); + return false; + } + + if (!strcmp(gGridName, gGridInfo[GRID_INFO_AGNI].mName)) + { + gInProductionGrid = TRUE; + } + + return true; +} + +bool LLAppViewer::initWindow() +{ + llinfos << "Initializing window..." << llendl; + + // store setting in a global for easy access and modification + gNoRender = gSavedSettings.getBOOL("DisableRendering"); + + // Hide the splash screen + LLSplashScreen::hide(); + + // HACK: Need a non-const char * for stupid window name (propagated deep down) + char window_title_str[256]; /* Flawfinder: ignore */ + strncpy(window_title_str, gWindowTitle.c_str(), sizeof(window_title_str) - 1); /* Flawfinder: ignore */ + window_title_str[sizeof(window_title_str) - 1] = '\0'; + + // always start windowed + gViewerWindow = new LLViewerWindow(window_title_str, "Second Life", + gSavedSettings.getS32("WindowX"), gSavedSettings.getS32("WindowY"), + gSavedSettings.getS32("WindowWidth"), gSavedSettings.getS32("WindowHeight"), + FALSE, gIgnorePixelDepth); + + if (gSavedSettings.getBOOL("FullScreen")) + { + gViewerWindow->toggleFullscreen(FALSE); + // request to go full screen... which will be delayed until login + } + + if (gSavedSettings.getBOOL("WindowMaximized")) + { + gViewerWindow->mWindow->maximize(); + gViewerWindow->getWindow()->setNativeAspectRatio(gSavedSettings.getF32("FullScreenAspectRatio")); + } + + LLUI::sWindow = gViewerWindow->getWindow(); + + LLAlertDialog::parseAlerts("alerts.xml"); + LLNotifyBox::parseNotify("notify.xml"); + + LLMediaEngine::initClass(); + + // + // Clean up the feature manager lookup table - settings were updated + // in the LLViewerWindow constructor + // + gFeatureManagerp->cleanupFeatureTables(); + + // Show watch cursor + gViewerWindow->setCursor(UI_CURSOR_WAIT); + + // Finish view initialization + gViewerWindow->initBase(); + + // show viewer window + gViewerWindow->mWindow->show(); + + return true; +} + +void LLAppViewer::writeDebug(const char *str) +{ + if (!mDebugFile) + { + std::string debug_filename = gDirUtilp->getExpandedFilename(LL_PATH_LOGS,"debug_info.log"); + llinfos << "Opening debug file " << debug_filename << llendl; + mDebugFile = LLFile::fopen(debug_filename.c_str(), "w"); /* Flawfinder: ignore */ + if (!mDebugFile) + { + llinfos << "Opening debug file " << debug_filename << " failed. Using stderr." << llendl; + mDebugFile = stderr; + } + } + fputs(str, mDebugFile); + fflush(mDebugFile); +} + +void LLAppViewer::closeDebug() +{ + if (mDebugFile) + { + fclose(mDebugFile); + } + mDebugFile = NULL; +} + +void LLAppViewer::cleanupSavedSettings() +{ + gSavedSettings.setBOOL("MouseSun", FALSE); + + gSavedSettings.setBOOL("FlyBtnState", FALSE); + + gSavedSettings.setBOOL("FirstPersonBtnState", FALSE); + gSavedSettings.setBOOL("ThirdPersonBtnState", TRUE); + gSavedSettings.setBOOL("BuildBtnState", FALSE); + + gSavedSettings.setBOOL("UseEnergy", TRUE); // force toggle to turn off, since sends message to simulator + + gSavedSettings.setBOOL("DebugWindowProc", gDebugWindowProc); + + gSavedSettings.setBOOL("AllowIdleAFK", gAllowIdleAFK); + gSavedSettings.setBOOL("ShowObjectUpdates", gShowObjectUpdates); + + if (!gNoRender) + { + if (gDebugView) + { + gSavedSettings.setBOOL("ShowDebugConsole", gDebugView->mDebugConsolep->getVisible()); + gSavedSettings.setBOOL("ShowDebugStats", gDebugView->mStatViewp->getVisible()); + } + } + + // save window position if not fullscreen + // as we don't track it in callbacks + BOOL fullscreen = gViewerWindow->mWindow->getFullscreen(); + BOOL maximized = gViewerWindow->mWindow->getMaximized(); + if (!fullscreen && !maximized) + { + LLCoordScreen window_pos; + + if (gViewerWindow->mWindow->getPosition(&window_pos)) + { + gSavedSettings.setS32("WindowX", window_pos.mX); + gSavedSettings.setS32("WindowY", window_pos.mY); + } + } + + gSavedSettings.setF32("MapScale", gMapScale ); + gSavedSettings.setF32("MiniMapScale", gMiniMapScale ); + gSavedSettings.setBOOL("AsyncKeyboard", gHandleKeysAsync); + gSavedSettings.setBOOL("ShowHoverTips", LLHoverView::sShowHoverTips); + + // Some things are cached in LLAgent. + if (gAgent.mInitialized) + { + gSavedSettings.setF32("RenderFarClip", gAgent.mDrawDistance); + } + + // *REMOVE: This is now done via LLAppViewer::setCrashBehavior() + // Left vestigially in case I borked it. + // gCrashSettings.setS32(CRASH_BEHAVIOR_SETTING, gCrashBehavior); +} + +void LLAppViewer::removeCacheFiles(const char* file_mask) +{ + char mask[LL_MAX_PATH]; /* Flawfinder: ignore */ + snprintf(mask, LL_MAX_PATH, "%s%s", gDirUtilp->getDirDelimiter().c_str(), file_mask); /* Flawfinder: ignore */ + gDirUtilp->deleteFilesInDir(gDirUtilp->getExpandedFilename(LL_PATH_CACHE, "").c_str(), mask); +} + +void LLAppViewer::writeSystemInfo() +{ + writeDebug("SL Log: "); + writeDebug(LLError::logFileName()); + writeDebug("\n"); + + std::string tmp_str = gSecondLife + + llformat(" version %d.%d.%d build %d", + LL_VERSION_MAJOR, LL_VERSION_MINOR, LL_VERSION_PATCH, LL_VERSION_BUILD); + writeDebug(tmp_str.c_str()); + writeDebug("\n"); + writeDebug(gSysCPU.getCPUString()); + writeDebug("\n"); + + tmp_str = llformat("RAM: %u KB\n", gSysMemory.getPhysicalMemoryKB()); + writeDebug(tmp_str.c_str()); + writeDebug("OS: "); + writeDebug(getOSInfo().getOSString().c_str()); + writeDebug("\n"); + + // Dump some debugging info + llinfos << gSecondLife << " version " + << LL_VERSION_MAJOR << "." + << LL_VERSION_MINOR << "." + << LL_VERSION_PATCH + << llendl; + + // 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)); + llinfos << "Local time: " << tbuffer << llendl; + + // query some system information + llinfos << "CPU info:\n" << gSysCPU << llendl; + llinfos << "Memory info:\n" << gSysMemory << llendl; + llinfos << "OS info: " << getOSInfo() << llendl; +} + +void LLAppViewer::handleViewerCrash() +{ + LLAppViewer* pApp = LLAppViewer::instance(); + if (pApp->beingDebugged()) + { + // This will drop us into the debugger. + abort(); + } + + // Returns whether a dialog was shown. + // Only do the logic in here once + if (pApp->mReportedCrash) + { + return; + } + pApp->mReportedCrash = TRUE; + + BOOL do_crash_report = FALSE; + + do_crash_report = TRUE; + + pApp->writeDebug("Viewer exe: "); + pApp->writeDebug(gDirUtilp->getExecutablePathAndName().c_str()); + pApp->writeDebug("\n"); + pApp->writeDebug("Cur path: "); + pApp->writeDebug(gDirUtilp->getCurPath().c_str()); + pApp->writeDebug("\n\n"); + + if (gMessageSystem && gDirUtilp) + { + std::string filename; + filename = gDirUtilp->getExpandedFilename(LL_PATH_LOGS, "stats.log"); + llofstream file(filename.c_str(), llofstream::binary); + if(file.good()) + { + gMessageSystem->summarizeLogs(file); + } + } + + if (gMessageSystem) + { + pApp->writeDebug(gMessageSystem->getCircuitInfoString()); + gMessageSystem->stopLogging(); + } + pApp->writeDebug("\n"); + if (gWorldp) + { + pApp->writeDebug(gWorldp->getInfoString()); + } + + // Close the debug file + pApp->closeDebug(); + LLError::logToFile(""); + + // Close the SecondLife.log + //pApp->removeMarkerFile(); + + // Call to pure virtual, handled by platform specifc llappviewer instance. + pApp->handleCrashReporting(); + + return; +} + +void LLAppViewer::setCrashBehavior(S32 cb) +{ + mCrashBehavior = cb; + gCrashSettings.setS32(CRASH_BEHAVIOR_SETTING, mCrashBehavior); +} + +bool LLAppViewer::anotherInstanceRunning() +{ + // We create a marker file when the program starts and remove the file when it finishes. + // If the file is currently locked, that means another process is already running. + + std::string marker_file = gDirUtilp->getExpandedFilename(LL_PATH_LOGS,"SecondLife.exec_marker"); + llinfos << "Checking marker file for lock..." << llendl; + + // If file doesn't exist, we create it + // If file does exist, try to get writing privilages + FILE* fMarker = LLFile::fopen(marker_file.c_str(), "rb"); // Flawfinder: ignore + if (fMarker != NULL) + { + // File exists, try opening with write permissions + fclose(fMarker); + fMarker = LLFile::fopen(marker_file.c_str(), "wb"); // Flawfinder: ignore + if (fMarker == NULL) + { + llinfos << "Marker file is locked." << llendl; + return TRUE; + } + + // *FIX:Mani - rather than have this exception here, + // LLFile::fopen() have consistent behavior across platforms? +#if LL_DARWIN + // Try to lock it. On Mac, this is the only way to test if it's actually locked. + if (flock(fileno(fMarker), LOCK_EX | LOCK_NB) == -1) + { + // Lock failed - somebody else has it. + fclose(fMarker); + llinfos << "Marker file is locked." << llendl; + return TRUE; + } +#endif + fclose(fMarker); + } + llinfos << "Marker file isn't locked." << llendl; + return FALSE; + +} + +void LLAppViewer::initMarkerFile() +{ + // *FIX:Mani - an actually cross platform LLFile lib would be nice. + +#if LL_SOLARIS + struct flock fl; + fl.l_whence = SEEK_SET; + fl.l_start = 0; + fl.l_len = 1; +#endif + // We create a marker file when the program starts and remove the file when it finishes. + // If the file is currently locked, that means another process is already running. + // If the file exists and isn't locked, we crashed on the last run. + + std::string marker_file = gDirUtilp->getExpandedFilename(LL_PATH_LOGS,"SecondLife.exec_marker"); + llinfos << "Checking marker file for lock..." << llendl; + + FILE* fMarker = LLFile::fopen(marker_file.c_str(), "rb"); // Flawfinder: ignore + if (fMarker != NULL) + { + // File exists, try opening with write permissions + fclose(fMarker); + fMarker = LLFile::fopen(marker_file.c_str(), "wb"); // Flawfinder: ignxore + if (fMarker == NULL) + { + // Another instance is running. Skip the rest of these operations. + llinfos << "Marker file is locked." << llendl; + return; + } +#if LL_DARWIN + // Try to lock it. On Mac, this is the only way to test if it's actually locked. + if (flock(fileno(fMarker), LOCK_EX | LOCK_NB) == -1) + { + // Lock failed - somebody else has it. + fclose(fMarker); + llinfos << "Marker file is locked." << llendl; + return; + } +#endif + + // No other instances; we'll lock this file now & delete on quit. + fclose(fMarker); + gLastExecFroze = TRUE; + llinfos << "Exec marker found: program froze on previous execution" << llendl; + } + + // Create the marker file for this execution & lock it +// FILE *fp_executing_marker; +#if LL_WINDOWS + mMarkerFile = LLFile::_fsopen(marker_file.c_str(), "w", _SH_DENYWR); +#else + mMarkerFile = LLFile::fopen(marker_file.c_str(), "w"); // Flawfinder: ignore + if (mMarkerFile) + { + int fd = fileno(mMarkerFile); + // Attempt to lock +#if LL_SOLARIS + fl.l_type = F_WRLCK; + if (fcntl(fd, F_SETLK, &fl) == -1) +#else + if (flock(fd, LOCK_EX | LOCK_NB) == -1) +#endif + { + llinfos << "Failed to lock file." << llendl; + } + } +#endif + if (mMarkerFile) + { + llinfos << "Marker file created." << llendl; + } + else + { + llinfos << "Failed to create marker file." << llendl; + } + +#if LL_WINDOWS + // Clean up SecondLife.dmp files, to avoid confusion + llinfos << "Removing SecondLife.dmp" << llendl; + std::string dmp_filename = gDirUtilp->getExpandedFilename(LL_PATH_LOGS, "SecondLife.dmp"); + LLFile::remove(dmp_filename.c_str()); +#endif + + // This is to keep the crash reporter from constantly sending stale message logs + // We wipe the message file now. + llinfos << "Removing message.log" << llendl; + std::string message_filename = gDirUtilp->getExpandedFilename(LL_PATH_LOGS, "message.log"); + LLFile::remove(message_filename.c_str()); + + llinfos << "Exiting initMarkerFile()." << llendl; +} + +void LLAppViewer::removeMarkerFile() +{ + llinfos << "removeMarkerFile()" << llendl; + if (mMarkerFile != NULL) + { + fclose(mMarkerFile); + mMarkerFile = NULL; + } + if( gDirUtilp ) + { + LLString marker_file = gDirUtilp->getExpandedFilename(LL_PATH_LOGS,"SecondLife.exec_marker"); + ll_apr_file_remove( marker_file ); + } +} + +void LLAppViewer::forceQuit() +{ + LLApp::setQuitting(); +} + +void LLAppViewer::requestQuit() +{ + llinfos << "requestQuit" << llendl; + + LLViewerRegion* region = gAgent.getRegion(); + + if( (LLStartUp::getStartupState() < STATE_STARTED) || !region ) + { + // Quit immediately + forceQuit(); + return; + } + + if (gHUDManager) + { + LLHUDEffectSpiral *effectp = (LLHUDEffectSpiral*)gHUDManager->createViewerEffect(LLHUDObject::LL_HUD_EFFECT_POINT, TRUE); + effectp->setPositionGlobal(gAgent.getPositionGlobal()); + effectp->setColor(LLColor4U(gAgent.getEffectColor())); + gHUDManager->sendEffects(); + } + + // Attempt to close all floaters that might be + // editing things. + if (gFloaterView) + { + // application is quitting + gFloaterView->closeAllChildren(true); + } + + send_stats(); + + gLogoutTimer.reset(); + mQuitRequested = true; +} + +static void finish_quit(S32 option, void *userdata) +{ + if (option == 0) + { + LLAppViewer::instance()->requestQuit(); + } +} + +void LLAppViewer::userQuit() +{ + gViewerWindow->alertXml("ConfirmQuit", finish_quit, NULL); +} + +static void finish_early_exit(S32 option, void* userdata) +{ + LLAppViewer::instance()->forceQuit(); +} + +void LLAppViewer::earlyExit(const LLString& msg) +{ + llwarns << "app_early_exit: " << msg << llendl; + gDoDisconnect = TRUE; +// LLStringBase<char>::format_map_t args; +// args["[MESSAGE]"] = mesg; +// gViewerWindow->alertXml("AppEarlyExit", args, finish_early_exit); + LLAlertDialog::showCritical(msg, finish_early_exit, NULL); +} + +void LLAppViewer::forceExit(S32 arg) +{ + removeMarkerFile(); + + // *FIX:Mani - This kind of exit hardly seems appropriate. + exit(arg); +} + +void LLAppViewer::abortQuit() +{ + llinfos << "abortQuit()" << llendl; + mQuitRequested = false; +} + +bool LLAppViewer::initCache() +{ + mPurgeCache = false; + // Purge cache if user requested it + if (gSavedSettings.getBOOL("PurgeCacheOnStartup") || + gSavedSettings.getBOOL("PurgeCacheOnNextStartup")) + { + gSavedSettings.setBOOL("PurgeCacheOnNextStartup", false); + mPurgeCache = true; + } + // Purge cache if it belongs to an old version + else + { + static const S32 cache_version = 5; + if (gSavedSettings.getS32("LocalCacheVersion") != cache_version) + { + mPurgeCache = true; + gSavedSettings.setS32("LocalCacheVersion", cache_version); + } + } + + // Setup and verify the cache location + LLString cache_location = gSavedSettings.getString("CacheLocation"); + LLString new_cache_location = gSavedSettings.getString("NewCacheLocation"); + if (new_cache_location != cache_location) + { + gDirUtilp->setCacheDir(gSavedSettings.getString("CacheLocation")); + purgeCache(); // purge old cache + gSavedSettings.setString("CacheLocation", new_cache_location); + } + + if (!gDirUtilp->setCacheDir(gSavedSettings.getString("CacheLocation"))) + { + llwarns << "Unable to set cache location" << llendl; + gSavedSettings.setString("CacheLocation", ""); + } + + if (mPurgeCache) + { + LLSplashScreen::update("Clearing cache..."); + purgeCache(); + } + + LLSplashScreen::update("Initializing Texture Cache..."); + + // Init the texture cache + // Allocate 80% of the cache size for textures + BOOL read_only = mSecondInstance ? true : false; + const S32 MB = 1024*1024; + S64 cache_size = (S64)(gSavedSettings.getU32("CacheSize")) * MB; + const S64 MAX_CACHE_SIZE = 1024*MB; + cache_size = llmin(cache_size, MAX_CACHE_SIZE); + S64 texture_cache_size = ((cache_size * 8)/10); + S64 extra = LLAppViewer::getTextureCache()->initCache(LL_PATH_CACHE, texture_cache_size, read_only); + texture_cache_size -= extra; + + LLSplashScreen::update("Initializing VFS..."); + + // Init the VFS + S64 vfs_size = cache_size - texture_cache_size; + const S64 MAX_VFS_SIZE = 1024 * MB; // 1 GB + vfs_size = llmin(vfs_size, MAX_VFS_SIZE); + vfs_size = (vfs_size / MB) * MB; // make sure it is MB aligned + U32 vfs_size_u32 = (U32)vfs_size; + U32 old_vfs_size = gSavedSettings.getU32("VFSOldSize") * MB; + bool resize_vfs = (vfs_size_u32 != old_vfs_size); + if (resize_vfs) + { + gSavedSettings.setU32("VFSOldSize", vfs_size_u32/MB); + } + llinfos << "VFS CACHE SIZE: " << vfs_size/(1024*1024) << " MB" << llendl; + + // This has to happen BEFORE starting the vfs + //time_t ltime; + srand(time(NULL)); // Flawfinder: ignore + U32 old_salt = gSavedSettings.getU32("VFSSalt"); + U32 new_salt; + char old_vfs_data_file[LL_MAX_PATH]; // Flawfinder: ignore + char old_vfs_index_file[LL_MAX_PATH]; // Flawfinder: ignore + char new_vfs_data_file[LL_MAX_PATH]; // Flawfinder: ignore + char new_vfs_index_file[LL_MAX_PATH]; // Flawfinder: ignore + char static_vfs_index_file[LL_MAX_PATH]; // Flawfinder: ignore + char static_vfs_data_file[LL_MAX_PATH]; // Flawfinder: ignore + + if (gMultipleViewersOK) + { + // don't mess with renaming the VFS in this case + new_salt = old_salt; + } + else + { + do + { + new_salt = rand(); + } while( new_salt == old_salt ); + } + + snprintf(old_vfs_data_file, LL_MAX_PATH, "%s%u", // Flawfinder: ignore + gDirUtilp->getExpandedFilename(LL_PATH_CACHE,VFS_DATA_FILE_BASE).c_str(), + old_salt); + + // make sure this file exists + llstat s; + S32 stat_result = LLFile::stat(old_vfs_data_file, &s); + if (stat_result) + { + // doesn't exist, look for a data file + std::string mask; + mask = gDirUtilp->getDirDelimiter(); + mask += VFS_DATA_FILE_BASE; + mask += "*"; + + std::string dir; + dir = gDirUtilp->getExpandedFilename(LL_PATH_CACHE,""); + + std::string found_file; + if (gDirUtilp->getNextFileInDir(dir, mask, found_file, false)) + { + snprintf(old_vfs_data_file, LL_MAX_PATH, "%s%s%s", dir.c_str(), gDirUtilp->getDirDelimiter().c_str(), found_file.c_str()); // Flawfinder: ignore + + S32 start_pos; + S32 length = strlen(found_file.c_str()); /* Flawfinder: ignore*/ + for (start_pos = length - 1; start_pos >= 0; start_pos--) + { + if (found_file[start_pos] == '.') + { + start_pos++; + break; + } + } + if (start_pos > 0) + { + sscanf(found_file.c_str() + start_pos, "%d", &old_salt); + } + llinfos << "Default vfs data file not present, found " << old_vfs_data_file << llendl; + llinfos << "Old salt: " << old_salt << llendl; + } + } + + snprintf(old_vfs_index_file, LL_MAX_PATH, "%s%u", // Flawfinder: ignore + gDirUtilp->getExpandedFilename(LL_PATH_CACHE,VFS_INDEX_FILE_BASE).c_str(), + old_salt); + + stat_result = LLFile::stat(old_vfs_index_file, &s); + if (stat_result) + { + // We've got a bad/missing index file, nukem! + llwarns << "Bad or missing vfx index file " << old_vfs_index_file << llendl; + llwarns << "Removing old vfs data file " << old_vfs_data_file << llendl; + LLFile::remove(old_vfs_data_file); + LLFile::remove(old_vfs_index_file); + + // Just in case, nuke any other old cache files in the directory. + std::string dir; + dir = gDirUtilp->getExpandedFilename(LL_PATH_CACHE,""); + + std::string mask; + mask = gDirUtilp->getDirDelimiter(); + mask += VFS_DATA_FILE_BASE; + mask += "*"; + + gDirUtilp->deleteFilesInDir(dir, mask); + + mask = gDirUtilp->getDirDelimiter(); + mask += VFS_INDEX_FILE_BASE; + mask += "*"; + + gDirUtilp->deleteFilesInDir(dir, mask); + } + + snprintf(new_vfs_data_file, LL_MAX_PATH, "%s%u", // Flawfinder: ignore + gDirUtilp->getExpandedFilename(LL_PATH_CACHE,VFS_DATA_FILE_BASE).c_str(), + new_salt); + + snprintf(new_vfs_index_file, LL_MAX_PATH, "%s%u", gDirUtilp->getExpandedFilename(LL_PATH_CACHE, VFS_INDEX_FILE_BASE).c_str(), // Flawfinder: ignore + new_salt); + + + strncpy(static_vfs_data_file, gDirUtilp->getExpandedFilename(LL_PATH_APP_SETTINGS,"static_data.db2").c_str(), LL_MAX_PATH -1); // Flawfinder: ignore + static_vfs_data_file[LL_MAX_PATH -1] = '\0'; + strncpy(static_vfs_index_file, gDirUtilp->getExpandedFilename(LL_PATH_APP_SETTINGS,"static_index.db2").c_str(), LL_MAX_PATH -1); // Flawfinder: ignore + static_vfs_index_file[LL_MAX_PATH -1] = '\0'; + + if (resize_vfs) + { + llinfos << "Removing old vfs and re-sizing" << llendl; + + LLFile::remove(old_vfs_data_file); + LLFile::remove(old_vfs_index_file); + } + else if (old_salt != new_salt) + { + // move the vfs files to a new name before opening + llinfos << "Renaming " << old_vfs_data_file << " to " << new_vfs_data_file << llendl; + llinfos << "Renaming " << old_vfs_index_file << " to " << new_vfs_index_file << llendl; + LLFile::rename(old_vfs_data_file, new_vfs_data_file); + LLFile::rename(old_vfs_index_file, new_vfs_index_file); + } + + // Startup the VFS... + gSavedSettings.setU32("VFSSalt", new_salt); + + // Don't remove VFS after viewer crashes. If user has corrupt data, they can reinstall. JC + gVFS = new LLVFS(new_vfs_index_file, new_vfs_data_file, false, vfs_size_u32, false); + if( VFSVALID_BAD_CORRUPT == gVFS->getValidState() ) + { + // Try again with fresh files + // (The constructor deletes corrupt files when it finds them.) + llwarns << "VFS corrupt, deleted. Making new VFS." << llendl; + delete gVFS; + gVFS = new LLVFS(new_vfs_index_file, new_vfs_data_file, false, vfs_size_u32, false); + } + + gStaticVFS = new LLVFS(static_vfs_index_file, static_vfs_data_file, true, 0, false); + + BOOL success = gVFS->isValid() && gStaticVFS->isValid(); + if( !success ) + { + return false; + } + else + { + LLVFile::initClass(); + return true; + } +} + +void LLAppViewer::purgeCache() +{ + llinfos << "Purging Texture Cache..." << llendl; + LLAppViewer::getTextureCache()->purgeCache(LL_PATH_CACHE); + llinfos << "Purging Cache..." << llendl; + std::string mask = gDirUtilp->getDirDelimiter() + "*.*"; + gDirUtilp->deleteFilesInDir(gDirUtilp->getExpandedFilename(LL_PATH_CACHE,"").c_str(),mask); +} + +const LLString& LLAppViewer::getSecondLifeTitle() const +{ + return gSecondLife; +} + +const LLString& LLAppViewer::getWindowTitle() const +{ + return gWindowTitle; +} + +void LLAppViewer::resetURIs() const +{ + // Clear URIs when picking a new server + gLoginURIs.clear(); + gHelperURI.clear(); +} + +const std::vector<std::string>& LLAppViewer::getLoginURIs() const +{ + if (gLoginURIs.empty()) + { + // not specified on the command line, use value from table + gLoginURIs = LLSRV::rewriteURI(gGridInfo[gGridChoice].mLoginURI); + } + return gLoginURIs; +} + +const std::string& LLAppViewer::getHelperURI() const +{ + if (gHelperURI.empty()) + { + // not specified on the command line, use value from table + gHelperURI = gGridInfo[gGridChoice].mHelperURI; + } + return gHelperURI; +} + +void LLAppViewer::addLoginURI(const std::string& uri) +{ + gLoginURIs.push_back(uri); +} + +void LLAppViewer::setHelperURI(const std::string& uri) +{ + gHelperURI = uri; +} + +// Callback from a dialog indicating user was logged out. +void finish_disconnect(S32 option, void* userdata) +{ + if (1 == option) + { + LLAppViewer::instance()->forceQuit(); + } +} + +// Callback from an early disconnect dialog, force an exit +void finish_forced_disconnect(S32 /* option */, void* /* userdata */) +{ + LLAppViewer::instance()->forceQuit(); +} + + +void LLAppViewer::forceDisconnect(const LLString& mesg) +{ + if (gDoDisconnect) + { + // Already popped up one of these dialogs, don't + // do this again. + return; + } + + // Translate the message if possible + LLString big_reason = LLAgent::sTeleportErrorMessages[mesg]; + if ( big_reason.size() == 0 ) + { + big_reason = mesg; + } + + LLStringBase<char>::format_map_t args; + gDoDisconnect = TRUE; + + if (LLStartUp::getStartupState() < STATE_STARTED) + { + // Tell users what happened + args["[ERROR_MESSAGE]"] = big_reason; + gViewerWindow->alertXml("ErrorMessage", args, finish_forced_disconnect); + } + else + { + args["[MESSAGE]"] = big_reason; + gViewerWindow->alertXml("YouHaveBeenLoggedOut", args, 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. + + mPurgeOnExit = TRUE; + +#if LL_WINDOWS + // Generates the minidump. + LLWinDebug::handleException(NULL); +#endif + LLAppViewer::handleViewerCrash(); + + 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()); +} + +// 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 && !gNoRender) + { + gSavedSettings.setVector3d("FocusPosOnLogout", gAgent.calcFocusPositionTargetGlobal()); + gSavedSettings.setVector3d("CameraPosOnLogout", gAgent.calcCameraPositionTargetGlobal()); + gViewerWindow->setCursor(UI_CURSOR_WAIT); + gAgent.changeCameraToThirdPerson( FALSE ); // don't animate, need immediate switch + gSavedSettings.setBOOL("ShowParcelOwners", FALSE); + idle(); + + LLString snap_filename = gDirUtilp->getLindenUserDir(); + snap_filename += gDirUtilp->getDirDelimiter(); + snap_filename += SCREEN_LAST_FILENAME; + gViewerWindow->saveSnapshot(snap_filename, gViewerWindow->getWindowWidth(), gViewerWindow->getWindowHeight(), FALSE, TRUE); + mSavedFinalSnapshot = TRUE; + } +} + +void LLAppViewer::loadNameCache() +{ + if (!gCacheName) return; + + std::string name_cache; + name_cache = gDirUtilp->getExpandedFilename(LL_PATH_CACHE, "name.cache"); + FILE* name_cache_fp = LLFile::fopen(name_cache.c_str(), "r"); // Flawfinder: ignore + if (name_cache_fp) + { + gCacheName->importFile(name_cache_fp); + fclose(name_cache_fp); + } +} + +void LLAppViewer::saveNameCache() +{ + if (!gCacheName) return; + + std::string name_cache; + name_cache = gDirUtilp->getExpandedFilename(LL_PATH_CACHE, "name.cache"); + FILE* name_cache_fp = LLFile::fopen(name_cache.c_str(), "w"); // Flawfinder: ignore + if (name_cache_fp) + { + gCacheName->exportFile(name_cache_fp); + fclose(name_cache_fp); + } +} + +/////////////////////////////////////////////////////// +// idle() +// +// Called every time the window is not doing anything. +// Receive packets, update statistics, and schedule a redisplay. +/////////////////////////////////////////////////////// +void LLAppViewer::idle() +{ + // Update frame timers + static LLTimer idle_timer; + + LLControlBase::updateAllListeners(); + + LLFrameTimer::updateFrameTime(); + LLEventTimer::updateClass(); + LLCriticalDamp::updateInterpolants(); + LLMortician::updateClass(); + F32 dt_raw = idle_timer.getElapsedTimeAndResetF32(); + + // 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; + + if (gQuitAfterSeconds > 0.f) + { + if (gRenderStartTime.getElapsedTimeF32() > gQuitAfterSeconds) + { + LLAppViewer::instance()->forceQuit(); + } + } + + // Must wait until both have avatar object and mute list, so poll + // here. + request_initial_instant_messages(); + + /////////////////////////////////// + // + // Special case idle if still starting up + // + + if (LLStartUp::getStartupState() < STATE_STARTED) + { + // Skip rest if idle startup returns false (essentially, no world yet) + if (!idle_startup()) + { + return; + } + } + + + F32 yaw = 0.f; // radians + + if (!gDisconnected) + { + LLFastTimer t(LLFastTimer::FTM_NETWORK); + + // Update spaceserver timeinfo + gWorldp->setSpaceTimeUSec(gWorldp->getSpaceTimeUSec() + (U32)(dt_raw * SEC_TO_MICROSEC)); + + + ////////////////////////////////////// + // + // Update simulator agent state + // + + if (gRotateRight) + { + gAgent.moveYaw(-1.f); + } + + // Handle automatic walking towards points + gAgentPilot.updateTarget(); + gAgent.autoPilot(&yaw); + + static LLFrameTimer agent_update_timer; + static U32 last_control_flags; + + // When appropriate, update agent location to the simulator. + F32 agent_update_time = agent_update_timer.getElapsedTimeF32(); + BOOL flags_changed = gAgent.controlFlagsDirty() || (last_control_flags != gAgent.getControlFlags()); + + if (flags_changed || (agent_update_time > (1.0f / (F32) AGENT_UPDATES_PER_SECOND))) + { + // Send avatar and camera info + last_control_flags = gAgent.getControlFlags(); + send_agent_update(TRUE); + agent_update_timer.reset(); + } + } + + ////////////////////////////////////// + // + // Manage statistics + // + // + + { + static LLFrameTimer viewer_stats_timer; + reset_statistics(); + + // Update session stats every large chunk of time + // *FIX: (???) SAMANTHA + if (viewer_stats_timer.getElapsedTimeF32() >= 300.f && !gDisconnected) + { + llinfos << "Transmitting sessions stats" << llendl; + send_stats(); + 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) + { + llinfos << "Dead object updates: " << gObjectList.mNumDeadObjectUpdates << llendl; + gObjectList.mNumDeadObjectUpdates = 0; + } + if (gObjectList.mNumUnknownKills) + { + llinfos << "Kills on unknown objects: " << gObjectList.mNumUnknownKills << llendl; + gObjectList.mNumUnknownKills = 0; + } + if (gObjectList.mNumUnknownUpdates) + { + llinfos << "Unknown object updates: " << gObjectList.mNumUnknownUpdates << llendl; + gObjectList.mNumUnknownUpdates = 0; + } + } + gFrameStats.addFrameData(); + } + + if (!gDisconnected) + { + LLFastTimer t(LLFastTimer::FTM_NETWORK); + + //////////////////////////////////////////////// + // + // Network processing + // + // NOTE: Starting at this point, we may still have pointers to "dead" objects + // floating throughout the various object lists. + // + + gFrameStats.start(LLFrameStats::IDLE_NETWORK); + idleNetwork(); + stop_glerror(); + + gFrameStats.start(LLFrameStats::AGENT_MISC); + + // Check for away from keyboard, kick idle agents. + idle_afk_check(); + + // Update statistics for this frame + update_statistics(gFrameCount); + + gViewerWindow->updateDebugText(); + } + + //////////////////////////////////////// + // + // Handle the regular UI idle callbacks as well as + // hover callbacks + // + + { +// LLFastTimer t(LLFastTimer::FTM_IDLE_CB); + + // Do event notifications if necessary. Yes, we may want to move this elsewhere. + gEventNotifier.update(); + + gIdleCallbacks.callFunctions(); + } + + if (gDisconnected) + { + return; + } + + gViewerWindow->handlePerFrameHover(); + + /////////////////////////////////////// + // Agent and camera movement + // + LLCoordGL current_mouse = gViewerWindow->getCurrentMouse(); + +// BOOL was_in_prelude = gAgent.inPrelude(); + + { + //LLFastTimer t(LLFastTimer::FTM_TEMP1); + + // After agent and camera moved, figure out if we need to + // deselect objects. + gSelectMgr->deselectAllIfTooFar(); + + } + + { + LLFastTimer t(LLFastTimer::FTM_RESET_DRAWORDER); + + ////////////////////////////////////////////// + // + // Clear draw orders + // + // Should actually be done after render, but handlePerFrameHover actually does a "render" + // to do its selection. + // + + gPipeline.resetDrawOrders(); + } + { + // Handle pending gesture processing + gGestureManager.update(); + + gAgent.updateAgentPosition(gFrameDTClamped, yaw, current_mouse.mX, current_mouse.mY); + } + + { + LLFastTimer t(LLFastTimer::FTM_OBJECTLIST_UPDATE); // Actually "object update" + gFrameStats.start(LLFrameStats::OBJECT_UPDATE); + + if (!(logoutRequestSent() && hasSavedFinalSnapshot())) + { + gObjectList.update(gAgent, *gWorldp); + } + } + + { + LLFastTimer t(LLFastTimer::FTM_UPDATE_SKY); + gSky.updateSky(); + } + + ////////////////////////////////////// + // + // Deletes objects... + // Has to be done after doing idleUpdates (which can kill objects) + // + + { + LLFastTimer t(LLFastTimer::FTM_CLEANUP); + gFrameStats.start(LLFrameStats::CLEAN_DEAD); + gObjectList.cleanDeadObjects(); + 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. + // + + { + //LLFastTimer t(LLFastTimer::FTM_TEMP3); + + gFrameStats.start(LLFrameStats::UPDATE_EFFECTS); + gSelectMgr->updateEffects(); + gHUDManager->cleanupEffects(); + gHUDManager->sendEffects(); + } + + stop_glerror(); + + //////////////////////////////////////// + // + // Unpack layer data that we've received + // + + { + LLFastTimer t(LLFastTimer::FTM_NETWORK); + gVLManager.unpackData(); + } + + ///////////////////////// + // + // Update surfaces, and surface textures as well. + // + + gWorldp->updateVisibilities(); + { + const F32 max_region_update_time = .001f; // 1ms + LLFastTimer t(LLFastTimer::FTM_REGION_UPDATE); + gWorldp->updateRegions(max_region_update_time); + } + + ///////////////////////// + // + // Update weather effects + // + + if (!gNoRender) + { + gWorldp->updateClouds(gFrameDTClamped); + gSky.propagateHeavenlyBodies(gFrameDTClamped); // moves sun, moon, and planets + + // Update wind vector + LLVector3 wind_position_region; + static LLVector3 average_wind; + + LLViewerRegion *regionp; + regionp = gWorldp->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(); + F32 cloud_density = regionp->mCloudLayer.getDensityRegion(wind_position_region); + + gSky.setCloudDensityAtAgent(cloud_density); + gSky.setWind(average_wind); + //LLVOWater::setWind(average_wind); + } + else + { + gWindVec.setVec(0.0f, 0.0f, 0.0f); + } + } + stop_glerror(); + + ////////////////////////////////////// + // + // Update images, using the image stats generated during object update/culling + // + // Can put objects onto the retextured list. + // + gFrameStats.start(LLFrameStats::IMAGE_UPDATE); + + LLFastTimer t(LLFastTimer::FTM_IMAGE_UPDATE); + + LLViewerImage::updateClass(gCamera->getVelocityStat()->getMean(), + gCamera->getAngularVelocityStat()->getMean()); + + gBumpImageList.updateImages(); // must be called before gImageList version so that it's textures are thrown out first. + + const F32 max_image_decode_time = 0.005f; // 5 ms decode time + gImageList.updateImages(max_image_decode_time); + stop_glerror(); + + ////////////////////////////////////// + // + // Sort and cull in the new renderer are moved to pipeline.cpp + // Here, particles are updated and drawables are moved. + // + + if (!gNoRender) + { + gFrameStats.start(LLFrameStats::UPDATE_MOVE); + gPipeline.updateMove(); + + gFrameStats.start(LLFrameStats::UPDATE_PARTICLES); + gWorldp->updateParticles(); + } + stop_glerror(); + + if (!LLViewerJoystick::sOverrideCamera) + { + gAgent.updateCamera(); + } + else + { + LLViewerJoystick::updateCamera(); + } + + // objects and camera should be in sync, do LOD calculations now + { + LLFastTimer t(LLFastTimer::FTM_LOD_UPDATE); + gObjectList.updateApparentAngles(gAgent); + } + + { + gFrameStats.start(LLFrameStats::AUDIO); + LLFastTimer t(LLFastTimer::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. + const F32 max_audio_decode_time = 0.002f; // 2 ms decode time + gAudiop->idle(max_audio_decode_time); + } + } + + // Handle shutdown process, for example, + // wait for floaters to close, send quit message, + // forcibly quit if it has taken too long + if (mQuitRequested) + { + idleShutdown(); + } + + stop_glerror(); +} + +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; + } + + 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); + char buffer[MAX_STRING]; // Flawfinder: ignore + snprintf(buffer, MAX_STRING, "Saving final data..."); // Flawfinder: ignore + gViewerWindow->setProgressString(buffer); + 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("Logging out..."); + 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) + { + 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; + + gVoiceClient->leaveChannel(); + } +} + +// +// 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 + +void LLAppViewer::idleNetwork() +{ + gObjectList.mNumNewObjects = 0; + S32 total_decoded = 0; + + if (!gSavedSettings.getBOOL("SpeedTest")) + { + LLFastTimer t(LLFastTimer::FTM_IDLE_NETWORK); // decode + + // deal with any queued name requests and replies. + gCacheName->processPending(); + + LLTimer check_message_timer; + // Read all available packets from network + stop_glerror(); + const S64 frame_count = gFrameCount; // U32->S64 + F32 total_time = 0.0f; + while (gMessageSystem->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; + } + stop_glerror(); + + 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. + gMessageSystem->processAcks(); + +#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(); + stop_glerror(); + + + // Decode enqueued messages... + S32 remaining_possible_decodes = MESSAGE_MAX_PER_FRAME - total_decoded; + + if( remaining_possible_decodes <= 0 ) + { + llinfos << "Maxed out number of messages per frame at " << MESSAGE_MAX_PER_FRAME << llendl; + } + + if (gPrintMessagesThisFrame) + { + llinfos << "Decoded " << total_decoded << " msgs this frame!" << llendl; + gPrintMessagesThisFrame = FALSE; + } + } + + gObjectList.mNumNewObjectsStat.addValue(gObjectList.mNumNewObjects); + + // Retransmit unacknowledged packets. + gXferManager->retransmitUnackedPackets(); + gAssetStorage->checkForTimeouts(); + + gViewerThrottle.updateDynamicThrottle(); +} + +void LLAppViewer::disconnectViewer() +{ + if (gDisconnected) + { + return; + } + // + // Cleanup after quitting. + // + // Save snapshot for next time, if we made it through initialization + + llinfos << "Disconnecting viewer!" << llendl; + + // Dump our frame statistics + gFrameStats.dump(); + + // Remember if we were flying + gSavedSettings.setBOOL("FlyingAtExit", gAgent.getFlying() ); + + // Un-minimize all windows so they don't get saved minimized + if (!gNoRender) + { + if (gFloaterView) + { + gFloaterView->restoreAll(); + } + } + + if (gSelectMgr) + { + gSelectMgr->deselectAll(); + } + + if (!gNoRender) + { + // save inventory if appropriate + gInventory.cache(gAgent.getInventoryRootID(), gAgent.getID()); + if(gInventoryLibraryRoot.notNull() && gInventoryLibraryOwner.notNull()) + { + gInventory.cache(gInventoryLibraryRoot, gInventoryLibraryOwner); + } + } + + saveNameCache(); + + // close inventory interface, close all windows + LLInventoryView::cleanup(); + + // Also writes cached agent settings to gSavedSettings + gAgent.cleanup(); + + gObjectList.destroy(); + delete gWorldp; + gWorldp = NULL; + + cleanup_xfer_manager(); + gDisconnected = TRUE; +} + +void LLAppViewer::forceErrorLLError() +{ + llerrs << "This is an llerror" << llendl; +} + +void LLAppViewer::forceErrorBreakpoint() +{ +#ifdef LL_WINDOWS + DebugBreak(); +#endif + return; +} + +void LLAppViewer::forceErrorBadMemoryAccess() +{ + S32* crash = NULL; + *crash = 0xDEADBEEF; + return; +} + +void LLAppViewer::forceErrorInifiniteLoop() +{ + while(true) + { + ; + } + return; +} + +void LLAppViewer::forceErrorSoftwareException() +{ + // *FIX: Any way to insure it won't be handled? + throw; +} |