aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/linden/indra/newview/llappviewer.cpp
diff options
context:
space:
mode:
authorJacek Antonelli2008-08-15 23:45:19 -0500
committerJacek Antonelli2008-08-15 23:45:19 -0500
commitb235c59d60472f818a9142c0886b95a0ff4191d7 (patch)
treed323c55587584b19cc43a03f58a178823f12d3cd /linden/indra/newview/llappviewer.cpp
parentSecond Life viewer sources 1.18.5.3 (diff)
downloadmeta-impy-b235c59d60472f818a9142c0886b95a0ff4191d7.zip
meta-impy-b235c59d60472f818a9142c0886b95a0ff4191d7.tar.gz
meta-impy-b235c59d60472f818a9142c0886b95a0ff4191d7.tar.bz2
meta-impy-b235c59d60472f818a9142c0886b95a0ff4191d7.tar.xz
Second Life viewer sources 1.18.6.0-RC
Diffstat (limited to '')
-rw-r--r--linden/indra/newview/llappviewer.cpp3894
1 files changed, 3894 insertions, 0 deletions
diff --git a/linden/indra/newview/llappviewer.cpp b/linden/indra/newview/llappviewer.cpp
new file mode 100644
index 0000000..45eaaa9
--- /dev/null
+++ b/linden/indra/newview/llappviewer.cpp
@@ -0,0 +1,3894 @@
1/**
2 * @file llappviewer.cpp
3 * @brief The LLAppViewer class definitions
4 *
5 * $LicenseInfo:firstyear=2007&license=viewergpl$
6 *
7 * Copyright (c) 2007, Linden Research, Inc.
8 *
9 * Second Life Viewer Source Code
10 * The source code in this file ("Source Code") is provided by Linden Lab
11 * to you under the terms of the GNU General Public License, version 2.0
12 * ("GPL"), unless you have obtained a separate licensing agreement
13 * ("Other License"), formally executed by you and Linden Lab. Terms of
14 * the GPL can be found in doc/GPL-license.txt in this distribution, or
15 * online at http://secondlife.com/developers/opensource/gplv2
16 *
17 * There are special exceptions to the terms and conditions of the GPL as
18 * it is applied to this Source Code. View the full text of the exception
19 * in the file doc/FLOSS-exception.txt in this software distribution, or
20 * online at http://secondlife.com/developers/opensource/flossexception
21 *
22 * By copying, modifying or distributing this software, you acknowledge
23 * that you have read and understood your obligations described above,
24 * and agree to abide by those obligations.
25 *
26 * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
27 * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
28 * COMPLETENESS OR PERFORMANCE.
29 * $/LicenseInfo$
30 */
31
32
33#include "llviewerprecompiledheaders.h"
34#include "llappviewer.h"
35
36#include "llversionviewer.h"
37#include "llfeaturemanager.h"
38#include "llvieweruictrlfactory.h"
39#include "llalertdialog.h"
40#include "llerrorcontrol.h"
41#include "llviewerimagelist.h"
42#include "llgroupmgr.h"
43#include "llagent.h"
44#include "llwindow.h"
45#include "llviewerstats.h"
46#include "llmd5.h"
47#include "llpumpio.h"
48#include "llfloateractivespeakers.h"
49#include "llimpanel.h"
50#include "llstartup.h"
51#include "llfocusmgr.h"
52#include "llviewerjoystick.h"
53#include "llcurl.h"
54#include "llfloatersnapshot.h"
55#include "llviewerwindow.h"
56#include "llviewerdisplay.h"
57#include "llviewermessage.h"
58#include "llviewerobjectlist.h"
59#include "llworldmap.h"
60#include "llmutelist.h"
61#include "llurldispatcher.h"
62
63#include "llweb.h"
64#include "llsecondlifeurls.h"
65
66#if LL_WINDOWS
67 #include "llwindebug.h"
68#endif
69
70#if LL_WINDOWS
71# include <share.h> // For _SH_DENYWR in initMarkerFile
72#else
73# include <sys/file.h> // For initMarkerFile support
74#endif
75
76
77
78#include "llnotify.h"
79#include "llmediaengine.h"
80#include "llviewerkeyboard.h"
81#include "lllfsthread.h"
82#include "llworkerthread.h"
83#include "lltexturecache.h"
84#include "lltexturefetch.h"
85#include "llimageworker.h"
86
87// The files below handle dependencies from cleanup.
88#include "llkeyframemotion.h"
89#include "llworldmap.h"
90#include "llhudmanager.h"
91#include "lltoolmgr.h"
92#include "llassetstorage.h"
93#include "llpolymesh.h"
94#include "lleconomy.h"
95#include "llcachename.h"
96#include "audioengine.h"
97#include "llviewermenu.h"
98#include "llselectmgr.h"
99#include "lltracker.h"
100#include "llmozlib.h"
101#include "llviewerparcelmgr.h"
102#include "llworldmapview.h"
103
104#include "lldebugview.h"
105#include "llconsole.h"
106#include "llcontainerview.h"
107#include "llhoverview.h"
108
109#include "llsdserialize.h"
110
111#include "llworld.h"
112#include "llhudeffecttrail.h"
113#include "llvectorperfoptions.h"
114#include "llurlsimstring.h"
115
116// Included so that constants/settings might be initialized
117// in save_settings_to_globals()
118#include "llbutton.h"
119#include "llcombobox.h"
120#include "llstatusbar.h"
121#include "llsurface.h"
122#include "llvosky.h"
123#include "llvotree.h"
124#include "llvoavatar.h"
125#include "llfolderview.h"
126#include "lltoolbar.h"
127#include "llframestats.h"
128#include "llagentpilot.h"
129#include "llsrv.h"
130
131// includes for idle() idleShutdown()
132#include "llviewercontrol.h"
133#include "lleventnotifier.h"
134#include "llcallbacklist.h"
135#include "pipeline.h"
136#include "llgesturemgr.h"
137#include "llsky.h"
138#include "llvlmanager.h"
139#include "llviewercamera.h"
140#include "lldrawpoolbump.h"
141#include "llvieweraudio.h"
142#include "llimview.h"
143#include "llviewerthrottle.h"
144//
145
146#include "llinventoryview.h"
147
148// *FIX: Remove these once the command line params thing is figured out.
149// Yuck!
150static int gTempArgC = 0;
151static char** gTempArgV;
152
153// *FIX: These extern globals should be cleaned up.
154// The globals either represent state/config/resource-storage of either
155// this app, or another 'component' of the viewer. App globals should be
156// moved into the app class, where as the other globals should be
157// moved out of here.
158// If a global symbol reference seems valid, it will be included
159// via header files above.
160
161//----------------------------------------------------------------------------
162// llviewernetwork.h
163#include "llviewernetwork.h"
164// extern EGridInfo gGridChoice;
165
166
167////// Windows-specific includes to the bottom - nasty defines in these pollute the preprocessor
168//
169#if LL_WINDOWS && LL_LCD_COMPILE
170 #include "lllcd.h"
171#endif
172//
173#if LL_QUICKTIME_ENABLED
174 #if LL_DARWIN
175 #include <QuickTime/QuickTime.h>
176 #else
177 // quicktime specific includes
178 #include "MacTypes.h"
179 #include "QTML.h"
180 #include "Movies.h"
181 #include "FixMath.h"
182 #endif
183#endif
184//
185//////
186
187
188//----------------------------------------------------------------------------
189// viewer.cpp - these are only used in viewer, should be easily moved.
190extern void disable_win_error_reporting();
191
192//#define APPLE_PREVIEW // Define this if you're doing a preview build on the Mac
193#if LL_RELEASE_FOR_DOWNLOAD
194// Default userserver for production builds is agni
195#ifndef APPLE_PREVIEW
196static EGridInfo GridDefaultChoice = GRID_INFO_AGNI;
197#else
198static EGridInfo GridDefaultChoice = GRID_INFO_ADITI;
199#endif
200#else
201// Default userserver for development builds is dmz
202static EGridInfo GridDefaultChoice = GRID_INFO_DMZ;
203#endif
204
205#if LL_WINDOWS
206extern void create_console();
207#endif
208
209
210#if LL_DARWIN
211#include <Carbon/Carbon.h>
212extern void init_apple_menu(const char* product);
213extern OSErr AEGURLHandler(const AppleEvent *messagein, AppleEvent *reply, long refIn);
214extern OSErr AEQuitHandler(const AppleEvent *messagein, AppleEvent *reply, long refIn);
215extern OSStatus simpleDialogHandler(EventHandlerCallRef handler, EventRef event, void *userdata);
216extern OSStatus DisplayReleaseNotes(void);
217#include <boost/tokenizer.hpp>
218#endif // LL_DARWIN
219
220
221#include "moviemaker.h"
222extern BOOL gbCapturing;
223
224#if !LL_SOLARIS
225 extern MovieMaker gMovieMaker;
226#endif
227
228extern BOOL gRandomizeFramerate;
229extern BOOL gPeriodicSlowFrame;
230
231#if LL_GSTREAMER_ENABLED
232void UnloadGStreamer();
233#endif
234
235////////////////////////////////////////////////////////////
236// All from the last globals push...
237bool gVerifySSLCert = true;
238BOOL gHandleKeysAsync = FALSE;
239
240BOOL gProbeHardware = TRUE; // Use DirectX 9 to probe for hardware
241
242S32 gYieldMS = 0; // set in parse_args, used in mainLoop
243BOOL gYieldTime = FALSE;
244
245const F32 DEFAULT_AFK_TIMEOUT = 5.f * 60.f; // time with no input before user flagged as Away From Keyboard
246
247F32 gSimLastTime; // Used in LLAppViewer::init and send_stats()
248F32 gSimFrames;
249
250LLString gDisabledMessage; // Set in LLAppViewer::initConfiguration used in idle_startup
251
252BOOL gHideLinks = FALSE; // Set in LLAppViewer::initConfiguration, used externally
253
254BOOL gInProductionGrid = FALSE;
255
256BOOL gAllowIdleAFK = TRUE;
257F32 gAFKTimeout = DEFAULT_AFK_TIMEOUT;
258BOOL gShowObjectUpdates = FALSE;
259BOOL gLogMessages = FALSE;
260std::string gChannelName = LL_CHANNEL;
261BOOL gUseAudio = TRUE;
262LLString gCmdLineFirstName;
263LLString gCmdLineLastName;
264LLString gCmdLinePassword;
265
266BOOL gAutoLogin = FALSE;
267
268const char* DEFAULT_SETTINGS_FILE = "settings.xml";
269BOOL gRequestInventoryLibrary = TRUE;
270BOOL gGodConnect = FALSE;
271BOOL gAcceptTOS = FALSE;
272BOOL gAcceptCriticalMessage = FALSE;
273
274LLUUID gViewerDigest; // MD5 digest of the viewer's executable file.
275BOOL gLastExecFroze = FALSE;
276
277LLSD gDebugInfo;
278
279U32 gFrameCount = 0;
280U32 gForegroundFrameCount = 0; // number of frames that app window was in foreground
281LLPumpIO* gServicePump = NULL;
282
283BOOL gPacificDaylightTime = FALSE;
284
285U64 gFrameTime = 0;
286F32 gFrameTimeSeconds = 0.f;
287F32 gFrameIntervalSeconds = 0.f;
288F32 gFPSClamped = 10.f; // Pretend we start at target rate.
289F32 gFrameDTClamped = 0.f; // Time between adjacent checks to network for packets
290U64 gStartTime = 0; // gStartTime is "private", used only to calculate gFrameTimeSeconds
291
292LLTimer gRenderStartTime;
293LLFrameTimer gForegroundTime;
294LLTimer gLogoutTimer;
295static const F32 LOGOUT_REQUEST_TIME = 6.f; // this will be cut short by the LogoutReply msg.
296F32 gLogoutMaxTime = LOGOUT_REQUEST_TIME;
297
298LLUUID gInventoryLibraryOwner;
299LLUUID gInventoryLibraryRoot;
300
301BOOL gDisableVoice = FALSE;
302BOOL gDisconnected = FALSE;
303
304// Map scale in pixels per region
305F32 gMapScale = 128.f;
306F32 gMiniMapScale = 128.f;
307
308// used to restore texture state after a mode switch
309LLFrameTimer gRestoreGLTimer;
310BOOL gRestoreGL = FALSE;
311BOOL gUseWireframe = FALSE;
312
313F32 gMouseSensitivity = 3.f;
314BOOL gInvertMouse = FALSE;
315
316// VFS globals - see llappviewer.h
317LLVFS* gStaticVFS = NULL;
318
319LLMemoryInfo gSysMemory;
320
321bool gPreloadImages = true;
322bool gPreloadSounds = true;
323
324LLString gLastVersionChannel;
325
326LLVector3 gWindVec(3.0, 3.0, 0.0);
327LLVector3 gRelativeWindVec(0.0, 0.0, 0.0);
328
329U32 gPacketsIn = 0;
330
331BOOL gPrintMessagesThisFrame = FALSE;
332
333BOOL gUseConsole = TRUE;
334
335BOOL gRandomizeFramerate = FALSE;
336BOOL gPeriodicSlowFrame = FALSE;
337
338BOOL gQAMode = FALSE;
339
340////////////////////////////////////////////////////////////
341// Internal globals... that should be removed.
342static F32 gQuitAfterSeconds = 0.f;
343static BOOL gRotateRight = FALSE;
344static BOOL gIgnorePixelDepth = FALSE;
345
346// Allow multiple viewers in ReleaseForDownload
347#if LL_RELEASE_FOR_DOWNLOAD
348static BOOL gMultipleViewersOK = FALSE;
349#else
350static BOOL gMultipleViewersOK = TRUE;
351#endif
352
353static std::map<std::string, std::string> gCommandLineSettings;
354static std::map<std::string, std::string> gCommandLineForcedSettings;
355
356static LLString gArgs;
357
358static LLString gOldSettingsFileName;
359static const char* LEGACY_DEFAULT_SETTINGS_FILE = "settings.ini";
360static BOOL gDoDisconnect = FALSE;
361static LLString gLaunchFileOnQuit;
362
363//----------------------------------------------------------------------------
364// File scope definitons
365const char *VFS_DATA_FILE_BASE = "data.db2.x.";
366const char *VFS_INDEX_FILE_BASE = "index.db2.x.";
367
368static LLString gSecondLife;
369static LLString gWindowTitle;
370#ifdef LL_WINDOWS
371 static char sWindowClass[] = "Second Life";
372#endif
373
374std::vector<std::string> gLoginURIs;
375static std::string gHelperURI;
376
377static const char USAGE[] = "\n"
378"usage:\tviewer [options]\n"
379"options:\n"
380" -login <first> <last> <password> log in as a user\n"
381" -autologin log in as last saved user\n"
382" -loginuri <URI> login server and CGI script to use\n"
383" -helperuri <URI> helper web CGI prefix to use\n"
384" -settings <filename> specify the filename of a\n"
385" configuration file\n"
386" default is settings.xml\n"
387" -setdefault <variable> <value> specify the value of a particular\n"
388" configuration variable which can be\n"
389" overridden by settings.xml\n"
390" -set <variable> <value> specify the value of a particular\n"
391" configuration variable that\n"
392" overrides all other settings\n"
393" -user <user_server_ip> specify userserver in dotted quad\n"
394#if !LL_RELEASE_FOR_DOWNLOAD
395" -sim <simulator_ip> specify the simulator ip address\n"
396#endif
397" -god log in as god if you have god access\n"
398" -purge delete files in cache\n"
399" -safe reset preferences, run in safe mode\n"
400" -noutc logs in local time, not UTC\n"
401" -nothread run vfs in single thread\n"
402" -noinvlib Do not request inventory library\n"
403" -multiple allow multiple viewers\n"
404" -nomultiple block multiple viewers\n"
405" -novoice disable voice\n"
406" -ignorepixeldepth ignore pixel depth settings\n"
407" -cooperative [ms] yield some idle time to local host\n"
408" -skin ui/branding skin folder to use\n"
409#if LL_WINDOWS
410" -noprobe disable hardware probe\n"
411#endif
412" -noquicktime disable QuickTime movies, speeds startup\n"
413" -nopreload don't preload UI images or sounds, speeds startup\n"
414// these seem to be unused
415//" -noenv turn off environmental effects\n"
416//" -proxy <proxy_ip> specify the proxy ip address\n"
417"\n";
418
419void idle_afk_check()
420{
421 // check idle timers
422 if (gAllowIdleAFK && (gAwayTriggerTimer.getElapsedTimeF32() > gAFKTimeout))
423 {
424 gAgent.setAFK();
425 }
426}
427
428// A callback set in LLAppViewer::init()
429static void ui_audio_callback(const LLUUID& uuid)
430{
431 if (gAudiop)
432 {
433 F32 volume = gSavedSettings.getF32("AudioLevelUI");
434 gAudiop->triggerSound(uuid, gAgent.getID(), volume);
435 }
436}
437
438void request_initial_instant_messages()
439{
440 static BOOL requested = FALSE;
441 if (!requested
442 && gMuteListp
443 && gMuteListp->isLoaded()
444 && gAgent.getAvatarObject())
445 {
446 // Auto-accepted inventory items may require the avatar object
447 // to build a correct name. Likewise, inventory offers from
448 // muted avatars require the mute list to properly mute.
449 LLMessageSystem* msg = gMessageSystem;
450 msg->newMessageFast(_PREHASH_RetrieveInstantMessages);
451 msg->nextBlockFast(_PREHASH_AgentData);
452 msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID());
453 msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
454 gAgent.sendReliableMessage();
455 requested = TRUE;
456 }
457}
458
459// Use these strictly for things that are constructed at startup,
460// or for things that are performance critical. JC
461static void saved_settings_to_globals()
462{
463 LLBUTTON_H_PAD = gSavedSettings.getS32("ButtonHPad");
464 LLBUTTON_V_PAD = gSavedSettings.getS32("ButtonVPad");
465 BTN_HEIGHT_SMALL = gSavedSettings.getS32("ButtonHeightSmall");
466 BTN_HEIGHT = gSavedSettings.getS32("ButtonHeight");
467
468 MENU_BAR_HEIGHT = gSavedSettings.getS32("MenuBarHeight");
469 MENU_BAR_WIDTH = gSavedSettings.getS32("MenuBarWidth");
470 STATUS_BAR_HEIGHT = gSavedSettings.getS32("StatusBarHeight");
471
472 LLCOMBOBOX_HEIGHT = BTN_HEIGHT - 2;
473 LLCOMBOBOX_WIDTH = 128;
474
475 LLSurface::setTextureSize(gSavedSettings.getU32("RegionTextureSize"));
476
477 LLVOSky::sNighttimeBrightness = gSavedSettings.getF32("RenderNightBrightness");
478
479 LLImageGL::sGlobalUseAnisotropic = gSavedSettings.getBOOL("RenderAnisotropic");
480 LLVOVolume::sLODFactor = gSavedSettings.getF32("RenderVolumeLODFactor");
481 LLVOVolume::sDistanceFactor = 1.f-LLVOVolume::sLODFactor * 0.1f;
482 LLVolumeImplFlexible::sUpdateFactor = gSavedSettings.getF32("RenderFlexTimeFactor");
483 LLVOTree::sTreeFactor = gSavedSettings.getF32("RenderTreeLODFactor");
484 LLVOAvatar::sLODFactor = gSavedSettings.getF32("RenderAvatarLODFactor");
485 LLVOAvatar::sMaxVisible = gSavedSettings.getS32("RenderAvatarMaxVisible");
486 LLVOAvatar::sVisibleInFirstPerson = gSavedSettings.getBOOL("FirstPersonAvatarVisible");
487 // clamp auto-open time to some minimum usable value
488 LLFolderView::sAutoOpenTime = llmax(0.25f, gSavedSettings.getF32("FolderAutoOpenDelay"));
489 LLToolBar::sInventoryAutoOpenTime = gSavedSettings.getF32("InventoryAutoOpenDelay");
490 LLSelectMgr::sRectSelectInclusive = gSavedSettings.getBOOL("RectangleSelectInclusive");
491 LLSelectMgr::sRenderHiddenSelections = gSavedSettings.getBOOL("RenderHiddenSelections");
492 LLSelectMgr::sRenderLightRadius = gSavedSettings.getBOOL("RenderLightRadius");
493
494 gFrameStats.setTrackStats(gSavedSettings.getBOOL("StatsSessionTrackFrameStats"));
495 gAgentPilot.mNumRuns = gSavedSettings.getS32("StatsNumRuns");
496 gAgentPilot.mQuitAfterRuns = gSavedSettings.getBOOL("StatsQuitAfterRuns");
497 gAgent.mHideGroupTitle = gSavedSettings.getBOOL("RenderHideGroupTitle");
498
499 gDebugWindowProc = gSavedSettings.getBOOL("DebugWindowProc");
500 gAllowIdleAFK = gSavedSettings.getBOOL("AllowIdleAFK");
501 gAFKTimeout = gSavedSettings.getF32("AFKTimeout");
502 gMouseSensitivity = gSavedSettings.getF32("MouseSensitivity");
503 gInvertMouse = gSavedSettings.getBOOL("InvertMouse");
504 gShowObjectUpdates = gSavedSettings.getBOOL("ShowObjectUpdates");
505 gMapScale = gSavedSettings.getF32("MapScale");
506 gMiniMapScale = gSavedSettings.getF32("MiniMapScale");
507 gHandleKeysAsync = gSavedSettings.getBOOL("AsyncKeyboard");
508 LLHoverView::sShowHoverTips = gSavedSettings.getBOOL("ShowHoverTips");
509
510#if LL_VECTORIZE
511 if (gSysCPU.hasAltivec())
512 {
513 gSavedSettings.setBOOL("VectorizeEnable", TRUE );
514 gSavedSettings.setU32("VectorizeProcessor", 0 );
515 }
516 else
517 if (gSysCPU.hasSSE2())
518 {
519 gSavedSettings.setBOOL("VectorizeEnable", TRUE );
520 gSavedSettings.setU32("VectorizeProcessor", 2 );
521 }
522 else
523 if (gSysCPU.hasSSE())
524 {
525 gSavedSettings.setBOOL("VectorizeEnable", TRUE );
526 gSavedSettings.setU32("VectorizeProcessor", 1 );
527 }
528 else
529 {
530 // Don't bother testing or running if CPU doesn't support it. JC
531 gSavedSettings.setBOOL("VectorizePerfTest", FALSE );
532 gSavedSettings.setBOOL("VectorizeEnable", FALSE );
533 gSavedSettings.setU32("VectorizeProcessor", 0 );
534 gSavedSettings.setBOOL("VectorizeSkin", FALSE);
535 }
536#else
537 // This build target doesn't support SSE, don't test/run.
538 gSavedSettings.setBOOL("VectorizePerfTest", FALSE );
539 gSavedSettings.setBOOL("VectorizeEnable", FALSE );
540 gSavedSettings.setU32("VectorizeProcessor", 0 );
541 gSavedSettings.setBOOL("VectorizeSkin", FALSE);
542#endif
543
544 // propagate push to talk preference to current status
545 gSavedSettings.setBOOL("PTTCurrentlyEnabled", gSavedSettings.getBOOL("EnablePushToTalk"));
546
547 settings_setup_listeners();
548
549 // gAgent.init() also loads from saved settings.
550}
551
552int parse_args(int argc, char **argv)
553{
554 // Sometimes IP addresses passed in on the command line have leading
555 // or trailing white space. Use LLString to clean that up.
556 LLString ip_string;
557 S32 j;
558
559 for (j = 1; j < argc; j++)
560 {
561 gArgs += argv[j];
562 gArgs += " ";
563
564 LLString argument = argv[j];
565 if ((!strcmp(argv[j], "-port")) && (++j < argc))
566 {
567 sscanf(argv[j], "%u", &(gAgent.mViewerPort));
568 }
569 else if ((!strcmp(argv[j], "-drop")) && (++j < argc))
570 {
571 sscanf(argv[j], "%f", &gPacketDropPercentage);
572 }
573 else if ((!strcmp(argv[j], "-inbw")) && (++j < argc))
574 {
575 sscanf(argv[j], "%f", &gInBandwidth);
576 }
577 else if ((!strcmp(argv[j], "-outbw")) && (++j < argc))
578 {
579 sscanf(argv[j], "%f", &gOutBandwidth);
580 }
581 else if (!strcmp(argv[j], "--aditi"))
582 {
583 gGridChoice = GRID_INFO_ADITI;
584 snprintf(gGridName, MAX_STRING, "%s", gGridInfo[gGridChoice].mName); // Flawfinder: ignore
585 }
586 else if (!strcmp(argv[j], "--agni"))
587 {
588 gGridChoice = GRID_INFO_AGNI;
589 snprintf(gGridName, MAX_STRING, "%s", gGridInfo[gGridChoice].mName); // Flawfinder: ignore
590 }
591 else if (!strcmp(argv[j], "--dmz"))
592 {
593 gGridChoice = GRID_INFO_DMZ;
594 snprintf(gGridName, MAX_STRING, "%s", gGridInfo[gGridChoice].mName); // Flawfinder: ignore
595 }
596 else if (!strcmp(argv[j], "--siva"))
597 {
598 gGridChoice = GRID_INFO_SIVA;
599 snprintf(gGridName, MAX_STRING, "%s", gGridInfo[gGridChoice].mName); // Flawfinder: ignore
600 }
601 else if (!strcmp(argv[j], "--shakti"))
602 {
603 gGridChoice = GRID_INFO_SHAKTI;
604 snprintf(gGridName, MAX_STRING, "%s", gGridInfo[gGridChoice].mName); // Flawfinder: ignore
605 }
606 else if (!strcmp(argv[j], "--durga"))
607 {
608 gGridChoice = GRID_INFO_DURGA;
609 snprintf(gGridName, MAX_STRING, "%s", gGridInfo[gGridChoice].mName); // Flawfinder: ignore
610 }
611 else if (!strcmp(argv[j], "--soma"))
612 {
613 gGridChoice = GRID_INFO_SOMA;
614 snprintf(gGridName, MAX_STRING, "%s", gGridInfo[gGridChoice].mName); // Flawfinder: ignore
615 }
616 else if (!strcmp(argv[j], "--ganga"))
617 {
618 gGridChoice = GRID_INFO_GANGA;
619 sprintf(gGridName,"%s", gGridInfo[gGridChoice].mName);
620 }
621 else if (!strcmp(argv[j], "--vaak"))
622 {
623 gGridChoice = GRID_INFO_VAAK;
624 sprintf(gGridName,"%s", gGridInfo[gGridChoice].mName);
625 }
626 else if (!strcmp(argv[j], "--uma"))
627 {
628 gGridChoice = GRID_INFO_UMA;
629 sprintf(gGridName,"%s", gGridInfo[gGridChoice].mName);
630 }
631 else if (!strcmp(argv[j], "-user") && (++j < argc))
632 {
633 if (!strcmp(argv[j], "-"))
634 {
635 gGridChoice = GRID_INFO_LOCAL;
636 snprintf(gGridName, MAX_STRING, "%s", LOOPBACK_ADDRESS_STRING); // Flawfinder: ignore
637 }
638 else
639 {
640 gGridChoice = GRID_INFO_OTHER;
641 ip_string.assign( argv[j] );
642 LLString::trim(ip_string);
643 snprintf(gGridName, MAX_STRING, "%s", ip_string.c_str()); // Flawfinder: ignore
644 }
645 }
646 else if (!strcmp(argv[j], "-loginuri") && (++j < argc))
647 {
648 LLAppViewer::instance()->addLoginURI(utf8str_trim(argv[j]));
649 }
650 else if (!strcmp(argv[j], "-helperuri") && (++j < argc))
651 {
652 LLAppViewer::instance()->setHelperURI(utf8str_trim(argv[j]));
653 }
654 else if (!strcmp(argv[j], "-debugviews"))
655 {
656 LLView::sDebugRects = TRUE;
657 }
658 else if (!strcmp(argv[j], "-skin") && (++j < argc))
659 {
660 std::string folder(argv[j]);
661 gDirUtilp->setSkinFolder(folder);
662 }
663 else if (!strcmp(argv[j], "-autologin") || !strcmp(argv[j], "--autologin")) // keep --autologin for compatibility
664 {
665 gAutoLogin = TRUE;
666 }
667 else if (!strcmp(argv[j], "-quitafter") && (++j < argc))
668 {
669 gQuitAfterSeconds = (F32)atof(argv[j]);
670 }
671 else if (!strcmp(argv[j], "-rotate"))
672 {
673 gRotateRight = TRUE;
674 }
675// else if (!strcmp(argv[j], "-noenv"))
676// {
677 //turn OFF environmental effects for slow machines/video cards
678// gRequestParaboloidMap = FALSE;
679// }
680 else if (!strcmp(argv[j], "-noaudio"))
681 {
682 gUseAudio = FALSE;
683 }
684 else if (!strcmp(argv[j], "-nosound")) // tends to be popular cmdline on Linux.
685 {
686 gUseAudio = FALSE;
687 }
688 else if (!strcmp(argv[j], "-noprobe"))
689 {
690 gProbeHardware = FALSE;
691 }
692 else if (!strcmp(argv[j], "-noquicktime"))
693 {
694 // Developers can log in faster if they don't load all the
695 // quicktime dlls.
696 gUseQuickTime = false;
697 }
698 else if (!strcmp(argv[j], "-nopreload"))
699 {
700 // Developers can log in faster if they don't decode sounds
701 // or images on startup, ~5 seconds faster.
702 gPreloadSounds = false;
703 gPreloadImages = false;
704 }
705 else if (!strcmp(argv[j], "-purge"))
706 {
707 LLAppViewer::instance()->purgeCache();
708 }
709 else if(!strcmp(argv[j], "-noinvlib"))
710 {
711 gRequestInventoryLibrary = FALSE;
712 }
713 else if (!strcmp(argv[j], "-log"))
714 {
715 gLogMessages = TRUE;
716 continue;
717 }
718 else if (!strcmp(argv[j], "-logfile") && (++j < argc))
719 {
720 // *NOTE: This buffer size is hard coded into scanf() below.
721 char logfile[256]; // Flawfinder: ignore
722 sscanf(argv[j], "%255s", logfile); // Flawfinder: ignore
723 llinfos << "Setting log file to " << logfile << llendl;
724 LLFile::remove(logfile);
725 LLError::logToFile(logfile);
726 }
727 else if (!strcmp(argv[j], "-settings") && (++j < argc))
728 {
729 gSettingsFileName = gDirUtilp->getExpandedFilename(LL_PATH_USER_SETTINGS, argv[j]);
730 }
731 else if (!strcmp(argv[j], "-setdefault") && (j + 2 < argc))
732 {
733 std::string control_name;
734 std::string control_value;
735
736 j++;
737 if (argv[j]) control_name = std::string(argv[j]);
738
739 j++;
740 if (argv[j]) control_value = std::string(argv[j]);
741
742 // grab control name and value
743 if (!control_name.empty())
744 {
745 gCommandLineSettings[control_name] = control_value;
746 }
747 }
748 else if (!strcmp(argv[j], "-set") && (j + 2 < argc))
749 {
750 std::string control_name;
751 std::string control_value;
752
753 j++;
754 if (argv[j]) control_name = std::string(argv[j]);
755
756 j++;
757 if (argv[j]) control_value = std::string(argv[j]);
758
759 // grab control name and value
760 if (!control_name.empty())
761 {
762 gCommandLineForcedSettings[control_name] = control_value;
763 }
764 }
765 else if (!strcmp(argv[j], "-login"))
766 {
767 if (j + 3 < argc)
768 {
769 j++;
770 gCmdLineFirstName = argv[j];
771 j++;
772 gCmdLineLastName = argv[j];
773 j++;
774 gCmdLinePassword = argv[j];
775 }
776 else
777 {
778 // only works if -login is last parameter on command line
779 llerrs << "Not enough parameters to -login. Did you mean -loginuri?" << llendl;
780 }
781 }
782 else if (!strcmp(argv[j], "-god"))
783 {
784 gGodConnect = TRUE;
785 }
786 else if (!strcmp(argv[j], "-noconsole"))
787 {
788 gUseConsole = FALSE;
789 }
790 else if (!strcmp(argv[j], "-safe"))
791 {
792 llinfos << "Setting viewer feature table to run in safe mode, resetting prefs" << llendl;
793 gFeatureManagerp->setSafe(TRUE);
794 }
795 else if (!strcmp(argv[j], "-multiple"))
796 {
797 gMultipleViewersOK = TRUE;
798 }
799 else if (!strcmp(argv[j], "-nomultiple"))
800 {
801 gMultipleViewersOK = FALSE;
802 }
803 else if (!strcmp(argv[j], "-novoice"))
804 {
805 gDisableVoice = TRUE;
806 }
807 else if (!strcmp(argv[j], "-nothread"))
808 {
809 LLVFile::ALLOW_ASYNC = FALSE;
810 llinfos << "Running VFS in nothread mode" << llendl;
811 }
812 // some programs don't respect the command line options in protocol handlers (I'm looking at you, Opera)
813 // so this allows us to parse the URL straight off the command line without a "-url" paramater
814 else if (LLURLDispatcher::isSLURL(argv[j])
815 || !strcmp(argv[j], "-url") && (++j < argc))
816 {
817 std::string slurl = argv[j];
818 if (LLURLDispatcher::isSLURLCommand(slurl))
819 {
820 LLStartUp::sSLURLCommand = slurl;
821 }
822 else
823 {
824 LLURLSimString::setString(slurl);
825 }
826 // *NOTE: After setting the url, bail. What can happen is
827 // that someone can use IE (or potentially other browsers)
828 // and do the rough equivalent of command injection and
829 // steal passwords. Phoenix. SL-55321
830 }
831 else if (!strcmp(argv[j], "-ignorepixeldepth"))
832 {
833 gIgnorePixelDepth = TRUE;
834 }
835 else if (!strcmp(argv[j], "-cooperative"))
836 {
837 S32 ms_to_yield = 0;
838 if(++j < argc)
839 {
840 S32 rv = sscanf(argv[j], "%d", &ms_to_yield);
841 if(0 == rv)
842 {
843 --j;
844 }
845 }
846 else
847 {
848 --j;
849 }
850 gYieldMS = ms_to_yield;
851 gYieldTime = TRUE;
852 }
853 else if (!strcmp(argv[j], "-no-verify-ssl-cert"))
854 {
855 gVerifySSLCert = false;
856 }
857 else if ( (!strcmp(argv[j], "--channel") || !strcmp(argv[j], "-channel")) && (++j < argc))
858 {
859 gChannelName = argv[j];
860 }
861#if LL_DARWIN
862 else if (!strncmp(argv[j], "-psn_", 5))
863 {
864 // this is the Finder passing the process session number
865 // we ignore this
866 }
867#endif
868 else if(!strncmp(argv[j], "-qa", 3))
869 {
870 gQAMode = TRUE;
871 }
872 else
873 {
874
875 // DBC - Mac OS X passes some stuff by default on the command line (e.g. psn).
876 // Second Life URLs are passed this way as well?
877 llwarns << "Possible unknown keyword " << argv[j] << llendl;
878
879 // print usage information
880 llinfos << USAGE << llendl;
881 // return 1;
882 }
883 }
884 return 0;
885}
886
887bool send_url_to_other_instance(const std::string& url)
888{
889#if LL_WINDOWS
890 wchar_t window_class[256]; /* Flawfinder: ignore */ // Assume max length < 255 chars.
891 mbstowcs(window_class, sWindowClass, 255);
892 window_class[255] = 0;
893 // Use the class instead of the window name.
894 HWND other_window = FindWindow(window_class, NULL);
895 if (other_window != NULL)
896 {
897 lldebugs << "Found other window with the name '" << gWindowTitle << "'" << llendl;
898 COPYDATASTRUCT cds;
899 const S32 SLURL_MESSAGE_TYPE = 0;
900 cds.dwData = SLURL_MESSAGE_TYPE;
901 cds.cbData = url.length() + 1;
902 cds.lpData = (void*)url.c_str();
903
904 LRESULT msg_result = SendMessage(other_window, WM_COPYDATA, NULL, (LPARAM)&cds);
905 lldebugs << "SendMessage(WM_COPYDATA) to other window '"
906 << gWindowTitle << "' returned " << msg_result << llendl;
907 return true;
908 }
909#endif
910 return false;
911}
912
913//----------------------------------------------------------------------------
914// LLAppViewer definition
915
916// Static members.
917// The single viewer app.
918LLAppViewer* LLAppViewer::sInstance = NULL;
919
920LLTextureCache* LLAppViewer::sTextureCache = NULL;
921LLWorkerThread* LLAppViewer::sImageDecodeThread = NULL;
922LLTextureFetch* LLAppViewer::sTextureFetch = NULL;
923
924LLAppViewer::LLAppViewer() :
925 mMarkerFile(NULL),
926 mLastExecFroze(false),
927 mCrashBehavior(CRASH_BEHAVIOR_ASK),
928 mReportedCrash(false),
929 mNumSessions(0),
930 mPurgeCache(false),
931 mPurgeOnExit(false),
932 mSecondInstance(false),
933 mSavedFinalSnapshot(false),
934 mQuitRequested(false),
935 mLogoutRequestSent(false)
936{
937 if(NULL != sInstance)
938 {
939 llerrs << "Oh no! An instance of LLAppViewer already exists! LLAppViewer is sort of like a singleton." << llendl;
940 }
941
942 sInstance = this;
943}
944
945LLAppViewer::~LLAppViewer()
946{
947 // If we got to this destructor somehow, the app didn't hang.
948 removeMarkerFile();
949}
950
951bool LLAppViewer::tempStoreCommandOptions(int argc, char** argv)
952{
953 gTempArgC = argc;
954 gTempArgV = argv;
955 return true;
956}
957
958bool LLAppViewer::init()
959{
960 // *NOTE:Mani - LLCurl::initClass is not thread safe.
961 // Called before threads are created.
962 LLCurl::initClass();
963
964 initThreads();
965
966 initEarlyConfiguration();
967
968 //
969 // Start of the application
970 //
971 // IMPORTANT! Do NOT put anything that will write
972 // into the log files during normal startup until AFTER
973 // we run the "program crashed last time" error handler below.
974 //
975
976 // Need to do this initialization before we do anything else, since anything
977 // that touches files should really go through the lldir API
978 gDirUtilp->initAppDirs("SecondLife");
979
980
981 initLogging();
982
983 //
984 // OK to write stuff to logs now, we've now crash reported if necessary
985 //
986
987 // Set up some defaults...
988 gSettingsFileName = gDirUtilp->getExpandedFilename(LL_PATH_USER_SETTINGS, DEFAULT_SETTINGS_FILE);
989 gOldSettingsFileName = gDirUtilp->getExpandedFilename(LL_PATH_USER_SETTINGS, LEGACY_DEFAULT_SETTINGS_FILE);
990
991 if (!initConfiguration())
992 return false;
993
994 //////////////////////////////////////////////////////////////////////////////
995 //////////////////////////////////////////////////////////////////////////////
996 //////////////////////////////////////////////////////////////////////////////
997 //////////////////////////////////////////////////////////////////////////////
998 // *FIX: The following code isn't grouped into functions yet.
999
1000 //
1001 // Write system information into the debug log (CPU, OS, etc.)
1002 //
1003 writeSystemInfo();
1004
1005 // Build a string representing the current version number.
1006 gCurrentVersion = llformat("%d.%d.%d", LL_VERSION_MAJOR, LL_VERSION_MINOR, LL_VERSION_PATCH );
1007
1008 //
1009 // Load the feature tables
1010 //
1011 llinfos << "Loading feature tables." << llendl;
1012
1013 gFeatureManagerp->loadFeatureTables();
1014 gFeatureManagerp->initCPUFeatureMasks();
1015
1016 // Merge with the command line overrides
1017 gSavedSettings.applyOverrides(gCommandLineSettings);
1018
1019 // Need to do this before calling parseAlerts
1020 gUICtrlFactory = new LLViewerUICtrlFactory();
1021
1022 // Pre-load alerts.xml to define the warnings settings (always loads from skins/xui/en-us/)
1023 // Do this *before* loading the settings file
1024 LLAlertDialog::parseAlerts("alerts.xml", &gSavedSettings, TRUE);
1025
1026 // Overwrite default settings with user settings
1027 llinfos << "Loading configuration file " << gSettingsFileName << llendl;
1028 if (0 == gSavedSettings.loadFromFile(gSettingsFileName))
1029 {
1030 llinfos << "Failed to load settings from " << gSettingsFileName << llendl;
1031 llinfos << "Loading legacy settings from " << gOldSettingsFileName << llendl;
1032 gSavedSettings.loadFromFileLegacy(gOldSettingsFileName);
1033 }
1034
1035 // need to do this here - need to have initialized global settings first
1036 LLString nextLoginLocation = gSavedSettings.getString( "NextLoginLocation" );
1037 if ( nextLoginLocation.length() )
1038 {
1039 LLURLSimString::setString( nextLoginLocation.c_str() );
1040 };
1041
1042 // Merge with the command line overrides
1043 gSavedSettings.applyOverrides(gCommandLineForcedSettings);
1044
1045 gLastRunVersion = gSavedSettings.getString("LastRunVersion");
1046
1047 fixup_settings();
1048
1049 // Get the single value from the crash settings file, if it exists
1050 std::string crash_settings_filename = gDirUtilp->getExpandedFilename(LL_PATH_USER_SETTINGS, CRASH_SETTINGS_FILE);
1051 gCrashSettings.loadFromFile(crash_settings_filename.c_str());
1052
1053 /////////////////////////////////////////////////
1054 // OS-specific login dialogs
1055 /////////////////////////////////////////////////
1056#if LL_WINDOWS
1057 /*
1058 // Display initial login screen, comes up quickly. JC
1059 {
1060 LLSplashScreen::hide();
1061
1062 INT_PTR result = DialogBox(hInstance, L"CONNECTBOX", NULL, login_dialog_func);
1063 if (result < 0)
1064 {
1065 llwarns << "Connect dialog box failed, returned " << result << llendl;
1066 return 1;
1067 }
1068 // success, result contains which button user clicked
1069 llinfos << "Connect dialog box clicked " << result << llendl;
1070
1071 LLSplashScreen::show();
1072 }
1073 */
1074#endif
1075
1076 // track number of times that app has run
1077 mNumSessions = gSavedSettings.getS32("NumSessions");
1078 mNumSessions++;
1079 gSavedSettings.setS32("NumSessions", mNumSessions);
1080
1081 gSavedSettings.setString("HelpLastVisitedURL",gSavedSettings.getString("HelpHomeURL"));
1082
1083 if (gSavedSettings.getBOOL("VerboseLogs"))
1084 {
1085 LLError::setPrintLocation(true);
1086 }
1087
1088#if !LL_RELEASE_FOR_DOWNLOAD
1089 if (gGridChoice == GRID_INFO_NONE)
1090 {
1091 // Development version: load last server choice by default (overridden by cmd line args)
1092
1093 S32 server = gSavedSettings.getS32("ServerChoice");
1094 if (server != 0)
1095 gGridChoice = (EGridInfo)llclamp(server, 0, (S32)GRID_INFO_COUNT - 1);
1096 if (server == GRID_INFO_OTHER)
1097 {
1098 LLString custom_server = gSavedSettings.getString("CustomServer");
1099 if (custom_server.empty())
1100 {
1101 snprintf(gGridName, MAX_STRING, "none"); /* Flawfinder: ignore */
1102 }
1103 else
1104 {
1105 snprintf(gGridName, MAX_STRING, "%s", custom_server.c_str()); /* Flawfinder: ignore */
1106 }
1107 }
1108 }
1109#endif
1110
1111 if (gGridChoice == GRID_INFO_NONE)
1112 {
1113 gGridChoice = GridDefaultChoice;
1114 }
1115
1116 // Load art UUID information, don't require these strings to be declared in code.
1117 LLString viewer_art_filename = gDirUtilp->getExpandedFilename(LL_PATH_APP_SETTINGS,"viewerart.xml");
1118 llinfos << "Loading art table from " << viewer_art_filename << llendl;
1119 gViewerArt.loadFromFile(viewer_art_filename.c_str(), FALSE);
1120 LLString textures_filename = gDirUtilp->getExpandedFilename(LL_PATH_SKINS, "textures", "textures.xml");
1121 llinfos << "Loading art table from " << textures_filename << llendl;
1122 gViewerArt.loadFromFile(textures_filename.c_str(), FALSE);
1123
1124 LLString colors_base_filename = gDirUtilp->getExpandedFilename(LL_PATH_APP_SETTINGS, "colors_base.xml");
1125 llinfos << "Loading base colors from " << colors_base_filename << llendl;
1126 gColors.loadFromFile(colors_base_filename.c_str(), FALSE, TYPE_COL4U);
1127
1128 // Load overrides from user colors file
1129 LLString user_colors_filename = gDirUtilp->getExpandedFilename(LL_PATH_APP_SETTINGS, "colors.xml");
1130 llinfos << "Loading user colors from " << user_colors_filename << llendl;
1131 if (gColors.loadFromFile(user_colors_filename.c_str(), FALSE, TYPE_COL4U) == 0)
1132 {
1133 llinfos << "Failed to load user colors from " << user_colors_filename << llendl;
1134 LLString user_legacy_colors_filename = gDirUtilp->getExpandedFilename(LL_PATH_APP_SETTINGS, "colors.ini");
1135 llinfos << "Loading legacy colors from " << user_legacy_colors_filename << llendl;
1136 gColors.loadFromFileLegacy(user_legacy_colors_filename.c_str(), FALSE, TYPE_COL4U);
1137 }
1138
1139 // Widget construction depends on LLUI being initialized
1140 LLUI::initClass(&gSavedSettings,
1141 &gColors,
1142 &gViewerArt,
1143 &gImageList,
1144 ui_audio_callback,
1145 &LLUI::sGLScaleFactor);
1146
1147 LLWeb::initClass(); // do this after LLUI
1148 gUICtrlFactory->setupPaths(); // update paths with correct language set
1149
1150 /////////////////////////////////////////////////
1151 //
1152 // Load settings files
1153 //
1154 //
1155 LLGroupMgr::parseRoleActions("role_actions.xml");
1156
1157 LLAgent::parseTeleportMessages("teleport_strings.xml");
1158
1159 mCrashBehavior = gCrashSettings.getS32(CRASH_BEHAVIOR_SETTING);
1160
1161 LLVectorPerformanceOptions::initClass();
1162
1163 // Move certain saved settings into global variables for speed
1164 saved_settings_to_globals();
1165
1166
1167 // Find partition serial number (Windows) or hardware serial (Mac)
1168 mSerialNumber = generateSerialNumber();
1169
1170 if(false == initHardwareTest())
1171 {
1172 // Early out from user choice.
1173 return false;
1174 }
1175
1176 // Always fetch the Ethernet MAC address, needed both for login
1177 // and password load.
1178 LLUUID::getNodeID(gMACAddress);
1179
1180 // Prepare for out-of-memory situations, during which we will crash on
1181 // purpose and save a dump.
1182#if LL_WINDOWS && LL_RELEASE_FOR_DOWNLOAD && LL_USE_SMARTHEAP
1183 MemSetErrorHandler(first_mem_error_handler);
1184#endif // LL_WINDOWS && LL_RELEASE_FOR_DOWNLOAD && LL_USE_SMARTHEAP
1185
1186 gViewerStats = new LLViewerStats();
1187
1188 //
1189 // Initialize the VFS, and gracefully handle initialization errors
1190 //
1191
1192 if (!initCache())
1193 {
1194 std::ostringstream msg;
1195 msg <<
1196 gSecondLife << " is unable to access a file that it needs.\n"
1197 "\n"
1198 "This can be because you somehow have multiple copies running, "
1199 "or your system incorrectly thinks a file is open. "
1200 "If this message persists, restart your computer and try again. "
1201 "If it continues to persist, you may need to completely uninstall " <<
1202 gSecondLife << " and reinstall it.";
1203 OSMessageBox(
1204 msg.str().c_str(),
1205 NULL,
1206 OSMB_OK);
1207 return 1;
1208 }
1209
1210#if LL_DARWIN
1211 // Display the release notes for the current version
1212 if(!gHideLinks && gCurrentVersion != gLastRunVersion)
1213 {
1214 // Current version and last run version don't match exactly. Display the release notes.
1215 DisplayReleaseNotes();
1216 }
1217#endif
1218
1219 //
1220 // Initialize the window
1221 //
1222 initWindow();
1223
1224 #if LL_WINDOWS && LL_LCD_COMPILE
1225 // start up an LCD window on a logitech keyboard, if there is one
1226 HINSTANCE hInstance = GetModuleHandle(NULL);
1227 gLcdScreen = new llLCD(hInstance);
1228 CreateLCDDebugWindows();
1229 #endif
1230
1231 gGLManager.getGLInfo(gDebugInfo);
1232 llinfos << gGLManager.getGLInfoString() << llendl;
1233
1234 //load key settings
1235 bind_keyboard_functions();
1236
1237 // Load Default bindings
1238 if (!gViewerKeyboard.loadBindings(gDirUtilp->getExpandedFilename(LL_PATH_APP_SETTINGS,"keys.ini").c_str()))
1239 {
1240 llerrs << "Unable to open keys.ini" << llendl;
1241 }
1242 // Load Custom bindings (override defaults)
1243 gViewerKeyboard.loadBindings(gDirUtilp->getExpandedFilename(LL_PATH_APP_SETTINGS,"custom_keys.ini").c_str());
1244
1245 // Calculate the digest for the executable (takes < 90ms on a fast machine).
1246 FILE* app_file = LLFile::fopen( gDirUtilp->getExecutablePathAndName().c_str(), "rb" ); /* Flawfinder: ignore */
1247 if( app_file )
1248 {
1249 LLMD5 app_md5;
1250 app_md5.update( app_file ); // Automatically closes the file
1251 app_md5.finalize();
1252 app_md5.raw_digest( gViewerDigest.mData );
1253 }
1254 llinfos << "Viewer Digest: " << gViewerDigest << llendl;
1255
1256 // If we don't have the right GL requirements, exit.
1257 // BUG: This should just be changed to a generic GL "Not good enough" flag
1258 if (!gGLManager.mHasMultitexture && !gNoRender)
1259 {
1260 std::ostringstream msg;
1261 msg <<
1262 "You do not appear to have the proper hardware requirements "
1263 "for " << gSecondLife << ". " << gSecondLife << " requires an OpenGL graphics "
1264 "card that has multitexture support. If this is the case, "
1265 "you may want to make sure that you have the latest drivers for "
1266 "your graphics card, and service packs and patches for your "
1267 "operating system.\n"
1268 "If you continue to have problems, please go to: "
1269 "www.secondlife.com/support ";
1270 OSMessageBox(
1271 msg.str().c_str(),
1272 NULL,
1273 OSMB_OK);
1274 return 0;
1275 }
1276
1277 // Save the current version to the prefs file
1278 gSavedSettings.setString("LastRunVersion", gCurrentVersion);
1279
1280 gSimLastTime = gRenderStartTime.getElapsedTimeF32();
1281 gSimFrames = (F32)gFrameCount;
1282
1283 return true;
1284}
1285
1286bool LLAppViewer::mainLoop()
1287{
1288 //-------------------------------------------
1289 // Run main loop until time to quit
1290 //-------------------------------------------
1291
1292 // Create IO Pump to use for HTTP Requests.
1293 gServicePump = new LLPumpIO(gAPRPoolp);
1294 LLHTTPClient::setPump(*gServicePump);
1295 LLHTTPClient::setCABundle(gDirUtilp->getCAFile());
1296
1297 // initialize voice stuff here
1298 gLocalSpeakerMgr = new LLLocalSpeakerMgr();
1299 gActiveChannelSpeakerMgr = new LLActiveSpeakerMgr();
1300
1301 LLVoiceChannel::initClass();
1302 LLVoiceClient::init(gServicePump);
1303
1304 LLMemType mt1(LLMemType::MTYPE_MAIN);
1305 LLTimer frameTimer,idleTimer;
1306 LLTimer debugTime;
1307
1308 // Handle messages
1309 while (!LLApp::isExiting())
1310 {
1311 LLFastTimer::reset(); // Should be outside of any timer instances
1312 {
1313 LLFastTimer t(LLFastTimer::FTM_FRAME);
1314
1315 {
1316 LLFastTimer t2(LLFastTimer::FTM_MESSAGES);
1317 #if LL_WINDOWS
1318 if (!LLWinDebug::setupExceptionHandler())
1319 {
1320 llwarns << " Someone took over my exception handler (post messagehandling)!" << llendl;
1321 }
1322 #endif
1323
1324 gViewerWindow->mWindow->gatherInput();
1325 }
1326
1327#if 1 && !RELEASE_FOR_DOWNLOAD
1328 // once per second debug info
1329 if (debugTime.getElapsedTimeF32() > 1.f)
1330 {
1331 debugTime.reset();
1332 }
1333#endif
1334 if (!LLApp::isExiting())
1335 {
1336 // Scan keyboard for movement keys. Command keys and typing
1337 // are handled by windows callbacks. Don't do this until we're
1338 // done initializing. JC
1339 if (gViewerWindow->mWindow->getVisible()
1340 && gViewerWindow->getActive()
1341 && !gViewerWindow->mWindow->getMinimized()
1342 && LLStartUp::getStartupState() == STATE_STARTED
1343 && !gViewerWindow->getShowProgress()
1344 && !gFocusMgr.focusLocked())
1345 {
1346 gKeyboard->scanKeyboard();
1347 LLViewerJoystick::scanJoystick();
1348 }
1349
1350 // Update state based on messages, user input, object idle.
1351 {
1352 LLFastTimer t3(LLFastTimer::FTM_IDLE);
1353 idle();
1354 LLCurl::process();
1355 // this pump is necessary to make the login screen show up
1356 gServicePump->pump();
1357 gServicePump->callback();
1358 }
1359
1360 if (gDoDisconnect && (LLStartUp::getStartupState() == STATE_STARTED))
1361 {
1362 saveFinalSnapshot();
1363 disconnectViewer();
1364 }
1365
1366 // Render scene.
1367 if (!LLApp::isExiting())
1368 {
1369 display();
1370
1371 LLFloaterSnapshot::update(); // take snapshots
1372
1373#if !LL_SOLARIS
1374 if (gbCapturing)
1375 {
1376 gMovieMaker.Snap();
1377 }
1378#endif
1379#if LL_WINDOWS && LL_LCD_COMPILE
1380 // update LCD Screen
1381 gLcdScreen->UpdateDisplay();
1382#endif
1383 }
1384
1385 }
1386
1387 // Sleep and run background threads
1388 {
1389 LLFastTimer t2(LLFastTimer::FTM_SLEEP);
1390 bool run_multiple_threads = gSavedSettings.getBOOL("RunMultipleThreads");
1391
1392 // yield some time to the os based on command line option
1393 if(gYieldTime)
1394 {
1395 ms_sleep(gYieldMS);
1396 }
1397
1398 // yield cooperatively when not running as foreground window
1399 if ( gNoRender
1400 || !gViewerWindow->mWindow->getVisible()
1401 || !gFocusMgr.getAppHasFocus())
1402 {
1403 // Sleep if we're not rendering, or the window is minimized.
1404 S32 milliseconds_to_sleep = llclamp(gSavedSettings.getS32("BackgroundYieldTime"), 0, 1000);
1405 // don't sleep when BackgroundYieldTime set to 0, since this will still yield to other threads
1406 // of equal priority on Windows
1407 if (milliseconds_to_sleep > 0)
1408 {
1409 ms_sleep(milliseconds_to_sleep);
1410 // also pause worker threads during this wait period
1411 LLAppViewer::getTextureCache()->pause();
1412 LLAppViewer::getImageDecodeThread()->pause();
1413 }
1414 }
1415
1416 if (gRandomizeFramerate)
1417 {
1418 ms_sleep(rand() % 200);
1419 }
1420
1421 if (gPeriodicSlowFrame
1422 && (gFrameCount % 10 == 0))
1423 {
1424 llinfos << "Periodic slow frame - sleeping 500 ms" << llendl;
1425 ms_sleep(500);
1426 }
1427
1428
1429 const F64 min_frame_time = 0.0; //(.0333 - .0010); // max video frame rate = 30 fps
1430 const F64 min_idle_time = 0.0; //(.0010); // min idle time = 1 ms
1431 const F64 max_idle_time = run_multiple_threads ? min_idle_time : .005; // 5 ms
1432 idleTimer.reset();
1433 while(1)
1434 {
1435 S32 work_pending = 0;
1436 S32 io_pending = 0;
1437 work_pending += LLAppViewer::getTextureCache()->update(1); // unpauses the texture cache thread
1438 work_pending += LLAppViewer::getImageDecodeThread()->update(1); // unpauses the image thread
1439 work_pending += LLAppViewer::getTextureFetch()->update(1); // unpauses the texture fetch thread
1440 io_pending += LLVFSThread::updateClass(1);
1441 io_pending += LLLFSThread::updateClass(1);
1442 if (io_pending > 1000)
1443 {
1444 ms_sleep(llmin(io_pending/100,100)); // give the vfs some time to catch up
1445 }
1446
1447 F64 frame_time = frameTimer.getElapsedTimeF64();
1448 F64 idle_time = idleTimer.getElapsedTimeF64();
1449 if (frame_time >= min_frame_time &&
1450 idle_time >= min_idle_time &&
1451 (!work_pending || idle_time >= max_idle_time))
1452 {
1453 break;
1454 }
1455 }
1456 frameTimer.reset();
1457
1458 // Prevent the worker threads from running while rendering.
1459 // if (LLThread::processorCount()==1) //pause() should only be required when on a single processor client...
1460 if (run_multiple_threads == FALSE)
1461 {
1462 LLAppViewer::getTextureCache()->pause();
1463 LLAppViewer::getImageDecodeThread()->pause();
1464 // LLAppViewer::getTextureFetch()->pause(); // Don't pause the fetch (IO) thread
1465 }
1466 //LLVFSThread::sLocal->pause(); // Prevent the VFS thread from running while rendering.
1467 //LLLFSThread::sLocal->pause(); // Prevent the LFS thread from running while rendering.
1468 }
1469
1470 }
1471 }
1472
1473 // Save snapshot for next time, if we made it through initialization
1474 if (STATE_STARTED == LLStartUp::getStartupState())
1475 {
1476 saveFinalSnapshot();
1477 }
1478
1479 delete gServicePump;
1480
1481 llinfos << "Exiting main_loop" << llendflush;
1482
1483 return true;
1484}
1485
1486bool LLAppViewer::cleanup()
1487{
1488 //flag all elements as needing to be destroyed immediately
1489 // to ensure shutdown order
1490 LLMortician::setZealous(TRUE);
1491
1492 LLVoiceClient::terminate();
1493
1494 disconnectViewer();
1495
1496 llinfos << "Viewer disconnected" << llendflush;
1497
1498 display_cleanup();
1499
1500 release_start_screen(); // just in case
1501
1502 LLError::logToFixedBuffer(NULL);
1503
1504 llinfos << "Cleaning Up" << llendflush;
1505
1506 LLKeyframeDataCache::clear();
1507
1508 // Must clean up texture references before viewer window is destroyed.
1509 LLHUDObject::cleanupHUDObjects();
1510 llinfos << "HUD Objects cleaned up" << llendflush;
1511
1512 // End TransferManager before deleting systems it depends on (Audio, VFS, AssetStorage)
1513#if 0 // this seems to get us stuck in an infinite loop...
1514 gTransferManager.cleanup();
1515#endif
1516
1517 // Clean up map data storage
1518 delete gWorldMap;
1519 gWorldMap = NULL;
1520
1521 delete gHUDManager;
1522 gHUDManager = NULL;
1523
1524 delete gToolMgr;
1525 gToolMgr = NULL;
1526
1527 delete gAssetStorage;
1528 gAssetStorage = NULL;
1529
1530 LLPolyMesh::freeAllMeshes();
1531
1532 delete gCacheName;
1533 gCacheName = NULL;
1534
1535 delete gGlobalEconomy;
1536 gGlobalEconomy = NULL;
1537
1538 delete gLocalSpeakerMgr;
1539 gLocalSpeakerMgr = NULL;
1540
1541 LLNotifyBox::cleanup();
1542
1543 llinfos << "Global stuff deleted" << llendflush;
1544
1545#if !LL_RELEASE_FOR_DOWNLOAD
1546 if (gAudiop)
1547 {
1548 gAudiop->shutdown();
1549 }
1550#else
1551 // This hack exists because fmod likes to occasionally hang forever
1552 // when shutting down for no apparent reason.
1553 llwarns << "Hack, skipping audio engine cleanup" << llendflush;
1554#endif
1555
1556
1557 // moved to main application shutdown for now because it's non-trivial and only needs to be done once
1558 // (even though it goes against the media framework design)
1559
1560 LLMediaEngine::cleanupClass();
1561
1562#if LL_QUICKTIME_ENABLED
1563 if (gQuickTimeInitialized)
1564 {
1565 // clean up media stuff
1566 llinfos << "Cleaning up QuickTime" << llendl;
1567 ExitMovies ();
1568 #if LL_WINDOWS
1569 // Only necessary/available on Windows.
1570 TerminateQTML ();
1571 #endif
1572 }
1573 llinfos << "Quicktime cleaned up" << llendflush;
1574#endif
1575
1576#if LL_GSTREAMER_ENABLED
1577 llinfos << "Cleaning up GStreamer" << llendl;
1578 UnloadGStreamer();
1579 llinfos << "GStreamer cleaned up" << llendflush;
1580#endif
1581
1582 llinfos << "Cleaning up feature manager" << llendflush;
1583 delete gFeatureManagerp;
1584 gFeatureManagerp = NULL;
1585
1586 // Patch up settings for next time
1587 // Must do this before we delete the viewer window,
1588 // such that we can suck rectangle information out of
1589 // it.
1590 cleanupSavedSettings();
1591 llinfos << "Settings patched up" << llendflush;
1592
1593 delete gAudiop;
1594 gAudiop = NULL;
1595
1596 // delete some of the files left around in the cache.
1597 removeCacheFiles("*.wav");
1598 removeCacheFiles("*.tmp");
1599 removeCacheFiles("*.lso");
1600 removeCacheFiles("*.out");
1601 removeCacheFiles("*.dsf");
1602 removeCacheFiles("*.bodypart");
1603 removeCacheFiles("*.clothing");
1604
1605 llinfos << "Cache files removed" << llendflush;
1606
1607
1608 cleanup_menus();
1609
1610 // Wait for any pending VFS IO
1611 while (1)
1612 {
1613 S32 pending = LLVFSThread::updateClass(0);
1614 pending += LLLFSThread::updateClass(0);
1615 if (!pending)
1616 {
1617 break;
1618 }
1619 llinfos << "Waiting for pending IO to finish: " << pending << llendflush;
1620 ms_sleep(100);
1621 }
1622 llinfos << "Shutting down." << llendflush;
1623
1624 // Destroy Windows(R) window, and make sure we're not fullscreen
1625 // This may generate window reshape and activation events.
1626 // Therefore must do this before destroying the message system.
1627 delete gViewerWindow;
1628 gViewerWindow = NULL;
1629 llinfos << "ViewerWindow deleted" << llendflush;
1630
1631 // viewer UI relies on keyboard so keep it aound until viewer UI isa gone
1632 delete gKeyboard;
1633 gKeyboard = NULL;
1634
1635 // Clean up selection managers after UI is destroyed, as UI
1636 // may be observing them.
1637 LLSelectMgr::cleanupGlobals();
1638
1639 LLViewerObject::cleanupVOClasses();
1640
1641 LLTracker::cleanupInstance();
1642
1643#if LL_LIBXUL_ENABLED
1644 // this must be done after floater cleanup (delete gViewerWindow) since
1645 // floaters potentially need the manager to destroy their contents.
1646 LLMozLib::getInstance()->reset();
1647#endif
1648
1649 // *FIX: This is handled in LLAppViewerWin32::cleanup().
1650 // I'm keeping the comment to remember its order in cleanup,
1651 // in case of unforseen dependency.
1652//#if LL_WINDOWS
1653// gDXHardware.cleanup();
1654//#endif // LL_WINDOWS
1655
1656#if LL_WINDOWS && LL_LCD_COMPILE
1657 // shut down the LCD window on a logitech keyboard, if there is one
1658 delete gLcdScreen;
1659 gLcdScreen = NULL;
1660#endif
1661
1662 if (!gVolumeMgr->cleanup())
1663 {
1664 llwarns << "Remaining references in the volume manager!" << llendflush;
1665 }
1666
1667 LLViewerParcelMgr::cleanupGlobals();
1668
1669 delete gViewerStats;
1670 gViewerStats = NULL;
1671
1672 //end_messaging_system();
1673
1674 LLFollowCamMgr::cleanupClass();
1675 LLVolumeMgr::cleanupClass();
1676 LLWorldMapView::cleanupClass();
1677 LLUI::cleanupClass();
1678
1679 //
1680 // Shut down the VFS's AFTER the decode manager cleans up (since it cleans up vfiles).
1681 // Also after viewerwindow is deleted, since it may have image pointers (which have vfiles)
1682 // Also after shutting down the messaging system since it has VFS dependencies
1683 //
1684 LLVFile::cleanupClass();
1685 llinfos << "VFS cleaned up" << llendflush;
1686
1687 // Store the time of our current logoff
1688 gSavedPerAccountSettings.setU32("LastLogoff", time_corrected());
1689
1690 // Must do this after all panels have been deleted because panels that have persistent rects
1691 // save their rects on delete.
1692 gSavedSettings.saveToFile(gSettingsFileName, TRUE);
1693 if (!gPerAccountSettingsFileName.empty())
1694 {
1695 gSavedPerAccountSettings.saveToFile(gPerAccountSettingsFileName, TRUE);
1696 }
1697 llinfos << "Saved settings" << llendflush;
1698
1699 std::string crash_settings_filename = gDirUtilp->getExpandedFilename(LL_PATH_USER_SETTINGS, CRASH_SETTINGS_FILE);
1700 // save all settings, even if equals defaults
1701 gCrashSettings.saveToFile(crash_settings_filename.c_str(), FALSE);
1702
1703 delete gUICtrlFactory;
1704 gUICtrlFactory = NULL;
1705
1706 gSavedSettings.cleanup();
1707 gViewerArt.cleanup();
1708 gColors.cleanup();
1709 gCrashSettings.cleanup();
1710
1711 if (gMuteListp)
1712 {
1713 // save mute list
1714 gMuteListp->cache(gAgent.getID());
1715
1716 delete gMuteListp;
1717 gMuteListp = NULL;
1718 }
1719
1720 if (mPurgeOnExit)
1721 {
1722 llinfos << "Purging all cache files on exit" << llendflush;
1723 char mask[LL_MAX_PATH]; /* Flawfinder: ignore */
1724 snprintf(mask, LL_MAX_PATH, "%s*.*", gDirUtilp->getDirDelimiter().c_str()); /* Flawfinder: ignore */
1725 gDirUtilp->deleteFilesInDir(gDirUtilp->getExpandedFilename(LL_PATH_CACHE,"").c_str(),mask);
1726 }
1727
1728 removeMarkerFile(); // Any crashes from here on we'll just have to ignore
1729
1730 closeDebug();
1731
1732 // Let threads finish
1733 LLTimer idleTimer;
1734 idleTimer.reset();
1735 const F64 max_idle_time = 5.f; // 5 seconds
1736 while(1)
1737 {
1738 S32 pending = 0;
1739 pending += LLAppViewer::getTextureCache()->update(1); // unpauses the worker thread
1740 pending += LLAppViewer::getImageDecodeThread()->update(1); // unpauses the image thread
1741 pending += LLAppViewer::getTextureFetch()->update(1); // unpauses the texture fetch thread
1742 pending += LLVFSThread::updateClass(0);
1743 pending += LLLFSThread::updateClass(0);
1744 F64 idle_time = idleTimer.getElapsedTimeF64();
1745 if (!pending || idle_time >= max_idle_time)
1746 {
1747 llwarns << "Quitting with pending background tasks." << llendl;
1748 break;
1749 }
1750 }
1751
1752 // Delete workers first
1753 // shotdown all worker threads before deleting them in case of co-dependencies
1754 sTextureCache->shutdown();
1755 sTextureFetch->shutdown();
1756 sImageDecodeThread->shutdown();
1757 delete sTextureCache;
1758 sTextureCache = NULL;
1759 delete sTextureFetch;
1760 sTextureFetch = NULL;
1761 delete sImageDecodeThread;
1762 sImageDecodeThread = NULL;
1763
1764 gImageList.shutdown(); // shutdown again in case a callback added something
1765
1766 // This should eventually be done in LLAppViewer
1767 LLImageJ2C::closeDSO();
1768 LLImageFormatted::cleanupClass();
1769 LLVFSThread::cleanupClass();
1770 LLLFSThread::cleanupClass();
1771
1772 llinfos << "VFS Thread finished" << llendflush;
1773
1774#ifndef LL_RELEASE_FOR_DOWNLOAD
1775 llinfos << "Auditing VFS" << llendl;
1776 gVFS->audit();
1777#endif
1778
1779 // For safety, the LLVFS has to be deleted *after* LLVFSThread. This should be cleaned up.
1780 // (LLVFS doesn't know about LLVFSThread so can't kill pending requests) -Steve
1781 delete gStaticVFS;
1782 gStaticVFS = NULL;
1783 delete gVFS;
1784 gVFS = NULL;
1785
1786 end_messaging_system();
1787
1788 // *NOTE:Mani - The following call is not thread safe.
1789 LLCurl::cleanup();
1790
1791 // If we're exiting to launch an URL, do that here so the screen
1792 // is at the right resolution before we launch IE.
1793 if (!gLaunchFileOnQuit.empty())
1794 {
1795#if LL_WINDOWS
1796 // Indicate an application is starting.
1797 SetCursor(LoadCursor(NULL, IDC_WAIT));
1798#endif
1799
1800 // HACK: Attempt to wait until the screen res. switch is complete.
1801 ms_sleep(1000);
1802
1803 LLWeb::loadURLExternal( gLaunchFileOnQuit );
1804 }
1805
1806
1807 llinfos << "Goodbye" << llendflush;
1808 // return 0;
1809 return true;
1810}
1811
1812bool LLAppViewer::initEarlyConfiguration()
1813{
1814 // *FIX: globals - This method sets a bunch of globals early in the init process.
1815 int argc = gTempArgC;
1816 char** argv = gTempArgV;
1817
1818 // HACK! We REALLY want to know what grid they were trying to connect to if they
1819 // crashed hard.
1820 // So we walk through the command line args ONLY looking for the
1821 // userserver arguments first. And we don't do ANYTHING but set
1822 // the gGridName (which gets passed to the crash reporter).
1823 // We're assuming that they're trying to log into the same grid as last
1824 // time, which seems fairly reasonable.
1825 snprintf(gGridName, MAX_STRING, "%s", gGridInfo[GridDefaultChoice].mName); // Flawfinder: ignore
1826 S32 j;
1827 for (j = 1; j < argc; j++)
1828 {
1829 if (!strcmp(argv[j], "--aditi"))
1830 {
1831 snprintf(gGridName, MAX_STRING, "%s", gGridInfo[GRID_INFO_ADITI].mName); // Flawfinder: ignore
1832 }
1833 else if (!strcmp(argv[j], "--agni"))
1834 {
1835 snprintf(gGridName, MAX_STRING, "%s", gGridInfo[GRID_INFO_AGNI].mName); // Flawfinder: ignore
1836 }
1837 else if (!strcmp(argv[j], "--dmz"))
1838 {
1839 snprintf(gGridName, MAX_STRING, "%s", gGridInfo[GRID_INFO_DMZ].mName); // Flawfinder: ignore
1840 }
1841 else if (!strcmp(argv[j], "--siva"))
1842 {
1843 snprintf(gGridName, MAX_STRING, "%s", gGridInfo[GRID_INFO_SIVA].mName); // Flawfinder: ignore
1844 }
1845 else if (!strcmp(argv[j], "--shakti"))
1846 {
1847 sprintf(gGridName,"%s", gGridInfo[GRID_INFO_SHAKTI].mName);
1848 }
1849 else if (!strcmp(argv[j], "--durga"))
1850 {
1851 snprintf(gGridName, MAX_STRING, "%s", gGridInfo[GRID_INFO_DURGA].mName); // Flawfinder: ignore
1852 }
1853 else if (!strcmp(argv[j], "--soma"))
1854 {
1855 snprintf(gGridName, MAX_STRING, "%s", gGridInfo[GRID_INFO_SOMA].mName); // Flawfinder: ignore
1856 }
1857 else if (!strcmp(argv[j], "--ganga"))
1858 {
1859 snprintf(gGridName, MAX_STRING, "%s", gGridInfo[GRID_INFO_GANGA].mName); // Flawfinder: ignore
1860 }
1861 else if (!strcmp(argv[j], "--vaak"))
1862 {
1863 sprintf(gGridName,"%s", gGridInfo[GRID_INFO_VAAK].mName);
1864 }
1865 else if (!strcmp(argv[j], "--uma"))
1866 {
1867 sprintf(gGridName,"%s", gGridInfo[GRID_INFO_UMA].mName);
1868 }
1869 else if (!strcmp(argv[j], "-user") && (++j < argc))
1870 {
1871 if (!strcmp(argv[j], "-"))
1872 {
1873 snprintf(gGridName, MAX_STRING, "%s", LOOPBACK_ADDRESS_STRING); // Flawfinder: ignore
1874 }
1875 else
1876 {
1877 snprintf(gGridName, MAX_STRING, "%s", argv[j]); // Flawfinder: ignore
1878 }
1879 }
1880 else if (!strcmp(argv[j], "-multiple"))
1881 {
1882 // Hack to detect -multiple so we can disable the marker file check (which will always fail)
1883 gMultipleViewersOK = TRUE;
1884 }
1885 else if (!strcmp(argv[j], "-novoice"))
1886 {
1887 // May need to know this early also
1888 gDisableVoice = TRUE;
1889 }
1890 else if (!strcmp(argv[j], "-url") && (++j < argc))
1891 {
1892 LLURLSimString::setString(argv[j]);
1893 }
1894 }
1895
1896 return true;
1897}
1898
1899bool LLAppViewer::initThreads()
1900{
1901#if MEM_TRACK_MEM
1902 static const bool enable_threads = false;
1903#else
1904 static const bool enable_threads = true;
1905#endif
1906 LLVFSThread::initClass(enable_threads && true);
1907 LLLFSThread::initClass(enable_threads && true);
1908
1909 // Image decoding
1910 LLAppViewer::sImageDecodeThread = new LLWorkerThread("ImageDecode", enable_threads && true);
1911 LLAppViewer::sTextureCache = new LLTextureCache(enable_threads && true);
1912 LLAppViewer::sTextureFetch = new LLTextureFetch(LLAppViewer::getTextureCache(), enable_threads && false);
1913 LLImageWorker::initClass(LLAppViewer::getImageDecodeThread());
1914 LLImageJ2C::openDSO();
1915
1916 // *FIX: no error handling here!
1917 return true;
1918}
1919
1920void errorCallback(const std::string &error_string)
1921{
1922#ifndef LL_RELEASE_FOR_DOWNLOAD
1923 OSMessageBox(error_string.c_str(), "Fatal Error", OSMB_OK);
1924#endif
1925 LLError::crashAndLoop(error_string);
1926}
1927
1928bool LLAppViewer::initLogging()
1929{
1930 //
1931 // Set up logging defaults for the viewer
1932 //
1933 LLError::initForApplication(
1934 gDirUtilp->getExpandedFilename(LL_PATH_APP_SETTINGS, ""));
1935 LLError::setFatalFunction(errorCallback);
1936
1937 // Remove the last ".old" log file.
1938 std::string old_log_file = gDirUtilp->getExpandedFilename(LL_PATH_LOGS,
1939 "SecondLife.old");
1940 LLFile::remove(old_log_file.c_str());
1941
1942 // Rename current log file to ".old"
1943 std::string log_file = gDirUtilp->getExpandedFilename(LL_PATH_LOGS,
1944 "SecondLife.log");
1945 LLFile::rename(log_file.c_str(), old_log_file.c_str());
1946
1947 // Set the log file to SecondLife.log
1948
1949 LLError::logToFile(log_file);
1950
1951 // *FIX:Mani no error handling here!
1952 return true;
1953}
1954
1955bool LLAppViewer::initConfiguration()
1956{
1957 // Ye olde parse_args()...
1958 if(!doConfigFromCommandLine())
1959 {
1960 return false;
1961 }
1962
1963 // XUI:translate
1964 gSecondLife = "Second Life";
1965
1966 // Read skin/branding settings if specified.
1967 if (! gDirUtilp->getSkinDir().empty() )
1968 {
1969 std::string skin_def_file = gDirUtilp->getExpandedFilename(LL_PATH_TOP_SKIN, "skin.xml");
1970 LLXmlTree skin_def_tree;
1971
1972 if (!skin_def_tree.parseFile(skin_def_file))
1973 {
1974 llerrs << "Failed to parse skin definition." << llendl;
1975 }
1976
1977 LLXmlTreeNode* rootp = skin_def_tree.getRoot();
1978 LLXmlTreeNode* disabled_message_node = rootp->getChildByName("disabled_message");
1979 if (disabled_message_node)
1980 {
1981 gDisabledMessage = disabled_message_node->getContents();
1982 }
1983
1984 static LLStdStringHandle hide_links_string = LLXmlTree::addAttributeString("hide_links");
1985 rootp->getFastAttributeBOOL(hide_links_string, gHideLinks);
1986
1987 // Legacy string. This flag really meant we didn't want to expose references to "Second Life".
1988 // Just set gHideLinks instead.
1989 static LLStdStringHandle silent_string = LLXmlTree::addAttributeString("silent_update");
1990 BOOL silent_update;
1991 rootp->getFastAttributeBOOL(silent_string, silent_update);
1992 gHideLinks = (gHideLinks || silent_update);
1993 }
1994
1995#if LL_DARWIN
1996 // Initialize apple menubar and various callbacks
1997 init_apple_menu(gSecondLife.c_str());
1998
1999#if __ppc__
2000 // If the CPU doesn't have Altivec (i.e. it's not at least a G4), don't go any further.
2001 // Only test PowerPC - all Intel Macs have SSE.
2002 if(!gSysCPU.hasAltivec())
2003 {
2004 std::ostringstream msg;
2005 msg << gSecondLife << " requires a processor with AltiVec (G4 or later).";
2006 OSMessageBox(
2007 msg.str().c_str(),
2008 NULL,
2009 OSMB_OK);
2010 removeMarkerFile();
2011 return false;
2012 }
2013#endif
2014
2015#endif // LL_DARWIN
2016
2017 // Display splash screen. Must be after above check for previous
2018 // crash as this dialog is always frontmost.
2019 std::ostringstream splash_msg;
2020 splash_msg << "Loading " << gSecondLife << "...";
2021 LLSplashScreen::show();
2022 LLSplashScreen::update(splash_msg.str().c_str());
2023
2024 LLVolumeMgr::initClass();
2025
2026 // Initialize the feature manager
2027 // The feature manager is responsible for determining what features
2028 // are turned on/off in the app.
2029 gFeatureManagerp = new LLFeatureManager;
2030
2031 gStartTime = totalTime();
2032
2033 ////////////////////////////////////////
2034 //
2035 // Process ini files
2036 //
2037
2038 // declare all possible setting variables
2039 declare_settings();
2040
2041#if !LL_RELEASE_FOR_DOWNLOAD
2042// only write the defaults for non-release builds!
2043 gSavedSettings.saveToFile(gDirUtilp->getExpandedFilename(LL_PATH_USER_SETTINGS,"settings_default.xml").c_str(), FALSE);
2044#endif
2045
2046 //
2047 // Set the name of the window
2048 //
2049#if LL_RELEASE_FOR_DOWNLOAD
2050 gWindowTitle = gSecondLife;
2051#elif LL_DEBUG
2052 gWindowTitle = gSecondLife + LLString(" [DEBUG] ") + gArgs;
2053#else
2054 gWindowTitle = gSecondLife + LLString(" ") + gArgs;
2055#endif
2056 LLString::truncate(gWindowTitle, 255);
2057
2058 if (!gMultipleViewersOK)
2059 {
2060 //
2061 // Check for another instance of the app running
2062 //
2063 //RN: if we received a URL, hand it off to the existing instance
2064 // don't call anotherInstanceRunning() when doing URL handoff, as
2065 // it relies on checking a marker file which will not work when running
2066 // out of different directories
2067 std::string slurl;
2068 if (!LLStartUp::sSLURLCommand.empty())
2069 {
2070 slurl = LLStartUp::sSLURLCommand;
2071 }
2072 else if (LLURLSimString::parse())
2073 {
2074 slurl = LLURLSimString::getURL();
2075 }
2076 if (!slurl.empty())
2077 {
2078 if (send_url_to_other_instance(slurl))
2079 {
2080 // successfully handed off URL to existing instance, exit
2081 return false;
2082 }
2083 }
2084
2085 mSecondInstance = anotherInstanceRunning();
2086
2087 if (mSecondInstance)
2088 {
2089 std::ostringstream msg;
2090 msg <<
2091 gSecondLife << " is already running.\n"
2092 "\n"
2093 "Check your task bar for a minimized copy of the program.\n"
2094 "If this message persists, restart your computer.",
2095 OSMessageBox(
2096 msg.str().c_str(),
2097 NULL,
2098 OSMB_OK);
2099 return false;
2100 }
2101
2102 initMarkerFile();
2103
2104#if LL_SEND_CRASH_REPORTS
2105 if (gLastExecFroze)
2106 {
2107 llinfos << "Last execution froze, requesting to send crash report." << llendl;
2108 //
2109 // Pop up a freeze or crash warning dialog
2110 //
2111 std::ostringstream msg;
2112 msg << gSecondLife
2113 << " appears to have frozen or crashed on the previous run.\n"
2114 << "Would you like to send a crash report?";
2115 std::string alert;
2116 alert = gSecondLife;
2117 alert += " Alert";
2118 S32 choice = OSMessageBox(msg.str().c_str(),
2119 alert.c_str(),
2120 OSMB_YESNO);
2121 if (OSBTN_YES == choice)
2122 {
2123 llinfos << "Sending crash report." << llendl;
2124
2125 removeMarkerFile();
2126#if LL_WINDOWS
2127 std::string exe_path = gDirUtilp->getAppRODataDir();
2128 exe_path += gDirUtilp->getDirDelimiter();
2129 exe_path += "win_crash_logger.exe";
2130
2131 std::string arg_string = "-previous -user ";
2132 arg_string += gGridName;
2133 arg_string += " -name \"";
2134 arg_string += gSecondLife;
2135 arg_string += "\"";
2136 // Spawn crash logger.
2137 // NEEDS to wait until completion, otherwise log files will get smashed.
2138 _spawnl(_P_WAIT, exe_path.c_str(), exe_path.c_str(), arg_string.c_str(), NULL);
2139#elif LL_DARWIN
2140 std::string command_str;
2141 command_str = "crashreporter.app/Contents/MacOS/crashreporter ";
2142 command_str += "-previous -user ";
2143 command_str += gGridName;
2144 // XXX -- We need to exit fullscreen mode for this to work.
2145 // XXX -- system() also doesn't wait for completion. Hmm...
2146 system(command_str.c_str()); /* Flawfinder: Ignore */
2147#elif LL_LINUX || LL_SOLARIS
2148 std::string cmd =gDirUtilp->getAppRODataDir();
2149 cmd += gDirUtilp->getDirDelimiter();
2150#if LL_LINUX
2151 cmd += "linux-crash-logger.bin";
2152#else // LL_SOLARIS
2153 cmd += "bin/solaris-crash-logger";
2154#endif
2155 char* const cmdargv[] =
2156 {(char*)cmd.c_str(),
2157 (char*)"-previous",
2158 (char*)"-user",
2159 (char*)gGridName,
2160 (char*)"-name",
2161 (char*)gSecondLife.c_str(),
2162 NULL};
2163 pid_t pid = fork();
2164 if (pid == 0)
2165 { // child
2166 execv(cmd.c_str(), cmdargv); /* Flawfinder: Ignore */
2167 llwarns << "execv failure when trying to start " << cmd << llendl;
2168 _exit(1); // avoid atexit()
2169 } else {
2170 if (pid > 0)
2171 {
2172 // wait for child proc to die
2173 int childExitStatus;
2174 waitpid(pid, &childExitStatus, 0);
2175 } else {
2176 llwarns << "fork failure." << llendl;
2177 }
2178 }
2179#endif
2180 }
2181 else
2182 {
2183 llinfos << "Not sending crash report." << llendl;
2184 }
2185 }
2186#endif // #if LL_SEND_CRASH_REPORTS
2187 }
2188 else
2189 {
2190 mSecondInstance = anotherInstanceRunning();
2191
2192 if (mSecondInstance)
2193 {
2194 gDisableVoice = TRUE;
2195 /* Don't start another instance if using -multiple
2196 //RN: if we received a URL, hand it off to the existing instance
2197 if (LLURLSimString::parse())
2198 {
2199 LLURLSimString::send_to_other_instance();
2200 return 1;
2201 }
2202 */
2203 }
2204
2205 initMarkerFile();
2206 }
2207
2208 return true; // Config was successful.
2209}
2210
2211bool LLAppViewer::doConfigFromCommandLine()
2212{
2213 // *FIX: This is what parse args used to do, minus the arg reading part.
2214 // Now the arg parsing is handled by LLApp::parseCommandOptions() and this
2215 // method need only interpret settings. Perhaps some day interested parties
2216 // can ask an app about a setting rather than have the app set
2217 // a gazzillion globals.
2218
2219 /////////////////////////////////////////
2220 //
2221 // Process command line arguments
2222 //
2223 S32 args_result = 0;
2224
2225#if LL_DARWIN
2226 {
2227 // On the Mac, read in arguments.txt (if it exists) and process it for additional arguments.
2228 LLString args;
2229 if(_read_file_into_string(args, "arguments.txt")) /* Flawfinder: ignore*/
2230 {
2231 // The arguments file exists.
2232 // It should consist of command line arguments separated by newlines.
2233 // Split it into individual arguments and build a fake argv[] to pass to parse_args.
2234 std::vector<std::string> arglist;
2235
2236 arglist.push_back("newview");
2237
2238 llinfos << "Reading additional command line arguments from arguments.txt..." << llendl;
2239
2240 typedef boost::tokenizer<boost::escaped_list_separator<char> > tokenizer;
2241 boost::escaped_list_separator<char> sep("\\", "\r\n ", "\"'");
2242 tokenizer tokens(args, sep);
2243 tokenizer::iterator token_iter;
2244
2245 for(token_iter = tokens.begin(); token_iter != tokens.end(); ++token_iter)
2246 {
2247 llinfos << "argument: '" << (token_iter->c_str()) << "'" << llendl;
2248
2249 arglist.push_back(*token_iter);
2250 }
2251
2252 char **fakeargv = new char*[arglist.size()];
2253 int i;
2254 for(i=0; i < arglist.size(); i++)
2255 fakeargv[i] = const_cast<char*>(arglist[i].c_str());
2256
2257 args_result = parse_args(arglist.size(), fakeargv);
2258 delete[] fakeargv;
2259 }
2260
2261 // Get the user's preferred language string based on the Mac OS localization mechanism.
2262 // To add a new localization:
2263 // go to the "Resources" section of the project
2264 // get info on "language.txt"
2265 // in the "General" tab, click the "Add Localization" button
2266 // create a new localization for the language you're adding
2267 // set the contents of the new localization of the file to the string corresponding to our localization
2268 // (i.e. "en-us", "ja", etc. Use the existing ones as a guide.)
2269 CFURLRef url = CFBundleCopyResourceURL(CFBundleGetMainBundle(), CFSTR("language"), CFSTR("txt"), NULL);
2270 char path[MAX_PATH];
2271 if(CFURLGetFileSystemRepresentation(url, false, (UInt8 *)path, sizeof(path)))
2272 {
2273 LLString lang;
2274 if(_read_file_into_string(lang, path)) /* Flawfinder: ignore*/
2275 {
2276 gCommandLineForcedSettings["SystemLanguage"] = lang;
2277 }
2278 }
2279 CFRelease(url);
2280 }
2281#endif
2282
2283 int argc = gTempArgC;
2284 char** argv = gTempArgV;
2285
2286 //
2287 // Parse the command line arguments
2288 //
2289 args_result |= parse_args(argc, argv);
2290 if (args_result)
2291 {
2292 removeMarkerFile();
2293 return false;
2294 }
2295
2296 if (!strcmp(gGridName, gGridInfo[GRID_INFO_AGNI].mName))
2297 {
2298 gInProductionGrid = TRUE;
2299 }
2300
2301 return true;
2302}
2303
2304bool LLAppViewer::initWindow()
2305{
2306 llinfos << "Initializing window..." << llendl;
2307
2308 // store setting in a global for easy access and modification
2309 gNoRender = gSavedSettings.getBOOL("DisableRendering");
2310
2311 // Hide the splash screen
2312 LLSplashScreen::hide();
2313
2314 // HACK: Need a non-const char * for stupid window name (propagated deep down)
2315 char window_title_str[256]; /* Flawfinder: ignore */
2316 strncpy(window_title_str, gWindowTitle.c_str(), sizeof(window_title_str) - 1); /* Flawfinder: ignore */
2317 window_title_str[sizeof(window_title_str) - 1] = '\0';
2318
2319 // always start windowed
2320 gViewerWindow = new LLViewerWindow(window_title_str, "Second Life",
2321 gSavedSettings.getS32("WindowX"), gSavedSettings.getS32("WindowY"),
2322 gSavedSettings.getS32("WindowWidth"), gSavedSettings.getS32("WindowHeight"),
2323 FALSE, gIgnorePixelDepth);
2324
2325 if (gSavedSettings.getBOOL("FullScreen"))
2326 {
2327 gViewerWindow->toggleFullscreen(FALSE);
2328 // request to go full screen... which will be delayed until login
2329 }
2330
2331 if (gSavedSettings.getBOOL("WindowMaximized"))
2332 {
2333 gViewerWindow->mWindow->maximize();
2334 gViewerWindow->getWindow()->setNativeAspectRatio(gSavedSettings.getF32("FullScreenAspectRatio"));
2335 }
2336
2337 LLUI::sWindow = gViewerWindow->getWindow();
2338
2339 LLAlertDialog::parseAlerts("alerts.xml");
2340 LLNotifyBox::parseNotify("notify.xml");
2341
2342 LLMediaEngine::initClass();
2343
2344 //
2345 // Clean up the feature manager lookup table - settings were updated
2346 // in the LLViewerWindow constructor
2347 //
2348 gFeatureManagerp->cleanupFeatureTables();
2349
2350 // Show watch cursor
2351 gViewerWindow->setCursor(UI_CURSOR_WAIT);
2352
2353 // Finish view initialization
2354 gViewerWindow->initBase();
2355
2356 // show viewer window
2357 gViewerWindow->mWindow->show();
2358
2359 return true;
2360}
2361
2362void LLAppViewer::closeDebug()
2363{
2364 std::string debug_filename = gDirUtilp->getExpandedFilename(LL_PATH_LOGS,"debug_info.log");
2365 llinfos << "Opening debug file " << debug_filename << llendl;
2366 std::ofstream out_file(debug_filename.c_str());
2367 LLSDSerialize::toPrettyXML(gDebugInfo, out_file);
2368 out_file.close();
2369}
2370
2371void LLAppViewer::cleanupSavedSettings()
2372{
2373 gSavedSettings.setBOOL("MouseSun", FALSE);
2374
2375 gSavedSettings.setBOOL("FlyBtnState", FALSE);
2376
2377 gSavedSettings.setBOOL("FirstPersonBtnState", FALSE);
2378 gSavedSettings.setBOOL("ThirdPersonBtnState", TRUE);
2379 gSavedSettings.setBOOL("BuildBtnState", FALSE);
2380
2381 gSavedSettings.setBOOL("UseEnergy", TRUE); // force toggle to turn off, since sends message to simulator
2382
2383 gSavedSettings.setBOOL("DebugWindowProc", gDebugWindowProc);
2384
2385 gSavedSettings.setBOOL("AllowIdleAFK", gAllowIdleAFK);
2386 gSavedSettings.setBOOL("ShowObjectUpdates", gShowObjectUpdates);
2387
2388 if (!gNoRender)
2389 {
2390 if (gDebugView)
2391 {
2392 gSavedSettings.setBOOL("ShowDebugConsole", gDebugView->mDebugConsolep->getVisible());
2393 gSavedSettings.setBOOL("ShowDebugStats", gDebugView->mStatViewp->getVisible());
2394 }
2395 }
2396
2397 // save window position if not fullscreen
2398 // as we don't track it in callbacks
2399 BOOL fullscreen = gViewerWindow->mWindow->getFullscreen();
2400 BOOL maximized = gViewerWindow->mWindow->getMaximized();
2401 if (!fullscreen && !maximized)
2402 {
2403 LLCoordScreen window_pos;
2404
2405 if (gViewerWindow->mWindow->getPosition(&window_pos))
2406 {
2407 gSavedSettings.setS32("WindowX", window_pos.mX);
2408 gSavedSettings.setS32("WindowY", window_pos.mY);
2409 }
2410 }
2411
2412 gSavedSettings.setF32("MapScale", gMapScale );
2413 gSavedSettings.setF32("MiniMapScale", gMiniMapScale );
2414 gSavedSettings.setBOOL("AsyncKeyboard", gHandleKeysAsync);
2415 gSavedSettings.setBOOL("ShowHoverTips", LLHoverView::sShowHoverTips);
2416
2417 // Some things are cached in LLAgent.
2418 if (gAgent.mInitialized)
2419 {
2420 gSavedSettings.setF32("RenderFarClip", gAgent.mDrawDistance);
2421 }
2422
2423 // *REMOVE: This is now done via LLAppViewer::setCrashBehavior()
2424 // Left vestigially in case I borked it.
2425 // gCrashSettings.setS32(CRASH_BEHAVIOR_SETTING, gCrashBehavior);
2426}
2427
2428void LLAppViewer::removeCacheFiles(const char* file_mask)
2429{
2430 char mask[LL_MAX_PATH]; /* Flawfinder: ignore */
2431 snprintf(mask, LL_MAX_PATH, "%s%s", gDirUtilp->getDirDelimiter().c_str(), file_mask); /* Flawfinder: ignore */
2432 gDirUtilp->deleteFilesInDir(gDirUtilp->getExpandedFilename(LL_PATH_CACHE, "").c_str(), mask);
2433}
2434
2435void LLAppViewer::writeSystemInfo()
2436{
2437 gDebugInfo["SLLog"] = LLError::logFileName();
2438
2439 gDebugInfo["ClientInfo"]["Name"] = gSecondLife;
2440 gDebugInfo["ClientInfo"]["MajorVersion"] = LL_VERSION_MAJOR;
2441 gDebugInfo["ClientInfo"]["MinorVersion"] = LL_VERSION_MINOR;
2442 gDebugInfo["ClientInfo"]["PatchVersion"] = LL_VERSION_PATCH;
2443 gDebugInfo["ClientInfo"]["BuildVersion"] = LL_VERSION_BUILD;
2444
2445 gDebugInfo["CPUInfo"]["CPUFamily"] = gSysCPU.getFamily();
2446 gDebugInfo["CPUInfo"]["CPUMhz"] = gSysCPU.getMhz();
2447 gDebugInfo["CPUInfo"]["CPUAltivec"] = gSysCPU.hasAltivec();
2448 gDebugInfo["CPUInfo"]["CPUSSE"] = gSysCPU.hasSSE();
2449 gDebugInfo["CPUInfo"]["CPUSSE2"] = gSysCPU.hasSSE2();
2450
2451 gDebugInfo["RAMInfo"] = llformat("%u", gSysMemory.getPhysicalMemoryKB());
2452 gDebugInfo["OSInfo"] = getOSInfo().getOSStringSimple();
2453
2454 // Dump some debugging info
2455 llinfos << gSecondLife
2456 << " version " << LL_VERSION_MAJOR << "." << LL_VERSION_MINOR << "." << LL_VERSION_PATCH
2457 << llendl;
2458
2459 // Dump the local time and time zone
2460 time_t now;
2461 time(&now);
2462 char tbuffer[256]; /* Flawfinder: ignore */
2463 strftime(tbuffer, 256, "%Y-%m-%dT%H:%M:%S %Z", localtime(&now));
2464 llinfos << "Local time: " << tbuffer << llendl;
2465
2466 // query some system information
2467 llinfos << "CPU info:\n" << gSysCPU << llendl;
2468 llinfos << "Memory info:\n" << gSysMemory << llendl;
2469 llinfos << "OS: " << getOSInfo().getOSStringSimple() << llendl;
2470 llinfos << "OS info: " << getOSInfo() << llendl;
2471}
2472
2473void LLAppViewer::handleViewerCrash()
2474{
2475 LLAppViewer* pApp = LLAppViewer::instance();
2476 if (pApp->beingDebugged())
2477 {
2478 // This will drop us into the debugger.
2479 abort();
2480 }
2481
2482 // Returns whether a dialog was shown.
2483 // Only do the logic in here once
2484 if (pApp->mReportedCrash)
2485 {
2486 return;
2487 }
2488 pApp->mReportedCrash = TRUE;
2489
2490 gDebugInfo["SettingsFilename"] = gSettingsFileName;
2491 gDebugInfo["CAFilename"] = gDirUtilp->getCAFile();
2492 gDebugInfo["ViewerExePath"] = gDirUtilp->getExecutablePathAndName().c_str();
2493 gDebugInfo["CurrentPath"] = gDirUtilp->getCurPath().c_str();
2494 gDebugInfo["CurrentSimHost"] = gAgent.getRegionHost().getHostName();
2495
2496 if (gMessageSystem && gDirUtilp)
2497 {
2498 std::string filename;
2499 filename = gDirUtilp->getExpandedFilename(LL_PATH_LOGS, "stats.log");
2500 llofstream file(filename.c_str(), llofstream::binary);
2501 if(file.good())
2502 {
2503 gMessageSystem->summarizeLogs(file);
2504 file.close();
2505 }
2506 }
2507
2508 if (gMessageSystem)
2509 {
2510 gMessageSystem->getCircuitInfo(gDebugInfo["CircuitInfo"]);
2511 gMessageSystem->stopLogging();
2512 }
2513 if (gWorldp)
2514 {
2515 gWorldp->getInfo(gDebugInfo);
2516 }
2517
2518 // Close the debug file
2519 pApp->closeDebug();
2520 LLError::logToFile("");
2521
2522 // Call to pure virtual, handled by platform specifc llappviewer instance.
2523 pApp->handleCrashReporting();
2524
2525 return;
2526}
2527
2528void LLAppViewer::setCrashBehavior(S32 cb)
2529{
2530 mCrashBehavior = cb;
2531 gCrashSettings.setS32(CRASH_BEHAVIOR_SETTING, mCrashBehavior);
2532}
2533
2534bool LLAppViewer::anotherInstanceRunning()
2535{
2536 // We create a marker file when the program starts and remove the file when it finishes.
2537 // If the file is currently locked, that means another process is already running.
2538
2539 std::string marker_file = gDirUtilp->getExpandedFilename(LL_PATH_LOGS,"SecondLife.exec_marker");
2540 llinfos << "Checking marker file for lock..." << llendl;
2541
2542 // If file doesn't exist, we create it
2543 // If file does exist, try to get writing privilages
2544 FILE* fMarker = LLFile::fopen(marker_file.c_str(), "rb"); // Flawfinder: ignore
2545 if (fMarker != NULL)
2546 {
2547 // File exists, try opening with write permissions
2548 fclose(fMarker);
2549 fMarker = LLFile::fopen(marker_file.c_str(), "wb"); // Flawfinder: ignore
2550 if (fMarker == NULL)
2551 {
2552 llinfos << "Marker file is locked." << llendl;
2553 return TRUE;
2554 }
2555
2556 // *FIX:Mani - rather than have this exception here,
2557 // LLFile::fopen() have consistent behavior across platforms?
2558#if LL_DARWIN
2559 // Try to lock it. On Mac, this is the only way to test if it's actually locked.
2560 if (flock(fileno(fMarker), LOCK_EX | LOCK_NB) == -1)
2561 {
2562 // Lock failed - somebody else has it.
2563 fclose(fMarker);
2564 llinfos << "Marker file is locked." << llendl;
2565 return TRUE;
2566 }
2567#endif
2568 fclose(fMarker);
2569 }
2570 llinfos << "Marker file isn't locked." << llendl;
2571 return FALSE;
2572
2573}
2574
2575void LLAppViewer::initMarkerFile()
2576{
2577 // *FIX:Mani - an actually cross platform LLFile lib would be nice.
2578
2579#if LL_SOLARIS
2580 struct flock fl;
2581 fl.l_whence = SEEK_SET;
2582 fl.l_start = 0;
2583 fl.l_len = 1;
2584#endif
2585 // We create a marker file when the program starts and remove the file when it finishes.
2586 // If the file is currently locked, that means another process is already running.
2587 // If the file exists and isn't locked, we crashed on the last run.
2588
2589 std::string marker_file = gDirUtilp->getExpandedFilename(LL_PATH_LOGS,"SecondLife.exec_marker");
2590 llinfos << "Checking marker file for lock..." << llendl;
2591
2592 FILE* fMarker = LLFile::fopen(marker_file.c_str(), "rb"); // Flawfinder: ignore
2593 if (fMarker != NULL)
2594 {
2595 // File exists, try opening with write permissions
2596 fclose(fMarker);
2597 fMarker = LLFile::fopen(marker_file.c_str(), "wb"); // Flawfinder: ignxore
2598 if (fMarker == NULL)
2599 {
2600 // Another instance is running. Skip the rest of these operations.
2601 llinfos << "Marker file is locked." << llendl;
2602 return;
2603 }
2604#if LL_DARWIN
2605 // Try to lock it. On Mac, this is the only way to test if it's actually locked.
2606 if (flock(fileno(fMarker), LOCK_EX | LOCK_NB) == -1)
2607 {
2608 // Lock failed - somebody else has it.
2609 fclose(fMarker);
2610 llinfos << "Marker file is locked." << llendl;
2611 return;
2612 }
2613#endif
2614
2615 // No other instances; we'll lock this file now & delete on quit.
2616 fclose(fMarker);
2617 gLastExecFroze = TRUE;
2618 llinfos << "Exec marker found: program froze on previous execution" << llendl;
2619 }
2620
2621 // Create the marker file for this execution & lock it
2622// FILE *fp_executing_marker;
2623#if LL_WINDOWS
2624 mMarkerFile = LLFile::_fsopen(marker_file.c_str(), "w", _SH_DENYWR);
2625#else
2626 mMarkerFile = LLFile::fopen(marker_file.c_str(), "w"); // Flawfinder: ignore
2627 if (mMarkerFile)
2628 {
2629 int fd = fileno(mMarkerFile);
2630 // Attempt to lock
2631#if LL_SOLARIS
2632 fl.l_type = F_WRLCK;
2633 if (fcntl(fd, F_SETLK, &fl) == -1)
2634#else
2635 if (flock(fd, LOCK_EX | LOCK_NB) == -1)
2636#endif
2637 {
2638 llinfos << "Failed to lock file." << llendl;
2639 }
2640 }
2641#endif
2642 if (mMarkerFile)
2643 {
2644 llinfos << "Marker file created." << llendl;
2645 }
2646 else
2647 {
2648 llinfos << "Failed to create marker file." << llendl;
2649 }
2650
2651#if LL_WINDOWS
2652 // Clean up SecondLife.dmp files, to avoid confusion
2653 llinfos << "Removing SecondLife.dmp" << llendl;
2654 std::string dmp_filename = gDirUtilp->getExpandedFilename(LL_PATH_LOGS, "SecondLife.dmp");
2655 LLFile::remove(dmp_filename.c_str());
2656#endif
2657
2658 // This is to keep the crash reporter from constantly sending stale message logs
2659 // We wipe the message file now.
2660 llinfos << "Removing message.log" << llendl;
2661 std::string message_filename = gDirUtilp->getExpandedFilename(LL_PATH_LOGS, "message.log");
2662 LLFile::remove(message_filename.c_str());
2663
2664 llinfos << "Exiting initMarkerFile()." << llendl;
2665}
2666
2667void LLAppViewer::removeMarkerFile()
2668{
2669 llinfos << "removeMarkerFile()" << llendl;
2670 if (mMarkerFile != NULL)
2671 {
2672 fclose(mMarkerFile);
2673 mMarkerFile = NULL;
2674 }
2675 if( gDirUtilp )
2676 {
2677 LLString marker_file = gDirUtilp->getExpandedFilename(LL_PATH_LOGS,"SecondLife.exec_marker");
2678 ll_apr_file_remove( marker_file );
2679 }
2680}
2681
2682void LLAppViewer::forceQuit()
2683{
2684 LLApp::setQuitting();
2685}
2686
2687void LLAppViewer::requestQuit()
2688{
2689 llinfos << "requestQuit" << llendl;
2690
2691 LLViewerRegion* region = gAgent.getRegion();
2692
2693 if( (LLStartUp::getStartupState() < STATE_STARTED) || !region )
2694 {
2695 // Quit immediately
2696 forceQuit();
2697 return;
2698 }
2699
2700 if (gHUDManager)
2701 {
2702 LLHUDEffectSpiral *effectp = (LLHUDEffectSpiral*)gHUDManager->createViewerEffect(LLHUDObject::LL_HUD_EFFECT_POINT, TRUE);
2703 effectp->setPositionGlobal(gAgent.getPositionGlobal());
2704 effectp->setColor(LLColor4U(gAgent.getEffectColor()));
2705 gHUDManager->sendEffects();
2706 }
2707
2708 // Attempt to close all floaters that might be
2709 // editing things.
2710 if (gFloaterView)
2711 {
2712 // application is quitting
2713 gFloaterView->closeAllChildren(true);
2714 }
2715
2716 send_stats();
2717
2718 gLogoutTimer.reset();
2719 mQuitRequested = true;
2720}
2721
2722static void finish_quit(S32 option, void *userdata)
2723{
2724 if (option == 0)
2725 {
2726 LLAppViewer::instance()->requestQuit();
2727 }
2728}
2729
2730void LLAppViewer::userQuit()
2731{
2732 gViewerWindow->alertXml("ConfirmQuit", finish_quit, NULL);
2733}
2734
2735static void finish_early_exit(S32 option, void* userdata)
2736{
2737 LLAppViewer::instance()->forceQuit();
2738}
2739
2740void LLAppViewer::earlyExit(const LLString& msg)
2741{
2742 llwarns << "app_early_exit: " << msg << llendl;
2743 gDoDisconnect = TRUE;
2744// LLStringBase<char>::format_map_t args;
2745// args["[MESSAGE]"] = mesg;
2746// gViewerWindow->alertXml("AppEarlyExit", args, finish_early_exit);
2747 LLAlertDialog::showCritical(msg, finish_early_exit, NULL);
2748}
2749
2750void LLAppViewer::forceExit(S32 arg)
2751{
2752 removeMarkerFile();
2753
2754 // *FIX:Mani - This kind of exit hardly seems appropriate.
2755 exit(arg);
2756}
2757
2758void LLAppViewer::abortQuit()
2759{
2760 llinfos << "abortQuit()" << llendl;
2761 mQuitRequested = false;
2762}
2763
2764bool LLAppViewer::initCache()
2765{
2766 mPurgeCache = false;
2767 // Purge cache if user requested it
2768 if (gSavedSettings.getBOOL("PurgeCacheOnStartup") ||
2769 gSavedSettings.getBOOL("PurgeCacheOnNextStartup"))
2770 {
2771 gSavedSettings.setBOOL("PurgeCacheOnNextStartup", false);
2772 mPurgeCache = true;
2773 }
2774 // Purge cache if it belongs to an old version
2775 else
2776 {
2777 static const S32 cache_version = 5;
2778 if (gSavedSettings.getS32("LocalCacheVersion") != cache_version)
2779 {
2780 mPurgeCache = true;
2781 gSavedSettings.setS32("LocalCacheVersion", cache_version);
2782 }
2783 }
2784
2785 // Setup and verify the cache location
2786 LLString cache_location = gSavedSettings.getString("CacheLocation");
2787 LLString new_cache_location = gSavedSettings.getString("NewCacheLocation");
2788 if (new_cache_location != cache_location)
2789 {
2790 gDirUtilp->setCacheDir(gSavedSettings.getString("CacheLocation"));
2791 purgeCache(); // purge old cache
2792 gSavedSettings.setString("CacheLocation", new_cache_location);
2793 }
2794
2795 if (!gDirUtilp->setCacheDir(gSavedSettings.getString("CacheLocation")))
2796 {
2797 llwarns << "Unable to set cache location" << llendl;
2798 gSavedSettings.setString("CacheLocation", "");
2799 }
2800
2801 if (mPurgeCache)
2802 {
2803 LLSplashScreen::update("Clearing cache...");
2804 purgeCache();
2805 }
2806
2807 LLSplashScreen::update("Initializing Texture Cache...");
2808
2809 // Init the texture cache
2810 // Allocate 80% of the cache size for textures
2811 BOOL read_only = mSecondInstance ? true : false;
2812 const S32 MB = 1024*1024;
2813 S64 cache_size = (S64)(gSavedSettings.getU32("CacheSize")) * MB;
2814 const S64 MAX_CACHE_SIZE = 1024*MB;
2815 cache_size = llmin(cache_size, MAX_CACHE_SIZE);
2816 S64 texture_cache_size = ((cache_size * 8)/10);
2817 S64 extra = LLAppViewer::getTextureCache()->initCache(LL_PATH_CACHE, texture_cache_size, read_only);
2818 texture_cache_size -= extra;
2819
2820 LLSplashScreen::update("Initializing VFS...");
2821
2822 // Init the VFS
2823 S64 vfs_size = cache_size - texture_cache_size;
2824 const S64 MAX_VFS_SIZE = 1024 * MB; // 1 GB
2825 vfs_size = llmin(vfs_size, MAX_VFS_SIZE);
2826 vfs_size = (vfs_size / MB) * MB; // make sure it is MB aligned
2827 U32 vfs_size_u32 = (U32)vfs_size;
2828 U32 old_vfs_size = gSavedSettings.getU32("VFSOldSize") * MB;
2829 bool resize_vfs = (vfs_size_u32 != old_vfs_size);
2830 if (resize_vfs)
2831 {
2832 gSavedSettings.setU32("VFSOldSize", vfs_size_u32/MB);
2833 }
2834 llinfos << "VFS CACHE SIZE: " << vfs_size/(1024*1024) << " MB" << llendl;
2835
2836 // This has to happen BEFORE starting the vfs
2837 //time_t ltime;
2838 srand(time(NULL)); // Flawfinder: ignore
2839 U32 old_salt = gSavedSettings.getU32("VFSSalt");
2840 U32 new_salt;
2841 char old_vfs_data_file[LL_MAX_PATH]; // Flawfinder: ignore
2842 char old_vfs_index_file[LL_MAX_PATH]; // Flawfinder: ignore
2843 char new_vfs_data_file[LL_MAX_PATH]; // Flawfinder: ignore
2844 char new_vfs_index_file[LL_MAX_PATH]; // Flawfinder: ignore
2845 char static_vfs_index_file[LL_MAX_PATH]; // Flawfinder: ignore
2846 char static_vfs_data_file[LL_MAX_PATH]; // Flawfinder: ignore
2847
2848 if (gMultipleViewersOK)
2849 {
2850 // don't mess with renaming the VFS in this case
2851 new_salt = old_salt;
2852 }
2853 else
2854 {
2855 do
2856 {
2857 new_salt = rand();
2858 } while( new_salt == old_salt );
2859 }
2860
2861 snprintf(old_vfs_data_file, LL_MAX_PATH, "%s%u", // Flawfinder: ignore
2862 gDirUtilp->getExpandedFilename(LL_PATH_CACHE,VFS_DATA_FILE_BASE).c_str(),
2863 old_salt);
2864
2865 // make sure this file exists
2866 llstat s;
2867 S32 stat_result = LLFile::stat(old_vfs_data_file, &s);
2868 if (stat_result)
2869 {
2870 // doesn't exist, look for a data file
2871 std::string mask;
2872 mask = gDirUtilp->getDirDelimiter();
2873 mask += VFS_DATA_FILE_BASE;
2874 mask += "*";
2875
2876 std::string dir;
2877 dir = gDirUtilp->getExpandedFilename(LL_PATH_CACHE,"");
2878
2879 std::string found_file;
2880 if (gDirUtilp->getNextFileInDir(dir, mask, found_file, false))
2881 {
2882 snprintf(old_vfs_data_file, LL_MAX_PATH, "%s%s%s", dir.c_str(), gDirUtilp->getDirDelimiter().c_str(), found_file.c_str()); // Flawfinder: ignore
2883
2884 S32 start_pos;
2885 S32 length = strlen(found_file.c_str()); /* Flawfinder: ignore*/
2886 for (start_pos = length - 1; start_pos >= 0; start_pos--)
2887 {
2888 if (found_file[start_pos] == '.')
2889 {
2890 start_pos++;
2891 break;
2892 }
2893 }
2894 if (start_pos > 0)
2895 {
2896 sscanf(found_file.c_str() + start_pos, "%d", &old_salt);
2897 }
2898 llinfos << "Default vfs data file not present, found " << old_vfs_data_file << llendl;
2899 llinfos << "Old salt: " << old_salt << llendl;
2900 }
2901 }
2902
2903 snprintf(old_vfs_index_file, LL_MAX_PATH, "%s%u", // Flawfinder: ignore
2904 gDirUtilp->getExpandedFilename(LL_PATH_CACHE,VFS_INDEX_FILE_BASE).c_str(),
2905 old_salt);
2906
2907 stat_result = LLFile::stat(old_vfs_index_file, &s);
2908 if (stat_result)
2909 {
2910 // We've got a bad/missing index file, nukem!
2911 llwarns << "Bad or missing vfx index file " << old_vfs_index_file << llendl;
2912 llwarns << "Removing old vfs data file " << old_vfs_data_file << llendl;
2913 LLFile::remove(old_vfs_data_file);
2914 LLFile::remove(old_vfs_index_file);
2915
2916 // Just in case, nuke any other old cache files in the directory.
2917 std::string dir;
2918 dir = gDirUtilp->getExpandedFilename(LL_PATH_CACHE,"");
2919
2920 std::string mask;
2921 mask = gDirUtilp->getDirDelimiter();
2922 mask += VFS_DATA_FILE_BASE;
2923 mask += "*";
2924
2925 gDirUtilp->deleteFilesInDir(dir, mask);
2926
2927 mask = gDirUtilp->getDirDelimiter();
2928 mask += VFS_INDEX_FILE_BASE;
2929 mask += "*";
2930
2931 gDirUtilp->deleteFilesInDir(dir, mask);
2932 }
2933
2934 snprintf(new_vfs_data_file, LL_MAX_PATH, "%s%u", // Flawfinder: ignore
2935 gDirUtilp->getExpandedFilename(LL_PATH_CACHE,VFS_DATA_FILE_BASE).c_str(),
2936 new_salt);
2937
2938 snprintf(new_vfs_index_file, LL_MAX_PATH, "%s%u", gDirUtilp->getExpandedFilename(LL_PATH_CACHE, VFS_INDEX_FILE_BASE).c_str(), // Flawfinder: ignore
2939 new_salt);
2940
2941
2942 strncpy(static_vfs_data_file, gDirUtilp->getExpandedFilename(LL_PATH_APP_SETTINGS,"static_data.db2").c_str(), LL_MAX_PATH -1); // Flawfinder: ignore
2943 static_vfs_data_file[LL_MAX_PATH -1] = '\0';
2944 strncpy(static_vfs_index_file, gDirUtilp->getExpandedFilename(LL_PATH_APP_SETTINGS,"static_index.db2").c_str(), LL_MAX_PATH -1); // Flawfinder: ignore
2945 static_vfs_index_file[LL_MAX_PATH -1] = '\0';
2946
2947 if (resize_vfs)
2948 {
2949 llinfos << "Removing old vfs and re-sizing" << llendl;
2950
2951 LLFile::remove(old_vfs_data_file);
2952 LLFile::remove(old_vfs_index_file);
2953 }
2954 else if (old_salt != new_salt)
2955 {
2956 // move the vfs files to a new name before opening
2957 llinfos << "Renaming " << old_vfs_data_file << " to " << new_vfs_data_file << llendl;
2958 llinfos << "Renaming " << old_vfs_index_file << " to " << new_vfs_index_file << llendl;
2959 LLFile::rename(old_vfs_data_file, new_vfs_data_file);
2960 LLFile::rename(old_vfs_index_file, new_vfs_index_file);
2961 }
2962
2963 // Startup the VFS...
2964 gSavedSettings.setU32("VFSSalt", new_salt);
2965
2966 // Don't remove VFS after viewer crashes. If user has corrupt data, they can reinstall. JC
2967 gVFS = new LLVFS(new_vfs_index_file, new_vfs_data_file, false, vfs_size_u32, false);
2968 if( VFSVALID_BAD_CORRUPT == gVFS->getValidState() )
2969 {
2970 // Try again with fresh files
2971 // (The constructor deletes corrupt files when it finds them.)
2972 llwarns << "VFS corrupt, deleted. Making new VFS." << llendl;
2973 delete gVFS;
2974 gVFS = new LLVFS(new_vfs_index_file, new_vfs_data_file, false, vfs_size_u32, false);
2975 }
2976
2977 gStaticVFS = new LLVFS(static_vfs_index_file, static_vfs_data_file, true, 0, false);
2978
2979 BOOL success = gVFS->isValid() && gStaticVFS->isValid();
2980 if( !success )
2981 {
2982 return false;
2983 }
2984 else
2985 {
2986 LLVFile::initClass();
2987 return true;
2988 }
2989}
2990
2991void LLAppViewer::purgeCache()
2992{
2993 llinfos << "Purging Texture Cache..." << llendl;
2994 LLAppViewer::getTextureCache()->purgeCache(LL_PATH_CACHE);
2995 llinfos << "Purging Cache..." << llendl;
2996 std::string mask = gDirUtilp->getDirDelimiter() + "*.*";
2997 gDirUtilp->deleteFilesInDir(gDirUtilp->getExpandedFilename(LL_PATH_CACHE,"").c_str(),mask);
2998}
2999
3000const LLString& LLAppViewer::getSecondLifeTitle() const
3001{
3002 return gSecondLife;
3003}
3004
3005const LLString& LLAppViewer::getWindowTitle() const
3006{
3007 return gWindowTitle;
3008}
3009
3010void LLAppViewer::resetURIs() const
3011{
3012 // Clear URIs when picking a new server
3013 gLoginURIs.clear();
3014 gHelperURI.clear();
3015}
3016
3017const std::vector<std::string>& LLAppViewer::getLoginURIs() const
3018{
3019 if (gLoginURIs.empty())
3020 {
3021 // not specified on the command line, use value from table
3022 gLoginURIs = LLSRV::rewriteURI(gGridInfo[gGridChoice].mLoginURI);
3023 }
3024 return gLoginURIs;
3025}
3026
3027const std::string& LLAppViewer::getHelperURI() const
3028{
3029 if (gHelperURI.empty())
3030 {
3031 // not specified on the command line, use value from table
3032 gHelperURI = gGridInfo[gGridChoice].mHelperURI;
3033 }
3034 return gHelperURI;
3035}
3036
3037void LLAppViewer::addLoginURI(const std::string& uri)
3038{
3039 gLoginURIs.push_back(uri);
3040}
3041
3042void LLAppViewer::setHelperURI(const std::string& uri)
3043{
3044 gHelperURI = uri;
3045}
3046
3047// Callback from a dialog indicating user was logged out.
3048void finish_disconnect(S32 option, void* userdata)
3049{
3050 if (1 == option)
3051 {
3052 LLAppViewer::instance()->forceQuit();
3053 }
3054}
3055
3056// Callback from an early disconnect dialog, force an exit
3057void finish_forced_disconnect(S32 /* option */, void* /* userdata */)
3058{
3059 LLAppViewer::instance()->forceQuit();
3060}
3061
3062
3063void LLAppViewer::forceDisconnect(const LLString& mesg)
3064{
3065 if (gDoDisconnect)
3066 {
3067 // Already popped up one of these dialogs, don't
3068 // do this again.
3069 return;
3070 }
3071
3072 // Translate the message if possible
3073 LLString big_reason = LLAgent::sTeleportErrorMessages[mesg];
3074 if ( big_reason.size() == 0 )
3075 {
3076 big_reason = mesg;
3077 }
3078
3079 LLStringBase<char>::format_map_t args;
3080 gDoDisconnect = TRUE;
3081
3082 if (LLStartUp::getStartupState() < STATE_STARTED)
3083 {
3084 // Tell users what happened
3085 args["[ERROR_MESSAGE]"] = big_reason;
3086 gViewerWindow->alertXml("ErrorMessage", args, finish_forced_disconnect);
3087 }
3088 else
3089 {
3090 args["[MESSAGE]"] = big_reason;
3091 gViewerWindow->alertXml("YouHaveBeenLoggedOut", args, finish_disconnect );
3092 }
3093}
3094
3095void LLAppViewer::badNetworkHandler()
3096{
3097 // Dump the packet
3098 gMessageSystem->dumpPacketToLog();
3099
3100 // Flush all of our caches on exit in the case of disconnect due to
3101 // invalid packets.
3102
3103 mPurgeOnExit = TRUE;
3104
3105#if LL_WINDOWS
3106 // Generates the minidump.
3107 LLWinDebug::handleException(NULL);
3108#endif
3109 LLAppViewer::handleViewerCrash();
3110
3111 std::ostringstream message;
3112 message <<
3113 "The viewer has detected mangled network data indicative\n"
3114 "of a bad upstream network connection or an incomplete\n"
3115 "local installation of " << LLAppViewer::instance()->getSecondLifeTitle() << ". \n"
3116 " \n"
3117 "Try uninstalling and reinstalling to see if this resolves \n"
3118 "the issue. \n"
3119 " \n"
3120 "If the problem continues, see the Tech Support FAQ at: \n"
3121 "www.secondlife.com/support";
3122 forceDisconnect(message.str());
3123}
3124
3125// This routine may get called more than once during the shutdown process.
3126// This can happen because we need to get the screenshot before the window
3127// is destroyed.
3128void LLAppViewer::saveFinalSnapshot()
3129{
3130 if (!mSavedFinalSnapshot && !gNoRender)
3131 {
3132 gSavedSettings.setVector3d("FocusPosOnLogout", gAgent.calcFocusPositionTargetGlobal());
3133 gSavedSettings.setVector3d("CameraPosOnLogout", gAgent.calcCameraPositionTargetGlobal());
3134 gViewerWindow->setCursor(UI_CURSOR_WAIT);
3135 gAgent.changeCameraToThirdPerson( FALSE ); // don't animate, need immediate switch
3136 gSavedSettings.setBOOL("ShowParcelOwners", FALSE);
3137 idle();
3138
3139 LLString snap_filename = gDirUtilp->getLindenUserDir();
3140 snap_filename += gDirUtilp->getDirDelimiter();
3141 snap_filename += SCREEN_LAST_FILENAME;
3142 gViewerWindow->saveSnapshot(snap_filename, gViewerWindow->getWindowWidth(), gViewerWindow->getWindowHeight(), FALSE, TRUE);
3143 mSavedFinalSnapshot = TRUE;
3144 }
3145}
3146
3147void LLAppViewer::loadNameCache()
3148{
3149 if (!gCacheName) return;
3150
3151 std::string name_cache;
3152 name_cache = gDirUtilp->getExpandedFilename(LL_PATH_CACHE, "name.cache");
3153 FILE* name_cache_fp = LLFile::fopen(name_cache.c_str(), "r"); // Flawfinder: ignore
3154 if (name_cache_fp)
3155 {
3156 gCacheName->importFile(name_cache_fp);
3157 fclose(name_cache_fp);
3158 }
3159}
3160
3161void LLAppViewer::saveNameCache()
3162{
3163 if (!gCacheName) return;
3164
3165 std::string name_cache;
3166 name_cache = gDirUtilp->getExpandedFilename(LL_PATH_CACHE, "name.cache");
3167 FILE* name_cache_fp = LLFile::fopen(name_cache.c_str(), "w"); // Flawfinder: ignore
3168 if (name_cache_fp)
3169 {
3170 gCacheName->exportFile(name_cache_fp);
3171 fclose(name_cache_fp);
3172 }
3173}
3174
3175/*! @brief This class is an LLFrameTimer that can be created with
3176 an elapsed time that starts counting up from the given value
3177 rather than 0.0.
3178
3179 Otherwise it behaves the same way as LLFrameTimer.
3180*/
3181class LLFrameStatsTimer : public LLFrameTimer
3182{
3183public:
3184 LLFrameStatsTimer(F64 elapsed_already = 0.0)
3185 : LLFrameTimer()
3186 {
3187 mStartTime -= elapsed_already;
3188 }
3189};
3190
3191///////////////////////////////////////////////////////
3192// idle()
3193//
3194// Called every time the window is not doing anything.
3195// Receive packets, update statistics, and schedule a redisplay.
3196///////////////////////////////////////////////////////
3197void LLAppViewer::idle()
3198{
3199 // Update frame timers
3200 static LLTimer idle_timer;
3201
3202 LLControlBase::updateAllListeners();
3203
3204 LLFrameTimer::updateFrameTime();
3205 LLEventTimer::updateClass();
3206 LLCriticalDamp::updateInterpolants();
3207 LLMortician::updateClass();
3208 F32 dt_raw = idle_timer.getElapsedTimeAndResetF32();
3209
3210 // Cap out-of-control frame times
3211 // Too low because in menus, swapping, debugger, etc.
3212 // Too high because idle called with no objects in view, etc.
3213 const F32 MIN_FRAME_RATE = 1.f;
3214 const F32 MAX_FRAME_RATE = 200.f;
3215
3216 F32 frame_rate_clamped = 1.f / dt_raw;
3217 frame_rate_clamped = llclamp(frame_rate_clamped, MIN_FRAME_RATE, MAX_FRAME_RATE);
3218 gFrameDTClamped = 1.f / frame_rate_clamped;
3219
3220 // Global frame timer
3221 // Smoothly weight toward current frame
3222 gFPSClamped = (frame_rate_clamped + (4.f * gFPSClamped)) / 5.f;
3223
3224 if (gQuitAfterSeconds > 0.f)
3225 {
3226 if (gRenderStartTime.getElapsedTimeF32() > gQuitAfterSeconds)
3227 {
3228 LLAppViewer::instance()->forceQuit();
3229 }
3230 }
3231
3232 // Must wait until both have avatar object and mute list, so poll
3233 // here.
3234 request_initial_instant_messages();
3235
3236 ///////////////////////////////////
3237 //
3238 // Special case idle if still starting up
3239 //
3240
3241 if (LLStartUp::getStartupState() < STATE_STARTED)
3242 {
3243 // Skip rest if idle startup returns false (essentially, no world yet)
3244 if (!idle_startup())
3245 {
3246 return;
3247 }
3248 }
3249
3250
3251 F32 yaw = 0.f; // radians
3252
3253 if (!gDisconnected)
3254 {
3255 LLFastTimer t(LLFastTimer::FTM_NETWORK);
3256
3257 // Update spaceserver timeinfo
3258 gWorldp->setSpaceTimeUSec(gWorldp->getSpaceTimeUSec() + (U32)(dt_raw * SEC_TO_MICROSEC));
3259
3260
3261 //////////////////////////////////////
3262 //
3263 // Update simulator agent state
3264 //
3265
3266 if (gRotateRight)
3267 {
3268 gAgent.moveYaw(-1.f);
3269 }
3270
3271 // Handle automatic walking towards points
3272 gAgentPilot.updateTarget();
3273 gAgent.autoPilot(&yaw);
3274
3275 static LLFrameTimer agent_update_timer;
3276 static U32 last_control_flags;
3277
3278 // When appropriate, update agent location to the simulator.
3279 F32 agent_update_time = agent_update_timer.getElapsedTimeF32();
3280 BOOL flags_changed = gAgent.controlFlagsDirty() || (last_control_flags != gAgent.getControlFlags());
3281
3282 if (flags_changed || (agent_update_time > (1.0f / (F32) AGENT_UPDATES_PER_SECOND)))
3283 {
3284 // Send avatar and camera info
3285 last_control_flags = gAgent.getControlFlags();
3286 send_agent_update(TRUE);
3287 agent_update_timer.reset();
3288 }
3289 }
3290
3291 //////////////////////////////////////
3292 //
3293 // Manage statistics
3294 //
3295 //
3296
3297 {
3298 // Initialize the viewer_stats_timer with an already elapsed time
3299 // of SEND_STATS_PERIOD so that the initial stats report will
3300 // be sent immediately.
3301 static LLFrameStatsTimer viewer_stats_timer(SEND_STATS_PERIOD);
3302 reset_statistics();
3303
3304 // Update session stats every large chunk of time
3305 // *FIX: (???) SAMANTHA
3306 if (viewer_stats_timer.getElapsedTimeF32() >= SEND_STATS_PERIOD && !gDisconnected)
3307 {
3308 llinfos << "Transmitting sessions stats" << llendl;
3309 send_stats();
3310 viewer_stats_timer.reset();
3311 }
3312
3313 // Print the object debugging stats
3314 static LLFrameTimer object_debug_timer;
3315 if (object_debug_timer.getElapsedTimeF32() > 5.f)
3316 {
3317 object_debug_timer.reset();
3318 if (gObjectList.mNumDeadObjectUpdates)
3319 {
3320 llinfos << "Dead object updates: " << gObjectList.mNumDeadObjectUpdates << llendl;
3321 gObjectList.mNumDeadObjectUpdates = 0;
3322 }
3323 if (gObjectList.mNumUnknownKills)
3324 {
3325 llinfos << "Kills on unknown objects: " << gObjectList.mNumUnknownKills << llendl;
3326 gObjectList.mNumUnknownKills = 0;
3327 }
3328 if (gObjectList.mNumUnknownUpdates)
3329 {
3330 llinfos << "Unknown object updates: " << gObjectList.mNumUnknownUpdates << llendl;
3331 gObjectList.mNumUnknownUpdates = 0;
3332 }
3333 }
3334 gFrameStats.addFrameData();
3335 }
3336
3337 if (!gDisconnected)
3338 {
3339 LLFastTimer t(LLFastTimer::FTM_NETWORK);
3340
3341 ////////////////////////////////////////////////
3342 //
3343 // Network processing
3344 //
3345 // NOTE: Starting at this point, we may still have pointers to "dead" objects
3346 // floating throughout the various object lists.
3347 //
3348
3349 gFrameStats.start(LLFrameStats::IDLE_NETWORK);
3350 idleNetwork();
3351 stop_glerror();
3352
3353 gFrameStats.start(LLFrameStats::AGENT_MISC);
3354
3355 // Check for away from keyboard, kick idle agents.
3356 idle_afk_check();
3357
3358 // Update statistics for this frame
3359 update_statistics(gFrameCount);
3360
3361 gViewerWindow->updateDebugText();
3362 }
3363
3364 ////////////////////////////////////////
3365 //
3366 // Handle the regular UI idle callbacks as well as
3367 // hover callbacks
3368 //
3369
3370 {
3371// LLFastTimer t(LLFastTimer::FTM_IDLE_CB);
3372
3373 // Do event notifications if necessary. Yes, we may want to move this elsewhere.
3374 gEventNotifier.update();
3375
3376 gIdleCallbacks.callFunctions();
3377 }
3378
3379 if (gDisconnected)
3380 {
3381 return;
3382 }
3383
3384 gViewerWindow->handlePerFrameHover();
3385
3386 ///////////////////////////////////////
3387 // Agent and camera movement
3388 //
3389 LLCoordGL current_mouse = gViewerWindow->getCurrentMouse();
3390
3391// BOOL was_in_prelude = gAgent.inPrelude();
3392
3393 {
3394 //LLFastTimer t(LLFastTimer::FTM_TEMP1);
3395
3396 // After agent and camera moved, figure out if we need to
3397 // deselect objects.
3398 gSelectMgr->deselectAllIfTooFar();
3399
3400 }
3401
3402 {
3403 LLFastTimer t(LLFastTimer::FTM_RESET_DRAWORDER);
3404
3405 //////////////////////////////////////////////
3406 //
3407 // Clear draw orders
3408 //
3409 // Should actually be done after render, but handlePerFrameHover actually does a "render"
3410 // to do its selection.
3411 //
3412
3413 gPipeline.resetDrawOrders();
3414 }
3415 {
3416 // Handle pending gesture processing
3417 gGestureManager.update();
3418
3419 gAgent.updateAgentPosition(gFrameDTClamped, yaw, current_mouse.mX, current_mouse.mY);
3420 }
3421
3422 {
3423 LLFastTimer t(LLFastTimer::FTM_OBJECTLIST_UPDATE); // Actually "object update"
3424 gFrameStats.start(LLFrameStats::OBJECT_UPDATE);
3425
3426 if (!(logoutRequestSent() && hasSavedFinalSnapshot()))
3427 {
3428 gObjectList.update(gAgent, *gWorldp);
3429 }
3430 }
3431
3432 {
3433 LLFastTimer t(LLFastTimer::FTM_UPDATE_SKY);
3434 gSky.updateSky();
3435 }
3436
3437 //////////////////////////////////////
3438 //
3439 // Deletes objects...
3440 // Has to be done after doing idleUpdates (which can kill objects)
3441 //
3442
3443 {
3444 LLFastTimer t(LLFastTimer::FTM_CLEANUP);
3445 gFrameStats.start(LLFrameStats::CLEAN_DEAD);
3446 gObjectList.cleanDeadObjects();
3447 LLDrawable::cleanupDeadDrawables();
3448 }
3449
3450 //
3451 // After this point, in theory we should never see a dead object
3452 // in the various object/drawable lists.
3453 //
3454
3455 //////////////////////////////////////
3456 //
3457 // Update/send HUD effects
3458 //
3459 // At this point, HUD effects may clean up some references to
3460 // dead objects.
3461 //
3462
3463 {
3464 //LLFastTimer t(LLFastTimer::FTM_TEMP3);
3465
3466 gFrameStats.start(LLFrameStats::UPDATE_EFFECTS);
3467 gSelectMgr->updateEffects();
3468 gHUDManager->cleanupEffects();
3469 gHUDManager->sendEffects();
3470 }
3471
3472 stop_glerror();
3473
3474 ////////////////////////////////////////
3475 //
3476 // Unpack layer data that we've received
3477 //
3478
3479 {
3480 LLFastTimer t(LLFastTimer::FTM_NETWORK);
3481 gVLManager.unpackData();
3482 }
3483
3484 /////////////////////////
3485 //
3486 // Update surfaces, and surface textures as well.
3487 //
3488
3489 gWorldp->updateVisibilities();
3490 {
3491 const F32 max_region_update_time = .001f; // 1ms
3492 LLFastTimer t(LLFastTimer::FTM_REGION_UPDATE);
3493 gWorldp->updateRegions(max_region_update_time);
3494 }
3495
3496 /////////////////////////
3497 //
3498 // Update weather effects
3499 //
3500
3501 if (!gNoRender)
3502 {
3503 gWorldp->updateClouds(gFrameDTClamped);
3504 gSky.propagateHeavenlyBodies(gFrameDTClamped); // moves sun, moon, and planets
3505
3506 // Update wind vector
3507 LLVector3 wind_position_region;
3508 static LLVector3 average_wind;
3509
3510 LLViewerRegion *regionp;
3511 regionp = gWorldp->resolveRegionGlobal(wind_position_region, gAgent.getPositionGlobal()); // puts agent's local coords into wind_position
3512 if (regionp)
3513 {
3514 gWindVec = regionp->mWind.getVelocity(wind_position_region);
3515
3516 // Compute average wind and use to drive motion of water
3517
3518 average_wind = regionp->mWind.getAverage();
3519 F32 cloud_density = regionp->mCloudLayer.getDensityRegion(wind_position_region);
3520
3521 gSky.setCloudDensityAtAgent(cloud_density);
3522 gSky.setWind(average_wind);
3523 //LLVOWater::setWind(average_wind);
3524 }
3525 else
3526 {
3527 gWindVec.setVec(0.0f, 0.0f, 0.0f);
3528 }
3529 }
3530 stop_glerror();
3531
3532 //////////////////////////////////////
3533 //
3534 // Update images, using the image stats generated during object update/culling
3535 //
3536 // Can put objects onto the retextured list.
3537 //
3538 gFrameStats.start(LLFrameStats::IMAGE_UPDATE);
3539
3540 LLFastTimer t(LLFastTimer::FTM_IMAGE_UPDATE);
3541
3542 LLViewerImage::updateClass(gCamera->getVelocityStat()->getMean(),
3543 gCamera->getAngularVelocityStat()->getMean());
3544
3545 gBumpImageList.updateImages(); // must be called before gImageList version so that it's textures are thrown out first.
3546
3547 const F32 max_image_decode_time = 0.005f; // 5 ms decode time
3548 gImageList.updateImages(max_image_decode_time);
3549 stop_glerror();
3550
3551 //////////////////////////////////////
3552 //
3553 // Sort and cull in the new renderer are moved to pipeline.cpp
3554 // Here, particles are updated and drawables are moved.
3555 //
3556
3557 if (!gNoRender)
3558 {
3559 gFrameStats.start(LLFrameStats::UPDATE_MOVE);
3560 gPipeline.updateMove();
3561
3562 gFrameStats.start(LLFrameStats::UPDATE_PARTICLES);
3563 gWorldp->updateParticles();
3564 }
3565 stop_glerror();
3566
3567 if (!LLViewerJoystick::sOverrideCamera)
3568 {
3569 gAgent.updateCamera();
3570 }
3571 else
3572 {
3573 LLViewerJoystick::updateCamera();
3574 }
3575
3576 // objects and camera should be in sync, do LOD calculations now
3577 {
3578 LLFastTimer t(LLFastTimer::FTM_LOD_UPDATE);
3579 gObjectList.updateApparentAngles(gAgent);
3580 }
3581
3582 {
3583 gFrameStats.start(LLFrameStats::AUDIO);
3584 LLFastTimer t(LLFastTimer::FTM_AUDIO_UPDATE);
3585
3586 if (gAudiop)
3587 {
3588 audio_update_volume(false);
3589 audio_update_listener();
3590 audio_update_wind(false);
3591
3592 // this line actually commits the changes we've made to source positions, etc.
3593 const F32 max_audio_decode_time = 0.002f; // 2 ms decode time
3594 gAudiop->idle(max_audio_decode_time);
3595 }
3596 }
3597
3598 // Handle shutdown process, for example,
3599 // wait for floaters to close, send quit message,
3600 // forcibly quit if it has taken too long
3601 if (mQuitRequested)
3602 {
3603 idleShutdown();
3604 }
3605
3606 stop_glerror();
3607}
3608
3609void LLAppViewer::idleShutdown()
3610{
3611 // Wait for all modal alerts to get resolved
3612 if (LLModalDialog::activeCount() > 0)
3613 {
3614 return;
3615 }
3616
3617 // close IM interface
3618 if(gIMMgr)
3619 {
3620 gIMMgr->disconnectAllSessions();
3621 }
3622
3623 // Wait for all floaters to get resolved
3624 if (gFloaterView
3625 && !gFloaterView->allChildrenClosed())
3626 {
3627 return;
3628 }
3629
3630 static bool saved_snapshot = false;
3631 if (!saved_snapshot)
3632 {
3633 saved_snapshot = true;
3634 saveFinalSnapshot();
3635 return;
3636 }
3637
3638 const F32 SHUTDOWN_UPLOAD_SAVE_TIME = 5.f;
3639
3640 S32 pending_uploads = gAssetStorage->getNumPendingUploads();
3641 if (pending_uploads > 0
3642 && gLogoutTimer.getElapsedTimeF32() < SHUTDOWN_UPLOAD_SAVE_TIME
3643 && !logoutRequestSent())
3644 {
3645 static S32 total_uploads = 0;
3646 // Sometimes total upload count can change during logout.
3647 total_uploads = llmax(total_uploads, pending_uploads);
3648 gViewerWindow->setShowProgress(TRUE);
3649 S32 finished_uploads = total_uploads - pending_uploads;
3650 F32 percent = 100.f * finished_uploads / total_uploads;
3651 gViewerWindow->setProgressPercent(percent);
3652 char buffer[MAX_STRING]; // Flawfinder: ignore
3653 snprintf(buffer, MAX_STRING, "Saving final data..."); // Flawfinder: ignore
3654 gViewerWindow->setProgressString(buffer);
3655 return;
3656 }
3657
3658 // All floaters are closed. Tell server we want to quit.
3659 if( !logoutRequestSent() )
3660 {
3661 sendLogoutRequest();
3662
3663 // Wait for a LogoutReply message
3664 gViewerWindow->setShowProgress(TRUE);
3665 gViewerWindow->setProgressPercent(100.f);
3666 gViewerWindow->setProgressString("Logging out...");
3667 return;
3668 }
3669
3670 // Make sure that we quit if we haven't received a reply from the server.
3671 if( logoutRequestSent()
3672 && gLogoutTimer.getElapsedTimeF32() > gLogoutMaxTime )
3673 {
3674 forceQuit();
3675 return;
3676 }
3677}
3678
3679void LLAppViewer::sendLogoutRequest()
3680{
3681 if(!mLogoutRequestSent)
3682 {
3683 LLMessageSystem* msg = gMessageSystem;
3684 msg->newMessageFast(_PREHASH_LogoutRequest);
3685 msg->nextBlockFast(_PREHASH_AgentData);
3686 msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID() );
3687 msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
3688 gAgent.sendReliableMessage();
3689
3690 gLogoutTimer.reset();
3691 gLogoutMaxTime = LOGOUT_REQUEST_TIME;
3692 mLogoutRequestSent = TRUE;
3693
3694 gVoiceClient->leaveChannel();
3695 }
3696}
3697
3698//
3699// Handle messages, and all message related stuff
3700//
3701
3702#define TIME_THROTTLE_MESSAGES
3703
3704#ifdef TIME_THROTTLE_MESSAGES
3705#define CHECK_MESSAGES_DEFAULT_MAX_TIME .020f // 50 ms = 50 fps (just for messages!)
3706static F32 CheckMessagesMaxTime = CHECK_MESSAGES_DEFAULT_MAX_TIME;
3707#endif
3708
3709void LLAppViewer::idleNetwork()
3710{
3711 gObjectList.mNumNewObjects = 0;
3712 S32 total_decoded = 0;
3713
3714 if (!gSavedSettings.getBOOL("SpeedTest"))
3715 {
3716 LLFastTimer t(LLFastTimer::FTM_IDLE_NETWORK); // decode
3717
3718 // deal with any queued name requests and replies.
3719 gCacheName->processPending();
3720
3721 LLTimer check_message_timer;
3722 // Read all available packets from network
3723 stop_glerror();
3724 const S64 frame_count = gFrameCount; // U32->S64
3725 F32 total_time = 0.0f;
3726 while (gMessageSystem->checkAllMessages(frame_count, gServicePump))
3727 {
3728 if (gDoDisconnect)
3729 {
3730 // We're disconnecting, don't process any more messages from the server
3731 // We're usually disconnecting due to either network corruption or a
3732 // server going down, so this is OK.
3733 break;
3734 }
3735 stop_glerror();
3736
3737 total_decoded++;
3738 gPacketsIn++;
3739
3740 if (total_decoded > MESSAGE_MAX_PER_FRAME)
3741 {
3742 break;
3743 }
3744
3745#ifdef TIME_THROTTLE_MESSAGES
3746 // Prevent slow packets from completely destroying the frame rate.
3747 // This usually happens due to clumps of avatars taking huge amount
3748 // of network processing time (which needs to be fixed, but this is
3749 // a good limit anyway).
3750 total_time = check_message_timer.getElapsedTimeF32();
3751 if (total_time >= CheckMessagesMaxTime)
3752 break;
3753#endif
3754 }
3755 // Handle per-frame message system processing.
3756 gMessageSystem->processAcks();
3757
3758#ifdef TIME_THROTTLE_MESSAGES
3759 if (total_time >= CheckMessagesMaxTime)
3760 {
3761 // Increase CheckMessagesMaxTime so that we will eventually catch up
3762 CheckMessagesMaxTime *= 1.035f; // 3.5% ~= x2 in 20 frames, ~8x in 60 frames
3763 }
3764 else
3765 {
3766 // Reset CheckMessagesMaxTime to default value
3767 CheckMessagesMaxTime = CHECK_MESSAGES_DEFAULT_MAX_TIME;
3768 }
3769#endif
3770
3771
3772
3773 // we want to clear the control after sending out all necessary agent updates
3774 gAgent.resetControlFlags();
3775 stop_glerror();
3776
3777
3778 // Decode enqueued messages...
3779 S32 remaining_possible_decodes = MESSAGE_MAX_PER_FRAME - total_decoded;
3780
3781 if( remaining_possible_decodes <= 0 )
3782 {
3783 llinfos << "Maxed out number of messages per frame at " << MESSAGE_MAX_PER_FRAME << llendl;
3784 }
3785
3786 if (gPrintMessagesThisFrame)
3787 {
3788 llinfos << "Decoded " << total_decoded << " msgs this frame!" << llendl;
3789 gPrintMessagesThisFrame = FALSE;
3790 }
3791 }
3792
3793 gObjectList.mNumNewObjectsStat.addValue(gObjectList.mNumNewObjects);
3794
3795 // Retransmit unacknowledged packets.
3796 gXferManager->retransmitUnackedPackets();
3797 gAssetStorage->checkForTimeouts();
3798
3799 gViewerThrottle.updateDynamicThrottle();
3800}
3801
3802void LLAppViewer::disconnectViewer()
3803{
3804 if (gDisconnected)
3805 {
3806 return;
3807 }
3808 //
3809 // Cleanup after quitting.
3810 //
3811 // Save snapshot for next time, if we made it through initialization
3812
3813 llinfos << "Disconnecting viewer!" << llendl;
3814
3815 // Dump our frame statistics
3816 gFrameStats.dump();
3817
3818 // Remember if we were flying
3819 gSavedSettings.setBOOL("FlyingAtExit", gAgent.getFlying() );
3820
3821 // Un-minimize all windows so they don't get saved minimized
3822 if (!gNoRender)
3823 {
3824 if (gFloaterView)
3825 {
3826 gFloaterView->restoreAll();
3827 }
3828 }
3829
3830 if (gSelectMgr)
3831 {
3832 gSelectMgr->deselectAll();
3833 }
3834
3835 if (!gNoRender)
3836 {
3837 // save inventory if appropriate
3838 gInventory.cache(gAgent.getInventoryRootID(), gAgent.getID());
3839 if(gInventoryLibraryRoot.notNull() && gInventoryLibraryOwner.notNull())
3840 {
3841 gInventory.cache(gInventoryLibraryRoot, gInventoryLibraryOwner);
3842 }
3843 }
3844
3845 saveNameCache();
3846
3847 // close inventory interface, close all windows
3848 LLInventoryView::cleanup();
3849
3850 // Also writes cached agent settings to gSavedSettings
3851 gAgent.cleanup();
3852
3853 gObjectList.destroy();
3854 delete gWorldp;
3855 gWorldp = NULL;
3856
3857 cleanup_xfer_manager();
3858 gDisconnected = TRUE;
3859}
3860
3861void LLAppViewer::forceErrorLLError()
3862{
3863 llerrs << "This is an llerror" << llendl;
3864}
3865
3866void LLAppViewer::forceErrorBreakpoint()
3867{
3868#ifdef LL_WINDOWS
3869 DebugBreak();
3870#endif
3871 return;
3872}
3873
3874void LLAppViewer::forceErrorBadMemoryAccess()
3875{
3876 S32* crash = NULL;
3877 *crash = 0xDEADBEEF;
3878 return;
3879}
3880
3881void LLAppViewer::forceErrorInifiniteLoop()
3882{
3883 while(true)
3884 {
3885 ;
3886 }
3887 return;
3888}
3889
3890void LLAppViewer::forceErrorSoftwareException()
3891{
3892 // *FIX: Any way to insure it won't be handled?
3893 throw;
3894}