aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/linden/indra/newview/viewer.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/viewer.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 'linden/indra/newview/viewer.cpp')
-rw-r--r--linden/indra/newview/viewer.cpp6368
1 files changed, 0 insertions, 6368 deletions
diff --git a/linden/indra/newview/viewer.cpp b/linden/indra/newview/viewer.cpp
deleted file mode 100644
index 3b24371..0000000
--- a/linden/indra/newview/viewer.cpp
+++ /dev/null
@@ -1,6368 +0,0 @@
1/**
2 * @file viewer.cpp
3 * @brief A window into the virtual world.
4 *
5 * $LicenseInfo:firstyear=2000&license=viewergpl$
6 *
7 * Copyright (c) 2000-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#include "llviewerprecompiledheaders.h"
33
34#include "viewer.h"
35
36#include "llparcel.h"
37#include "llviewerparcelmgr.h"
38#include "llviewerjoystick.h"
39
40// System library headers
41#include <errno.h>
42#include <stdexcept>
43#if LL_WINDOWS
44# include <share.h>
45#else
46# include <sys/file.h>
47# include <signal.h>
48#endif
49#include <sys/stat.h>
50#include <memory>
51#include <boost/tokenizer.hpp>
52
53#if LL_WINDOWS
54#include <fcntl.h> //_O_APPEND
55#include <io.h> //_open_osfhandle()
56#include <errorrep.h> // for AddERExcludedApplicationA()
57#include <process.h> // _spawnl()
58#include <tchar.h> // For TCHAR support
59
60#if LL_WINDOWS && _MSC_VER < 1400
61//#define LL_USE_SMARTHEAP 0
62#else
63#define LL_USE_SMARTHEAP 0
64#endif
65
66#if LL_WINDOWS && LL_RELEASE_FOR_DOWNLOAD && LL_USE_SMARTHEAP
67#include "smrtheap/smrtheap.h"
68#endif // LL_WINDOWS && LL_RELEASE_FOR_DOWNLOAD && LL_USE_SMARTHEAP
69
70#elif LL_DARWIN || LL_LINUX || LL_SOLARIS
71
72 # include <sys/socket.h>
73// # include <sys/stat.h> // mkdir()
74 # include <netinet/in.h>
75 # include <arpa/inet.h> // inet_ntoa()
76
77 #if LL_LINUX
78 # include <dlfcn.h> // RTLD_LAZY
79 # include <execinfo.h> // backtrace - glibc only
80 # ifndef LL_ELFBIN
81 #define LL_ELFBIN 1
82 # endif // LL_ELFBIN
83 # if LL_ELFBIN
84 # include <cxxabi.h> // for symbol demangling
85 # include "ELFIO.h" // for better backtraces
86 # endif // LL_ELFBIN
87 #elif LL_SOLARIS
88 # include <sys/types.h>
89 # include <unistd.h>
90 # include <fcntl.h>
91 # include <ucontext.h>
92 #endif
93
94 #if LL_DARWIN
95 #include <Carbon/Carbon.h>
96 // Apple sucks! AssertMacros.h defines these COMMONLY used names.
97 #ifdef check
98 #undef check
99 #endif
100 #ifdef verify
101 #undef verify
102 #endif // verify
103 #ifdef require
104 #undef require
105 #endif
106 #endif
107#endif // !LL_WINDOWS
108
109// Support for sending crash reports from the viewer?
110//#define LL_SEND_CRASH_REPORTS 0
111
112
113//
114// Linden library headers
115//
116
117#include "audioengine.h"
118#include "llcommon.h"
119#include "llapr.h"
120#include "llares.h"
121#include "llcachename.h"
122#include "llcurl.h"
123#include "llcriticaldamp.h"
124#include "lldir.h"
125#include "lleconomy.h"
126#include "llerrorcontrol.h"
127#include "llhttpnode.h"
128#include "llflexibleobject.h"
129#include "llfasttimer.h"
130#include "llfocusmgr.h"
131#include "llgroupmgr.h"
132#include "llimage.h"
133#include "llimageworker.h"
134#include "lllfsthread.h"
135#include "llmemtype.h"
136#include "llmd5.h"
137#include "llsecondlifeurls.h"
138#include "llversionviewer.h"
139#include "llvfile.h"
140#include "llvfs.h"
141#include "llwindow.h" // for shell_open
142#include "llworkerthread.h"
143#include "llvfsthread.h"
144#include "llxfermanager.h"
145#include "message.h"
146#include "llvoavatar.h"
147#include "llglslshader.h"
148
149//
150// Viewer headers
151//
152
153#include "llagent.h"
154#include "llagentpilot.h"
155#include "llbutton.h" // For constants
156#include "llcallbacklist.h"
157#include "llchatbar.h"
158//#include "llcombobox.h" // For constants
159#include "llconsole.h"
160#include "llcontainerview.h"
161#include "lldebugview.h"
162#include "lldrawpoolbump.h"
163#include "lldrawpoolterrain.h"
164#include "lleventnotifier.h"
165#include "llfasttimerview.h"
166#include "llfeaturemanager.h"
167#include "llfirstuse.h"
168#include "llfloateractivespeakers.h"
169#include "llfloatertools.h"
170#include "llfloaterworldmap.h"
171#include "llfloaterhtmlhelp.h"
172#include "llfloatersaveavatar.h"
173#include "llfloatersnapshot.h"
174#include "llfolderview.h"
175#include "llframestats.h"
176#include "llgesturemgr.h"
177#include "llhoverview.h"
178#include "llhudeffecttrail.h"
179#include "llhudmanager.h"
180#include "llhttpclient.h"
181#include "llimview.h"
182#include "llimpanel.h"
183#include "llinventorymodel.h"
184#include "llinventoryview.h"
185#include "llkeyboard.h"
186#include "llkeyframemotion.h"
187#include "llpanellogin.h"
188#include "llmutelist.h"
189#include "llmenugl.h"
190#include "llnamelistctrl.h"
191#include "llnamebox.h"
192#include "llnameeditor.h"
193#include "llpumpio.h"
194#include "llnotify.h"
195#include "llselectmgr.h"
196#include "llsky.h"
197#include "llsrv.h"
198#include "llstartup.h"
199#include "llstatusbar.h"
200#include "llsurface.h"
201#include "lltexlayer.h"
202#include "lltexturecache.h"
203#include "lltexturefetch.h"
204#include "lltoolbar.h"
205#include "lltoolmgr.h"
206#include "lltracker.h"
207#include "llurldispatcher.h"
208#include "llurlsimstring.h"
209#include "llurlwhitelist.h"
210#include "llv4math.h" // LL_VECTORIZE
211#include "llviewerbuild.h"
212#include "llviewercamera.h"
213#include "llviewercontrol.h"
214#include "llviewerjointmesh.h"
215#include "llviewerimagelist.h"
216#include "llviewerkeyboard.h"
217#include "llviewermenu.h"
218#include "llviewermessage.h"
219#include "llviewernetwork.h"
220#include "llviewerobjectlist.h"
221#include "llviewerparcelmgr.h"
222#include "llviewerregion.h"
223#include "llviewerstats.h"
224#include "llviewerthrottle.h"
225#include "llvieweruictrlfactory.h"
226#include "llviewerwindow.h"
227#include "llvlmanager.h"
228#include "llvoavatar.h"
229#include "llvograss.h"
230#include "llvotree.h"
231#include "llvovolume.h" // To set a static member.
232#include "llvowater.h"
233#include "llvolume.h"
234#include "llvolumemgr.h"
235#include "llvolumemessage.h"
236#include "llweb.h"
237#include "llworld.h"
238#include "llworldmap.h"
239#include "llworldmapview.h"
240#include "pipeline.h"
241#include "llface.h"
242#include "audiosettings.h"
243#include "res/resource.h"
244#include "llvoiceclient.h"
245
246#if LL_WINDOWS
247#include "llwindebug.h"
248#include "lldxhardware.h"
249#include "llwindowwin32.h"
250
251// for Logitech LCD keyboards / speakers
252#ifndef LL_LOGITECH_LCD_H
253#include "lllogitechlcd.h"
254#endif
255extern void CreateLCDDebugWindows();
256
257#endif // LL_WINDOWS
258
259#if LL_QUICKTIME_ENABLED
260#if LL_DARWIN
261#include <QuickTime/QuickTime.h>
262#else
263// quicktime specific includes
264#include "MacTypes.h"
265#include "QTML.h"
266#include "Movies.h"
267#include "FixMath.h"
268#endif
269#endif
270
271#if LL_GSTREAMER_ENABLED
272// ugh, do this instead of pulling in the gstreamer headers which indirectly
273// clash with expat in the monster that is viewer.cpp ... sigh.
274void UnloadGStreamer();
275#endif // LL_GSTREAMER_ENABLED
276
277#include "llmediaengine.h"
278
279#if LL_LIBXUL_ENABLED
280#include "llmozlib.h"
281#endif // LL_LIBXUL_ENABLED
282
283/////////////////////////////////////////////////////////////////////////////////
284// Support for crash handling.
285/////////////////////////////////////////////////////////////////////////////////
286
287void errorCallback(const std::string &error_string);
288S32 gCrashBehavior = CRASH_BEHAVIOR_ASK;
289void (*gCrashCallback)(void) = NULL;
290BOOL gReportedCrash = FALSE;
291
292bool gVerifySSLCert = true;
293
294BOOL gHandleKeysAsync = FALSE;
295
296// Use DirectX 9 to probe for hardware
297BOOL gProbeHardware = TRUE;
298std::string gSerialNumber;
299
300/////////////////////////////////////////////////////////////////////////////////
301// Application constants
302/////////////////////////////////////////////////////////////////////////////////
303
304BOOL gAgentMovementCompleted = FALSE;
305BOOL gHaveSavedSnapshot = FALSE;
306
307S32 gYieldMS = 0;
308BOOL gYieldTime = FALSE;
309
310const U32 COLLISION_LIST_DEPTH = 300; // only looks at the first so many collisionable objects
311const S32 MAX_CONSOLE_LINES = 500;
312const S32 NUM_SESSIONS_BEFORE_SHOW_PROFILE = 5;
313const F32 DEFAULT_AFK_TIMEOUT = 5.f * 60.f; // time with no input before user flagged as Away From Keyboard
314
315const char *VFS_DATA_FILE_BASE = "data.db2.x.";
316const char *VFS_INDEX_FILE_BASE = "index.db2.x.";
317
318F32 gSimLastTime;
319F32 gSimFrames;
320
321//#define RENDER_CLOUD_DENSITY // uncomment to look at cloud density
322
323/////////////////////////////////////////////////////////////////////////////////
324// Globals
325/////////////////////////////////////////////////////////////////////////////////
326
327//
328// Core Application globals
329//
330#if LL_WINDOWS
331llLCD *gLcdScreen = NULL;
332#endif
333
334LLString gSecondLife;
335LLString gWindowTitle;
336static char sWindowClass[] = "Second Life";
337LLString gDisabledMessage;
338BOOL gHideLinks = FALSE;
339
340// This is whether or not we are connect to a production grid.
341BOOL gInProductionGrid = FALSE;
342
343//#define APPLE_PREVIEW // Define this if you're doing a preview build on the Mac
344#if LL_RELEASE_FOR_DOWNLOAD
345// Default userserver for production builds is agni
346#ifndef APPLE_PREVIEW
347static EUserServerDomain UserServerDefaultChoice = USERSERVER_AGNI;
348#else
349static EUserServerDomain UserServerDefaultChoice = USERSERVER_ADITI;
350#endif
351#else
352// Default userserver for development builds is dmz
353static EUserServerDomain UserServerDefaultChoice = USERSERVER_DMZ;
354#endif
355
356#ifdef TOGGLE_HACKED_GODLIKE_VIEWER
357BOOL gHackGodmode = FALSE;
358#endif
359
360std::vector<std::string> gLoginURIs;
361static std::string gHelperURI;
362
363LLAgent gAgent;
364LLPipeline gPipeline;
365
366// Set true when the viewer has been disconnected from the server, for example,
367// by the user being kicked.
368BOOL gDoDisconnect = FALSE;
369BOOL gDisconnected = FALSE;
370
371// Tells us to clean up the cache directory in the case of network corruption
372BOOL gPurgeOnExit = FALSE;
373BOOL gPurgeCache = FALSE;
374
375// Allow multiple viewers in ReleaseForDownload
376#if LL_RELEASE_FOR_DOWNLOAD
377BOOL gMultipleViewersOK = FALSE;
378#else
379BOOL gMultipleViewersOK = TRUE;
380#endif
381BOOL gSecondInstance = FALSE;
382BOOL gDisableVoice = FALSE;
383
384LLString gArgs;
385
386// Setting this true will cause the app to exit cleanly at the end of the frame.
387BOOL gQuit = FALSE;
388// Set when user has indicated desire to quit, but may have modified documents open
389BOOL gQuitRequested = FALSE;
390LLString gLaunchFileOnQuit;
391BOOL gDoneLogout = FALSE;
392
393BOOL gInitializationComplete = FALSE; // used in windows handlers to determine if OK to call idle()
394BOOL gAutoLogin = FALSE;
395LLString gOldSettingsFileName;
396BOOL gPrintMessagesThisFrame = FALSE;
397const char* DEFAULT_SETTINGS_FILE = "settings.xml";
398const char* LEGACY_DEFAULT_SETTINGS_FILE = "settings.ini";
399BOOL gUseWireframe = FALSE;
400LLUUID gViewerDigest; // MD5 digest of the viewer's executable file.
401LLPumpIO* gServicePump = NULL;
402S32 gNumSessions = 0;
403
404BOOL gAllowIdleAFK = TRUE;
405F32 gAFKTimeout = DEFAULT_AFK_TIMEOUT;
406F32 gMouseSensitivity = 3.f;
407BOOL gInvertMouse = FALSE;
408BOOL gLogoutRequestSent = FALSE;
409LLTimer gLogoutTimer;
410BOOL gShowObjectUpdates = FALSE;
411
412const F32 LOGOUT_REQUEST_TIME = 6.f; // this will be cut short by the LogoutReply msg.
413F32 gLogoutMaxTime = LOGOUT_REQUEST_TIME;
414
415// Map scale in pixels per region
416F32 gMapScale = 128.f;
417F32 gMiniMapScale = 128.f;
418
419// Sky object, globals
420LLSky gSky;
421
422// HUD display lines in lower right
423BOOL gDisplayWindInfo = FALSE;
424BOOL gDisplayCameraPos = FALSE;
425BOOL gDisplayNearestWater = FALSE;
426BOOL gDoNearestWaterSearch = FALSE;
427BOOL gDisplayFOV = FALSE;
428
429// used to restore texture state after a mode switch
430LLFrameTimer gRestoreGLTimer;
431BOOL gRestoreGL = FALSE;
432
433
434// VFS globals - see viewer.h
435LLVFS* gStaticVFS = NULL;
436
437// Threads
438LLTextureCache* gTextureCache = NULL;
439LLWorkerThread* gImageDecodeThread = NULL;
440LLTextureFetch* gTextureFetch = NULL;
441
442// Debugging
443FILE *gDebugFile = NULL; // File pointer used by the function which writes debug data.
444BOOL gRandomizeFramerate = FALSE;
445BOOL gPeriodicSlowFrame = FALSE;
446std::map<S32,LLFrameTimer> gDebugTimers;
447
448//LLVector3 gCameraVelocitySmoothed;
449//
450// Timing/Performance/statistics globals
451//
452
453// Frame timing
454U64 gFrameTime = 0;
455F32 gFrameTimeSeconds = 0.f;
456F32 gFrameIntervalSeconds = 0.f;
457U32 gFrameCount = 0;
458U32 gForegroundFrameCount = 0; // number of frames that app window was in foreground
459U64 gStartTime = 0; // gStartTime is "private", used only to calculate gFrameTimeSeconds
460U64 gSpaceTime = 0; // gSpaceTime is the time, according to the spaceserver.
461
462// Timing Globals
463LLTimer gRenderStartTime;
464LLFrameTimer gForegroundTime;
465F32 gQuitAfterSeconds = 0.f;
466BOOL gRotateRight = FALSE;
467
468// Is the Pacific time zone (aka server time zone)
469// current in daylight savings time?
470BOOL gPacificDaylightTime = FALSE;
471
472//
473// Simulator/SpaceServer configuration information
474//
475U32 gSecondsPerDay = 0;
476U32 gSecondsPerYear = 0;
477
478LLString gLastVersionChannel;
479
480//
481// Region/Object globals
482//
483
484
485LLVector3 gWindVec(3.0, 3.0, 0.0);
486LLVector3 gRelativeWindVec(0.0, 0.0, 0.0);
487
488//
489// Globals for controls...
490//
491
492BOOL gVelocityInterpolate = TRUE; // (These are written once/frame with the data from gSavedSettings)
493BOOL gPingInterpolate = TRUE;
494
495//
496// System info
497//
498
499LLMemoryInfo gSysMemory;
500LLOSInfo gSysOS;
501
502// file globals
503static const char USAGE[] = "\n"
504"usage:\tviewer [options]\n"
505"options:\n"
506" -login <first> <last> <password> log in as a user\n"
507" -autologin log in as last saved user\n"
508" -loginuri <URI> login server and CGI script to use\n"
509" -helperuri <URI> helper web CGI prefix to use\n"
510" -settings <filename> specify the filename of a\n"
511" configuration file\n"
512" default is settings.xml\n"
513" -setdefault <variable> <value> specify the value of a particular\n"
514" configuration variable which can be\n"
515" overridden by settings.xml\n"
516" -set <variable> <value> specify the value of a particular\n"
517" configuration variable that\n"
518" overrides all other settings\n"
519" -user <user_server_ip> specify userserver in dotted quad\n"
520#if !LL_RELEASE_FOR_DOWNLOAD
521" -sim <simulator_ip> specify the simulator ip address\n"
522#endif
523" -god log in as god if you have god access\n"
524" -purge delete files in cache\n"
525" -safe reset preferences, run in safe mode\n"
526" -noutc logs in local time, not UTC\n"
527" -nothread run vfs in single thread\n"
528" -noinvlib Do not request inventory library\n"
529" -multiple allow multiple viewers\n"
530" -nomultiple block multiple viewers\n"
531" -novoice disable voice\n"
532" -ignorepixeldepth ignore pixel depth settings\n"
533" -cooperative [ms] yield some idle time to local host\n"
534" -skin ui/branding skin folder to use\n"
535#if LL_WINDOWS
536" -noprobe disable hardware probe\n"
537#endif
538" -noquicktime disable QuickTime movies, speeds startup\n"
539" -nopreload don't preload UI images or sounds, speeds startup\n"
540// these seem to be unused
541//" -noenv turn off environmental effects\n"
542//" -proxy <proxy_ip> specify the proxy ip address\n"
543"\n";
544
545
546// Variables used for passing data out of main
547BOOL gGodConnect = FALSE;
548BOOL gUseConsole = TRUE;
549BOOL gUseAudio = TRUE;
550BOOL gUseFMOD = TRUE;
551BOOL gLogMessages = FALSE;
552BOOL gRequestInventoryLibrary = TRUE;
553BOOL gAcceptTOS = FALSE;
554BOOL gAcceptCriticalMessage = FALSE;
555// this is the channel the viewer uses to check for updates/login
556std::string gChannelName = LL_CHANNEL;
557
558LLUUID gInventoryLibraryOwner;
559LLUUID gInventoryLibraryRoot;
560bool gPreloadImages = true;
561bool gPreloadSounds = true;
562
563LLString gCmdLineFirstName;
564LLString gCmdLineLastName;
565LLString gCmdLinePassword;
566std::map<std::string, std::string> gCommandLineSettings;
567std::map<std::string, std::string> gCommandLineForcedSettings;
568BOOL gLastExecFroze = FALSE;
569BOOL gIgnorePixelDepth = FALSE;
570
571
572LLPointer<LLImageGL> gDisconnectedImagep = NULL;
573
574
575/*
576class LLFirstInventoryLoadObserver : public LLInventoryObserver
577{
578 // Called when the inventory is first loaded.
579 // Must be allocated on the heap so that the inventory model can destroy it
580 // in case the app ends before the inventory finishes loading.
581public:
582 virtual void changed(U32 mask);
583 void observe(LLInventoryModel* model);
584protected:
585 LLInventoryModel* mInventory;
586};
587*/
588
589///////////////////////
590//
591// Forward declarations
592//
593
594//
595// Application initialization and cleanup
596//
597void init_marker_file();
598void init_crash_handler();
599void init_logging();
600void create_console();
601void write_system_info();
602int parse_args(int argc, char **argv);
603void saved_settings_to_globals();
604BOOL init_cache();
605void purge_cache();
606void cleanup_app();
607void disconnect_viewer(void *); // Don't use directly - use do_disconnect()
608
609//
610// Debugging, logging, and error reporting
611//
612void write_debug(const char *str); // Write a string to the debug log
613void write_debug(const std::string& str);
614void close_debug(); // Close the debug log
615void catch_signals();
616void viewer_crash_callback();
617void remove_marker_file();
618#if !LL_WINDOWS
619void release_signals();
620#endif
621
622void bad_network_handler();
623
624#if LL_WINDOWS
625void disable_win_error_reporting();
626#endif
627std::string get_serial_number();
628bool send_url_to_other_instance(const std::string& url);
629BOOL another_instance_running();
630void main_loop();
631
632//
633// Callbacks
634//
635// Callbacks and other stuff that's not directly used in main
636//
637void uuid_table_request_file_callback(void **user_data, S32 result, LLExtStat ext_status);
638void send_stats();
639
640//
641// Apple specific stuff
642//
643#if LL_DARWIN
644void init_apple_menu(const char* product);
645OSErr AEGURLHandler(const AppleEvent *messagein, AppleEvent *reply, long refIn);
646OSErr AEQuitHandler(const AppleEvent *messagein, AppleEvent *reply, long refIn);
647OSStatus simpleDialogHandler(EventHandlerCallRef handler, EventRef event, void *userdata);
648OSStatus DisplayReleaseNotes(void);
649#endif // LL_DARWIN
650
651void ui_audio_callback(const LLUUID& uuid)
652{
653 if (gAudiop)
654 {
655 F32 volume = gSavedSettings.getF32("AudioLevelUI");
656 gAudiop->triggerSound(uuid, gAgent.getID(), volume);
657 }
658}
659
660#if LL_WINDOWS
661BOOL CALLBACK login_dialog_func(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lparam)
662{
663 char buffer[MAX_STRING]; /* Flawfinder: ignore */
664 switch(msg)
665 {
666 case WM_INITDIALOG:
667 {
668 LLString first_name = gCmdLineFirstName;
669 LLString last_name = gCmdLineLastName;
670 LLString password = gCmdLinePassword;
671
672 if (!first_name.empty())
673 {
674 SetDlgItemTextA( hwnd, IDC_EDIT_FIRSTNAME, first_name.c_str());
675 }
676 if (!last_name.empty())
677 {
678 SetDlgItemTextA( hwnd, IDC_EDIT_LASTNAME, last_name.c_str());
679 }
680 if (!password.empty())
681 {
682 SetDlgItemTextA( hwnd, IDC_EDIT_PASSWORD, password.c_str());
683 }
684
685 first_name = gSavedSettings.getString("FirstName");
686 last_name = gSavedSettings.getString("LastName");
687 password = load_password_from_disk();
688
689 if (!first_name.empty())
690 {
691 SetDlgItemTextA( hwnd, IDC_EDIT_FIRSTNAME, first_name.c_str());
692 }
693 if (!last_name.empty())
694 {
695 SetDlgItemTextA( hwnd, IDC_EDIT_LASTNAME, last_name.c_str());
696 }
697 if (!password.empty())
698 {
699 SetDlgItemTextA( hwnd, IDC_EDIT_PASSWORD, password.c_str());
700 }
701
702 // Focus on password if other fields are full
703 if (first_name.empty())
704 {
705 SetFocus(GetDlgItem(hwnd, IDC_EDIT_FIRSTNAME));
706 }
707 else if (last_name.empty())
708 {
709 SetFocus(GetDlgItem(hwnd, IDC_EDIT_LASTNAME));
710 }
711 else if (password.empty())
712 {
713 SetFocus(GetDlgItem(hwnd, IDC_EDIT_PASSWORD));
714 }
715 else
716 {
717 SetFocus(GetDlgItem(hwnd, IDC_EDIT_FIRSTNAME));
718 }
719
720 BOOL remember_password = gSavedSettings.getBOOL("RememberPassword");
721 if (remember_password)
722 {
723 CheckDlgButton(hwnd, IDC_REMEMBER_PASSWORD, BST_CHECKED);
724 }
725 else
726 {
727 CheckDlgButton(hwnd, IDC_REMEMBER_PASSWORD, BST_UNCHECKED);
728 }
729 return TRUE;
730 }
731
732 case WM_COMMAND:
733 switch(LOWORD(wParam))
734 {
735 case IDOK:
736 {
737 // copy login name and password into buffer
738 if (GetDlgItemTextA(hwnd, IDC_EDIT_FIRSTNAME, buffer, MAX_STRING))
739 {
740 gCmdLineFirstName = buffer;
741 }
742 if (GetDlgItemTextA(hwnd, IDC_EDIT_LASTNAME, buffer, MAX_STRING))
743 {
744 gCmdLineLastName = buffer;
745 }
746 if (GetDlgItemTextA(hwnd, IDC_EDIT_PASSWORD, buffer, MAX_STRING))
747 {
748 gCmdLinePassword = buffer;
749 }
750 BOOL remember_password = (IsDlgButtonChecked(hwnd, IDC_REMEMBER_PASSWORD) == BST_CHECKED);
751 gSavedSettings.setBOOL("RememberPassword", remember_password);
752 EndDialog(hwnd, 0); // return success
753 return TRUE; // handled
754 }
755 }
756 // If we get here, we didn't handle it
757 return FALSE;
758
759 case WM_CLOSE:
760 case WM_DESTROY:
761 EndDialog(hwnd, 666); // assume user wants normal login screen
762 return TRUE; // handled
763
764 default:
765 return FALSE;
766 }
767}
768#endif
769
770#if LL_WINDOWS && LL_RELEASE_FOR_DOWNLOAD && LL_USE_SMARTHEAP
771
772MEM_BOOL MEM_CALLBACK second_mem_error_handler(MEM_ERROR_INFO *errorInfo);
773
774MEM_BOOL MEM_CALLBACK first_mem_error_handler(MEM_ERROR_INFO *errorInfo)
775{
776 MemSetErrorHandler(second_mem_error_handler);
777
778 // Really should free up reserved memory here and warn users
779 // with dialog they have precious little time left in Second
780 // Life!
781
782 llerrs << "Memory allocation failed; aborting." << llendl;
783 // llerrs << "Memory allocation failed; reserve memory released." << llendl;
784
785 // NOTREACHED better not be, but see second_mem_error_handler
786 // Could do that freeing up reserved memory thing here and
787 // return 1;
788 return 0;
789}
790
791MEM_BOOL MEM_CALLBACK second_mem_error_handler(MEM_ERROR_INFO *errorInfo)
792{
793 // Just in case "llerrs" and "llendl" cause another out-of-memory.
794 LLError::crashAndLoop("");
795 // NOTREACHED better not be!
796 return 0;
797}
798
799#endif // LL_WINDOWS && LL_RELEASE_FOR_DOWNLOAD && LL_USE_SMARTHEAP
800
801
802#if LL_WINDOWS
803int APIENTRY WinMain(HINSTANCE hInstance,
804 HINSTANCE hPrevInstance,
805 LPSTR lpCmdLine,
806 int nCmdShow)
807#else
808int main( int argc, char **argv )
809#endif
810{
811 LLMemType mt1(LLMemType::MTYPE_STARTUP);
812
813#if LL_SOLARIS && defined(__sparc)
814 asm ("ta\t6"); // NOTE: Make sure memory alignment is enforced on SPARC
815#endif
816
817#if LL_DARWIN
818 // Set the working dir to <bundle>/Contents/Resources
819 (void) chdir(gDirUtilp->getAppRODataDir().c_str());
820#endif
821
822#if 1
823 // This will eventually be done in LLApp
824 LLCommon::initClass();
825 // This should eventually be done in LLAppViewer
826# if MEM_TRACK_MEM
827 static const bool enable_threads = false;
828# else
829 static const bool enable_threads = true;
830# endif
831 LLVFSThread::initClass(enable_threads && true);
832 LLLFSThread::initClass(enable_threads && true);
833 // Image decoding
834 gImageDecodeThread = new LLWorkerThread("ImageDecode", enable_threads && true);
835 gTextureCache = new LLTextureCache(enable_threads && true);
836 gTextureFetch = new LLTextureFetch(gTextureCache, enable_threads && false);
837 LLImageWorker::initClass(gImageDecodeThread);
838 LLImageJ2C::openDSO();
839#endif
840
841#if LL_WINDOWS
842 // In Win32, we need to generate argc and argv ourselves...
843 // Note: GetCommandLine() returns a potentially return a LPTSTR
844 // which can resolve to a LPWSTR (unicode string).
845 // (That's why it's different from lpCmdLine which is a LPSTR.)
846 // We don't currently do unicode, so call the non-unicode version
847 // directly.
848 LPSTR cmd_line_including_exe_name = GetCommandLineA();
849
850 gIconResource = MAKEINTRESOURCE(IDI_LL_ICON);
851
852 const S32 MAX_ARGS = 100;
853 int argc = 0;
854 char* argv[MAX_ARGS]; /* Flawfinder: ignore */
855
856 char *token = NULL;
857 if( cmd_line_including_exe_name[0] == '\"' )
858 {
859 // Exe name is enclosed in quotes
860 token = strtok( cmd_line_including_exe_name, "\"" );
861 argv[argc++] = token;
862 token = strtok( NULL, " \t," );
863 }
864 else
865 {
866 // Exe name is not enclosed in quotes
867 token = strtok( cmd_line_including_exe_name, " \t," );
868 }
869
870 while( (token != NULL) && (argc < MAX_ARGS) )
871 {
872 argv[argc++] = token;
873 /* Get next token: */
874 if (*(token + strlen(token) + 1) == '\"') /* Flawfinder: ignore*/
875 {
876 token = strtok( NULL, "\"");
877 }
878 else
879 {
880 token = strtok( NULL, " \t," );
881 }
882 }
883#endif
884
885 // HACK! We REALLY want to know what grid they were trying to connect to if they
886 // crashed hard.
887 // So we walk through the command line args ONLY looking for the
888 // userserver arguments first. And we don't do ANYTHING but set
889 // the gUserServerName (which gets passed to the crash reporter).
890 // We're assuming that they're trying to log into the same grid as last
891 // time, which seems fairly reasonable.
892 snprintf(gUserServerName, MAX_STRING, "%s", gUserServerDomainName[UserServerDefaultChoice].mName); /* Flawfinder: ignore */
893 S32 j;
894 for (j = 1; j < argc; j++)
895 {
896 if (!strcmp(argv[j], "--aditi"))
897 {
898 snprintf(gUserServerName, MAX_STRING, "%s", gUserServerDomainName[USERSERVER_ADITI].mName); /* Flawfinder: ignore */
899 }
900 else if (!strcmp(argv[j], "--agni"))
901 {
902 snprintf(gUserServerName, MAX_STRING, "%s", gUserServerDomainName[USERSERVER_AGNI].mName); /* Flawfinder: ignore */
903 }
904 else if (!strcmp(argv[j], "--dmz"))
905 {
906 snprintf(gUserServerName, MAX_STRING, "%s", gUserServerDomainName[USERSERVER_DMZ].mName); /* Flawfinder: ignore */
907 }
908 else if (!strcmp(argv[j], "--siva"))
909 {
910 snprintf(gUserServerName, MAX_STRING, "%s", gUserServerDomainName[USERSERVER_SIVA].mName); /* Flawfinder: ignore */
911 }
912 else if (!strcmp(argv[j], "--shakti"))
913 {
914 sprintf(gUserServerName,"%s", gUserServerDomainName[USERSERVER_SHAKTI].mName);
915 }
916 else if (!strcmp(argv[j], "--durga"))
917 {
918 snprintf(gUserServerName, MAX_STRING, "%s", gUserServerDomainName[USERSERVER_DURGA].mName); /* Flawfinder: ignore */
919 }
920 else if (!strcmp(argv[j], "--soma"))
921 {
922 snprintf(gUserServerName, MAX_STRING, "%s", gUserServerDomainName[USERSERVER_SOMA].mName); /* Flawfinder: ignore */
923 }
924 else if (!strcmp(argv[j], "--ganga"))
925 {
926 snprintf(gUserServerName, MAX_STRING, "%s", gUserServerDomainName[USERSERVER_GANGA].mName); /* Flawfinder: ignore */
927 }
928 else if (!strcmp(argv[j], "--vaak"))
929 {
930 sprintf(gUserServerName,"%s", gUserServerDomainName[USERSERVER_VAAK].mName);
931 }
932 else if (!strcmp(argv[j], "--uma"))
933 {
934 sprintf(gUserServerName,"%s", gUserServerDomainName[USERSERVER_UMA].mName);
935 }
936 else if (!strcmp(argv[j], "-user") && (++j < argc))
937 {
938 if (!strcmp(argv[j], "-"))
939 {
940 snprintf(gUserServerName, MAX_STRING, "%s", LOOPBACK_ADDRESS_STRING); /* Flawfinder: ignore */
941 }
942 else
943 {
944 snprintf(gUserServerName, MAX_STRING, "%s", argv[j]); /* Flawfinder: ignore */
945 }
946 }
947 else if (!strcmp(argv[j], "-multiple"))
948 {
949 // Hack to detect -multiple so we can disable the marker file check (which will always fail)
950 gMultipleViewersOK = TRUE;
951 }
952 else if (!strcmp(argv[j], "-novoice"))
953 {
954 // May need to know this early also
955 gDisableVoice = TRUE;
956 }
957 }
958
959 //
960 // Start of the application
961 //
962 // IMPORTANT! Do NOT put anything that will write
963 // into the log files during normal startup until AFTER
964 // we run the "program crashed last time" error handler below.
965 //
966
967 // Need to do this initialization before we do anything else, since anything
968 // that touches files should really go through the lldir API
969 gDirUtilp->initAppDirs("SecondLife");
970
971 //
972 // Set up logging defaults for the viewer
973 //
974 LLError::initForApplication(
975 gDirUtilp->getExpandedFilename(LL_PATH_APP_SETTINGS, ""));
976 LLError::setFatalFunction(errorCallback);
977
978
979#if LL_RELEASE_FOR_DOWNLOAD && LL_SEND_CRASH_REPORTS
980 //
981 // Crash log if we hard crashed.
982 // Initialize crash logging
983 //
984 init_crash_handler();
985#endif
986 // Set up SecondLife.log
987 init_logging();
988
989 //
990 // OK to write stuff to logs now, we've now crash reported if necessary
991 //
992
993 // Set up some defaults...
994 gSettingsFileName = gDirUtilp->getExpandedFilename(LL_PATH_USER_SETTINGS, DEFAULT_SETTINGS_FILE);
995 gOldSettingsFileName = gDirUtilp->getExpandedFilename(LL_PATH_USER_SETTINGS, LEGACY_DEFAULT_SETTINGS_FILE);
996
997 /////////////////////////////////////////
998 //
999 // Process command line arguments
1000 //
1001 S32 args_result = 0;
1002
1003#if LL_DARWIN
1004 {
1005 // On the Mac, read in arguments.txt (if it exists) and process it for additional arguments.
1006 std::string args;
1007 if(_read_file_into_string(args, "arguments.txt"))
1008 {
1009 // The arguments file exists.
1010 // It should consist of command line arguments separated by newlines.
1011 // Split it into individual arguments and build a fake argv[] to pass to parse_args.
1012 std::vector<std::string> arglist;
1013
1014 arglist.push_back("newview");
1015
1016 llinfos << "Reading additional command line arguments from arguments.txt..." << llendl;
1017
1018 typedef boost::tokenizer<boost::escaped_list_separator<char> > tokenizer;
1019 boost::escaped_list_separator<char> sep("\\", "\r\n ", "\"'");
1020 tokenizer tokens(args, sep);
1021 tokenizer::iterator token_iter;
1022
1023 for(token_iter = tokens.begin(); token_iter != tokens.end(); ++token_iter)
1024 {
1025 llinfos << "argument: '" << (token_iter->c_str()) << "'" << llendl;
1026
1027 arglist.push_back(*token_iter);
1028 }
1029
1030 char **fakeargv = new char*[arglist.size()];
1031 int i;
1032 for(i=0; i < arglist.size(); i++)
1033 fakeargv[i] = const_cast<char*>(arglist[i].c_str());
1034
1035 args_result = parse_args(arglist.size(), fakeargv);
1036 delete[] fakeargv;
1037 }
1038
1039 // Get the user's preferred language string based on the Mac OS localization mechanism.
1040 // To add a new localization:
1041 // go to the "Resources" section of the project
1042 // get info on "language.txt"
1043 // in the "General" tab, click the "Add Localization" button
1044 // create a new localization for the language you're adding
1045 // set the contents of the new localization of the file to the string corresponding to our localization
1046 // (i.e. "en-us", "ja", etc. Use the existing ones as a guide.)
1047 CFURLRef url = CFBundleCopyResourceURL(CFBundleGetMainBundle(), CFSTR("language"), CFSTR("txt"), NULL);
1048 char path[MAX_PATH];
1049 if(CFURLGetFileSystemRepresentation(url, false, (UInt8 *)path, sizeof(path)))
1050 {
1051 std::string lang;
1052 if(_read_file_into_string(lang, path))
1053 {
1054 gCommandLineForcedSettings["SystemLanguage"] = lang;
1055 }
1056 }
1057 CFRelease(url);
1058 }
1059#endif
1060
1061 //
1062 // Parse the command line arguments
1063 //
1064 args_result |= parse_args(argc, argv);
1065 if (args_result)
1066 {
1067 remove_marker_file();
1068 return args_result;
1069 }
1070
1071 if (!strcmp(gUserServerName, gUserServerDomainName[USERSERVER_AGNI].mName))
1072 {
1073 gInProductionGrid = TRUE;
1074 }
1075
1076 // *TODO:translate
1077 gSecondLife = "Second Life";
1078
1079 // Read skin/branding settings if specified.
1080 if (! gDirUtilp->getSkinDir().empty() )
1081 {
1082 std::string skin_def_file = gDirUtilp->getExpandedFilename(LL_PATH_TOP_SKIN, "skin.xml");
1083 LLXmlTree skin_def_tree;
1084
1085 if (!skin_def_tree.parseFile(skin_def_file))
1086 {
1087 llerrs << "Failed to parse skin definition." << llendl;
1088 }
1089
1090 LLXmlTreeNode* rootp = skin_def_tree.getRoot();
1091 LLXmlTreeNode* disabled_message_node = rootp->getChildByName("disabled_message");
1092 if (disabled_message_node)
1093 {
1094 gDisabledMessage = disabled_message_node->getContents();
1095 }
1096
1097 static LLStdStringHandle hide_links_string = LLXmlTree::addAttributeString("hide_links");
1098 rootp->getFastAttributeBOOL(hide_links_string, gHideLinks);
1099
1100 // Legacy string. This flag really meant we didn't want to expose references to "Second Life".
1101 // Just set gHideLinks instead.
1102 static LLStdStringHandle silent_string = LLXmlTree::addAttributeString("silent_update");
1103 BOOL silent_update;
1104 rootp->getFastAttributeBOOL(silent_string, silent_update);
1105 gHideLinks = (gHideLinks || silent_update);
1106 }
1107
1108#if LL_DARWIN
1109 // Initialize apple menubar and various callbacks
1110 init_apple_menu(gSecondLife.c_str());
1111
1112#if __ppc__
1113 // If the CPU doesn't have Altivec (i.e. it's not at least a G4), don't go any further.
1114 // Only test PowerPC - all Intel Macs have SSE.
1115 if(!gSysCPU.hasAltivec())
1116 {
1117 std::ostringstream msg;
1118 msg << gSecondLife << " requires a processor with AltiVec (G4 or later).";
1119 OSMessageBox(
1120 msg.str().c_str(),
1121 NULL,
1122 OSMB_OK);
1123 remove_marker_file();
1124 return 1;
1125 }
1126#endif
1127
1128#endif // LL_DARWIN
1129
1130 // Display splash screen. Must be after above check for previous
1131 // crash as this dialog is always frontmost.
1132 std::ostringstream splash_msg;
1133 // *TODO:translate
1134 splash_msg << "Loading " << gSecondLife << "...";
1135 LLSplashScreen::show();
1136 LLSplashScreen::update(splash_msg.str().c_str());
1137
1138 LLVolumeMgr::initClass();
1139
1140 // Initialize the feature manager
1141 // The feature manager is responsible for determining what features
1142 // are turned on/off in the app.
1143 gFeatureManagerp = new LLFeatureManager;
1144
1145 gStartTime = totalTime();
1146
1147
1148 ////////////////////////////////////////
1149 //
1150 // Process ini files
1151 //
1152
1153 // declare all possible setting variables
1154 declare_settings();
1155
1156#if !LL_RELEASE_FOR_DOWNLOAD
1157// only write the defaults for non-release builds!
1158 gSavedSettings.saveToFile(gDirUtilp->getExpandedFilename(LL_PATH_USER_SETTINGS,"settings_default.xml").c_str(), FALSE);
1159#endif
1160
1161 //
1162 // Set the name of the window
1163 //
1164#if LL_RELEASE_FOR_DOWNLOAD
1165 gWindowTitle = gSecondLife;
1166#elif LL_DEBUG
1167 gWindowTitle = gSecondLife + LLString(" [DEBUG] ") + gArgs;
1168#else
1169 gWindowTitle = gSecondLife + LLString(" ") + gArgs;
1170#endif
1171 LLString::truncate(gWindowTitle, 255);
1172
1173 if (!gMultipleViewersOK)
1174 {
1175 //
1176 // Check for another instance of the app running
1177 //
1178
1179 // RN: if we received a URL, hand it off to the existing instance
1180 // don't call another_instance_running() when doing URL handoff, as
1181 // it relies on checking a marker file which will not work when running
1182 // out of different directories
1183 std::string slurl;
1184 if (!LLStartUp::sSLURLCommand.empty())
1185 {
1186 slurl = LLStartUp::sSLURLCommand;
1187 }
1188 else if (LLURLSimString::parse())
1189 {
1190 slurl = LLURLSimString::getURL();
1191 }
1192 if (!slurl.empty())
1193 {
1194 if (send_url_to_other_instance(slurl))
1195 {
1196 // successfully handed off URL to existing instance, exit
1197 return 1;
1198 }
1199 }
1200
1201 gSecondInstance = another_instance_running();
1202
1203 if (gSecondInstance)
1204 {
1205 std::ostringstream msg;
1206 msg <<
1207 gSecondLife << " is already running.\n"
1208 "\n"
1209 "Check your task bar for a minimized copy of the program.\n"
1210 "If this message persists, restart your computer.",
1211 OSMessageBox(
1212 msg.str().c_str(),
1213 NULL,
1214 OSMB_OK);
1215 return 1;
1216 }
1217
1218 init_marker_file();
1219
1220#if LL_SEND_CRASH_REPORTS
1221 if (gLastExecFroze)
1222 {
1223 llinfos << "Last execution froze, requesting to send crash report." << llendl;
1224 //
1225 // Pop up a freeze or crash warning dialog
1226 //
1227 std::ostringstream msg;
1228 msg << gSecondLife
1229 << " appears to have frozen or crashed on the previous run.\n"
1230 << "Would you like to send a crash report?";
1231 std::string alert;
1232 alert = gSecondLife;
1233 alert += " Alert";
1234 S32 choice = OSMessageBox(msg.str().c_str(),
1235 alert.c_str(),
1236 OSMB_YESNO);
1237 if (OSBTN_YES == choice)
1238 {
1239 llinfos << "Sending crash report." << llendl;
1240
1241 remove_marker_file();
1242#if LL_WINDOWS
1243 std::string exe_path = gDirUtilp->getAppRODataDir();
1244 exe_path += gDirUtilp->getDirDelimiter();
1245 exe_path += "win_crash_logger.exe";
1246
1247 std::string arg_string = "-previous -user ";
1248 arg_string += gUserServerName;
1249 arg_string += " -name \"";
1250 arg_string += gSecondLife;
1251 arg_string += "\"";
1252 // Spawn crash logger.
1253 // NEEDS to wait until completion, otherwise log files will get smashed.
1254 _spawnl(_P_WAIT, exe_path.c_str(), exe_path.c_str(), arg_string.c_str(), NULL);
1255#elif LL_DARWIN
1256 std::string command_str;
1257 command_str = "crashreporter.app/Contents/MacOS/crashreporter ";
1258 command_str += "-previous -user ";
1259 command_str += gUserServerName;
1260 // XXX -- We need to exit fullscreen mode for this to work.
1261 // XXX -- system() also doesn't wait for completion. Hmm...
1262 system(command_str.c_str()); /* Flawfinder: Ignore */
1263#elif LL_LINUX || LL_SOLARIS
1264 std::string cmd =gDirUtilp->getAppRODataDir();
1265 cmd += gDirUtilp->getDirDelimiter();
1266#if LL_LINUX
1267 cmd += "linux-crash-logger.bin";
1268#else // LL_SOLARIS
1269 cmd += "bin/solaris-crash-logger";
1270#endif
1271 char* const cmdargv[] =
1272 {(char*)cmd.c_str(),
1273 (char*)"-previous",
1274 (char*)"-user",
1275 (char*)gUserServerName,
1276 (char*)"-name",
1277 (char*)gSecondLife.c_str(),
1278 NULL};
1279 fflush(NULL);
1280 pid_t pid = fork();
1281 if (pid == 0)
1282 { // child
1283 execv(cmd.c_str(), cmdargv); /* Flawfinder: Ignore */
1284 llwarns << "execv failure when trying to start " << cmd << llendl;
1285 _exit(1); // avoid atexit()
1286 } else {
1287 if (pid > 0)
1288 {
1289 // wait for child proc to die
1290 int childExitStatus;
1291 waitpid(pid, &childExitStatus, 0);
1292 } else {
1293 llwarns << "fork failure." << llendl;
1294 }
1295 }
1296#endif
1297 }
1298 else
1299 {
1300 llinfos << "Not sending crash report." << llendl;
1301 }
1302 }
1303#endif // #if LL_SEND_CRASH_REPORTS
1304 }
1305 else
1306 {
1307 gSecondInstance = another_instance_running();
1308
1309 if (gSecondInstance)
1310 {
1311 gDisableVoice = TRUE;
1312 }
1313
1314 init_marker_file();
1315 }
1316
1317 //
1318 // Write system information into the debug log (CPU, OS, etc.)
1319 //
1320 write_system_info();
1321
1322#if LL_WINDOWS
1323 //
1324 // Turn off Windows XP Error Reporting
1325 // (Don't send our data to Microsoft--at least until we are Logo approved and have a way
1326 // of getting the data back from them.)
1327 //
1328 llinfos << "Turning off Windows error reporting." << llendl;
1329 disable_win_error_reporting();
1330#endif // LL_WINDOWS
1331
1332 // Build a string representing the current version number.
1333 gCurrentVersion = llformat("%s %d.%d.%d.%d", gChannelName.c_str(), LL_VERSION_MAJOR, LL_VERSION_MINOR, LL_VERSION_PATCH, LL_VERSION_BUILD );
1334
1335 //
1336 // Various introspection concerning the libs we're using
1337 //
1338 llinfos << "J2C Engine is: " << LLImageJ2C::getEngineInfo() << llendl;
1339
1340 //
1341 // Load the feature tables
1342 //
1343 llinfos << "Loading feature tables." << llendl;
1344
1345 gFeatureManagerp->loadFeatureTables();
1346 gFeatureManagerp->initCPUFeatureMasks();
1347
1348 // Merge with the command line overrides
1349 gSavedSettings.applyOverrides(gCommandLineSettings);
1350
1351 // Need to do this before calling parseAlerts
1352 gUICtrlFactory = new LLViewerUICtrlFactory();
1353
1354 // Pre-load alerts.xml to define the warnings settings (always loads from skins/xui/en-us/)
1355 // Do this *before* loading the settings file
1356 LLAlertDialog::parseAlerts("alerts.xml", &gSavedSettings, TRUE);
1357
1358 // Overwrite default settings with user settings
1359 llinfos << "Loading configuration file " << gSettingsFileName << llendl;
1360 if (0 == gSavedSettings.loadFromFile(gSettingsFileName))
1361 {
1362 llinfos << "Failed to load settings from " << gSettingsFileName << llendl;
1363 llinfos << "Loading legacy settings from " << gOldSettingsFileName << llendl;
1364 gSavedSettings.loadFromFileLegacy(gOldSettingsFileName);
1365 }
1366
1367 // need to do this here - need to have initialized global settings first
1368 LLString nextLoginLocation = gSavedSettings.getString( "NextLoginLocation" );
1369 if ( nextLoginLocation.length() )
1370 {
1371 LLURLSimString::setString( nextLoginLocation.c_str() );
1372 };
1373
1374 // Merge with the command line overrides
1375 gSavedSettings.applyOverrides(gCommandLineForcedSettings);
1376
1377 gLastRunVersion = gSavedSettings.getString("LastRunVersion");
1378
1379 fixup_settings();
1380
1381 // Get the single value from the crash settings file, if it exists
1382 std::string crash_settings_filename = gDirUtilp->getExpandedFilename(LL_PATH_USER_SETTINGS, CRASH_SETTINGS_FILE);
1383 gCrashSettings.loadFromFile(crash_settings_filename.c_str());
1384
1385 /////////////////////////////////////////////////
1386 // OS-specific login dialogs
1387 /////////////////////////////////////////////////
1388#if LL_WINDOWS
1389 /*
1390 // Display initial login screen, comes up quickly. JC
1391 {
1392 LLSplashScreen::hide();
1393
1394 INT_PTR result = DialogBox(hInstance, L"CONNECTBOX", NULL, login_dialog_func);
1395 if (result < 0)
1396 {
1397 llwarns << "Connect dialog box failed, returned " << result << llendl;
1398 return 1;
1399 }
1400 // success, result contains which button user clicked
1401 llinfos << "Connect dialog box clicked " << result << llendl;
1402
1403 LLSplashScreen::show();
1404 }
1405 */
1406#endif
1407
1408 // track number of times that app has run
1409 gNumSessions = gSavedSettings.getS32("NumSessions");
1410 gNumSessions++;
1411 gSavedSettings.setS32("NumSessions", gNumSessions);
1412
1413 gSavedSettings.setString("HelpLastVisitedURL",gSavedSettings.getString("HelpHomeURL"));
1414
1415 if (gSavedSettings.getBOOL("VerboseLogs"))
1416 {
1417 LLError::setPrintLocation(true);
1418 }
1419
1420#if !LL_RELEASE_FOR_DOWNLOAD
1421 if (gUserServerChoice == USERSERVER_NONE)
1422 {
1423 // Development version: load last server choice by default (overridden by cmd line args)
1424
1425 S32 server = gSavedSettings.getS32("ServerChoice");
1426 if (server != 0)
1427 gUserServerChoice = (EUserServerDomain)llclamp(server, 0, (S32)USERSERVER_COUNT - 1);
1428 if (server == USERSERVER_OTHER)
1429 {
1430 LLString custom_server = gSavedSettings.getString("CustomServer");
1431 if (custom_server.empty())
1432 {
1433 snprintf(gUserServerName, MAX_STRING, "none"); /* Flawfinder: ignore */
1434 }
1435 else
1436 {
1437 snprintf(gUserServerName, MAX_STRING, "%s", custom_server.c_str()); /* Flawfinder: ignore */
1438 }
1439 }
1440 }
1441#endif
1442
1443 if (gUserServerChoice == USERSERVER_NONE)
1444 {
1445 gUserServerChoice = UserServerDefaultChoice;
1446 }
1447
1448 // Load art UUID information, don't require these strings to be declared in code.
1449 LLString viewer_art_filename = gDirUtilp->getExpandedFilename(LL_PATH_APP_SETTINGS,"viewerart.xml");
1450 llinfos << "Loading art table from " << viewer_art_filename << llendl;
1451 gViewerArt.loadFromFile(viewer_art_filename.c_str(), FALSE);
1452 LLString textures_filename = gDirUtilp->getExpandedFilename(LL_PATH_SKINS, "textures", "textures.xml");
1453 llinfos << "Loading art table from " << textures_filename << llendl;
1454 gViewerArt.loadFromFile(textures_filename.c_str(), FALSE);
1455
1456 LLString colors_base_filename = gDirUtilp->getExpandedFilename(LL_PATH_APP_SETTINGS, "colors_base.xml");
1457 llinfos << "Loading base colors from " << colors_base_filename << llendl;
1458 gColors.loadFromFile(colors_base_filename.c_str(), FALSE, TYPE_COL4U);
1459
1460 // Load overrides from user colors file
1461 LLString user_colors_filename = gDirUtilp->getExpandedFilename(LL_PATH_APP_SETTINGS, "colors.xml");
1462 llinfos << "Loading user colors from " << user_colors_filename << llendl;
1463 if (gColors.loadFromFile(user_colors_filename.c_str(), FALSE, TYPE_COL4U) == 0)
1464 {
1465 llinfos << "Failed to load user colors from " << user_colors_filename << llendl;
1466 LLString user_legacy_colors_filename = gDirUtilp->getExpandedFilename(LL_PATH_APP_SETTINGS, "colors.ini");
1467 llinfos << "Loading legacy colors from " << user_legacy_colors_filename << llendl;
1468 gColors.loadFromFileLegacy(user_legacy_colors_filename.c_str(), FALSE, TYPE_COL4U);
1469 }
1470
1471 // Widget construction depends on LLUI being initialized
1472 LLUI::initClass(&gSavedSettings,
1473 &gColors,
1474 &gViewerArt,
1475 &gImageList,
1476 ui_audio_callback,
1477 &LLUI::sGLScaleFactor);
1478
1479 gUICtrlFactory->setupPaths(); // update paths with correct language set
1480
1481 /////////////////////////////////////////////////
1482 //
1483 // Load settings files
1484 //
1485 //
1486 LLGroupMgr::parseRoleActions("role_actions.xml");
1487
1488 LLAgent::parseTeleportMessages("teleport_strings.xml");
1489
1490 // Move certain saved settings into global variables for speed
1491 saved_settings_to_globals();
1492
1493 // 1.6.10: Default crash reporting to on, because this dialog doesn't
1494 // make any sense for a new user.
1495 /*
1496 if (!gSavedSettings.getBOOL("AskedAboutCrashReports"))
1497 {
1498 // NOTE: Be sure to use gSecondLife if this becomes uncommented.
1499 S32 retval = OSMessageBox(
1500 "Welcome! You have successfully installed Second Life.\n"
1501 "\n"
1502 "Should performance issues or crashes occur, Second Life\n"
1503 "can automatically send technical data about your computer\n"
1504 "to Linden Lab to diagnose and correct the problem.\n"
1505 "This information is used solely to address technical issues\n"
1506 "and is kept strictly confidential.\n"
1507 "You can change this setting under Preferences / General.\n"
1508 "Automatically send technical data if problems occur?",
1509 "Second Life",
1510 OSMB_YESNO);
1511 if (OSBTN_YES == retval)
1512 {
1513 gAutoReportCrashes = TRUE;
1514 }
1515 else
1516 {
1517 gAutoReportCrashes = FALSE;
1518 }
1519
1520 // Setting will be saved automatically at shutdown.
1521 gSavedSettings.setBOOL("AskedAboutCrashReports", TRUE);
1522 }
1523 */
1524
1525 // Find partition serial number (Windows) or hardware serial (Mac)
1526 gSerialNumber = get_serial_number();
1527
1528#if LL_WINDOWS
1529 //
1530 // Do driver verification and initialization based on DirectX
1531 // hardware polling and driver versions
1532 //
1533 if (gProbeHardware)
1534 {
1535 BOOL vram_only = !gSavedSettings.getBOOL("ProbeHardwareOnStartup");
1536
1537 LLSplashScreen::update("Detecting hardware...");
1538
1539 llinfos << "Attempting to poll DirectX for hardware info" << llendl;
1540 gDXHardware.setWriteDebugFunc(write_debug);
1541 BOOL probe_ok = gDXHardware.getInfo(vram_only);
1542
1543 if (!probe_ok
1544 && gSavedSettings.getWarning("AboutDirectX9"))
1545 {
1546 llinfos << "DirectX probe failed, alerting user." << llendl;
1547
1548 // Warn them that runnin without DirectX 9 will
1549 // not allow us to tell them about driver issues
1550 std::ostringstream msg;
1551 msg <<
1552 gSecondLife << " is unable to detect DirectX 9.0b or greater.\n"
1553 "\n" <<
1554 gSecondLife << " uses DirectX to detect hardware and/or\n"
1555 "outdated drivers that can cause stability problems,\n"
1556 "poor performance and crashes. While you can run\n" <<
1557 gSecondLife << " without it, we highly recommend running\n"
1558 "with DirectX 9.0b\n"
1559 "\n"
1560 "Do you wish to continue?\n";
1561 S32 button = OSMessageBox(
1562 msg.str().c_str(),
1563 "Warning",
1564 OSMB_YESNO);
1565 if (OSBTN_NO== button)
1566 {
1567 llinfos << "User quitting after failed DirectX 9 detection" << llendl;
1568 LLWeb::loadURLExternal(DIRECTX_9_URL);
1569 return 0;
1570 }
1571 gSavedSettings.setWarning("AboutDirectX9", FALSE);
1572 }
1573 llinfos << "Done polling DirectX for hardware info" << llendl;
1574
1575 // Only probe once after installation
1576 gSavedSettings.setBOOL("ProbeHardwareOnStartup", FALSE);
1577
1578 // Disable so debugger can work
1579 LLSplashScreen::update(splash_msg.str().c_str());
1580 }
1581
1582 if (!LLWinDebug::setupExceptionHandler())
1583 {
1584 llwarns << " Someone took over my exception handler (post hardware probe)!" << llendl;
1585 }
1586
1587 gGLManager.mVRAM = gDXHardware.getVRAM();
1588 llinfos << "Detected VRAM: " << gGLManager.mVRAM << llendl;
1589#endif
1590
1591 // Always fetch the Ethernet MAC address, needed both for login
1592 // and password load.
1593 LLUUID::getNodeID(gMACAddress);
1594
1595 // Prepare for out-of-memory situations, during which we will crash on
1596 // purpose and save a dump.
1597#if LL_WINDOWS && LL_RELEASE_FOR_DOWNLOAD && LL_USE_SMARTHEAP
1598 MemSetErrorHandler(first_mem_error_handler);
1599#endif // LL_WINDOWS && LL_RELEASE_FOR_DOWNLOAD && LL_USE_SMARTHEAP
1600
1601 gViewerStats = new LLViewerStats();
1602
1603 //
1604 // Initialize the VFS, and gracefully handle initialization errors
1605 //
1606
1607 if (!init_cache())
1608 {
1609 std::ostringstream msg;
1610 msg <<
1611 gSecondLife << " is unable to access a file that it needs.\n"
1612 "\n"
1613 "This can be because you somehow have multiple copies running, "
1614 "or your system incorrectly thinks a file is open. "
1615 "If this message persists, restart your computer and try again. "
1616 "If it continues to persist, you may need to completely uninstall " <<
1617 gSecondLife << " and reinstall it.";
1618 OSMessageBox(
1619 msg.str().c_str(),
1620 NULL,
1621 OSMB_OK);
1622 return 1;
1623 }
1624
1625#if LL_DARWIN
1626 // Display the release notes for the current version
1627 if(!gHideLinks && gCurrentVersion != gLastRunVersion)
1628 {
1629 // Current version and last run version don't match exactly. Display the release notes.
1630 DisplayReleaseNotes();
1631 }
1632#endif
1633
1634 //
1635 // Initialize the window
1636 //
1637
1638 // pop up debug console if necessary
1639#if LL_WINDOWS
1640 if (gUseConsole && gSavedSettings.getBOOL("ShowConsoleWindow"))
1641 {
1642 create_console();
1643 }
1644#endif
1645
1646 llinfos << "Initializing window..." << llendl;
1647
1648 // store setting in a global for easy access and modification
1649 gNoRender = gSavedSettings.getBOOL("DisableRendering");
1650
1651 // Hide the splash screen
1652 LLSplashScreen::hide();
1653
1654 // HACK: Need a non-const char * for stupid window name (propagated deep down)
1655 char window_title_str[256]; /* Flawfinder: ignore */
1656 strncpy(window_title_str, gWindowTitle.c_str(), sizeof(window_title_str) - 1); /* Flawfinder: ignore */
1657 window_title_str[sizeof(window_title_str) - 1] = '\0';
1658
1659 // always start windowed
1660 gViewerWindow = new LLViewerWindow(window_title_str, sWindowClass,
1661 gSavedSettings.getS32("WindowX"), gSavedSettings.getS32("WindowY"),
1662 gSavedSettings.getS32("WindowWidth"), gSavedSettings.getS32("WindowHeight"),
1663 FALSE, gIgnorePixelDepth);
1664
1665 if (gSavedSettings.getBOOL("FullScreen"))
1666 {
1667 gViewerWindow->toggleFullscreen(FALSE);
1668 // request to go full screen... which will be delayed until login
1669 }
1670
1671 if (gSavedSettings.getBOOL("WindowMaximized"))
1672 {
1673 gViewerWindow->mWindow->maximize();
1674 gViewerWindow->getWindow()->setNativeAspectRatio(gSavedSettings.getF32("FullScreenAspectRatio"));
1675 }
1676
1677 LLUI::sWindow = gViewerWindow->getWindow();
1678
1679 LLAlertDialog::parseAlerts("alerts.xml");
1680 LLNotifyBox::parseNotify("notify.xml");
1681
1682 LLMediaEngine::initClass();
1683
1684 //
1685 // Clean up the feature manager lookup table - settings were updated
1686 // in the LLViewerWindow constructor
1687 //
1688 gFeatureManagerp->cleanupFeatureTables();
1689
1690 // Show watch cursor
1691 gViewerWindow->setCursor(UI_CURSOR_WAIT);
1692
1693
1694#if LL_WINDOWS && LL_LCD_COMPILE
1695 // start up an LCD window on a logitech keyboard, if there is one
1696 gLcdScreen = new llLCD(hInstance);
1697 CreateLCDDebugWindows();
1698#endif
1699
1700 // Finish view initialization
1701 gViewerWindow->initBase();
1702
1703 // show viewer window
1704 gViewerWindow->mWindow->show();
1705
1706 write_debug(gGLManager.getGLInfoString());
1707 llinfos << gGLManager.getGLInfoString() << llendl;
1708
1709 //load key settings
1710 bind_keyboard_functions();
1711
1712 // Load Default bindings
1713 if (!gViewerKeyboard.loadBindings(gDirUtilp->getExpandedFilename(LL_PATH_APP_SETTINGS,"keys.ini").c_str()))
1714 {
1715 llerrs << "Unable to open keys.ini" << llendl;
1716 }
1717 // Load Custom bindings (override defaults)
1718 gViewerKeyboard.loadBindings(gDirUtilp->getExpandedFilename(LL_PATH_APP_SETTINGS,"custom_keys.ini").c_str());
1719
1720 // Calculate the digest for the executable (takes < 90ms on a fast machine).
1721 FILE* app_file = LLFile::fopen( gDirUtilp->getExecutablePathAndName().c_str(), "rb" ); /* Flawfinder: ignore */
1722 if( app_file )
1723 {
1724 LLMD5 app_md5;
1725 app_md5.update( app_file ); // Automatically closes the file
1726 app_md5.finalize();
1727 app_md5.raw_digest( gViewerDigest.mData );
1728 }
1729 llinfos << "Viewer Digest: " << gViewerDigest << llendl;
1730
1731 // If we don't have the right GL requirements, exit.
1732 // BUG: This should just be changed to a generic GL "Not good enough" flag
1733 if (!gGLManager.mHasMultitexture && !gNoRender)
1734 {
1735 std::ostringstream msg;
1736 msg <<
1737 "You do not appear to have the proper hardware requirements "
1738 "for " << gSecondLife << ". " << gSecondLife << " requires an OpenGL graphics "
1739 "card that has multitexture support. If this is the case, "
1740 "you may want to make sure that you have the latest drivers for "
1741 "your graphics card, and service packs and patches for your "
1742 "operating system.\n"
1743 "If you continue to have problems, please go to: "
1744 "www.secondlife.com/support ";
1745 OSMessageBox(
1746 msg.str().c_str(),
1747 NULL,
1748 OSMB_OK);
1749 return 0;
1750 }
1751
1752 // Save the current version to the prefs file
1753 gSavedSettings.setString("LastRunVersion", gCurrentVersion);
1754
1755 gSimLastTime = gRenderStartTime.getElapsedTimeF32();
1756 gSimFrames = (F32)gFrameCount;
1757
1758 //-------------------------------------------
1759 // Run main loop until time to quit
1760 //-------------------------------------------
1761 main_loop();
1762
1763 cleanup_app();
1764
1765 // If we're exiting to launch an URL, do that here so the screen
1766 // is at the right resolution before we launch IE.
1767 if (!gLaunchFileOnQuit.empty())
1768 {
1769#if LL_WINDOWS
1770 // Indicate an application is starting.
1771 SetCursor(LoadCursor(NULL, IDC_WAIT));
1772#endif
1773
1774 // HACK: Attempt to wait until the screen res. switch is complete.
1775 ms_sleep(1000);
1776
1777 LLWeb::loadURLExternal( gLaunchFileOnQuit );
1778 }
1779
1780 llinfos << "Goodbye" << llendflush;
1781 return 0;
1782}
1783
1784bool send_url_to_other_instance(const std::string& url)
1785{
1786#if LL_WINDOWS
1787 wchar_t window_class[256]; /* Flawfinder: ignore */ // Assume max length < 255 chars.
1788 mbstowcs(window_class, sWindowClass, 255);
1789 window_class[255] = 0;
1790 // Use the class instead of the window name.
1791 HWND other_window = FindWindow(window_class, NULL);
1792 if (other_window != NULL)
1793 {
1794 lldebugs << "Found other window with the name '" << gWindowTitle << "'" << llendl;
1795 COPYDATASTRUCT cds;
1796 const S32 SLURL_MESSAGE_TYPE = 0;
1797 cds.dwData = SLURL_MESSAGE_TYPE;
1798 cds.cbData = url.length() + 1;
1799 cds.lpData = (void*)url.c_str();
1800
1801 LRESULT msg_result = SendMessage(other_window, WM_COPYDATA, NULL, (LPARAM)&cds);
1802 lldebugs << "SendMessage(WM_COPYDATA) to other window '"
1803 << gWindowTitle << "' returned " << msg_result << llendl;
1804 return true;
1805 }
1806#endif
1807 return false;
1808}
1809
1810BOOL another_instance_running()
1811{
1812 // We create a marker file when the program starts and remove the file when it finishes.
1813 // If the file is currently locked, that means another process is already running.
1814
1815 std::string marker_file = gDirUtilp->getExpandedFilename(LL_PATH_LOGS,"SecondLife.exec_marker");
1816 llinfos << "Checking marker file for lock..." << llendl;
1817
1818 // If file doesn't exist, we create it
1819 // If file does exist, try to get writing privilages
1820 FILE* fMarker = LLFile::fopen(marker_file.c_str(), "rb"); /* Flawfinder: ignore */
1821 if (fMarker != NULL)
1822 {
1823 // File exists, try opening with write permissions
1824 fclose(fMarker);
1825 fMarker = LLFile::fopen(marker_file.c_str(), "wb"); /* Flawfinder: ignore */
1826 if (fMarker == NULL)
1827 {
1828 llinfos << "Marker file is locked." << llendl;
1829 return TRUE;
1830 }
1831#if LL_DARWIN
1832 // Try to lock it. On Mac, this is the only way to test if it's actually locked.
1833 if (flock(fileno(fMarker), LOCK_EX | LOCK_NB) == -1)
1834 {
1835 // Lock failed - somebody else has it.
1836 fclose(fMarker);
1837 llinfos << "Marker file is locked." << llendl;
1838 return TRUE;
1839 }
1840#endif
1841 fclose(fMarker);
1842 }
1843 llinfos << "Marker file isn't locked." << llendl;
1844 return FALSE;
1845}
1846
1847
1848void check_for_events()
1849{
1850 LLFastTimer t2(LLFastTimer::FTM_MESSAGES);
1851#if LL_WINDOWS
1852 if (!LLWinDebug::setupExceptionHandler())
1853 {
1854 llwarns << " Someone took over my exception handler (post messagehandling)!" << llendl;
1855 }
1856#endif
1857
1858 gViewerWindow->mWindow->gatherInput();
1859}
1860
1861#include "moviemaker.h"
1862extern BOOL gbCapturing;
1863
1864#if !LL_SOLARIS
1865extern MovieMaker gMovieMaker;
1866#endif
1867
1868void main_loop()
1869{
1870 // Create IO Pump to use for HTTP Requests.
1871 gServicePump = new LLPumpIO(gAPRPoolp);
1872 LLHTTPClient::setPump(*gServicePump);
1873 LLHTTPClient::setCABundle(gDirUtilp->getCAFile());
1874
1875 // initialize voice stuff here
1876 gLocalSpeakerMgr = new LLLocalSpeakerMgr();
1877 gActiveChannelSpeakerMgr = new LLActiveSpeakerMgr();
1878
1879 LLVoiceChannel::initClass();
1880 LLVoiceClient::init(gServicePump);
1881
1882 LLMemType mt1(LLMemType::MTYPE_MAIN);
1883 LLTimer frameTimer,idleTimer;
1884 LLTimer debugTime;
1885
1886 // Handle messages
1887 while (!gQuit)
1888 {
1889 LLFastTimer::reset(); // Should be outside of any timer instances
1890 {
1891 LLFastTimer t(LLFastTimer::FTM_FRAME);
1892
1893 check_for_events();
1894
1895#if 1 && !RELEASE_FOR_DOWNLOAD
1896 // once per second debug info
1897 if (debugTime.getElapsedTimeF32() > 1.f)
1898 {
1899 debugTime.reset();
1900 }
1901#endif
1902 if (!gQuit)
1903 {
1904 // Scan keyboard for movement keys. Command keys and typing
1905 // are handled by windows callbacks. Don't do this until we're
1906 // done initializing. JC
1907 if (gViewerWindow->mWindow->getVisible()
1908 && gViewerWindow->getActive()
1909 && !gViewerWindow->mWindow->getMinimized()
1910 && LLStartUp::getStartupState() == STATE_STARTED
1911 && !gViewerWindow->getShowProgress()
1912 && !gFocusMgr.focusLocked())
1913 {
1914 gKeyboard->scanKeyboard();
1915 LLViewerJoystick::scanJoystick();
1916 }
1917
1918 // Update state based on messages, user input, object idle.
1919 {
1920 LLFastTimer t3(LLFastTimer::FTM_IDLE);
1921 idle();
1922 LLCurl::process();
1923 gAres->process();
1924 // this pump is necessary to make the login screen show up
1925 gServicePump->pump();
1926 gServicePump->callback();
1927 }
1928
1929 if (gDoDisconnect && (LLStartUp::getStartupState() == STATE_STARTED))
1930 {
1931 save_final_snapshot(NULL);
1932 disconnect_viewer(NULL);
1933 }
1934
1935 // Render scene.
1936 if (!gQuit)
1937 {
1938 display();
1939
1940 LLFloaterSnapshot::update(); // take snapshots
1941
1942#if !LL_SOLARIS
1943 if (gbCapturing)
1944 {
1945 gMovieMaker.Snap();
1946 }
1947#endif
1948#if LL_WINDOWS && LL_LCD_COMPILE
1949 // update LCD Screen
1950 gLcdScreen->UpdateDisplay();
1951#endif
1952 }
1953 }
1954
1955 // Sleep and run background threads
1956 {
1957 LLFastTimer t2(LLFastTimer::FTM_SLEEP);
1958 bool run_multiple_threads = gSavedSettings.getBOOL("RunMultipleThreads");
1959
1960 // yield some time to the os based on command line option
1961 if(gYieldTime)
1962 {
1963 ms_sleep(gYieldMS);
1964 }
1965
1966 // yield cooperatively when not running as foreground window
1967 if ( gNoRender
1968 || !gViewerWindow->mWindow->getVisible()
1969 || !gFocusMgr.getAppHasFocus())
1970 {
1971 // Sleep if we're not rendering, or the window is minimized.
1972 S32 milliseconds_to_sleep = llclamp(gSavedSettings.getS32("BackgroundYieldTime"), 0, 1000);
1973 // don't sleep when BackgroundYieldTime set to 0, since this will still yield to other threads
1974 // of equal priority on Windows
1975 if (milliseconds_to_sleep > 0)
1976 {
1977 ms_sleep(milliseconds_to_sleep);
1978 // also pause worker threads during this wait period
1979 gTextureCache->pause();
1980 gImageDecodeThread->pause();
1981 }
1982 }
1983
1984 if (gRandomizeFramerate)
1985 {
1986 ms_sleep(rand() % 200);
1987 }
1988
1989 if (gPeriodicSlowFrame
1990 && (gFrameCount % 10 == 0))
1991 {
1992 llinfos << "Periodic slow frame - sleeping 500 ms" << llendl;
1993 ms_sleep(500);
1994 }
1995
1996
1997 const F64 min_frame_time = 0.0; //(.0333 - .0010); // max video frame rate = 30 fps
1998 const F64 min_idle_time = 0.0; //(.0010); // min idle time = 1 ms
1999 const F64 max_idle_time = run_multiple_threads ? min_idle_time : .005; // 5 ms
2000 idleTimer.reset();
2001 while(1)
2002 {
2003 S32 work_pending = 0;
2004 S32 io_pending = 0;
2005 work_pending += gTextureCache->update(1); // unpauses the texture cache thread
2006 work_pending += gImageDecodeThread->update(1); // unpauses the image thread
2007 work_pending += gTextureFetch->update(1); // unpauses the texture fetch thread
2008 io_pending += LLVFSThread::updateClass(1);
2009 io_pending += LLLFSThread::updateClass(1);
2010 if (io_pending > 1000)
2011 {
2012 ms_sleep(llmin(io_pending/100,100)); // give the vfs some time to catch up
2013 }
2014
2015 F64 frame_time = frameTimer.getElapsedTimeF64();
2016 F64 idle_time = idleTimer.getElapsedTimeF64();
2017 if (frame_time >= min_frame_time &&
2018 idle_time >= min_idle_time &&
2019 (!work_pending || idle_time >= max_idle_time))
2020 {
2021 break;
2022 }
2023 }
2024 frameTimer.reset();
2025
2026 // Prevent the worker threads from running while rendering.
2027 // if (LLThread::processorCount()==1) //pause() should only be required when on a single processor client...
2028 if (run_multiple_threads == FALSE)
2029 {
2030 gTextureCache->pause();
2031 gImageDecodeThread->pause();
2032 // gTextureFetch->pause(); // Don't pause the fetch (IO) thread
2033 }
2034 //LLVFSThread::sLocal->pause(); // Prevent the VFS thread from running while rendering.
2035 //LLLFSThread::sLocal->pause(); // Prevent the LFS thread from running while rendering.
2036 }
2037
2038 }
2039 }
2040
2041 // Save snapshot for next time, if we made it through initialization
2042 if (STATE_STARTED == LLStartUp::getStartupState())
2043 {
2044 save_final_snapshot(NULL);
2045 }
2046
2047 delete gServicePump;
2048
2049 llinfos << "Exiting main_loop" << llendflush;
2050}
2051
2052
2053void process_keystrokes_async()
2054{
2055#if LL_WINDOWS
2056 MSG msg;
2057 // look through all input messages, leaving them in the event queue
2058 while( PeekMessage(&msg, NULL, 0, 0, PM_NOREMOVE | PM_NOYIELD))
2059 {
2060 // on first mouse message, break out
2061 if (msg.message >= WM_MOUSEFIRST &&
2062 msg.message <= WM_MOUSELAST ||
2063 msg.message == WM_QUIT)
2064 {
2065 break;
2066 }
2067
2068 // this is a message we want to handle now, so remove it from the event queue
2069 PeekMessage(&msg, NULL, msg.message, msg.message, PM_REMOVE | PM_NOYIELD);
2070 // if (msg.message == WM_KEYDOWN)
2071 // {
2072 // llinfos << "Process async key down " << (U32)msg.wParam << llendl;
2073 // }
2074 TranslateMessage(&msg);
2075 DispatchMessage(&msg);
2076 }
2077
2078 // Scan keyboard for movement keys. Command keys and typing
2079 // are handled by windows callbacks. Don't do this until we're
2080 // done initializing. JC
2081 if (gViewerWindow->mWindow->getVisible()
2082 && gViewerWindow->getActive()
2083 && !gViewerWindow->mWindow->getMinimized()
2084 && LLStartUp::getStartupState() == STATE_STARTED
2085 && !gViewerWindow->getShowProgress()
2086 && !gFocusMgr.focusLocked())
2087 {
2088 gKeyboard->scanKeyboard();
2089 }
2090#endif
2091}
2092
2093////////////////////////////////////////////////
2094//
2095// Functions
2096//
2097
2098FILE *gMarkerFile = NULL;
2099
2100void remove_marker_file()
2101{
2102 llinfos << "remove_marker_file()" << llendl;
2103 if (gMarkerFile != NULL)
2104 {
2105 fclose(gMarkerFile);
2106 gMarkerFile = NULL;
2107 }
2108 if( gDirUtilp )
2109 {
2110 LLString marker_file = gDirUtilp->getExpandedFilename(LL_PATH_LOGS,"SecondLife.exec_marker");
2111 ll_apr_file_remove( marker_file );
2112 }
2113}
2114
2115void init_marker_file()
2116{
2117#if LL_SOLARIS
2118 struct flock fl;
2119 fl.l_whence = SEEK_SET;
2120 fl.l_start = 0;
2121 fl.l_len = 1;
2122#endif
2123 // We create a marker file when the program starts and remove the file when it finishes.
2124 // If the file is currently locked, that means another process is already running.
2125 // If the file exists and isn't locked, we crashed on the last run.
2126
2127 std::string marker_file = gDirUtilp->getExpandedFilename(LL_PATH_LOGS,"SecondLife.exec_marker");
2128 llinfos << "Checking marker file for lock..." << llendl;
2129
2130 FILE* fMarker = LLFile::fopen(marker_file.c_str(), "rb"); /* Flawfinder: ignore */
2131 if (fMarker != NULL)
2132 {
2133 // File exists, try opening with write permissions
2134 fclose(fMarker);
2135 fMarker = LLFile::fopen(marker_file.c_str(), "wb"); /* Flawfinder: ignore */
2136 if (fMarker == NULL)
2137 {
2138 // Another instance is running. Skip the rest of these operations.
2139 llinfos << "Marker file is locked." << llendl;
2140 return;
2141 }
2142#if LL_DARWIN
2143 // Try to lock it. On Mac, this is the only way to test if it's actually locked.
2144 if (flock(fileno(fMarker), LOCK_EX | LOCK_NB) == -1)
2145 {
2146 // Lock failed - somebody else has it.
2147 fclose(fMarker);
2148 llinfos << "Marker file is locked." << llendl;
2149 return;
2150 }
2151#endif
2152
2153 // No other instances; we'll lock this file now & delete on quit.
2154 fclose(fMarker);
2155 gLastExecFroze = TRUE;
2156 llinfos << "Exec marker found: program froze on previous execution" << llendl;
2157 }
2158
2159 // Create the marker file for this execution & lock it
2160// FILE *fp_executing_marker;
2161#if LL_WINDOWS
2162 gMarkerFile = LLFile::_fsopen(marker_file.c_str(), "w", _SH_DENYWR);
2163#else
2164 gMarkerFile = LLFile::fopen(marker_file.c_str(), "w"); /* Flawfinder: ignore */
2165 if (gMarkerFile)
2166 {
2167 int fd = fileno(gMarkerFile);
2168 // Attempt to lock
2169#if LL_SOLARIS
2170 fl.l_type = F_WRLCK;
2171 if (fcntl(fd, F_SETLK, &fl) == -1)
2172#else
2173 if (flock(fd, LOCK_EX | LOCK_NB) == -1)
2174#endif
2175 {
2176 llinfos << "Failed to lock file." << llendl;
2177 }
2178 }
2179#endif
2180 if (gMarkerFile)
2181 {
2182 llinfos << "Marker file created." << llendl;
2183 }
2184 else
2185 {
2186 llinfos << "Failed to create marker file." << llendl;
2187 }
2188
2189#if LL_WINDOWS
2190 // Clean up SecondLife.dmp files, to avoid confusion
2191 llinfos << "Removing SecondLife.dmp" << llendl;
2192 std::string dmp_filename = gDirUtilp->getExpandedFilename(LL_PATH_LOGS, "SecondLife.dmp");
2193 LLFile::remove(dmp_filename.c_str());
2194#endif
2195
2196 // This is to keep the crash reporter from constantly sending stale message logs
2197 // We wipe the message file now.
2198 llinfos << "Removing message.log" << llendl;
2199 std::string message_filename = gDirUtilp->getExpandedFilename(LL_PATH_LOGS, "message.log");
2200 LLFile::remove(message_filename.c_str());
2201
2202 llinfos << "Exiting init_marker_file()." << llendl;
2203}
2204
2205void init_crash_handler()
2206{
2207 //////////////////////////////////////////
2208 //
2209 // Set up error handling and logging for LL_RELEASE_FOR_DOWNLOAD
2210 //
2211
2212 // Signal handling (or Win32 exception handling
2213 catch_signals();
2214
2215 // Set the crash callback for the viewer
2216 gCrashCallback = viewer_crash_callback;
2217}
2218
2219void init_logging()
2220{
2221 // Remove the last ".old" log file.
2222 std::string old_log_file = gDirUtilp->getExpandedFilename(LL_PATH_LOGS,
2223 "SecondLife.old");
2224 LLFile::remove(old_log_file.c_str());
2225
2226#if LL_LINUX || LL_SOLARIS
2227 // Remove the last stack trace, if any
2228 std::string old_stack_file =
2229 gDirUtilp->getExpandedFilename(LL_PATH_LOGS,"stack_trace.log");
2230 LLFile::remove(old_stack_file.c_str());
2231#endif // LL_LINUX || LL_SOLARIS
2232
2233 // Rename current log file to ".old"
2234 std::string log_file = gDirUtilp->getExpandedFilename(LL_PATH_LOGS,
2235 "SecondLife.log");
2236 LLFile::rename(log_file.c_str(), old_log_file.c_str());
2237
2238 // Set the log file to SecondLife.log
2239
2240 LLError::logToFile(log_file);
2241}
2242
2243void write_system_info()
2244{
2245 write_debug("SL Log: ");
2246 write_debug(LLError::logFileName());
2247 write_debug("\n");
2248
2249 std::string tmp_str = gSecondLife
2250 + llformat(" version %d.%d.%d build %d",
2251 LL_VERSION_MAJOR, LL_VERSION_MINOR, LL_VERSION_PATCH, LL_VIEWER_BUILD);
2252 write_debug(tmp_str.c_str());
2253 write_debug("\n");
2254 write_debug(gSysCPU.getCPUString());
2255 write_debug("\n");
2256
2257 tmp_str = llformat("RAM: %u KB\n", gSysMemory.getPhysicalMemoryKB());
2258 write_debug(tmp_str.c_str());
2259 write_debug("OS: ");
2260 write_debug(gSysOS.getOSString().c_str());
2261 write_debug("\n");
2262
2263 // Dump some debugging info
2264 llinfos << gSecondLife << " version "
2265 << LL_VERSION_MAJOR << "."
2266 << LL_VERSION_MINOR << "."
2267 << LL_VERSION_PATCH
2268 << llendl;
2269
2270 // Dump the local time and time zone
2271 time_t now;
2272 time(&now);
2273 char tbuffer[256]; /* Flawfinder: ignore */
2274 strftime(tbuffer, 256, "%Y-%m-%dT%H:%M:%S %Z", localtime(&now));
2275 llinfos << "Local time: " << tbuffer << llendl;
2276
2277 // query some system information
2278 llinfos << "CPU info:\n" << gSysCPU << llendl;
2279 llinfos << "Memory info:\n" << gSysMemory << llendl;
2280 llinfos << "OS info: " << gSysOS << llendl;
2281}
2282
2283#if LL_WINDOWS
2284void disable_win_error_reporting()
2285{
2286 const char win_xp_string[] = "Microsoft Windows XP";
2287 BOOL is_win_xp = ( gSysOS.getOSString().substr(0, strlen(win_xp_string) ) == win_xp_string ); /* Flawfinder: ignore*/
2288 if( is_win_xp )
2289 {
2290 // Note: we need to use run-time dynamic linking, because load-time dynamic linking will fail
2291 // on systems that don't have the library installed (all non-Windows XP systems)
2292 HINSTANCE fault_rep_dll_handle = LoadLibrary(L"faultrep.dll"); /* Flawfinder: ignore */
2293 if( fault_rep_dll_handle )
2294 {
2295 pfn_ADDEREXCLUDEDAPPLICATIONA pAddERExcludedApplicationA = (pfn_ADDEREXCLUDEDAPPLICATIONA) GetProcAddress(fault_rep_dll_handle, "AddERExcludedApplicationA");
2296 if( pAddERExcludedApplicationA )
2297 {
2298
2299 // Strip the path off the name
2300 const char* executable_name = gDirUtilp->getExecutableFilename().c_str();
2301
2302 if( 0 == pAddERExcludedApplicationA( executable_name ) )
2303 {
2304 U32 error_code = GetLastError();
2305 llinfos << "AddERExcludedApplication() failed with error code " << error_code << llendl;
2306 }
2307 else
2308 {
2309 llinfos << "AddERExcludedApplication() success for " << executable_name << llendl;
2310 }
2311 }
2312 FreeLibrary( fault_rep_dll_handle );
2313 }
2314 }
2315}
2316#endif // LL_WINDOWS
2317
2318// On Windows, get the C:\ volume serial number.
2319// On Mac, return the hardware serial number.
2320std::string get_serial_number()
2321{
2322 char serial_md5[MD5HEX_STR_SIZE]; /* Flawfinder: ignore */
2323 serial_md5[0] = 0;
2324
2325#if LL_WINDOWS
2326 DWORD serial = 0;
2327 DWORD flags = 0;
2328 BOOL success = GetVolumeInformation(
2329 L"C:\\",
2330 NULL, // volume name buffer
2331 0, // volume name buffer size
2332 &serial, // volume serial
2333 NULL, // max component length
2334 &flags, // file system flags
2335 NULL, // file system name buffer
2336 0); // file system name buffer size
2337 if (success)
2338 {
2339 LLMD5 md5;
2340 md5.update( (unsigned char*)&serial, sizeof(DWORD));
2341 md5.finalize();
2342 md5.hex_digest(serial_md5);
2343 }
2344 else
2345 {
2346 llwarns << "GetVolumeInformation failed" << llendl;
2347 }
2348 return serial_md5;
2349
2350#elif LL_DARWIN
2351 // JC: Sample code from http://developer.apple.com/technotes/tn/tn1103.html
2352 CFStringRef serialNumber = NULL;
2353 io_service_t platformExpert = IOServiceGetMatchingService(kIOMasterPortDefault,
2354 IOServiceMatching("IOPlatformExpertDevice"));
2355 if (platformExpert) {
2356 serialNumber = (CFStringRef) IORegistryEntryCreateCFProperty(platformExpert,
2357 CFSTR(kIOPlatformSerialNumberKey),
2358 kCFAllocatorDefault, 0);
2359 IOObjectRelease(platformExpert);
2360 }
2361
2362 if (serialNumber)
2363 {
2364 char buffer[MAX_STRING]; /* Flawfinder: ignore */
2365 if (CFStringGetCString(serialNumber, buffer, MAX_STRING, kCFStringEncodingASCII))
2366 {
2367 LLMD5 md5( (unsigned char*)buffer );
2368 md5.hex_digest(serial_md5);
2369 }
2370 CFRelease(serialNumber);
2371 }
2372
2373 return serial_md5;
2374
2375#elif LL_LINUX || LL_SOLARIS
2376 // TODO
2377 return serial_md5;
2378
2379#endif
2380}
2381
2382#if LL_LINUX
2383#define MAX_STACK_TRACE_DEPTH 40
2384// This uses glibc's basic built-in stack-trace functions for a not very
2385// amazing backtrace.
2386static inline BOOL do_basic_glibc_backtrace()
2387{
2388 void *array[MAX_STACK_TRACE_DEPTH];
2389 size_t size;
2390 char **strings;
2391 size_t i;
2392 BOOL success = FALSE;
2393
2394 size = backtrace(array, MAX_STACK_TRACE_DEPTH);
2395 strings = backtrace_symbols(array, size);
2396
2397 std::string strace_filename = gDirUtilp->getExpandedFilename(LL_PATH_LOGS,"stack_trace.log");
2398 llinfos << "Opening stack trace file " << strace_filename << llendl;
2399 FILE* StraceFile = LLFile::fopen(strace_filename.c_str(), "w"); /* Flawfinder: ignore */
2400 if (!StraceFile)
2401 {
2402 llinfos << "Opening stack trace file " << strace_filename << " failed. Using stderr." << llendl;
2403 StraceFile = stderr;
2404 }
2405
2406 if (size)
2407 {
2408 for (i = 0; i < size; i++)
2409 fputs((std::string(strings[i])+"\n").c_str(),
2410 StraceFile);
2411
2412 success = TRUE;
2413 }
2414
2415 if (StraceFile != stderr)
2416 fclose(StraceFile);
2417
2418 free (strings);
2419 return success;
2420}
2421
2422#if LL_ELFBIN
2423// This uses glibc's basic built-in stack-trace functions together with
2424// ELFIO's ability to parse the .symtab ELF section for better symbol
2425// extraction without exporting symbols (which'd cause subtle, fatal bugs).
2426static inline BOOL do_elfio_glibc_backtrace()
2427{
2428 void *array[MAX_STACK_TRACE_DEPTH];
2429 size_t btsize;
2430 char **strings;
2431 BOOL success = FALSE;
2432
2433 std::string appfilename = gDirUtilp->getExecutablePathAndName();
2434
2435 std::string strace_filename = gDirUtilp->getExpandedFilename(LL_PATH_LOGS,"stack_trace.log");
2436 llinfos << "Opening stack trace file " << strace_filename << llendl;
2437 FILE* StraceFile = LLFile::fopen(strace_filename.c_str(), "w"); /* Flawfinder: ignore */
2438 if (!StraceFile)
2439 {
2440 llinfos << "Opening stack trace file " << strace_filename << " failed. Using stderr." << llendl;
2441 StraceFile = stderr;
2442 }
2443
2444 // get backtrace address list and basic symbol info
2445 btsize = backtrace(array, MAX_STACK_TRACE_DEPTH);
2446 strings = backtrace_symbols(array, btsize);
2447
2448 // create ELF reader for our app binary
2449 IELFI* pReader;
2450 const IELFISection* pSec = NULL;
2451 IELFISymbolTable* pSymTbl = 0;
2452 if (ERR_ELFIO_NO_ERROR != ELFIO::GetInstance()->CreateELFI(&pReader) ||
2453 ERR_ELFIO_NO_ERROR != pReader->Load(appfilename.c_str()) ||
2454 // find symbol table, create reader-object
2455 NULL == (pSec = pReader->GetSection( ".symtab" )) ||
2456 ERR_ELFIO_NO_ERROR != pReader->CreateSectionReader(IELFI::ELFI_SYMBOL, pSec, (void**)&pSymTbl) )
2457 {
2458 // Failed to open our binary and read its symbol table somehow
2459 llinfos << "Could not initialize ELF symbol reading - doing basic backtrace." << llendl;
2460 if (StraceFile != stderr)
2461 fclose(StraceFile);
2462 // note that we may be leaking some of the above ELFIO
2463 // objects now, but it's expected that we'll be dead soon
2464 // and we want to tread delicately until we get *some* kind
2465 // of useful backtrace.
2466 return do_basic_glibc_backtrace();
2467 }
2468
2469 // iterate over trace and symtab, looking for plausible symbols
2470 std::string name;
2471 Elf32_Addr value;
2472 Elf32_Word ssize;
2473 unsigned char bind;
2474 unsigned char type;
2475 Elf32_Half section;
2476 int nSymNo = pSymTbl->GetSymbolNum();
2477 size_t btpos;
2478 for (btpos = 0; btpos < btsize; ++btpos)
2479 {
2480 fprintf(StraceFile, "%d:\t", btpos);
2481 int symidx;
2482 for (symidx = 0; symidx < nSymNo; ++symidx)
2483 {
2484 if (ERR_ELFIO_NO_ERROR ==
2485 pSymTbl->GetSymbol(symidx, name, value, ssize,
2486 bind, type, section))
2487 {
2488 // check if trace address within symbol range
2489 if (uintptr_t(array[btpos]) >= value &&
2490 uintptr_t(array[btpos]) < value+ssize)
2491 {
2492 char *demangled_str = NULL;
2493 int demangle_result = 1;
2494 demangled_str =
2495 abi::__cxa_demangle
2496 (name.c_str(), NULL, NULL,
2497 &demangle_result);
2498 if (0 == demangle_result &&
2499 NULL != demangled_str) {
2500 fprintf(StraceFile,
2501 "ELF(%s", demangled_str);
2502 free(demangled_str);
2503 }
2504 else // failed demangle; print it raw
2505 {
2506 fprintf(StraceFile,
2507 "ELF(%s", name.c_str());
2508 }
2509 // print offset from symbol start
2510 fprintf(StraceFile,
2511 "+0x%lx) [%p]\n",
2512 uintptr_t(array[btpos]) -
2513 value,
2514 array[btpos]);
2515 goto got_sym; // early escape
2516 }
2517 }
2518 }
2519 // Fallback:
2520 // Didn't find a suitable symbol in the binary - it's probably
2521 // a symbol in a DSO; use glibc's idea of what it should be.
2522 fprintf(StraceFile, "%s\n", strings[btpos]);
2523 got_sym:;
2524 }
2525
2526 if (StraceFile != stderr)
2527 fclose(StraceFile);
2528
2529 pSymTbl->Release();
2530 pSec->Release();
2531 pReader->Release();
2532
2533 free(strings);
2534
2535 llinfos << "Finished generating stack trace." << llendl;
2536
2537 success = TRUE;
2538 return success;
2539}
2540#endif // LL_ELFBIN
2541#endif // LL_LINUX
2542
2543/* Report whether we're being run under the control of a debugger. */
2544static inline bool being_debugged()
2545{
2546 static enum {unknown, no, yes} debugged = unknown;
2547
2548 if (debugged == unknown)
2549 {
2550#if LL_LINUX
2551 pid_t ppid = getppid();
2552 char *name;
2553 int ret;
2554
2555 ret = asprintf(&name, "/proc/%d/exe", ppid);
2556 if (ret != -1)
2557 {
2558 char buf[1024];
2559 ssize_t n;
2560
2561 n = readlink(name, buf, sizeof(buf) - 1);
2562 if (n != -1)
2563 {
2564 char *base = strrchr(buf, '/');
2565 buf[n + 1] = '\0';
2566 if (base == NULL)
2567 {
2568 base = buf;
2569 } else {
2570 base += 1;
2571 }
2572
2573 if (strcmp(base, "gdb") == 0)
2574 {
2575 debugged = yes;
2576 }
2577 }
2578 free(name);
2579 }
2580#endif // LL_LINUX
2581 }
2582
2583 return debugged == yes;
2584}
2585
2586#ifdef LL_SOLARIS
2587static inline BOOL do_basic_glibc_backtrace()
2588{
2589 BOOL success = FALSE;
2590
2591 std::string strace_filename = gDirUtilp->getExpandedFilename(LL_PATH_LOGS,"stack_trace.log");
2592 llinfos << "Opening stack trace file " << strace_filename << llendl;
2593 FILE* StraceFile = LLFile::fopen(strace_filename.c_str(), "w");
2594 if (!StraceFile)
2595 {
2596 llinfos << "Opening stack trace file " << strace_filename << " failed. Using stderr." << llendl;
2597 StraceFile = stderr;
2598 }
2599
2600 printstack(fileno(StraceFile));
2601
2602 if (StraceFile != stderr)
2603 fclose(StraceFile);
2604
2605 return success;
2606}
2607#endif // LL_SOLARIS
2608
2609void viewer_crash_callback()
2610{
2611 // This will drop us into the debugger.
2612 if (being_debugged())
2613 {
2614 abort();
2615 }
2616
2617 // Returns whether a dialog was shown.
2618 // Only do the logic in here once
2619 if (gReportedCrash)
2620 {
2621 return;
2622 }
2623 gReportedCrash = TRUE;
2624
2625 BOOL do_crash_report = FALSE;
2626
2627 do_crash_report = TRUE;
2628
2629 write_debug("Viewer exe: ");
2630 write_debug(gDirUtilp->getExecutablePathAndName().c_str());
2631 write_debug("\n");
2632 write_debug("Cur path: ");
2633 write_debug(gDirUtilp->getCurPath().c_str());
2634 write_debug("\n\n");
2635
2636 if (gMessageSystem && gDirUtilp)
2637 {
2638 std::string filename;
2639 filename = gDirUtilp->getExpandedFilename(LL_PATH_LOGS, "stats.log");
2640 llofstream file(filename.c_str(), llofstream::binary);
2641 if(file.good())
2642 {
2643 gMessageSystem->summarizeLogs(file);
2644 }
2645 }
2646
2647 if (gMessageSystem)
2648 {
2649 write_debug(gMessageSystem->getCircuitInfoString());
2650 gMessageSystem->stopLogging();
2651 }
2652 write_debug("\n");
2653 if (gWorldp)
2654 {
2655 write_debug(gWorldp->getInfoString());
2656 }
2657
2658 // Close the debug file
2659 close_debug();
2660 LLError::logToFile("");
2661
2662 // Close the SecondLife.log
2663
2664 remove_marker_file();
2665
2666#if LL_WINDOWS
2667 // Windows
2668 std::string exe_path = gDirUtilp->getAppRODataDir();
2669 exe_path += gDirUtilp->getDirDelimiter();
2670 exe_path += "win_crash_logger.exe";
2671
2672 std::string arg_string = "-user ";
2673 arg_string += gUserServerName;
2674
2675 switch(gCrashBehavior)
2676 {
2677 case CRASH_BEHAVIOR_ASK:
2678 default:
2679 arg_string += " -dialog ";
2680 _spawnl(_P_NOWAIT, exe_path.c_str(), exe_path.c_str(), arg_string.c_str(), NULL);
2681 break;
2682
2683 case CRASH_BEHAVIOR_ALWAYS_SEND:
2684 _spawnl(_P_NOWAIT, exe_path.c_str(), exe_path.c_str(), arg_string.c_str(), NULL);
2685 break;
2686
2687 case CRASH_BEHAVIOR_NEVER_SEND:
2688 break;
2689 }
2690
2691#elif LL_DARWIN
2692 // Macintosh
2693 LLString command_str;
2694 command_str = "crashreporter.app/Contents/MacOS/crashreporter ";
2695 command_str += "-user ";
2696 command_str += gUserServerName;
2697 command_str += " &"; // This backgrounds the command so system() doesn't block until the crashreporter exits.
2698 system(command_str.c_str()); /* Flawfinder: ignore */
2699
2700 // Sometimes signals don't seem to quit the viewer.
2701 // Make sure we exit so as to not totally confuse the user.
2702 exit(1);
2703#elif LL_LINUX || LL_SOLARIS
2704 // Always generate the report, have the logger do the asking, and
2705 // don't wait for the logger before exiting (-> total cleanup).
2706 if (CRASH_BEHAVIOR_NEVER_SEND != gCrashBehavior)
2707 {
2708 // This backtrace writes into stack_trace.log
2709# if LL_ELFBIN
2710 do_elfio_glibc_backtrace(); // more useful backtrace
2711# else
2712 do_basic_glibc_backtrace(); // only slightly useful backtrace
2713# endif // LL_ELFBIN
2714 // launch the actual crash logger
2715 char* ask_dialog = "-dialog";
2716 if (CRASH_BEHAVIOR_ASK != gCrashBehavior)
2717 ask_dialog = ""; // omit '-dialog' option
2718 std::string cmd =gDirUtilp->getAppRODataDir();
2719 cmd += gDirUtilp->getDirDelimiter();
2720 cmd += "linux-crash-logger.bin";
2721 char* const cmdargv[] =
2722 {(char*)cmd.c_str(),
2723 ask_dialog,
2724 (char*)"-user",
2725 (char*)gUserServerName,
2726 (char*)"-name",
2727 (char*)gSecondLife.c_str(),
2728 NULL};
2729 fflush(NULL);
2730 pid_t pid = fork();
2731 if (pid == 0)
2732 { // child
2733 execv(cmd.c_str(), cmdargv); /* Flawfinder: ignore */
2734 llwarns << "execv failure when trying to start " << cmd << llendl;
2735 _exit(1); // avoid atexit()
2736 } else {
2737 if (pid > 0)
2738 {
2739 // DO NOT wait for child proc to die; we want
2740 // the logger to outlive us while we quit to
2741 // free up the screen/keyboard/etc.
2742 ////int childExitStatus;
2743 ////waitpid(pid, &childExitStatus, 0);
2744 } else {
2745 llwarns << "fork failure." << llendl;
2746 }
2747 }
2748 }
2749 // Sometimes signals don't seem to quit the viewer.
2750 // Make sure we exit so as to not totally confuse the user.
2751 exit(1);
2752#else
2753 #error do something with your platform.
2754#endif // LL_DARWIN
2755
2756 return;
2757}
2758
2759
2760
2761BOOL init_cache()
2762{
2763 gPurgeCache = FALSE;
2764 // Purge cache if user requested it
2765 if (gSavedSettings.getBOOL("PurgeCacheOnStartup") ||
2766 gSavedSettings.getBOOL("PurgeCacheOnNextStartup"))
2767 {
2768 gSavedSettings.setBOOL("PurgeCacheOnNextStartup", FALSE);
2769 gPurgeCache = TRUE;
2770 }
2771 // Purge cache if it belongs to an old version
2772 else
2773 {
2774 static const S32 cache_version = 5;
2775 if (gSavedSettings.getS32("LocalCacheVersion") != cache_version)
2776 {
2777 gPurgeCache = TRUE;
2778 gSavedSettings.setS32("LocalCacheVersion", cache_version);
2779 }
2780 }
2781
2782 // Setup and verify the cache location
2783 LLString cache_location = gSavedSettings.getString("CacheLocation");
2784 LLString new_cache_location = gSavedSettings.getString("NewCacheLocation");
2785 if (new_cache_location != cache_location)
2786 {
2787 gDirUtilp->setCacheDir(gSavedSettings.getString("CacheLocation"));
2788 purge_cache(); // purge old cache
2789 gSavedSettings.setString("CacheLocation", new_cache_location);
2790 }
2791
2792 if (!gDirUtilp->setCacheDir(gSavedSettings.getString("CacheLocation")))
2793 {
2794 llwarns << "Unable to set cache location" << llendl;
2795 gSavedSettings.setString("CacheLocation", "");
2796 }
2797
2798 if (gPurgeCache)
2799 {
2800 LLSplashScreen::update("Clearing cache...");
2801 purge_cache();
2802 }
2803
2804 LLSplashScreen::update("Initializing Texture Cache...");
2805
2806 // Init the texture cache
2807 // Allocate 80% of the cache size for textures
2808 BOOL read_only = gSecondInstance ? TRUE : FALSE;
2809 const S32 MB = 1024*1024;
2810 S64 cache_size = (S64)(gSavedSettings.getU32("CacheSize")) * MB;
2811 const S64 MAX_CACHE_SIZE = 1024*MB;
2812 cache_size = llmin(cache_size, MAX_CACHE_SIZE);
2813 S64 texture_cache_size = ((cache_size * 8)/10);
2814 S64 extra = gTextureCache->initCache(LL_PATH_CACHE, texture_cache_size, read_only);
2815 texture_cache_size -= extra;
2816
2817 LLSplashScreen::update("Initializing VFS...");
2818
2819 // Init the VFS
2820 S64 vfs_size = cache_size - texture_cache_size;
2821 const S64 MAX_VFS_SIZE = 1024 * MB; // 1 GB
2822 vfs_size = llmin(vfs_size, MAX_VFS_SIZE);
2823 vfs_size = (vfs_size / MB) * MB; // make sure it is MB aligned
2824 U32 vfs_size_u32 = (U32)vfs_size;
2825 U32 old_vfs_size = gSavedSettings.getU32("VFSOldSize") * MB;
2826 bool resize_vfs = (vfs_size_u32 != old_vfs_size);
2827 if (resize_vfs)
2828 {
2829 gSavedSettings.setU32("VFSOldSize", vfs_size_u32/MB);
2830 }
2831 llinfos << "VFS CACHE SIZE: " << vfs_size/(1024*1024) << " MB" << llendl;
2832
2833 // This has to happen BEFORE starting the vfs
2834 //time_t ltime;
2835 srand(time(NULL)); /* Flawfinder: ignore */
2836 U32 old_salt = gSavedSettings.getU32("VFSSalt");
2837 U32 new_salt;
2838 char old_vfs_data_file[LL_MAX_PATH]; /* Flawfinder: ignore */
2839 char old_vfs_index_file[LL_MAX_PATH]; /* Flawfinder: ignore */
2840 char new_vfs_data_file[LL_MAX_PATH]; /* Flawfinder: ignore */
2841 char new_vfs_index_file[LL_MAX_PATH]; /* Flawfinder: ignore */
2842 char static_vfs_index_file[LL_MAX_PATH]; /* Flawfinder: ignore */
2843 char static_vfs_data_file[LL_MAX_PATH]; /* Flawfinder: ignore */
2844
2845 if (gMultipleViewersOK)
2846 {
2847 // don't mess with renaming the VFS in this case
2848 new_salt = old_salt;
2849 }
2850 else
2851 {
2852 do
2853 {
2854 new_salt = rand();
2855 } while( new_salt == old_salt );
2856 }
2857
2858 snprintf(old_vfs_data_file, LL_MAX_PATH, "%s%u", /* Flawfinder: ignore */
2859 gDirUtilp->getExpandedFilename(LL_PATH_CACHE,VFS_DATA_FILE_BASE).c_str(),
2860 old_salt);
2861
2862 // make sure this file exists
2863 llstat s;
2864 S32 stat_result = LLFile::stat(old_vfs_data_file, &s);
2865 if (stat_result)
2866 {
2867 // doesn't exist, look for a data file
2868 std::string mask;
2869 mask = gDirUtilp->getDirDelimiter();
2870 mask += VFS_DATA_FILE_BASE;
2871 mask += "*";
2872
2873 std::string dir;
2874 dir = gDirUtilp->getExpandedFilename(LL_PATH_CACHE,"");
2875
2876 std::string found_file;
2877 if (gDirUtilp->getNextFileInDir(dir, mask, found_file, FALSE))
2878 {
2879 snprintf(old_vfs_data_file, LL_MAX_PATH, "%s%s%s", dir.c_str(), gDirUtilp->getDirDelimiter().c_str(), found_file.c_str()); /* Flawfinder: ignore */
2880
2881 S32 start_pos;
2882 S32 length = strlen(found_file.c_str()); /* Flawfinder: ignore*/
2883 for (start_pos = length - 1; start_pos >= 0; start_pos--)
2884 {
2885 if (found_file[start_pos] == '.')
2886 {
2887 start_pos++;
2888 break;
2889 }
2890 }
2891 if (start_pos > 0)
2892 {
2893 sscanf(found_file.c_str() + start_pos, "%d", &old_salt);
2894 }
2895 llinfos << "Default vfs data file not present, found " << old_vfs_data_file << llendl;
2896 llinfos << "Old salt: " << old_salt << llendl;
2897 }
2898 }
2899
2900 snprintf(old_vfs_index_file, LL_MAX_PATH, "%s%u", /* Flawfinder: ignore */
2901 gDirUtilp->getExpandedFilename(LL_PATH_CACHE,VFS_INDEX_FILE_BASE).c_str(),
2902 old_salt);
2903
2904 stat_result = LLFile::stat(old_vfs_index_file, &s);
2905 if (stat_result)
2906 {
2907 // We've got a bad/missing index file, nukem!
2908 llwarns << "Bad or missing vfx index file " << old_vfs_index_file << llendl;
2909 llwarns << "Removing old vfs data file " << old_vfs_data_file << llendl;
2910 LLFile::remove(old_vfs_data_file);
2911 LLFile::remove(old_vfs_index_file);
2912
2913 // Just in case, nuke any other old cache files in the directory.
2914 std::string dir;
2915 dir = gDirUtilp->getExpandedFilename(LL_PATH_CACHE,"");
2916
2917 std::string mask;
2918 mask = gDirUtilp->getDirDelimiter();
2919 mask += VFS_DATA_FILE_BASE;
2920 mask += "*";
2921
2922 gDirUtilp->deleteFilesInDir(dir, mask);
2923
2924 mask = gDirUtilp->getDirDelimiter();
2925 mask += VFS_INDEX_FILE_BASE;
2926 mask += "*";
2927
2928 gDirUtilp->deleteFilesInDir(dir, mask);
2929 }
2930
2931 snprintf(new_vfs_data_file, LL_MAX_PATH, "%s%u", /* Flawfinder: ignore */
2932 gDirUtilp->getExpandedFilename(LL_PATH_CACHE,VFS_DATA_FILE_BASE).c_str(),
2933 new_salt);
2934
2935 snprintf(new_vfs_index_file, LL_MAX_PATH, "%s%u", gDirUtilp->getExpandedFilename(LL_PATH_CACHE, VFS_INDEX_FILE_BASE).c_str(), /* Flawfinder: ignore */
2936 new_salt);
2937
2938
2939 strncpy(static_vfs_data_file, gDirUtilp->getExpandedFilename(LL_PATH_APP_SETTINGS,"static_data.db2").c_str(), LL_MAX_PATH -1); /* Flawfinder: ignore */
2940 static_vfs_data_file[LL_MAX_PATH -1] = '\0';
2941 strncpy(static_vfs_index_file, gDirUtilp->getExpandedFilename(LL_PATH_APP_SETTINGS,"static_index.db2").c_str(), LL_MAX_PATH -1); /* Flawfinder: ignore */
2942 static_vfs_index_file[LL_MAX_PATH -1] = '\0';
2943
2944 if (resize_vfs)
2945 {
2946 llinfos << "Removing old vfs and re-sizing" << llendl;
2947
2948 LLFile::remove(old_vfs_data_file);
2949 LLFile::remove(old_vfs_index_file);
2950 }
2951 else if (old_salt != new_salt)
2952 {
2953 // move the vfs files to a new name before opening
2954 llinfos << "Renaming " << old_vfs_data_file << " to " << new_vfs_data_file << llendl;
2955 llinfos << "Renaming " << old_vfs_index_file << " to " << new_vfs_index_file << llendl;
2956 LLFile::rename(old_vfs_data_file, new_vfs_data_file);
2957 LLFile::rename(old_vfs_index_file, new_vfs_index_file);
2958 }
2959
2960 // Startup the VFS...
2961 gSavedSettings.setU32("VFSSalt", new_salt);
2962
2963 // Don't remove VFS after viewer crashes. If user has corrupt data, they can reinstall. JC
2964 gVFS = new LLVFS(new_vfs_index_file, new_vfs_data_file, FALSE, vfs_size_u32, FALSE);
2965 if( VFSVALID_BAD_CORRUPT == gVFS->getValidState() )
2966 {
2967 // Try again with fresh files
2968 // (The constructor deletes corrupt files when it finds them.)
2969 llwarns << "VFS corrupt, deleted. Making new VFS." << llendl;
2970 delete gVFS;
2971 gVFS = new LLVFS(new_vfs_index_file, new_vfs_data_file, FALSE, vfs_size_u32, FALSE);
2972 }
2973
2974 gStaticVFS = new LLVFS(static_vfs_index_file, static_vfs_data_file, TRUE, 0, FALSE);
2975
2976 BOOL success = gVFS->isValid() && gStaticVFS->isValid();
2977 if( !success )
2978 {
2979 return FALSE;
2980 }
2981 else
2982 {
2983 LLVFile::initClass();
2984 return TRUE;
2985 }
2986}
2987
2988#if LL_DARWIN
2989
2990OSErr AEGURLHandler(const AppleEvent *messagein, AppleEvent *reply, long refIn)
2991{
2992 OSErr result = noErr;
2993 DescType actualType;
2994 char buffer[1024]; /* Flawfinder: ignore */
2995 Size size;
2996
2997 result = AEGetParamPtr (
2998 messagein,
2999 keyDirectObject,
3000 typeCString,
3001 &actualType,
3002 (Ptr)buffer,
3003 sizeof(buffer),
3004 &size);
3005
3006 if(result == noErr)
3007 {
3008 std::string url = buffer;
3009 LLURLDispatcher::dispatch(url);
3010 }
3011
3012 return(result);
3013}
3014
3015OSErr AEQuitHandler(const AppleEvent *messagein, AppleEvent *reply, long refIn)
3016{
3017 OSErr result = noErr;
3018
3019 app_user_quit();
3020
3021 return(result);
3022}
3023
3024OSStatus simpleDialogHandler(EventHandlerCallRef handler, EventRef event, void *userdata)
3025{
3026 OSStatus result = eventNotHandledErr;
3027 OSStatus err;
3028 UInt32 evtClass = GetEventClass(event);
3029 UInt32 evtKind = GetEventKind(event);
3030 WindowRef window = (WindowRef)userdata;
3031
3032 if((evtClass == kEventClassCommand) && (evtKind == kEventCommandProcess))
3033 {
3034 HICommand cmd;
3035 err = GetEventParameter(event, kEventParamDirectObject, typeHICommand, NULL, sizeof(cmd), NULL, &cmd);
3036
3037 if(err == noErr)
3038 {
3039 switch(cmd.commandID)
3040 {
3041 case kHICommandOK:
3042 QuitAppModalLoopForWindow(window);
3043 result = noErr;
3044 break;
3045
3046 case kHICommandCancel:
3047 QuitAppModalLoopForWindow(window);
3048 result = userCanceledErr;
3049 break;
3050 }
3051 }
3052 }
3053
3054 return(result);
3055}
3056
3057OSStatus DisplayReleaseNotes(void)
3058{
3059 OSStatus err;
3060 IBNibRef nib = NULL;
3061 WindowRef window = NULL;
3062
3063 err = CreateNibReference(CFSTR("SecondLife"), &nib);
3064
3065 if(err == noErr)
3066 {
3067 CreateWindowFromNib(nib, CFSTR("Release Notes"), &window);
3068 }
3069
3070 if(err == noErr)
3071 {
3072 // Get the text view control
3073 HIViewRef textView;
3074 ControlID id;
3075
3076 id.signature = 'text';
3077 id.id = 0;
3078
3079 std::string releaseNotesText;
3080 _read_file_into_string(releaseNotesText, "releasenotes.txt");
3081
3082 err = HIViewFindByID(HIViewGetRoot(window), id, &textView);
3083
3084 if(err == noErr)
3085 {
3086 // Convert from the encoding used in the release notes.
3087 CFStringRef str = CFStringCreateWithBytes(
3088 NULL,
3089 (const UInt8*)releaseNotesText.c_str(),
3090 releaseNotesText.size(),
3091 kCFStringEncodingWindowsLatin1, // This matches the way the Windows version displays the release notes.
3092 FALSE);
3093
3094 if(str != NULL)
3095 {
3096 int size = CFStringGetLength(str);
3097
3098 if(size > 0)
3099 {
3100 UniChar *chars = new UniChar[size + 1];
3101 CFStringGetCharacters(str, CFRangeMake(0, size), chars);
3102
3103 err = TXNSetData(HITextViewGetTXNObject(textView), kTXNUnicodeTextData, chars, size * sizeof(UniChar), kTXNStartOffset, kTXNStartOffset);
3104
3105 delete[] chars;
3106 }
3107
3108 CFRelease(str);
3109 }
3110 else
3111 {
3112 // Creating the string failed. Probably an encoding problem. Display SOMETHING...
3113 err = TXNSetData(HITextViewGetTXNObject(textView), kTXNTextData, releaseNotesText.c_str(), releaseNotesText.size(), kTXNStartOffset, kTXNStartOffset);
3114 }
3115 }
3116
3117 // Set the selection to the beginning of the text and scroll it into view.
3118 if(err == noErr)
3119 {
3120 err = TXNSetSelection(HITextViewGetTXNObject(textView), kTXNStartOffset, kTXNStartOffset);
3121 }
3122
3123 if(err == noErr)
3124 {
3125 // This function returns void.
3126 TXNShowSelection(HITextViewGetTXNObject(textView), false);
3127 }
3128 }
3129
3130 if(err == noErr)
3131 {
3132 ShowWindow(window);
3133 }
3134
3135 if(err == noErr)
3136 {
3137 // Set up an event handler for the window.
3138 EventHandlerRef handler = NULL;
3139 EventTypeSpec handlerEvents[] =
3140 {
3141 { kEventClassCommand, kEventCommandProcess }
3142 };
3143
3144 InstallWindowEventHandler(
3145 window,
3146 NewEventHandlerUPP(simpleDialogHandler),
3147 GetEventTypeCount (handlerEvents),
3148 handlerEvents,
3149 (void*)window,
3150 &handler);
3151 }
3152
3153 if(err == noErr)
3154 {
3155 RunAppModalLoopForWindow(window);
3156 }
3157
3158 if(window != NULL)
3159 {
3160 DisposeWindow(window);
3161 }
3162
3163 if(nib != NULL)
3164 {
3165 DisposeNibReference(nib);
3166 }
3167
3168 return(err);
3169}
3170
3171void init_apple_menu(const char* product)
3172{
3173 // Load up a proper menu bar.
3174 {
3175 OSStatus err;
3176 IBNibRef nib = NULL;
3177 // NOTE: DO NOT translate or brand this string. It's an internal name in the .nib file, and MUST match exactly.
3178 err = CreateNibReference(CFSTR("SecondLife"), &nib);
3179
3180 if(err == noErr)
3181 {
3182 // NOTE: DO NOT translate or brand this string. It's an internal name in the .nib file, and MUST match exactly.
3183 SetMenuBarFromNib(nib, CFSTR("MenuBar"));
3184 }
3185
3186 if(nib != NULL)
3187 {
3188 DisposeNibReference(nib);
3189 }
3190 }
3191
3192 // Install a handler for 'gurl' AppleEvents. This is how secondlife:// URLs get passed to the viewer.
3193
3194 if(AEInstallEventHandler('GURL', 'GURL', NewAEEventHandlerUPP(AEGURLHandler),0, false) != noErr)
3195 {
3196 // Couldn't install AppleEvent handler. This error shouldn't be fatal.
3197 llinfos << "Couldn't install 'GURL' AppleEvent handler. Continuing..." << llendl;
3198 }
3199
3200 // Install a handler for 'quit' AppleEvents. This makes quitting the application from the dock work.
3201 if(AEInstallEventHandler(kCoreEventClass, kAEQuitApplication, NewAEEventHandlerUPP(AEQuitHandler),0, false) != noErr)
3202 {
3203 // Couldn't install AppleEvent handler. This error shouldn't be fatal.
3204 llinfos << "Couldn't install Quit AppleEvent handler. Continuing..." << llendl;
3205 }
3206}
3207#endif
3208
3209void user_logout()
3210{
3211 if (!gDoneLogout)
3212 {
3213 LLMessageSystem* msg = gMessageSystem;
3214 if (msg)
3215 {
3216 gSavedSettings.setBOOL("LoggedIn", FALSE);
3217 }
3218 gDoneLogout = TRUE;
3219 }
3220}
3221
3222
3223// This routine may get called more than once during the shutdown process.
3224// This can happen because we need to get the screenshot before the window
3225// is destroyed.
3226void save_final_snapshot(void*)
3227{
3228 if (!gHaveSavedSnapshot && !gNoRender)
3229 {
3230 gSavedSettings.setVector3d("FocusPosOnLogout", gAgent.calcFocusPositionTargetGlobal());
3231 gSavedSettings.setVector3d("CameraPosOnLogout", gAgent.calcCameraPositionTargetGlobal());
3232 gViewerWindow->setCursor(UI_CURSOR_WAIT);
3233 gAgent.changeCameraToThirdPerson( FALSE ); // don't animate, need immediate switch
3234 gSavedSettings.setBOOL("ShowParcelOwners", FALSE);
3235 idle();
3236
3237 LLString snap_filename = gDirUtilp->getLindenUserDir();
3238 snap_filename += gDirUtilp->getDirDelimiter();
3239 snap_filename += SCREEN_LAST_FILENAME;
3240 gViewerWindow->saveSnapshot(snap_filename, gViewerWindow->getWindowWidth(), gViewerWindow->getWindowHeight(), FALSE, TRUE);
3241 gHaveSavedSnapshot = TRUE;
3242 }
3243}
3244
3245// If you pass a path+file or URL in as a
3246// Note: This may get called multiple times during shutdown.
3247void app_force_quit(const char* launch_file_on_quit)
3248{
3249 // Don't actually DO anything complicated here, because
3250 // it's called from signal handlers for the Mac on SIGTERM.
3251 // It calls this with NULL.
3252 if (launch_file_on_quit)
3253 {
3254 gLaunchFileOnQuit.assign( (const char*)launch_file_on_quit );
3255 }
3256
3257 gQuit = TRUE;
3258}
3259
3260static void finish_quit(S32 option, void *userdata)
3261{
3262 if (option == 0)
3263 {
3264 app_request_quit();
3265 }
3266}
3267
3268void app_user_quit()
3269{
3270 gViewerWindow->alertXml("ConfirmQuit", finish_quit, NULL);
3271}
3272
3273
3274// Don't quit instantly. Instead, request to be logged off.
3275// Called from control-Q handler, Windows(tm) close-window message (WM_CLOSE), and Mac Quit AppleEvent handler.
3276void app_request_quit()
3277{
3278 llinfos << "app_request_quit" << llendl;
3279
3280 LLViewerRegion* region = gAgent.getRegion();
3281
3282 if( (LLStartUp::getStartupState() < STATE_STARTED) || !region )
3283 {
3284 // Quit immediately
3285 app_force_quit(NULL);
3286 return;
3287 }
3288
3289 if (gHUDManager)
3290 {
3291 LLHUDEffectSpiral *effectp = (LLHUDEffectSpiral*)gHUDManager->createViewerEffect(LLHUDObject::LL_HUD_EFFECT_POINT, TRUE);
3292 effectp->setPositionGlobal(gAgent.getPositionGlobal());
3293 effectp->setColor(LLColor4U(gAgent.getEffectColor()));
3294 gHUDManager->sendEffects();
3295 }
3296
3297 // Attempt to close all floaters that might be
3298 // editing things.
3299 if (gFloaterView)
3300 {
3301 // application is quitting
3302 gFloaterView->closeAllChildren(true);
3303 }
3304
3305 send_stats();
3306
3307 gLogoutTimer.reset();
3308 gQuitRequested = TRUE;
3309}
3310
3311
3312// User didn't really want to quit, for example, clicked "Cancel"
3313// in a floater save dialog.
3314void app_abort_quit()
3315{
3316 llinfos << "app_abort_quit()" << llendl;
3317 gQuitRequested = FALSE;
3318}
3319
3320void idle_shutdown()
3321{
3322 // Wait for all modal alerts to get resolved
3323 if (LLModalDialog::activeCount() > 0)
3324 {
3325 return;
3326 }
3327
3328 // close IM interface
3329 if(gIMMgr)
3330 {
3331 gIMMgr->disconnectAllSessions();
3332 }
3333
3334 // Wait for all floaters to get resolved
3335 if (gFloaterView
3336 && !gFloaterView->allChildrenClosed())
3337 {
3338 return;
3339 }
3340
3341 static bool saved_snapshot = false;
3342 if (!saved_snapshot)
3343 {
3344 saved_snapshot = true;
3345 save_final_snapshot(NULL);
3346 return;
3347 }
3348
3349 const F32 SHUTDOWN_UPLOAD_SAVE_TIME = 5.f;
3350
3351 S32 pending_uploads = gAssetStorage->getNumPendingUploads();
3352 if (pending_uploads > 0
3353 && gLogoutTimer.getElapsedTimeF32() < SHUTDOWN_UPLOAD_SAVE_TIME
3354 && !gLogoutRequestSent)
3355 {
3356 static S32 total_uploads = 0;
3357 // Sometimes total upload count can change during logout.
3358 total_uploads = llmax(total_uploads, pending_uploads);
3359 gViewerWindow->setShowProgress(TRUE);
3360 S32 finished_uploads = total_uploads - pending_uploads;
3361 F32 percent = 100.f * finished_uploads / total_uploads;
3362 gViewerWindow->setProgressPercent(percent);
3363 char buffer[MAX_STRING]; /* Flawfinder: ignore */
3364 snprintf(buffer, MAX_STRING, "Saving final data..."); /* Flawfinder: ignore */
3365 gViewerWindow->setProgressString(buffer);
3366 return;
3367 }
3368
3369 // All floaters are closed. Tell server we want to quit.
3370 if( !gLogoutRequestSent )
3371 {
3372 send_logout_request();
3373
3374 // Wait for a LogoutReply message
3375 gViewerWindow->setShowProgress(TRUE);
3376 gViewerWindow->setProgressPercent(100.f);
3377 gViewerWindow->setProgressString("Logging out...");
3378 return;
3379 }
3380
3381 // Make sure that we quit if we haven't received a reply from the server.
3382 if( gLogoutRequestSent
3383 && gLogoutTimer.getElapsedTimeF32() > gLogoutMaxTime )
3384 {
3385 app_force_quit(NULL);
3386 return;
3387 }
3388}
3389
3390
3391U32 gTotalLandIn = 0, gTotalLandOut = 0;
3392U32 gTotalWaterIn = 0, gTotalWaterOut = 0;
3393
3394F32 gAveLandCompression = 0.f, gAveWaterCompression = 0.f;
3395F32 gBestLandCompression = 1.f, gBestWaterCompression = 1.f;
3396F32 gWorstLandCompression = 0.f, gWorstWaterCompression = 0.f;
3397
3398F32 gFPSClamped = 10.f; // Pretend we start at target rate.
3399F32 gFrameDTClamped = 0.f; // Time between adjacent checks to network for packets
3400F32 gFrameDT = 0.f;
3401
3402//S32 gDecodedBits = 0;
3403U32 gPacketsIn = 0;
3404
3405U32 gTotalWorldBytes = 0, gTotalObjectBytes = 0, gTotalTextureBytes = 0, gSimPingCount = 0;
3406U32 gObjectBits = 0;
3407F32 gAvgSimPing = 0.f;
3408
3409
3410extern U32 gVisCompared;
3411extern U32 gVisTested;
3412
3413
3414void update_statistics(U32 frame_count)
3415{
3416 gTotalWorldBytes += gVLManager.getTotalBytes();
3417 gTotalObjectBytes += gObjectBits / 8;
3418 gTotalTextureBytes += LLViewerImageList::sTextureBits / 8;
3419
3420 // make sure we have a valid time delta for this frame
3421 if (gFrameIntervalSeconds > 0.f)
3422 {
3423 if (gAgent.getCameraMode() == CAMERA_MODE_MOUSELOOK)
3424 {
3425 gViewerStats->incStat(LLViewerStats::ST_MOUSELOOK_SECONDS, gFrameIntervalSeconds);
3426 }
3427 else if (gAgent.getCameraMode() == CAMERA_MODE_CUSTOMIZE_AVATAR)
3428 {
3429 gViewerStats->incStat(LLViewerStats::ST_AVATAR_EDIT_SECONDS, gFrameIntervalSeconds);
3430 }
3431 else if (gFloaterTools && gFloaterTools->getVisible())
3432 {
3433 gViewerStats->incStat(LLViewerStats::ST_TOOLBOX_SECONDS, gFrameIntervalSeconds);
3434 }
3435 }
3436 gViewerStats->setStat(LLViewerStats::ST_ENABLE_VBO, (F64)gSavedSettings.getBOOL("RenderVBOEnable"));
3437 gViewerStats->setStat(LLViewerStats::ST_LIGHTING_DETAIL, (F64)gSavedSettings.getS32("RenderLightingDetail"));
3438 gViewerStats->setStat(LLViewerStats::ST_DRAW_DIST, (F64)gSavedSettings.getF32("RenderFarClip"));
3439 gViewerStats->setStat(LLViewerStats::ST_CHAT_BUBBLES, (F64)gSavedSettings.getBOOL("UseChatBubbles"));
3440#if 0 // 1.9.2
3441 gViewerStats->setStat(LLViewerStats::ST_SHADER_OBJECTS, (F64)gSavedSettings.getS32("VertexShaderLevelObject"));
3442 gViewerStats->setStat(LLViewerStats::ST_SHADER_AVATAR, (F64)gSavedSettings.getBOOL("VertexShaderLevelAvatar"));
3443 gViewerStats->setStat(LLViewerStats::ST_SHADER_ENVIRONMENT, (F64)gSavedSettings.getBOOL("VertexShaderLevelEnvironment"));
3444#endif
3445 gViewerStats->setStat(LLViewerStats::ST_FRAME_SECS, gDebugView->mFastTimerView->getTime(LLFastTimer::FTM_FRAME));
3446 F64 idle_secs = gDebugView->mFastTimerView->getTime(LLFastTimer::FTM_IDLE);
3447 F64 network_secs = gDebugView->mFastTimerView->getTime(LLFastTimer::FTM_NETWORK);
3448 gViewerStats->setStat(LLViewerStats::ST_UPDATE_SECS, idle_secs - network_secs);
3449 gViewerStats->setStat(LLViewerStats::ST_NETWORK_SECS, network_secs);
3450 gViewerStats->setStat(LLViewerStats::ST_IMAGE_SECS, gDebugView->mFastTimerView->getTime(LLFastTimer::FTM_IMAGE_UPDATE));
3451 gViewerStats->setStat(LLViewerStats::ST_REBUILD_SECS, gDebugView->mFastTimerView->getTime(LLFastTimer::FTM_REBUILD));
3452 gViewerStats->setStat(LLViewerStats::ST_RENDER_SECS, gDebugView->mFastTimerView->getTime(LLFastTimer::FTM_RENDER_GEOMETRY));
3453
3454 LLCircuitData *cdp = gMessageSystem->mCircuitInfo.findCircuit(gAgent.getRegion()->getHost());
3455 if (cdp)
3456 {
3457 gViewerStats->mSimPingStat.addValue(cdp->getPingDelay());
3458 gAvgSimPing = ((gAvgSimPing * (F32)gSimPingCount) + (F32)(cdp->getPingDelay())) / ((F32)gSimPingCount + 1);
3459 gSimPingCount++;
3460 }
3461 else
3462 {
3463 gViewerStats->mSimPingStat.addValue(10000);
3464 }
3465
3466 if (gFocusMgr.getAppHasFocus())
3467 {
3468 gViewerStats->mFPSStat.addValue(1);
3469 }
3470 F32 layer_bits = (F32)(gVLManager.getLandBits() + gVLManager.getWindBits() + gVLManager.getCloudBits());
3471 gViewerStats->mLayersKBitStat.addValue(layer_bits/1024.f);
3472 gViewerStats->mObjectKBitStat.addValue(gObjectBits/1024.f);
3473 gViewerStats->mTextureKBitStat.addValue(LLViewerImageList::sTextureBits/1024.f);
3474 gViewerStats->mVFSPendingOperations.addValue(LLVFile::getVFSThread()->getPending());
3475 gViewerStats->mAssetKBitStat.addValue(gTransferManager.getTransferBitsIn(LLTCT_ASSET)/1024.f);
3476 gTransferManager.resetTransferBitsIn(LLTCT_ASSET);
3477
3478 static S32 tex_bits_idle_count = 0;
3479 if (LLViewerImageList::sTextureBits == 0)
3480 {
3481 if (++tex_bits_idle_count >= 30)
3482 gDebugTimers[0].pause();
3483 }
3484 else
3485 {
3486 tex_bits_idle_count = 0;
3487 gDebugTimers[0].unpause();
3488 }
3489
3490 gViewerStats->mTexturePacketsStat.addValue(LLViewerImageList::sTexturePackets);
3491
3492 gViewerStats->mUserserverPingStat.addValue(0); // userserver doesn't exist, therefore ping time is always awesome
3493
3494 // log when the LibXUL (aka Mozilla) widget is used and opened so we can monitor framerate changes
3495 #if LL_LIBXUL_ENABLED
3496 {
3497 BOOL result = gViewerHtmlHelp.getFloaterOpened();
3498 gViewerStats->setStat(LLViewerStats::ST_LIBXUL_WIDGET_USED, (F64)result);
3499 }
3500 #endif
3501
3502 {
3503 static F32 visible_avatar_frames = 0.f;
3504 static F32 avg_visible_avatars = 0;
3505 F32 visible_avatars = (F32)LLVOAvatar::sNumVisibleAvatars;
3506 if (visible_avatars > 0.f)
3507 {
3508 visible_avatar_frames = 1.f;
3509 avg_visible_avatars = (avg_visible_avatars * (F32)(visible_avatar_frames - 1.f) + visible_avatars) / visible_avatar_frames;
3510 }
3511 gViewerStats->setStat(LLViewerStats::ST_VISIBLE_AVATARS, (F64)avg_visible_avatars);
3512 }
3513 gWorldp->updateNetStats();
3514 gWorldp->requestCacheMisses();
3515
3516 // Reset all of these values.
3517 gVLManager.resetBitCounts();
3518 gObjectBits = 0;
3519// gDecodedBits = 0;
3520
3521 LLViewerImageList::sTextureBits = 0;
3522 LLViewerImageList::sTexturePackets = 0;
3523
3524#if LL_WINDOWS && LL_LCD_COMPILE
3525 bool LCDenabled = gLcdScreen->Enabled();
3526 gViewerStats->setStat(LLViewerStats::ST_LOGITECH_LCD, LCDenabled);
3527#else
3528 gViewerStats->setStat(LLViewerStats::ST_LOGITECH_LCD, false);
3529#endif
3530
3531}
3532
3533//
3534// Handle messages, and all message related stuff
3535//
3536
3537#define TIME_THROTTLE_MESSAGES
3538
3539#ifdef TIME_THROTTLE_MESSAGES
3540#define CHECK_MESSAGES_DEFAULT_MAX_TIME .020f // 50 ms = 50 fps (just for messages!)
3541static F32 CheckMessagesMaxTime = CHECK_MESSAGES_DEFAULT_MAX_TIME;
3542#endif
3543
3544void idle_network()
3545{
3546 gObjectList.mNumNewObjects = 0;
3547 S32 total_decoded = 0;
3548
3549 if (!gSavedSettings.getBOOL("SpeedTest"))
3550 {
3551 LLFastTimer t(LLFastTimer::FTM_IDLE_NETWORK); // decode
3552
3553 // deal with any queued name requests and replies.
3554 gCacheName->processPending();
3555
3556 LLTimer check_message_timer;
3557 // Read all available packets from network
3558 stop_glerror();
3559 const S64 frame_count = gFrameCount; // U32->S64
3560 F32 total_time = 0.0f;
3561 while (gMessageSystem->checkAllMessages(frame_count, gServicePump))
3562 {
3563 if (gDoDisconnect)
3564 {
3565 // We're disconnecting, don't process any more messages from the server
3566 // We're usually disconnecting due to either network corruption or a
3567 // server going down, so this is OK.
3568 break;
3569 }
3570 stop_glerror();
3571
3572 total_decoded++;
3573 gPacketsIn++;
3574
3575 if (total_decoded > MESSAGE_MAX_PER_FRAME)
3576 {
3577 break;
3578 }
3579
3580#ifdef TIME_THROTTLE_MESSAGES
3581 // Prevent slow packets from completely destroying the frame rate.
3582 // This usually happens due to clumps of avatars taking huge amount
3583 // of network processing time (which needs to be fixed, but this is
3584 // a good limit anyway).
3585 total_time = check_message_timer.getElapsedTimeF32();
3586 if (total_time >= CheckMessagesMaxTime)
3587 break;
3588#endif
3589 }
3590 // Handle per-frame message system processing.
3591 gMessageSystem->processAcks();
3592
3593#ifdef TIME_THROTTLE_MESSAGES
3594 if (total_time >= CheckMessagesMaxTime)
3595 {
3596 // Increase CheckMessagesMaxTime so that we will eventually catch up
3597 CheckMessagesMaxTime *= 1.035f; // 3.5% ~= x2 in 20 frames, ~8x in 60 frames
3598 }
3599 else
3600 {
3601 // Reset CheckMessagesMaxTime to default value
3602 CheckMessagesMaxTime = CHECK_MESSAGES_DEFAULT_MAX_TIME;
3603 }
3604#endif
3605
3606
3607
3608 // we want to clear the control after sending out all necessary agent updates
3609 gAgent.resetControlFlags();
3610 stop_glerror();
3611
3612
3613 // Decode enqueued messages...
3614 S32 remaining_possible_decodes = MESSAGE_MAX_PER_FRAME - total_decoded;
3615
3616 if( remaining_possible_decodes <= 0 )
3617 {
3618 llinfos << "Maxed out number of messages per frame at " << MESSAGE_MAX_PER_FRAME << llendl;
3619 }
3620
3621 if (gPrintMessagesThisFrame)
3622 {
3623 llinfos << "Decoded " << total_decoded << " msgs this frame!" << llendl;
3624 gPrintMessagesThisFrame = FALSE;
3625 }
3626 }
3627
3628 gObjectList.mNumNewObjectsStat.addValue(gObjectList.mNumNewObjects);
3629
3630 // Retransmit unacknowledged packets.
3631 gXferManager->retransmitUnackedPackets();
3632 gAssetStorage->checkForTimeouts();
3633
3634 gViewerThrottle.updateDynamicThrottle();
3635}
3636
3637void idle_afk_check()
3638{
3639 // check idle timers
3640 if (gAllowIdleAFK && (gAwayTriggerTimer.getElapsedTimeF32() > gAFKTimeout))
3641 {
3642 gAgent.setAFK();
3643 }
3644}
3645void request_initial_instant_messages()
3646{
3647 static BOOL requested = FALSE;
3648 if (!requested
3649 && gMuteListp
3650 && gMuteListp->isLoaded()
3651 && gAgent.getAvatarObject())
3652 {
3653 // Auto-accepted inventory items may require the avatar object
3654 // to build a correct name. Likewise, inventory offers from
3655 // muted avatars require the mute list to properly mute.
3656 LLMessageSystem* msg = gMessageSystem;
3657 msg->newMessageFast(_PREHASH_RetrieveInstantMessages);
3658 msg->nextBlockFast(_PREHASH_AgentData);
3659 msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID());
3660 msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
3661 gAgent.sendReliableMessage();
3662 requested = TRUE;
3663 }
3664}
3665
3666///////////////////////////////////////////////////////
3667// idle()
3668//
3669// Called every time the window is not doing anything.
3670// Receive packets, update statistics, and schedule a redisplay.
3671///////////////////////////////////////////////////////
3672
3673void idle()
3674{
3675 // Update frame timers
3676 static LLTimer idle_timer;
3677
3678 LLControlBase::updateAllListeners();
3679
3680 LLFrameTimer::updateFrameTime();
3681 LLEventTimer::updateClass();
3682 LLCriticalDamp::updateInterpolants();
3683 LLMortician::updateClass();
3684 F32 dt_raw = idle_timer.getElapsedTimeAndResetF32();
3685
3686 // Cap out-of-control frame times
3687 // Too low because in menus, swapping, debugger, etc.
3688 // Too high because idle called with no objects in view, etc.
3689 const F32 MIN_FRAME_RATE = 1.f;
3690 const F32 MAX_FRAME_RATE = 200.f;
3691
3692 F32 frame_rate_clamped = 1.f / dt_raw;
3693 frame_rate_clamped = llclamp(frame_rate_clamped, MIN_FRAME_RATE, MAX_FRAME_RATE);
3694 gFrameDTClamped = 1.f / frame_rate_clamped;
3695
3696 // Global frame timer
3697 // Smoothly weight toward current frame
3698 gFPSClamped = (frame_rate_clamped + (4.f * gFPSClamped)) / 5.f;
3699
3700 if (gQuitAfterSeconds > 0.f)
3701 {
3702 if (gRenderStartTime.getElapsedTimeF32() > gQuitAfterSeconds)
3703 {
3704 app_force_quit(NULL);
3705 }
3706 }
3707
3708 // Must wait until both have avatar object and mute list, so poll
3709 // here.
3710 request_initial_instant_messages();
3711
3712 ///////////////////////////////////
3713 //
3714 // Special case idle if still starting up
3715 //
3716
3717 if (LLStartUp::getStartupState() < STATE_STARTED)
3718 {
3719 // Skip rest if idle startup returns false (essentially, no world yet)
3720 if (!idle_startup())
3721 {
3722 return;
3723 }
3724 }
3725
3726
3727 F32 yaw = 0.f; // radians
3728
3729 if (!gDisconnected)
3730 {
3731 LLFastTimer t(LLFastTimer::FTM_NETWORK);
3732
3733 // Update spaceserver timeinfo
3734 gWorldp->setSpaceTimeUSec(gWorldp->getSpaceTimeUSec() + (U32)(dt_raw * SEC_TO_MICROSEC));
3735
3736
3737 //////////////////////////////////////
3738 //
3739 // Update simulator agent state
3740 //
3741
3742 if (gRotateRight)
3743 {
3744 gAgent.moveYaw(-1.f);
3745 }
3746
3747 // Handle automatic walking towards points
3748 gAgentPilot.updateTarget();
3749 gAgent.autoPilot(&yaw);
3750
3751 static LLFrameTimer agent_update_timer;
3752 static U32 last_control_flags;
3753
3754 // When appropriate, update agent location to the simulator.
3755 F32 agent_update_time = agent_update_timer.getElapsedTimeF32();
3756 BOOL flags_changed = gAgent.controlFlagsDirty() || (last_control_flags != gAgent.getControlFlags());
3757
3758 if (flags_changed || (agent_update_time > (1.0f / (F32) AGENT_UPDATES_PER_SECOND)))
3759 {
3760 // Send avatar and camera info
3761 last_control_flags = gAgent.getControlFlags();
3762 send_agent_update(TRUE);
3763 agent_update_timer.reset();
3764 }
3765 }
3766
3767 //////////////////////////////////////
3768 //
3769 // Manage statistics
3770 //
3771 //
3772
3773 {
3774 static LLFrameTimer viewer_stats_timer;
3775 reset_statistics();
3776
3777 // Update session stats every large chunk of time
3778 // *FIX: (???) SAMANTHA
3779
3780 if (viewer_stats_timer.getElapsedTimeF32() >= 300.f && !gDisconnected)
3781 {
3782 llinfos << "Transmitting sessions stats" << llendl;
3783 send_stats();
3784 viewer_stats_timer.reset();
3785 }
3786
3787 // Print the object debugging stats
3788 static LLFrameTimer object_debug_timer;
3789 if (object_debug_timer.getElapsedTimeF32() > 5.f)
3790 {
3791 object_debug_timer.reset();
3792 if (gObjectList.mNumDeadObjectUpdates)
3793 {
3794 llinfos << "Dead object updates: " << gObjectList.mNumDeadObjectUpdates << llendl;
3795 gObjectList.mNumDeadObjectUpdates = 0;
3796 }
3797 if (gObjectList.mNumUnknownKills)
3798 {
3799 llinfos << "Kills on unknown objects: " << gObjectList.mNumUnknownKills << llendl;
3800 gObjectList.mNumUnknownKills = 0;
3801 }
3802 if (gObjectList.mNumUnknownUpdates)
3803 {
3804 llinfos << "Unknown object updates: " << gObjectList.mNumUnknownUpdates << llendl;
3805 gObjectList.mNumUnknownUpdates = 0;
3806 }
3807 }
3808 gFrameStats.addFrameData();
3809 }
3810
3811 if (!gDisconnected)
3812 {
3813 LLFastTimer t(LLFastTimer::FTM_NETWORK);
3814
3815 ////////////////////////////////////////////////
3816 //
3817 // Network processing
3818 //
3819 // NOTE: Starting at this point, we may still have pointers to "dead" objects
3820 // floating throughout the various object lists.
3821 //
3822
3823 gFrameStats.start(LLFrameStats::IDLE_NETWORK);
3824 idle_network();
3825 stop_glerror();
3826
3827 gFrameStats.start(LLFrameStats::AGENT_MISC);
3828
3829 // Check for away from keyboard, kick idle agents.
3830 idle_afk_check();
3831
3832 // Update statistics for this frame
3833 update_statistics(gFrameCount);
3834
3835 gViewerWindow->updateDebugText();
3836 }
3837
3838 ////////////////////////////////////////
3839 //
3840 // Handle the regular UI idle callbacks as well as
3841 // hover callbacks
3842 //
3843
3844 {
3845// LLFastTimer t(LLFastTimer::FTM_IDLE_CB);
3846
3847 // Do event notifications if necessary. Yes, we may want to move this elsewhere.
3848 gEventNotifier.update();
3849
3850 gIdleCallbacks.callFunctions();
3851 }
3852
3853 if (gDisconnected)
3854 {
3855 return;
3856 }
3857
3858 gViewerWindow->handlePerFrameHover();
3859
3860 ///////////////////////////////////////
3861 // Agent and camera movement
3862 //
3863 LLCoordGL current_mouse = gViewerWindow->getCurrentMouse();
3864
3865// BOOL was_in_prelude = gAgent.inPrelude();
3866
3867 {
3868 //LLFastTimer t(LLFastTimer::FTM_TEMP1);
3869
3870 // After agent and camera moved, figure out if we need to
3871 // deselect objects.
3872 gSelectMgr->deselectAllIfTooFar();
3873 gSelectMgr->update(); // once per frame updat
3874 }
3875
3876 {
3877 LLFastTimer t(LLFastTimer::FTM_RESET_DRAWORDER);
3878
3879 //////////////////////////////////////////////
3880 //
3881 // Clear draw orders
3882 //
3883 // Should actually be done after render, but handlePerFrameHover actually does a "render"
3884 // to do its selection.
3885 //
3886
3887 gPipeline.resetDrawOrders();
3888 }
3889 {
3890 // Handle pending gesture processing
3891 gGestureManager.update();
3892
3893 gAgent.updateAgentPosition(gFrameDTClamped, yaw, current_mouse.mX, current_mouse.mY);
3894 }
3895
3896 {
3897 LLFastTimer t(LLFastTimer::FTM_OBJECTLIST_UPDATE); // Actually "object update"
3898 gFrameStats.start(LLFrameStats::OBJECT_UPDATE);
3899
3900 if (!(gLogoutRequestSent && gHaveSavedSnapshot))
3901 {
3902 gObjectList.update(gAgent, *gWorldp);
3903 }
3904 }
3905
3906 {
3907 LLFastTimer t(LLFastTimer::FTM_UPDATE_SKY);
3908 gSky.updateSky();
3909 }
3910
3911 //////////////////////////////////////
3912 //
3913 // Deletes objects...
3914 // Has to be done after doing idleUpdates (which can kill objects)
3915 //
3916
3917 {
3918 LLFastTimer t(LLFastTimer::FTM_CLEANUP);
3919 gFrameStats.start(LLFrameStats::CLEAN_DEAD);
3920 gObjectList.cleanDeadObjects();
3921 LLDrawable::cleanupDeadDrawables();
3922 }
3923
3924 //
3925 // After this point, in theory we should never see a dead object
3926 // in the various object/drawable lists.
3927 //
3928
3929 //////////////////////////////////////
3930 //
3931 // Update/send HUD effects
3932 //
3933 // At this point, HUD effects may clean up some references to
3934 // dead objects.
3935 //
3936
3937 {
3938 //LLFastTimer t(LLFastTimer::FTM_TEMP3);
3939
3940 gFrameStats.start(LLFrameStats::UPDATE_EFFECTS);
3941 gSelectMgr->updateEffects();
3942 gHUDManager->cleanupEffects();
3943 gHUDManager->sendEffects();
3944 }
3945
3946 stop_glerror();
3947
3948 ////////////////////////////////////////
3949 //
3950 // Unpack layer data that we've received
3951 //
3952
3953 {
3954 LLFastTimer t(LLFastTimer::FTM_NETWORK);
3955 gVLManager.unpackData();
3956 }
3957
3958 /////////////////////////
3959 //
3960 // Update surfaces, and surface textures as well.
3961 //
3962
3963 gWorldp->updateVisibilities();
3964 {
3965 const F32 max_region_update_time = .001f; // 1ms
3966 LLFastTimer t(LLFastTimer::FTM_REGION_UPDATE);
3967 gWorldp->updateRegions(max_region_update_time);
3968 }
3969
3970 /////////////////////////
3971 //
3972 // Update weather effects
3973 //
3974
3975 if (!gNoRender)
3976 {
3977 gWorldp->updateClouds(gFrameDTClamped);
3978 gSky.propagateHeavenlyBodies(gFrameDTClamped); // moves sun, moon, and planets
3979
3980 // Update wind vector
3981 LLVector3 wind_position_region;
3982 static LLVector3 average_wind;
3983
3984 LLViewerRegion *regionp;
3985 regionp = gWorldp->resolveRegionGlobal(wind_position_region, gAgent.getPositionGlobal()); // puts agent's local coords into wind_position
3986 if (regionp)
3987 {
3988 gWindVec = regionp->mWind.getVelocity(wind_position_region);
3989
3990 // Compute average wind and use to drive motion of water
3991
3992 average_wind = regionp->mWind.getAverage();
3993 F32 cloud_density = regionp->mCloudLayer.getDensityRegion(wind_position_region);
3994
3995 gSky.setCloudDensityAtAgent(cloud_density);
3996 gSky.setWind(average_wind);
3997 //LLVOWater::setWind(average_wind);
3998 }
3999 else
4000 {
4001 gWindVec.setVec(0.0f, 0.0f, 0.0f);
4002 }
4003 }
4004 stop_glerror();
4005
4006 //////////////////////////////////////
4007 //
4008 // Update images, using the image stats generated during object update/culling
4009 //
4010 // Can put objects onto the retextured list.
4011 //
4012 gFrameStats.start(LLFrameStats::IMAGE_UPDATE);
4013
4014 LLFastTimer t(LLFastTimer::FTM_IMAGE_UPDATE);
4015
4016 LLViewerImage::updateClass(gCamera->getVelocityStat()->getMean(),
4017 gCamera->getAngularVelocityStat()->getMean());
4018
4019 gBumpImageList.updateImages(); // must be called before gImageList version so that it's textures are thrown out first.
4020
4021 const F32 max_image_decode_time = 0.005f; // 5 ms decode time
4022 gImageList.updateImages(max_image_decode_time);
4023 stop_glerror();
4024
4025 //////////////////////////////////////
4026 //
4027 // Sort and cull in the new renderer are moved to pipeline.cpp
4028 // Here, particles are updated and drawables are moved.
4029 //
4030
4031 if (!gNoRender)
4032 {
4033 gFrameStats.start(LLFrameStats::UPDATE_MOVE);
4034 gPipeline.updateMove();
4035
4036 gFrameStats.start(LLFrameStats::UPDATE_PARTICLES);
4037 gWorldp->updateParticles();
4038 }
4039 stop_glerror();
4040
4041 if (!LLViewerJoystick::sOverrideCamera)
4042 {
4043 gAgent.updateCamera();
4044 }
4045 else
4046 {
4047 LLViewerJoystick::updateCamera();
4048 }
4049
4050 // objects and camera should be in sync, do LOD calculations now
4051 {
4052 LLFastTimer t(LLFastTimer::FTM_LOD_UPDATE);
4053 gObjectList.updateApparentAngles(gAgent);
4054 }
4055
4056 {
4057 gFrameStats.start(LLFrameStats::AUDIO);
4058 LLFastTimer t(LLFastTimer::FTM_AUDIO_UPDATE);
4059
4060 if (gAudiop)
4061 {
4062 audio_update_volume(false);
4063 audio_update_listener();
4064 audio_update_wind(false);
4065
4066 // this line actually commits the changes we've made to source positions, etc.
4067 const F32 max_audio_decode_time = 0.002f; // 2 ms decode time
4068 gAudiop->idle(max_audio_decode_time);
4069 }
4070 }
4071
4072 // Handle shutdown process, for example,
4073 // wait for floaters to close, send quit message,
4074 // forcibly quit if it has taken too long
4075 if (gQuitRequested)
4076 {
4077 idle_shutdown();
4078 }
4079
4080 stop_glerror();
4081}
4082
4083
4084F32 mouse_x_from_center(S32 x)
4085{
4086 return ((F32) x / (F32) gViewerWindow->getWindowWidth() ) - 0.5f;
4087}
4088
4089
4090F32 mouse_y_from_center(S32 y)
4091{
4092 return ((F32) y / (F32) gViewerWindow->getWindowHeight() ) - 0.5f;
4093}
4094
4095
4096/////////////////////////////////////////////////////////
4097
4098void init_audio()
4099{
4100 if (!gAudiop)
4101 {
4102 llwarns << "Failed to create an appropriate Audio Engine" << llendl;
4103 return;
4104 }
4105 LLVector3d lpos_global = gAgent.getCameraPositionGlobal();
4106 LLVector3 lpos_global_f;
4107
4108 lpos_global_f.setVec(lpos_global);
4109
4110 gAudiop->setListener(lpos_global_f,
4111 LLVector3::zero, // gCamera->getVelocity(), // !!! BUG need to replace this with smoothed velocity!
4112 gCamera->getUpAxis(),
4113 gCamera->getAtAxis());
4114
4115// load up our initial set of sounds we'll want so they're in memory and ready to be played
4116
4117 BOOL mute_audio = gSavedSettings.getBOOL("MuteAudio");
4118
4119 if (!mute_audio && gPreloadSounds)
4120 {
4121 gAudiop->preloadSound(LLUUID(gSavedSettings.getString("UISndAlert")));
4122 gAudiop->preloadSound(LLUUID(gSavedSettings.getString("UISndBadKeystroke")));
4123 //gAudiop->preloadSound(LLUUID(gSavedSettings.getString("UISndChatFromObject")));
4124 gAudiop->preloadSound(LLUUID(gSavedSettings.getString("UISndClick")));
4125 gAudiop->preloadSound(LLUUID(gSavedSettings.getString("UISndClickRelease")));
4126 gAudiop->preloadSound(LLUUID(gSavedSettings.getString("UISndHealthReductionF")));
4127 gAudiop->preloadSound(LLUUID(gSavedSettings.getString("UISndHealthReductionM")));
4128 //gAudiop->preloadSound(LLUUID(gSavedSettings.getString("UISndIncomingChat")));
4129 //gAudiop->preloadSound(LLUUID(gSavedSettings.getString("UISndIncomingIM")));
4130 //gAudiop->preloadSound(LLUUID(gSavedSettings.getString("UISndInvApplyToObject")));
4131 gAudiop->preloadSound(LLUUID(gSavedSettings.getString("UISndInvalidOp")));
4132 //gAudiop->preloadSound(LLUUID(gSavedSettings.getString("UISndInventoryCopyToInv")));
4133 gAudiop->preloadSound(LLUUID(gSavedSettings.getString("UISndMoneyChangeDown")));
4134 gAudiop->preloadSound(LLUUID(gSavedSettings.getString("UISndMoneyChangeUp")));
4135 //gAudiop->preloadSound(LLUUID(gSavedSettings.getString("UISndObjectCopyToInv")));
4136 gAudiop->preloadSound(LLUUID(gSavedSettings.getString("UISndObjectCreate")));
4137 gAudiop->preloadSound(LLUUID(gSavedSettings.getString("UISndObjectDelete")));
4138 gAudiop->preloadSound(LLUUID(gSavedSettings.getString("UISndObjectRezIn")));
4139 gAudiop->preloadSound(LLUUID(gSavedSettings.getString("UISndObjectRezOut")));
4140 gAudiop->preloadSound(LLUUID(gSavedSettings.getString("UISndPieMenuAppear")));
4141 gAudiop->preloadSound(LLUUID(gSavedSettings.getString("UISndPieMenuHide")));
4142 gAudiop->preloadSound(LLUUID(gSavedSettings.getString("UISndPieMenuSliceHighlight0")));
4143 gAudiop->preloadSound(LLUUID(gSavedSettings.getString("UISndPieMenuSliceHighlight1")));
4144 gAudiop->preloadSound(LLUUID(gSavedSettings.getString("UISndPieMenuSliceHighlight2")));
4145 gAudiop->preloadSound(LLUUID(gSavedSettings.getString("UISndPieMenuSliceHighlight3")));
4146 gAudiop->preloadSound(LLUUID(gSavedSettings.getString("UISndPieMenuSliceHighlight4")));
4147 gAudiop->preloadSound(LLUUID(gSavedSettings.getString("UISndPieMenuSliceHighlight5")));
4148 gAudiop->preloadSound(LLUUID(gSavedSettings.getString("UISndPieMenuSliceHighlight6")));
4149 gAudiop->preloadSound(LLUUID(gSavedSettings.getString("UISndPieMenuSliceHighlight7")));
4150 gAudiop->preloadSound(LLUUID(gSavedSettings.getString("UISndSnapshot")));
4151 //gAudiop->preloadSound(LLUUID(gSavedSettings.getString("UISndStartAutopilot")));
4152 //gAudiop->preloadSound(LLUUID(gSavedSettings.getString("UISndStartFollowpilot")));
4153 gAudiop->preloadSound(LLUUID(gSavedSettings.getString("UISndStartIM")));
4154 //gAudiop->preloadSound(LLUUID(gSavedSettings.getString("UISndStopAutopilot")));
4155 gAudiop->preloadSound(LLUUID(gSavedSettings.getString("UISndTeleportOut")));
4156 //gAudiop->preloadSound(LLUUID(gSavedSettings.getString("UISndTextureApplyToObject")));
4157 //gAudiop->preloadSound(LLUUID(gSavedSettings.getString("UISndTextureCopyToInv")));
4158 gAudiop->preloadSound(LLUUID(gSavedSettings.getString("UISndTyping")));
4159 gAudiop->preloadSound(LLUUID(gSavedSettings.getString("UISndWindowClose")));
4160 gAudiop->preloadSound(LLUUID(gSavedSettings.getString("UISndWindowOpen")));
4161 }
4162
4163 audio_update_volume(true);
4164}
4165
4166void audio_update_volume(bool force_update)
4167{
4168 F32 master_volume = gSavedSettings.getF32("AudioLevelMaster");
4169 BOOL mute_audio = gSavedSettings.getBOOL("MuteAudio");
4170 if (!gViewerWindow->getActive() && (gSavedSettings.getBOOL("MuteWhenMinimized")))
4171 {
4172 mute_audio = TRUE;
4173 }
4174 F32 mute_volume = mute_audio ? 0.0f : 1.0f;
4175
4176 // Sound Effects
4177 if (gAudiop)
4178 {
4179 gAudiop->setMasterGain ( master_volume );
4180
4181 gAudiop->setDopplerFactor(gSavedSettings.getF32("AudioLevelDoppler"));
4182 gAudiop->setDistanceFactor(gSavedSettings.getF32("AudioLevelDistance"));
4183 gAudiop->setRolloffFactor(gSavedSettings.getF32("AudioLevelRolloff"));
4184#ifdef kAUDIO_ENABLE_WIND
4185 gAudiop->enableWind(!mute_audio);
4186#endif
4187
4188 gAudiop->setMuted(mute_audio);
4189
4190 if (force_update)
4191 {
4192 audio_update_wind(true);
4193 }
4194 }
4195
4196 // Streaming Music
4197 if (gAudiop)
4198 {
4199 F32 music_volume = gSavedSettings.getF32("AudioLevelMusic");
4200 music_volume = mute_volume * master_volume * (music_volume*music_volume);
4201 gAudiop->setInternetStreamGain ( music_volume );
4202 }
4203
4204 // Streaming Media
4205 if(LLMediaEngine::getInstance())
4206 {
4207 F32 media_volume = gSavedSettings.getF32("AudioLevelMedia");
4208 media_volume = mute_volume * master_volume * (media_volume*media_volume);
4209 LLMediaEngine::getInstance()->setVolume(media_volume);
4210 }
4211
4212 // Voice
4213 if (gVoiceClient)
4214 {
4215 F32 voice_volume = gSavedSettings.getF32("AudioLevelVoice");
4216 voice_volume = mute_volume * master_volume * voice_volume;
4217 gVoiceClient->setVoiceVolume(voice_volume);
4218 gVoiceClient->setMicGain(gSavedSettings.getF32("AudioLevelMic"));
4219
4220 if (!gViewerWindow->getActive() && (gSavedSettings.getBOOL("MuteWhenMinimized")))
4221 {
4222 gVoiceClient->setMuteMic(true);
4223 }
4224 else
4225 {
4226 gVoiceClient->setMuteMic(false);
4227 }
4228 }
4229}
4230
4231void audio_update_listener()
4232{
4233 if (gAudiop)
4234 {
4235 // update listener position because agent has moved
4236 LLVector3d lpos_global = gAgent.getCameraPositionGlobal();
4237 LLVector3 lpos_global_f;
4238 lpos_global_f.setVec(lpos_global);
4239
4240 gAudiop->setListener(lpos_global_f,
4241 // gCameraVelocitySmoothed,
4242 // LLVector3::zero,
4243 gAgent.getVelocity(), // !!! *TODO: need to replace this with smoothed velocity!
4244 gCamera->getUpAxis(),
4245 gCamera->getAtAxis());
4246 }
4247}
4248
4249void audio_update_wind(bool force_update)
4250{
4251#ifdef kAUDIO_ENABLE_WIND
4252 //
4253 // Extract height above water to modulate filter by whether above/below water
4254 //
4255 LLViewerRegion* region = gAgent.getRegion();
4256 if (region)
4257 {
4258 static F32 last_camera_water_height = -1000.f;
4259 LLVector3 camera_pos = gAgent.getCameraPositionAgent();
4260 F32 camera_water_height = camera_pos.mV[VZ] - region->getWaterHeight();
4261
4262 //
4263 // Don't update rolloff factor unless water surface has been crossed
4264 //
4265 if (force_update || (last_camera_water_height * camera_water_height) < 0.f)
4266 {
4267 if (camera_water_height < 0.f)
4268 {
4269 gAudiop->setRolloffFactor(gSavedSettings.getF32("AudioLevelRolloff") * LL_ROLLOFF_MULTIPLIER_UNDER_WATER);
4270 }
4271 else
4272 {
4273 gAudiop->setRolloffFactor(gSavedSettings.getF32("AudioLevelRolloff"));
4274 }
4275 }
4276 // this line rotates the wind vector to be listener (agent) relative
4277 // unfortunately we have to pre-translate to undo the translation that
4278 // occurs in the transform call
4279 gRelativeWindVec = gAgent.getFrameAgent().rotateToLocal(gWindVec - gAgent.getVelocity());
4280
4281 // don't use the setter setMaxWindGain() because we don't
4282 // want to screw up the fade-in on startup by setting actual source gain
4283 // outside the fade-in.
4284 gAudiop->mMaxWindGain = gSavedSettings.getF32("AudioLevelAmbient");
4285
4286 last_camera_water_height = camera_water_height;
4287 gAudiop->updateWind(gRelativeWindVec, camera_water_height);
4288 }
4289#endif
4290}
4291
4292
4293/////////////////////////////////////////////////////////
4294
4295BOOL raycast_for_new_obj_pos( S32 x, S32 y, LLViewerObject** hit_obj, S32* hit_face,
4296 BOOL* b_hit_land, LLVector3* ray_start_region, LLVector3* ray_end_region, LLViewerRegion** region )
4297{
4298 F32 max_dist_from_camera = gSavedSettings.getF32( "MaxSelectDistance" ) - 1.f;
4299
4300 // Viewer-side pick to find the right sim to create the object on.
4301 // First find the surface the object will be created on.
4302 gViewerWindow->hitObjectOrLandGlobalImmediate(x, y, NULL, FALSE);
4303
4304 // Note: use the frontmost non-flora version because (a) plants usually have lots of alpha and (b) pants' Havok
4305 // representations (if any) are NOT the same as their viewer representation.
4306 *hit_obj = gObjectList.findObject( gLastHitNonFloraObjectID );
4307 *hit_face = gLastHitNonFloraObjectFace;
4308 *b_hit_land = !(*hit_obj) && !gLastHitNonFloraPosGlobal.isExactlyZero();
4309 LLVector3d land_pos_global = gLastHitNonFloraPosGlobal;
4310
4311 // Make sure there's a surface to place the new object on.
4312 BOOL bypass_sim_raycast = FALSE;
4313 LLVector3d surface_pos_global;
4314 if (*b_hit_land)
4315 {
4316 surface_pos_global = land_pos_global;
4317 bypass_sim_raycast = TRUE;
4318 }
4319 else
4320 if (*hit_obj)
4321 {
4322 surface_pos_global = (*hit_obj)->getPositionGlobal();
4323 }
4324 else
4325 {
4326 return FALSE;
4327 }
4328
4329 // Make sure the surface isn't too far away.
4330 LLVector3d ray_start_global = gAgent.getCameraPositionGlobal();
4331 F32 dist_to_surface_sq = (F32)((surface_pos_global - ray_start_global).magVecSquared());
4332 if( dist_to_surface_sq > (max_dist_from_camera * max_dist_from_camera) )
4333 {
4334 return FALSE;
4335 }
4336
4337 // Find the sim where the surface lives.
4338 LLViewerRegion *regionp = gWorldp->getRegionFromPosGlobal(surface_pos_global);
4339 if (!regionp)
4340 {
4341 llwarns << "Trying to add object outside of all known regions!" << llendl;
4342 return FALSE;
4343 }
4344
4345 // Find the simulator-side ray that will be used to place the object accurately
4346 LLVector3d mouse_direction;
4347 mouse_direction.setVec( gViewerWindow->mouseDirectionGlobal( x, y ) );
4348
4349 *region = regionp;
4350 *ray_start_region = regionp->getPosRegionFromGlobal( ray_start_global );
4351 F32 near_clip = gCamera->getNear() + 0.01f; // Include an epsilon to avoid rounding issues.
4352 *ray_start_region += gCamera->getAtAxis() * near_clip;
4353
4354 if( bypass_sim_raycast )
4355 {
4356 // Hack to work around Havok's inability to ray cast onto height fields
4357 *ray_end_region = regionp->getPosRegionFromGlobal( surface_pos_global ); // ray end is the viewer's intersection point
4358 }
4359 else
4360 {
4361 LLVector3d ray_end_global = ray_start_global + (1.f + max_dist_from_camera) * mouse_direction; // add an epsilon to the sim version of the ray to avoid rounding problems.
4362 *ray_end_region = regionp->getPosRegionFromGlobal( ray_end_global );
4363 }
4364
4365 return TRUE;
4366}
4367
4368const LLVector3 DEFAULT_OBJECT_SCALE(0.5f, 0.5f, 0.5f);
4369
4370BOOL add_object( LLPCode pcode, S32 x, S32 y, U8 use_physics )
4371{
4372 LLVector3 ray_start_region;
4373 LLVector3 ray_end_region;
4374 LLViewerRegion* regionp = NULL;
4375 BOOL b_hit_land = FALSE;
4376 S32 hit_face = -1;
4377 LLViewerObject* hit_obj = NULL;
4378 U8 state = 0;
4379 BOOL success = raycast_for_new_obj_pos( x, y, &hit_obj, &hit_face, &b_hit_land, &ray_start_region, &ray_end_region, &regionp );
4380 if( !success )
4381 {
4382 return FALSE;
4383 }
4384
4385 if( hit_obj && (hit_obj->isAvatar() || hit_obj->isAttachment()) )
4386 {
4387 // Can't create objects on avatars or attachments
4388 return FALSE;
4389 }
4390
4391 if (NULL == regionp)
4392 {
4393 llwarns << "regionp was NULL; aborting function." << llendl;
4394 return FALSE;
4395 }
4396
4397 if (regionp->getRegionFlags() & REGION_FLAGS_SANDBOX)
4398 {
4399 LLFirstUse::useSandbox();
4400 }
4401
4402 // Set params for new object based on its PCode.
4403 LLQuaternion rotation;
4404 LLVector3 scale = DEFAULT_OBJECT_SCALE;
4405 U8 material = LL_MCODE_WOOD;
4406 BOOL create_selected = FALSE;
4407 LLVolumeParams volume_params;
4408
4409 switch (pcode)
4410 {
4411 case LL_PCODE_LEGACY_GRASS:
4412 // Randomize size of grass patch
4413 scale.setVec(10.f + ll_frand(20.f), 10.f + ll_frand(20.f), 1.f + ll_frand(2.f));
4414 state = rand() % LLVOGrass::sMaxGrassSpecies;
4415 break;
4416
4417
4418 case LL_PCODE_LEGACY_TREE:
4419 case LL_PCODE_TREE_NEW:
4420 state = rand() % LLVOTree::sMaxTreeSpecies;
4421 break;
4422
4423 case LL_PCODE_SPHERE:
4424 case LL_PCODE_CONE:
4425 case LL_PCODE_CUBE:
4426 case LL_PCODE_CYLINDER:
4427 case LL_PCODE_TORUS:
4428 case LLViewerObject::LL_VO_SQUARE_TORUS:
4429 case LLViewerObject::LL_VO_TRIANGLE_TORUS:
4430 default:
4431 create_selected = TRUE;
4432 break;
4433 }
4434
4435 // Play creation sound
4436 if (gAudiop)
4437 {
4438 F32 volume = gSavedSettings.getF32("AudioLevelUI");
4439 gAudiop->triggerSound( LLUUID(gSavedSettings.getString("UISndObjectCreate")), gAgent.getID(), volume);
4440 }
4441
4442 gMessageSystem->newMessageFast(_PREHASH_ObjectAdd);
4443 gMessageSystem->nextBlockFast(_PREHASH_AgentData);
4444 gMessageSystem->addUUIDFast(_PREHASH_AgentID, gAgent.getID());
4445 gMessageSystem->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
4446 gMessageSystem->addUUIDFast(_PREHASH_GroupID, gAgent.getGroupID());
4447 gMessageSystem->nextBlockFast(_PREHASH_ObjectData);
4448 gMessageSystem->addU8Fast(_PREHASH_Material, material);
4449
4450 U32 flags = 0; // not selected
4451 if (use_physics)
4452 {
4453 flags |= FLAGS_USE_PHYSICS;
4454 }
4455 if (create_selected)
4456 {
4457 flags |= FLAGS_CREATE_SELECTED;
4458 }
4459 gMessageSystem->addU32Fast(_PREHASH_AddFlags, flags );
4460
4461 LLPCode volume_pcode; // ...PCODE_VOLUME, or the original on error
4462 switch (pcode)
4463 {
4464 case LL_PCODE_SPHERE:
4465 rotation.setQuat(90.f * DEG_TO_RAD, LLVector3::y_axis);
4466
4467 volume_params.setType( LL_PCODE_PROFILE_CIRCLE_HALF, LL_PCODE_PATH_CIRCLE );
4468 volume_params.setBeginAndEndS( 0.f, 1.f );
4469 volume_params.setBeginAndEndT( 0.f, 1.f );
4470 volume_params.setRatio ( 1, 1 );
4471 volume_params.setShear ( 0, 0 );
4472 LLVolumeMessage::packVolumeParams(&volume_params, gMessageSystem);
4473 volume_pcode = LL_PCODE_VOLUME;
4474 break;
4475
4476 case LL_PCODE_TORUS:
4477 rotation.setQuat(90.f * DEG_TO_RAD, LLVector3::y_axis);
4478
4479 volume_params.setType( LL_PCODE_PROFILE_CIRCLE, LL_PCODE_PATH_CIRCLE );
4480 volume_params.setBeginAndEndS( 0.f, 1.f );
4481 volume_params.setBeginAndEndT( 0.f, 1.f );
4482 volume_params.setRatio ( 1.f, 0.25f ); // "top size"
4483 volume_params.setShear ( 0, 0 );
4484 LLVolumeMessage::packVolumeParams(&volume_params, gMessageSystem);
4485 volume_pcode = LL_PCODE_VOLUME;
4486 break;
4487
4488 case LLViewerObject::LL_VO_SQUARE_TORUS:
4489 rotation.setQuat(90.f * DEG_TO_RAD, LLVector3::y_axis);
4490
4491 volume_params.setType( LL_PCODE_PROFILE_SQUARE, LL_PCODE_PATH_CIRCLE );
4492 volume_params.setBeginAndEndS( 0.f, 1.f );
4493 volume_params.setBeginAndEndT( 0.f, 1.f );
4494 volume_params.setRatio ( 1.f, 0.25f ); // "top size"
4495 volume_params.setShear ( 0, 0 );
4496 LLVolumeMessage::packVolumeParams(&volume_params, gMessageSystem);
4497 volume_pcode = LL_PCODE_VOLUME;
4498 break;
4499
4500 case LLViewerObject::LL_VO_TRIANGLE_TORUS:
4501 rotation.setQuat(90.f * DEG_TO_RAD, LLVector3::y_axis);
4502
4503 volume_params.setType( LL_PCODE_PROFILE_EQUALTRI, LL_PCODE_PATH_CIRCLE );
4504 volume_params.setBeginAndEndS( 0.f, 1.f );
4505 volume_params.setBeginAndEndT( 0.f, 1.f );
4506 volume_params.setRatio ( 1.f, 0.25f ); // "top size"
4507 volume_params.setShear ( 0, 0 );
4508 LLVolumeMessage::packVolumeParams(&volume_params, gMessageSystem);
4509 volume_pcode = LL_PCODE_VOLUME;
4510 break;
4511
4512 case LL_PCODE_SPHERE_HEMI:
4513 volume_params.setType( LL_PCODE_PROFILE_CIRCLE_HALF, LL_PCODE_PATH_CIRCLE );
4514 //volume_params.setBeginAndEndS( 0.5f, 1.f );
4515 volume_params.setBeginAndEndT( 0.f, 0.5f );
4516 volume_params.setRatio ( 1, 1 );
4517 volume_params.setShear ( 0, 0 );
4518 LLVolumeMessage::packVolumeParams(&volume_params, gMessageSystem);
4519 volume_pcode = LL_PCODE_VOLUME;
4520 break;
4521
4522 case LL_PCODE_CUBE:
4523 volume_params.setType( LL_PCODE_PROFILE_SQUARE, LL_PCODE_PATH_LINE );
4524 volume_params.setBeginAndEndS( 0.f, 1.f );
4525 volume_params.setBeginAndEndT( 0.f, 1.f );
4526 volume_params.setRatio ( 1, 1 );
4527 volume_params.setShear ( 0, 0 );
4528 LLVolumeMessage::packVolumeParams(&volume_params, gMessageSystem);
4529 volume_pcode = LL_PCODE_VOLUME;
4530 break;
4531
4532 case LL_PCODE_PRISM:
4533 volume_params.setType( LL_PCODE_PROFILE_SQUARE, LL_PCODE_PATH_LINE );
4534 volume_params.setBeginAndEndS( 0.f, 1.f );
4535 volume_params.setBeginAndEndT( 0.f, 1.f );
4536 volume_params.setRatio ( 0, 1 );
4537 volume_params.setShear ( -0.5f, 0 );
4538 LLVolumeMessage::packVolumeParams(&volume_params, gMessageSystem);
4539 volume_pcode = LL_PCODE_VOLUME;
4540 break;
4541
4542 case LL_PCODE_PYRAMID:
4543 volume_params.setType( LL_PCODE_PROFILE_SQUARE, LL_PCODE_PATH_LINE );
4544 volume_params.setBeginAndEndS( 0.f, 1.f );
4545 volume_params.setBeginAndEndT( 0.f, 1.f );
4546 volume_params.setRatio ( 0, 0 );
4547 volume_params.setShear ( 0, 0 );
4548 LLVolumeMessage::packVolumeParams(&volume_params, gMessageSystem);
4549 volume_pcode = LL_PCODE_VOLUME;
4550 break;
4551
4552 case LL_PCODE_TETRAHEDRON:
4553 volume_params.setType( LL_PCODE_PROFILE_EQUALTRI, LL_PCODE_PATH_LINE );
4554 volume_params.setBeginAndEndS( 0.f, 1.f );
4555 volume_params.setBeginAndEndT( 0.f, 1.f );
4556 volume_params.setRatio ( 0, 0 );
4557 volume_params.setShear ( 0, 0 );
4558 LLVolumeMessage::packVolumeParams(&volume_params, gMessageSystem);
4559 volume_pcode = LL_PCODE_VOLUME;
4560 break;
4561
4562 case LL_PCODE_CYLINDER:
4563 volume_params.setType( LL_PCODE_PROFILE_CIRCLE, LL_PCODE_PATH_LINE );
4564 volume_params.setBeginAndEndS( 0.f, 1.f );
4565 volume_params.setBeginAndEndT( 0.f, 1.f );
4566 volume_params.setRatio ( 1, 1 );
4567 volume_params.setShear ( 0, 0 );
4568 LLVolumeMessage::packVolumeParams(&volume_params, gMessageSystem);
4569 volume_pcode = LL_PCODE_VOLUME;
4570 break;
4571
4572 case LL_PCODE_CYLINDER_HEMI:
4573 volume_params.setType( LL_PCODE_PROFILE_CIRCLE, LL_PCODE_PATH_LINE );
4574 volume_params.setBeginAndEndS( 0.25f, 0.75f );
4575 volume_params.setBeginAndEndT( 0.f, 1.f );
4576 volume_params.setRatio ( 1, 1 );
4577 volume_params.setShear ( 0, 0 );
4578 LLVolumeMessage::packVolumeParams(&volume_params, gMessageSystem);
4579 volume_pcode = LL_PCODE_VOLUME;
4580 break;
4581
4582 case LL_PCODE_CONE:
4583 volume_params.setType( LL_PCODE_PROFILE_CIRCLE, LL_PCODE_PATH_LINE );
4584 volume_params.setBeginAndEndS( 0.f, 1.f );
4585 volume_params.setBeginAndEndT( 0.f, 1.f );
4586 volume_params.setRatio ( 0, 0 );
4587 volume_params.setShear ( 0, 0 );
4588 LLVolumeMessage::packVolumeParams(&volume_params, gMessageSystem);
4589 volume_pcode = LL_PCODE_VOLUME;
4590 break;
4591
4592 case LL_PCODE_CONE_HEMI:
4593 volume_params.setType( LL_PCODE_PROFILE_CIRCLE, LL_PCODE_PATH_LINE );
4594 volume_params.setBeginAndEndS( 0.25f, 0.75f );
4595 volume_params.setBeginAndEndT( 0.f, 1.f );
4596 volume_params.setRatio ( 0, 0 );
4597 volume_params.setShear ( 0, 0 );
4598 LLVolumeMessage::packVolumeParams(&volume_params, gMessageSystem);
4599 volume_pcode = LL_PCODE_VOLUME;
4600 break;
4601
4602 default:
4603 LLVolumeMessage::packVolumeParams(0, gMessageSystem);
4604 volume_pcode = pcode;
4605 break;
4606 }
4607 gMessageSystem->addU8Fast(_PREHASH_PCode, volume_pcode);
4608
4609 gMessageSystem->addVector3Fast(_PREHASH_Scale, scale );
4610 gMessageSystem->addQuatFast(_PREHASH_Rotation, rotation );
4611 gMessageSystem->addVector3Fast(_PREHASH_RayStart, ray_start_region );
4612 gMessageSystem->addVector3Fast(_PREHASH_RayEnd, ray_end_region );
4613 gMessageSystem->addU8Fast(_PREHASH_BypassRaycast, (U8)b_hit_land );
4614 gMessageSystem->addU8Fast(_PREHASH_RayEndIsIntersection, (U8)FALSE );
4615 gMessageSystem->addU8Fast(_PREHASH_State, state);
4616
4617 // Limit raycast to a single object.
4618 // Speeds up server raycast + avoid problems with server ray hitting objects
4619 // that were clipped by the near plane or culled on the viewer.
4620 LLUUID ray_target_id;
4621 if( hit_obj )
4622 {
4623 ray_target_id = hit_obj->getID();
4624 }
4625 else
4626 {
4627 ray_target_id.setNull();
4628 }
4629 gMessageSystem->addUUIDFast(_PREHASH_RayTargetID, ray_target_id );
4630
4631 // Pack in name value pairs
4632 gMessageSystem->sendReliable(regionp->getHost());
4633
4634 // Spawns a message, so must be after above send
4635 if (create_selected)
4636 {
4637 gSelectMgr->deselectAll();
4638 gViewerWindow->getWindow()->incBusyCount();
4639 }
4640
4641 // VEFFECT: AddObject
4642 LLHUDEffectSpiral *effectp = (LLHUDEffectSpiral *)gHUDManager->createViewerEffect(LLHUDObject::LL_HUD_EFFECT_BEAM, TRUE);
4643 effectp->setSourceObject(gAgent.getAvatarObject());
4644 effectp->setPositionGlobal(regionp->getPosGlobalFromRegion(ray_end_region));
4645 effectp->setDuration(LL_HUD_DUR_SHORT);
4646 effectp->setColor(LLColor4U(gAgent.getEffectColor()));
4647
4648 gViewerStats->incStat(LLViewerStats::ST_CREATE_COUNT);
4649
4650 return TRUE;
4651}
4652
4653
4654
4655#if LL_WINDOWS
4656void create_console()
4657{
4658 int h_con_handle;
4659 long l_std_handle;
4660
4661 CONSOLE_SCREEN_BUFFER_INFO coninfo;
4662 FILE *fp;
4663
4664 // allocate a console for this app
4665 AllocConsole();
4666
4667 // set the screen buffer to be big enough to let us scroll text
4668 GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), &coninfo);
4669 coninfo.dwSize.Y = MAX_CONSOLE_LINES;
4670 SetConsoleScreenBufferSize(GetStdHandle(STD_OUTPUT_HANDLE), coninfo.dwSize);
4671
4672 // redirect unbuffered STDOUT to the console
4673 l_std_handle = (long)GetStdHandle(STD_OUTPUT_HANDLE);
4674 h_con_handle = _open_osfhandle(l_std_handle, _O_TEXT);
4675 fp = _fdopen( h_con_handle, "w" );
4676 *stdout = *fp;
4677 setvbuf( stdout, NULL, _IONBF, 0 );
4678
4679 // redirect unbuffered STDIN to the console
4680 l_std_handle = (long)GetStdHandle(STD_INPUT_HANDLE);
4681 h_con_handle = _open_osfhandle(l_std_handle, _O_TEXT);
4682 fp = _fdopen( h_con_handle, "r" );
4683 *stdin = *fp;
4684 setvbuf( stdin, NULL, _IONBF, 0 );
4685
4686 // redirect unbuffered STDERR to the console
4687 l_std_handle = (long)GetStdHandle(STD_ERROR_HANDLE);
4688 h_con_handle = _open_osfhandle(l_std_handle, _O_TEXT);
4689 fp = _fdopen( h_con_handle, "w" );
4690 *stderr = *fp;
4691 setvbuf( stderr, NULL, _IONBF, 0 );
4692}
4693#endif
4694
4695
4696
4697//-------------------------------------------------------------------
4698//-------------------------------------------------------------------
4699// Vector Performance Options
4700//-------------------------------------------------------------------
4701//-------------------------------------------------------------------
4702
4703// Initially, we test the performance of the vectorization code, then
4704// turn it off if it ends up being slower. JC
4705BOOL gVectorizePerfTest = TRUE;
4706BOOL gVectorizeEnable = FALSE;
4707U32 gVectorizeProcessor = 0;
4708BOOL gVectorizeSkin = FALSE;
4709
4710void update_vector_performances(void)
4711{
4712 char *vp;
4713
4714 switch(gVectorizeProcessor)
4715 {
4716 case 2: vp = "SSE2"; break; // *TODO: replace the magic #s
4717 case 1: vp = "SSE"; break;
4718 default: vp = "COMPILER DEFAULT"; break;
4719 }
4720 llinfos << "Vectorization : " << ( gVectorizeEnable ? "ENABLED" : "DISABLED" ) << llendl ;
4721 llinfos << "Vector Processor : " << vp << llendl ;
4722 llinfos << "Vectorized Skinning : " << ( gVectorizeSkin ? "ENABLED" : "DISABLED" ) << llendl ;
4723
4724 if(gVectorizeEnable && gVectorizeSkin)
4725 {
4726 switch(gVectorizeProcessor)
4727 {
4728 case 2:
4729 LLViewerJointMesh::sUpdateGeometryFunc = &LLViewerJointMesh::updateGeometrySSE2;
4730 break;
4731 case 1:
4732 LLViewerJointMesh::sUpdateGeometryFunc = &LLViewerJointMesh::updateGeometrySSE;
4733 break;
4734 default:
4735 LLViewerJointMesh::sUpdateGeometryFunc = &LLViewerJointMesh::updateGeometryVectorized;
4736 break;
4737 }
4738 }
4739 else
4740 {
4741 LLViewerJointMesh::sUpdateGeometryFunc = &LLViewerJointMesh::updateGeometryOriginal;
4742 }
4743}
4744
4745
4746class LLVectorizationEnableListener: public LLSimpleListener
4747{
4748 bool handleEvent(LLPointer<LLEvent> event, const LLSD& userdata)
4749 {
4750 gVectorizeEnable = event->getValue().asBoolean();
4751 update_vector_performances();
4752 return true;
4753 }
4754};
4755static LLVectorizationEnableListener vectorization_enable_listener;
4756
4757class LLVectorizeSkinListener: public LLSimpleListener
4758{
4759 bool handleEvent(LLPointer<LLEvent> event, const LLSD& userdata)
4760 {
4761 gVectorizeSkin = event->getValue().asBoolean();
4762 update_vector_performances();
4763 return true;
4764 }
4765};
4766static LLVectorizeSkinListener vectorize_skin_listener;
4767
4768class LLVectorProcessorListener: public LLSimpleListener
4769{
4770 bool handleEvent(LLPointer<LLEvent> event, const LLSD& userdata)
4771 {
4772 gVectorizeProcessor = event->getValue().asInteger();
4773 update_vector_performances();
4774 return true;
4775 }
4776};
4777static LLVectorProcessorListener vector_processor_listener;
4778
4779// Use these strictly for things that are constructed at startup,
4780// or for things that are performance critical. JC
4781void saved_settings_to_globals()
4782{
4783 LLBUTTON_H_PAD = gSavedSettings.getS32("ButtonHPad");
4784 LLBUTTON_V_PAD = gSavedSettings.getS32("ButtonVPad");
4785 BTN_HEIGHT_SMALL = gSavedSettings.getS32("ButtonHeightSmall");
4786 BTN_HEIGHT = gSavedSettings.getS32("ButtonHeight");
4787
4788 MENU_BAR_HEIGHT = gSavedSettings.getS32("MenuBarHeight");
4789 MENU_BAR_WIDTH = gSavedSettings.getS32("MenuBarWidth");
4790 STATUS_BAR_HEIGHT = gSavedSettings.getS32("StatusBarHeight");
4791
4792 LLCOMBOBOX_HEIGHT = BTN_HEIGHT - 2;
4793 LLCOMBOBOX_WIDTH = 128;
4794
4795 LLSurface::setTextureSize(gSavedSettings.getU32("RegionTextureSize"));
4796
4797 LLVOSky::sNighttimeBrightness = gSavedSettings.getF32("RenderNightBrightness");
4798
4799 LLImageGL::sGlobalUseAnisotropic = gSavedSettings.getBOOL("RenderAnisotropic");
4800 LLVOVolume::sLODFactor = gSavedSettings.getF32("RenderVolumeLODFactor");
4801 LLVOVolume::sDistanceFactor = 1.f-LLVOVolume::sLODFactor * 0.1f;
4802 LLVolumeImplFlexible::sUpdateFactor = gSavedSettings.getF32("RenderFlexTimeFactor");
4803 LLVOTree::sTreeFactor = gSavedSettings.getF32("RenderTreeLODFactor");
4804 LLVOAvatar::sLODFactor = gSavedSettings.getF32("RenderAvatarLODFactor");
4805 LLVOAvatar::sMaxVisible = gSavedSettings.getS32("RenderAvatarMaxVisible");
4806 LLVOAvatar::sVisibleInFirstPerson = gSavedSettings.getBOOL("FirstPersonAvatarVisible");
4807 // clamp auto-open time to some minimum usable value
4808 LLFolderView::sAutoOpenTime = llmax(0.25f, gSavedSettings.getF32("FolderAutoOpenDelay"));
4809 LLToolBar::sInventoryAutoOpenTime = gSavedSettings.getF32("InventoryAutoOpenDelay");
4810 LLSelectMgr::sRectSelectInclusive = gSavedSettings.getBOOL("RectangleSelectInclusive");
4811 LLSelectMgr::sRenderHiddenSelections = gSavedSettings.getBOOL("RenderHiddenSelections");
4812 LLSelectMgr::sRenderLightRadius = gSavedSettings.getBOOL("RenderLightRadius");
4813
4814 gFrameStats.setTrackStats(gSavedSettings.getBOOL("StatsSessionTrackFrameStats"));
4815 gAgentPilot.mNumRuns = gSavedSettings.getS32("StatsNumRuns");
4816 gAgentPilot.mQuitAfterRuns = gSavedSettings.getBOOL("StatsQuitAfterRuns");
4817 gAgent.mHideGroupTitle = gSavedSettings.getBOOL("RenderHideGroupTitle");
4818
4819 gDebugWindowProc = gSavedSettings.getBOOL("DebugWindowProc");
4820 gAllowIdleAFK = gSavedSettings.getBOOL("AllowIdleAFK");
4821 gAFKTimeout = gSavedSettings.getF32("AFKTimeout");
4822 gMouseSensitivity = gSavedSettings.getF32("MouseSensitivity");
4823 gInvertMouse = gSavedSettings.getBOOL("InvertMouse");
4824 gShowObjectUpdates = gSavedSettings.getBOOL("ShowObjectUpdates");
4825 gMapScale = gSavedSettings.getF32("MapScale");
4826 gMiniMapScale = gSavedSettings.getF32("MiniMapScale");
4827 gHandleKeysAsync = gSavedSettings.getBOOL("AsyncKeyboard");
4828 LLHoverView::sShowHoverTips = gSavedSettings.getBOOL("ShowHoverTips");
4829
4830#if LL_VECTORIZE
4831 if (gSysCPU.hasAltivec())
4832 {
4833 gSavedSettings.setBOOL("VectorizeEnable", TRUE );
4834 gSavedSettings.setU32("VectorizeProcessor", 0 );
4835 }
4836 else
4837 if (gSysCPU.hasSSE2())
4838 {
4839 gSavedSettings.setBOOL("VectorizeEnable", TRUE );
4840 gSavedSettings.setU32("VectorizeProcessor", 2 );
4841 }
4842 else
4843 if (gSysCPU.hasSSE())
4844 {
4845 gSavedSettings.setBOOL("VectorizeEnable", TRUE );
4846 gSavedSettings.setU32("VectorizeProcessor", 1 );
4847 }
4848 else
4849 {
4850 // Don't bother testing or running if CPU doesn't support it. JC
4851 gSavedSettings.setBOOL("VectorizePerfTest", FALSE );
4852 gSavedSettings.setBOOL("VectorizeEnable", FALSE );
4853 gSavedSettings.setU32("VectorizeProcessor", 0 );
4854 gSavedSettings.setBOOL("VectorizeSkin", FALSE);
4855 }
4856#else
4857 // This build target doesn't support SSE, don't test/run.
4858 gSavedSettings.setBOOL("VectorizePerfTest", FALSE );
4859 gSavedSettings.setBOOL("VectorizeEnable", FALSE );
4860 gSavedSettings.setU32("VectorizeProcessor", 0 );
4861 gSavedSettings.setBOOL("VectorizeSkin", FALSE);
4862#endif
4863
4864 gVectorizePerfTest = gSavedSettings.getBOOL("VectorizePerfTest");
4865 gVectorizeEnable = gSavedSettings.getBOOL("VectorizeEnable");
4866 gVectorizeProcessor = gSavedSettings.getU32("VectorizeProcessor");
4867 gVectorizeSkin = gSavedSettings.getBOOL("VectorizeSkin");
4868 update_vector_performances();
4869
4870 // Into a global in case we corrupt the list on crash.
4871 gCrashBehavior = gCrashSettings.getS32(CRASH_BEHAVIOR_SETTING);
4872
4873 // propagate push to talk preference to current status
4874 gSavedSettings.setBOOL("PTTCurrentlyEnabled", gSavedSettings.getBOOL("EnablePushToTalk"));
4875
4876 settings_setup_listeners();
4877
4878 // these are currently static in this file, so they can't move to settings_setup_listeners
4879 gSavedSettings.getControl("VectorizeEnable")->addListener(&vectorization_enable_listener);
4880 gSavedSettings.getControl("VectorizeProcessor")->addListener(&vector_processor_listener);
4881 gSavedSettings.getControl("VectorizeSkin")->addListener(&vectorize_skin_listener);
4882
4883 // gAgent.init() also loads from saved settings.
4884}
4885
4886void cleanup_saved_settings()
4887{
4888 gSavedSettings.setBOOL("MouseSun", FALSE);
4889
4890 gSavedSettings.setBOOL("FlyBtnState", FALSE);
4891
4892 gSavedSettings.setBOOL("FirstPersonBtnState", FALSE);
4893 gSavedSettings.setBOOL("ThirdPersonBtnState", TRUE);
4894 gSavedSettings.setBOOL("BuildBtnState", FALSE);
4895
4896 gSavedSettings.setBOOL("UseEnergy", TRUE); // force toggle to turn off, since sends message to simulator
4897
4898 gSavedSettings.setBOOL("DebugWindowProc", gDebugWindowProc);
4899
4900 gSavedSettings.setBOOL("AllowIdleAFK", gAllowIdleAFK);
4901 gSavedSettings.setBOOL("ShowObjectUpdates", gShowObjectUpdates);
4902
4903 if (!gNoRender)
4904 {
4905 if (gDebugView)
4906 {
4907 gSavedSettings.setBOOL("ShowDebugConsole", gDebugView->mDebugConsolep->getVisible());
4908 gSavedSettings.setBOOL("ShowDebugStats", gDebugView->mStatViewp->getVisible());
4909 }
4910 }
4911
4912 // save window position if not fullscreen
4913 // as we don't track it in callbacks
4914 BOOL fullscreen = gViewerWindow->mWindow->getFullscreen();
4915 BOOL maximized = gViewerWindow->mWindow->getMaximized();
4916 if (!fullscreen && !maximized)
4917 {
4918 LLCoordScreen window_pos;
4919
4920 if (gViewerWindow->mWindow->getPosition(&window_pos))
4921 {
4922 gSavedSettings.setS32("WindowX", window_pos.mX);
4923 gSavedSettings.setS32("WindowY", window_pos.mY);
4924 }
4925 }
4926
4927 gSavedSettings.setF32("MapScale", gMapScale );
4928 gSavedSettings.setF32("MiniMapScale", gMiniMapScale );
4929 gSavedSettings.setBOOL("AsyncKeyboard", gHandleKeysAsync);
4930 gSavedSettings.setBOOL("ShowHoverTips", LLHoverView::sShowHoverTips);
4931
4932 // Some things are cached in LLAgent.
4933 if (gAgent.mInitialized)
4934 {
4935 gSavedSettings.setF32("RenderFarClip", gAgent.mDrawDistance);
4936 }
4937
4938 gCrashSettings.setS32(CRASH_BEHAVIOR_SETTING, gCrashBehavior);
4939}
4940
4941
4942void callback_cache_name(const LLUUID& id, const char* firstname, const char* lastname, BOOL is_group, void* data)
4943{
4944 LLNameListCtrl::refreshAll(id, firstname, lastname, is_group);
4945 LLNameBox::refreshAll(id, firstname, lastname, is_group);
4946 LLNameEditor::refreshAll(id, firstname, lastname, is_group);
4947
4948 // TODO: Actually be intelligent about the refresh.
4949 // For now, just brute force refresh the dialogs.
4950 dialog_refresh_all();
4951}
4952
4953
4954void write_debug(const std::string& str)
4955{
4956 write_debug(str.c_str());
4957}
4958
4959void write_debug(const char *str)
4960{
4961 if (!gDebugFile)
4962 {
4963 std::string debug_filename = gDirUtilp->getExpandedFilename(LL_PATH_LOGS,"debug_info.log");
4964 llinfos << "Opening debug file " << debug_filename << llendl;
4965 gDebugFile = LLFile::fopen(debug_filename.c_str(), "w"); /* Flawfinder: ignore */
4966 if (!gDebugFile)
4967 {
4968 llinfos << "Opening debug file " << debug_filename << " failed. Using stderr." << llendl;
4969 gDebugFile = stderr;
4970 }
4971 }
4972 fputs(str, gDebugFile);
4973 fflush(gDebugFile);
4974}
4975
4976void close_debug()
4977{
4978 if (gDebugFile)
4979 {
4980 fclose(gDebugFile);
4981 }
4982 gDebugFile = NULL;
4983}
4984
4985void print_agent_nvpairs(void*)
4986{
4987 LLViewerObject *objectp;
4988
4989 llinfos << "Agent Name Value Pairs" << llendl;
4990
4991 objectp = gObjectList.findObject(gAgentID);
4992 if (objectp)
4993 {
4994 objectp->printNameValuePairs();
4995 }
4996 else
4997 {
4998 llinfos << "Can't find agent object" << llendl;
4999 }
5000
5001 llinfos << "Camera at " << gAgent.getCameraPositionGlobal() << llendl;
5002}
5003
5004
5005void reset_statistics()
5006{
5007 gPipeline.resetFrameStats(); // Reset per-frame statistics.
5008 if (LLSurface::sTextureUpdateTime)
5009 {
5010 LLSurface::sTexelsUpdatedPerSecStat.addValue(0.001f*(LLSurface::sTexelsUpdated / LLSurface::sTextureUpdateTime));
5011 LLSurface::sTexelsUpdated = 0;
5012 LLSurface::sTextureUpdateTime = 0.f;
5013 }
5014}
5015
5016
5017void output_statistics(void*)
5018{
5019 llinfos << "Number of orphans: " << gObjectList.getOrphanCount() << llendl;
5020 llinfos << "Number of dead objects: " << gObjectList.mNumDeadObjects << llendl;
5021 llinfos << "Num images: " << gImageList.getNumImages() << llendl;
5022 llinfos << "Texture usage: " << LLImageGL::sGlobalTextureMemory << llendl;
5023 llinfos << "Texture working set: " << LLImageGL::sBoundTextureMemory << llendl;
5024 llinfos << "Raw usage: " << LLImageRaw::sGlobalRawMemory << llendl;
5025 llinfos << "Formatted usage: " << LLImageFormatted::sGlobalFormattedMemory << llendl;
5026 llinfos << "Zombie Viewer Objects: " << LLViewerObject::getNumZombieObjects() << llendl;
5027 llinfos << "Number of lights: " << gPipeline.getLightCount() << llendl;
5028
5029 llinfos << "Memory Usage:" << llendl;
5030 llinfos << "--------------------------------" << llendl;
5031 llinfos << "Pipeline:" << llendl;
5032 llinfos << llendl;
5033
5034#if LL_SMARTHEAP
5035 llinfos << "--------------------------------" << llendl;
5036 {
5037 llinfos << "sizeof(LLVOVolume) = " << sizeof(LLVOVolume) << llendl;
5038
5039 U32 total_pool_size = 0;
5040 U32 total_used_size = 0;
5041 MEM_POOL_INFO pool_info;
5042 MEM_POOL_STATUS pool_status;
5043 U32 pool_num = 0;
5044 for(pool_status = MemPoolFirst( &pool_info, 1 );
5045 pool_status != MEM_POOL_END;
5046 pool_status = MemPoolNext( &pool_info, 1 ) )
5047 {
5048 llinfos << "Pool #" << pool_num << llendl;
5049 if( MEM_POOL_OK != pool_status )
5050 {
5051 llwarns << "Pool not ok" << llendl;
5052 continue;
5053 }
5054
5055 llinfos << "Pool blockSizeFS " << pool_info.blockSizeFS
5056 << " pageSize " << pool_info.pageSize
5057 << llendl;
5058
5059 U32 pool_count = MemPoolCount(pool_info.pool);
5060 llinfos << "Blocks " << pool_count << llendl;
5061
5062 U32 pool_size = MemPoolSize( pool_info.pool );
5063 if( pool_size == MEM_ERROR_RET )
5064 {
5065 llinfos << "MemPoolSize() failed (" << pool_num << ")" << llendl;
5066 }
5067 else
5068 {
5069 llinfos << "MemPool Size " << pool_size / 1024 << "K" << llendl;
5070 }
5071
5072 total_pool_size += pool_size;
5073
5074 if( !MemPoolLock( pool_info.pool ) )
5075 {
5076 llinfos << "MemPoolLock failed (" << pool_num << ") " << llendl;
5077 continue;
5078 }
5079
5080 U32 used_size = 0;
5081 MEM_POOL_ENTRY entry;
5082 entry.entry = NULL;
5083 while( MemPoolWalk( pool_info.pool, &entry ) == MEM_POOL_OK )
5084 {
5085 if( entry.isInUse )
5086 {
5087 used_size += entry.size;
5088 }
5089 }
5090
5091 MemPoolUnlock( pool_info.pool );
5092
5093 llinfos << "MemPool Used " << used_size/1024 << "K" << llendl;
5094 total_used_size += used_size;
5095 pool_num++;
5096 }
5097
5098 llinfos << "Total Pool Size " << total_pool_size/1024 << "K" << llendl;
5099 llinfos << "Total Used Size " << total_used_size/1024 << "K" << llendl;
5100
5101 }
5102#endif
5103
5104 llinfos << "--------------------------------" << llendl;
5105 llinfos << "Avatar Memory (partly overlaps with above stats):" << llendl;
5106 gTexStaticImageList.dumpByteCount();
5107 LLVOAvatar::dumpScratchTextureByteCount();
5108 LLTexLayerSetBuffer::dumpTotalByteCount();
5109 LLVOAvatar::dumpTotalLocalTextureByteCount();
5110 LLTexLayerParamAlpha::dumpCacheByteCount();
5111 LLVOAvatar::dumpBakedStatus();
5112
5113 llinfos << llendl;
5114
5115 llinfos << "Object counts:" << llendl;
5116 S32 i;
5117 S32 obj_counts[256];
5118// S32 app_angles[256];
5119 for (i = 0; i < 256; i++)
5120 {
5121 obj_counts[i] = 0;
5122 }
5123 for (i = 0; i < gObjectList.getNumObjects(); i++)
5124 {
5125 LLViewerObject *objectp = gObjectList.getObject(i);
5126 if (objectp)
5127 {
5128 obj_counts[objectp->getPCode()]++;
5129 }
5130 }
5131 for (i = 0; i < 256; i++)
5132 {
5133 if (obj_counts[i])
5134 {
5135 llinfos << LLPrimitive::pCodeToString(i) << ":" << obj_counts[i] << llendl;
5136 }
5137 }
5138
5139 /*
5140 llinfos << "Object size distribution" << llendl;
5141 llinfos << "------------------------" << llendl;
5142 for (i = 0; i < 256; i++)
5143 {
5144 obj_counts[i] = 0;
5145 //app_angles[i] = 0;
5146 }
5147 for (i = 0; i < gObjectList.getNumObjects(); i++)
5148 {
5149 LLViewerObject *objectp = gObjectList.getObject(i);
5150 S32 size = llmin(255, (S32)objectp->getRadius()/2);
5151 obj_counts[size]++;
5152 }
5153 for (i = 0; i < 256; i++)
5154 {
5155 if (obj_counts[i])
5156 {
5157 llinfos << i*2 << ":" << (i+1)*2 << " - " << obj_counts[i] << llendl;
5158 }
5159 }
5160 */
5161
5162 /*
5163
5164 llinfos << llendl;
5165 llinfos << "Lighting statistics" << llendl;
5166 llinfos << "-------------------" << llendl;
5167 S32 light_bins[256];
5168 S32 light_counts[256];
5169 for (i = 0; i < 256; i++)
5170 {
5171 light_bins[i] = 0;
5172 light_counts[i] = 0;
5173 }
5174 for (i = 0; i < gPipeline.mLightSet.count(); i++)
5175 {
5176 LLDrawable *drawablep = gPipeline.mLightSet[i];
5177 F32 light_range = drawablep->getMetric() * LIGHT_RANGE_FACTOR;
5178 S32 light_bin = light_range / 4;
5179 S32 count_bin = llmin(drawablep->mLightSet.getLength() / 25, 255);
5180
5181 if (count_bin > 20)
5182 {
5183 llinfos << drawablep->getVObj()->getPCodeString() << ":" << count_bin*25 << llendl;
5184 drawablep->getVObj()->dump();
5185 }
5186 //llinfos << i << ": " << light_range << llendl;
5187 light_bins[light_bin]++;
5188 light_counts[count_bin]++;
5189 }
5190 for (i = 0; i < 256; i++)
5191 {
5192 if (light_bins[i])
5193 {
5194 llinfos << i*4 << ":" << (i+1)*4 << " - " << light_bins[i] << llendl;
5195 }
5196 }
5197 llinfos << llendl;
5198 llinfos << "LightSet counts" << llendl;
5199 for (i = 0; i < 256; i++)
5200 {
5201 if (light_counts[i])
5202 {
5203 llinfos << i*25 << ":" << (i+1)*25 << " - " << light_counts[i] << llendl;
5204 }
5205 }
5206 */
5207}
5208
5209class ViewerStatsResponder : public LLHTTPClient::Responder
5210{
5211public:
5212 ViewerStatsResponder() { }
5213
5214 void error(U32 statusNum, const std::string& reason)
5215 {
5216 llinfos << "ViewerStatsResponder::error " << statusNum << " "
5217 << reason << llendl;
5218 }
5219
5220 void result(const LLSD& content)
5221 {
5222 llinfos << "ViewerStatsResponder::result" << llendl;
5223 }
5224};
5225
5226/*
5227 * The sim-side LLSD is in newsim/llagentinfo.cpp:forwardViewerStats.
5228 *
5229 * There's also a compatibility shim for the old fixed-format sim
5230 * stats in newsim/llagentinfo.cpp:processViewerStats.
5231 *
5232 * If you move stats around here, make the corresponding changes in
5233 * those locations, too.
5234 */
5235void send_stats()
5236{
5237 // IW 9/23/02 I elected not to move this into LLViewerStats
5238 // because it depends on too many viewer.cpp globals.
5239 // Someday we may want to merge all our stats into a central place
5240 // but that day is not today.
5241
5242 // Only send stats if the agent is connected to a region.
5243 if (!gAgent.getRegion() || gNoRender)
5244 {
5245 return;
5246 }
5247
5248 LLSD body;
5249 std::string url = gAgent.getRegion()->getCapability("ViewerStats");
5250
5251 if (url.empty()) {
5252 llwarns << "Could not get ViewerStats capability" << llendl;
5253 return;
5254 }
5255
5256 body["session_id"] = gAgentSessionID;
5257
5258 LLSD &agent = body["agent"];
5259
5260 time_t ltime;
5261 time(&ltime);
5262 F32 run_time = F32(LLFrameTimer::getElapsedSeconds());
5263
5264 agent["start_time"] = ltime - run_time;
5265 agent["run_time"] = run_time;
5266 // send fps only for time app spends in foreground
5267 agent["fps"] = (F32)gForegroundFrameCount / gForegroundTime.getElapsedTimeF32();
5268 agent["version"] = gCurrentVersion;
5269 agent["language"] = gSavedSettings.getString("Language");
5270
5271 agent["sim_fps"] = ((F32) gFrameCount - gSimFrames) /
5272 (F32) (gRenderStartTime.getElapsedTimeF32() - gSimLastTime);
5273
5274 gSimLastTime = gRenderStartTime.getElapsedTimeF32();
5275 gSimFrames = (F32) gFrameCount;
5276
5277 agent["agents_in_view"] = LLVOAvatar::sNumVisibleAvatars;
5278 agent["ping"] = gAvgSimPing;
5279 agent["meters_traveled"] = gAgent.getDistanceTraveled();
5280 agent["regions_visited"] = gAgent.getRegionsVisited();
5281 agent["mem_use"] = getCurrentRSS() / 1024.0;
5282
5283 LLSD &system = body["system"];
5284
5285 system["ram"] = (S32) gSysMemory.getPhysicalMemoryKB();
5286 system["os"] = gSysOS.getOSString();
5287 system["cpu"] = gSysCPU.getCPUString();
5288
5289 std::string gpu_desc = llformat(
5290 "%-6s Class %d ",
5291 gGLManager.mGLVendorShort.substr(0,6).c_str(),
5292 gFeatureManagerp->getGPUClass())
5293 + gFeatureManagerp->getGPUString();
5294
5295 system["gpu"] = gpu_desc;
5296 system["gpu_class"] = gFeatureManagerp->getGPUClass();
5297 system["gpu_vendor"] = gGLManager.mGLVendorShort;
5298 system["gpu_version"] = gGLManager.mDriverVersionVendorString;
5299
5300 LLSD &download = body["downloads"];
5301
5302 download["world_kbytes"] = gTotalWorldBytes / 1024.0;
5303 download["object_kbytes"] = gTotalObjectBytes / 1024.0;
5304 download["texture_kbytes"] = gTotalTextureBytes / 1024.0;
5305
5306 LLSD &in = body["stats"]["net"]["in"];
5307
5308 in["kbytes"] = gMessageSystem->mTotalBytesIn / 1024.0;
5309 in["packets"] = (S32) gMessageSystem->mPacketsIn;
5310 in["compressed_packets"] = (S32) gMessageSystem->mCompressedPacketsIn;
5311 in["savings"] = (gMessageSystem->mUncompressedBytesIn -
5312 gMessageSystem->mCompressedBytesIn) / 1024.0;
5313
5314 LLSD &out = body["stats"]["net"]["out"];
5315
5316 out["kbytes"] = gMessageSystem->mTotalBytesOut / 1024.0;
5317 out["packets"] = (S32) gMessageSystem->mPacketsOut;
5318 out["compressed_packets"] = (S32) gMessageSystem->mCompressedPacketsOut;
5319 out["savings"] = (gMessageSystem->mUncompressedBytesOut -
5320 gMessageSystem->mCompressedBytesOut) / 1024.0;
5321
5322 LLSD &fail = body["stats"]["failures"];
5323
5324 fail["send_packet"] = (S32) gMessageSystem->mSendPacketFailureCount;
5325 fail["dropped"] = (S32) gMessageSystem->mDroppedPackets;
5326 fail["resent"] = (S32) gMessageSystem->mResentPackets;
5327 fail["failed_resends"] = (S32) gMessageSystem->mFailedResendPackets;
5328 fail["off_circuit"] = (S32) gMessageSystem->mOffCircuitPackets;
5329 fail["invalid"] = (S32) gMessageSystem->mInvalidOnCircuitPackets;
5330
5331 gViewerStats->addToMessage(body);
5332
5333 LLHTTPClient::post(url, body, new ViewerStatsResponder());
5334}
5335
5336#if !LL_WINDOWS
5337// catch the first signal and send logout messages logout
5338void signal_handlers(S32 s)
5339{
5340 release_signals();
5341
5342 // Check for graceful exit conditions
5343 if(SIGTERM == s)
5344 {
5345 // Raise the flag telling the system to shut down
5346 app_force_quit(NULL);
5347 return;
5348 }
5349
5350# if LL_LINUX || LL_SOLARIS
5351 // Really useful to know what KIND of crash we got.
5352 // Might want this on OSX too!
5353 llwarns << "*** Caught signal " << s << llendl;
5354# endif
5355
5356 if (gCrashCallback)
5357 {
5358 gCrashCallback();
5359 }
5360}
5361#endif
5362
5363// Assign signal handler to be called in case we crash.
5364void catch_signals()
5365{
5366#if LL_WINDOWS
5367 // Win32 signal handling uses a Microsoft API instead of signal handlers
5368 LLWinDebug::setupExceptionHandler();
5369 return;
5370#else
5371 // This happens before log file output is properly set up, so this would go to the system log.
5372// llinfos << "Assigning signal handlers!" << llendl;
5373
5374 // Handle the signals that default to causing a core image to be created, as per the man page on signal(2).
5375 signal(SIGILL, signal_handlers);
5376 signal(SIGTRAP, signal_handlers);
5377 if (being_debugged())
5378 {
5379 // If we're being run under the control of a debugger, give
5380 // ourselves a way to bail into the debugger.
5381 signal(SIGABRT, SIG_DFL);
5382 } else {
5383 signal(SIGABRT, signal_handlers);
5384 }
5385 signal(SIGFPE, signal_handlers);
5386 signal(SIGBUS, signal_handlers);
5387 signal(SIGSEGV, signal_handlers);
5388 signal(SIGSYS, signal_handlers);
5389
5390 // SIGEMT is an 'emulator trap' which is not defined on linux.
5391#if !LL_LINUX && !LL_SOLARIS
5392 signal(SIGEMT, signal_handlers);
5393#endif
5394
5395 // This one is treated specially in the signal handler.
5396 signal(SIGTERM, signal_handlers);
5397
5398#endif // LL_WINDOWS
5399}
5400
5401#if !LL_WINDOWS
5402void release_signals()
5403{
5404 signal(SIGTERM, SIG_DFL);
5405 signal(SIGABRT, SIG_DFL);
5406 signal(SIGILL, SIG_DFL);
5407 signal(SIGFPE, SIG_DFL);
5408 signal(SIGSEGV, SIG_DFL);
5409
5410#ifdef SIGBUS
5411 signal(SIGBUS, SIG_DFL);
5412#endif
5413
5414#ifdef SIGSYS
5415 signal(SIGSYS, SIG_DFL);
5416#endif
5417}
5418#endif // !LL_WINDOWS
5419
5420void purge_cache()
5421{
5422 llinfos << "Purging Texture Cache..." << llendl;
5423 gTextureCache->purgeCache(LL_PATH_CACHE);
5424 llinfos << "Purging Cache..." << llendl;
5425 std::string mask = gDirUtilp->getDirDelimiter() + "*.*";
5426 gDirUtilp->deleteFilesInDir(gDirUtilp->getExpandedFilename(LL_PATH_CACHE,"").c_str(),mask);
5427}
5428
5429int parse_args(int argc, char **argv)
5430{
5431 // Sometimes IP addresses passed in on the command line have leading
5432 // or trailing white space. Use LLString to clean that up.
5433 LLString ip_string;
5434 S32 j;
5435
5436 for (j = 1; j < argc; j++)
5437 {
5438 gArgs += argv[j];
5439 gArgs += " ";
5440
5441 if ((!strcmp(argv[j], "-port")) && (++j < argc))
5442 {
5443 sscanf(argv[j], "%u", &(gAgent.mViewerPort));
5444 }
5445 else if ((!strcmp(argv[j], "-drop")) && (++j < argc))
5446 {
5447 sscanf(argv[j], "%f", &gPacketDropPercentage);
5448 }
5449 else if ((!strcmp(argv[j], "-inbw")) && (++j < argc))
5450 {
5451 sscanf(argv[j], "%f", &gInBandwidth);
5452 }
5453 else if ((!strcmp(argv[j], "-outbw")) && (++j < argc))
5454 {
5455 sscanf(argv[j], "%f", &gOutBandwidth);
5456 }
5457 else if (!strcmp(argv[j], "--aditi"))
5458 {
5459 gUserServerChoice = USERSERVER_ADITI;
5460 snprintf(gUserServerName, MAX_STRING, "%s", gUserServerDomainName[gUserServerChoice].mName); /* Flawfinder: ignore */
5461 }
5462 else if (!strcmp(argv[j], "--agni"))
5463 {
5464 gUserServerChoice = USERSERVER_AGNI;
5465 snprintf(gUserServerName, MAX_STRING, "%s", gUserServerDomainName[gUserServerChoice].mName); /* Flawfinder: ignore */
5466 }
5467 else if (!strcmp(argv[j], "--dmz"))
5468 {
5469 gUserServerChoice = USERSERVER_DMZ;
5470 snprintf(gUserServerName, MAX_STRING, "%s", gUserServerDomainName[gUserServerChoice].mName); /* Flawfinder: ignore */
5471 }
5472 else if (!strcmp(argv[j], "--siva"))
5473 {
5474 gUserServerChoice = USERSERVER_SIVA;
5475 snprintf(gUserServerName, MAX_STRING, "%s", gUserServerDomainName[gUserServerChoice].mName); /* Flawfinder: ignore */
5476 }
5477 else if (!strcmp(argv[j], "--shakti"))
5478 {
5479 gUserServerChoice = USERSERVER_SHAKTI;
5480 snprintf(gUserServerName, MAX_STRING, "%s", gUserServerDomainName[gUserServerChoice].mName); /* Flawfinder: ignore */
5481 }
5482 else if (!strcmp(argv[j], "--durga"))
5483 {
5484 gUserServerChoice = USERSERVER_DURGA;
5485 snprintf(gUserServerName, MAX_STRING, "%s", gUserServerDomainName[gUserServerChoice].mName); /* Flawfinder: ignore */
5486 }
5487 else if (!strcmp(argv[j], "--soma"))
5488 {
5489 gUserServerChoice = USERSERVER_SOMA;
5490 snprintf(gUserServerName, MAX_STRING, "%s", gUserServerDomainName[gUserServerChoice].mName); /* Flawfinder: ignore */
5491 }
5492 else if (!strcmp(argv[j], "--ganga"))
5493 {
5494 gUserServerChoice = USERSERVER_GANGA;
5495 sprintf(gUserServerName,"%s", gUserServerDomainName[gUserServerChoice].mName);
5496 }
5497 else if (!strcmp(argv[j], "--vaak"))
5498 {
5499 gUserServerChoice = USERSERVER_VAAK;
5500 sprintf(gUserServerName,"%s", gUserServerDomainName[gUserServerChoice].mName);
5501 }
5502 else if (!strcmp(argv[j], "--uma"))
5503 {
5504 gUserServerChoice = USERSERVER_UMA;
5505 sprintf(gUserServerName,"%s", gUserServerDomainName[gUserServerChoice].mName);
5506 }
5507 else if (!strcmp(argv[j], "-user") && (++j < argc))
5508 {
5509 if (!strcmp(argv[j], "-"))
5510 {
5511 gUserServerChoice = USERSERVER_LOCAL;
5512 snprintf(gUserServerName, MAX_STRING, "%s", LOOPBACK_ADDRESS_STRING); /* Flawfinder: ignore */
5513 }
5514 else
5515 {
5516 gUserServerChoice = USERSERVER_OTHER;
5517 ip_string.assign( argv[j] );
5518 LLString::trim(ip_string);
5519 snprintf(gUserServerName, MAX_STRING, "%s", ip_string.c_str()); /* Flawfinder: ignore */
5520 }
5521 }
5522 else if (!strcmp(argv[j], "-loginuri") && (++j < argc))
5523 {
5524 gLoginURIs.push_back(utf8str_trim(argv[j]));
5525 }
5526 else if (!strcmp(argv[j], "-helperuri") && (++j < argc))
5527 {
5528 gHelperURI = argv[j];
5529 gHelperURI = utf8str_trim(gHelperURI);
5530 }
5531 else if (!strcmp(argv[j], "-debugviews"))
5532 {
5533 LLView::sDebugRects = TRUE;
5534 }
5535 else if (!strcmp(argv[j], "-skin") && (++j < argc))
5536 {
5537 std::string folder(argv[j]);
5538 gDirUtilp->setSkinFolder(folder);
5539 }
5540 else if (!strcmp(argv[j], "-autologin") || !strcmp(argv[j], "--autologin")) // keep --autologin for compatibility
5541 {
5542 gAutoLogin = TRUE;
5543 }
5544 else if (!strcmp(argv[j], "-quitafter") && (++j < argc))
5545 {
5546 gQuitAfterSeconds = (F32)atof(argv[j]);
5547 }
5548 else if (!strcmp(argv[j], "-rotate"))
5549 {
5550 gRotateRight = TRUE;
5551 }
5552// else if (!strcmp(argv[j], "-noenv"))
5553// {
5554 //turn OFF environmental effects for slow machines/video cards
5555// gRequestParaboloidMap = FALSE;
5556// }
5557 else if (!strcmp(argv[j], "-noaudio"))
5558 {
5559 gUseAudio = FALSE;
5560 }
5561 else if (!strcmp(argv[j], "-nosound")) // tends to be popular cmdline on Linux.
5562 {
5563 gUseAudio = FALSE;
5564 }
5565 else if (!strcmp(argv[j], "-nofmod"))
5566 {
5567 gUseFMOD = FALSE;
5568 }
5569 else if (!strcmp(argv[j], "-noprobe"))
5570 {
5571 gProbeHardware = FALSE;
5572 }
5573 else if (!strcmp(argv[j], "-noquicktime"))
5574 {
5575 // Developers can log in faster if they don't load all the
5576 // quicktime dlls.
5577 gUseQuickTime = false;
5578 }
5579 else if (!strcmp(argv[j], "-nopreload"))
5580 {
5581 // Developers can log in faster if they don't decode sounds
5582 // or images on startup, ~5 seconds faster.
5583 gPreloadSounds = false;
5584 gPreloadImages = false;
5585 }
5586 else if (!strcmp(argv[j], "-purge"))
5587 {
5588 purge_cache();
5589 }
5590 else if(!strcmp(argv[j], "-noinvlib"))
5591 {
5592 gRequestInventoryLibrary = FALSE;
5593 }
5594 else if (!strcmp(argv[j], "-log"))
5595 {
5596 gLogMessages = TRUE;
5597 continue;
5598 }
5599 else if (!strcmp(argv[j], "-logfile") && (++j < argc))
5600 {
5601 // *NOTE: This buffer size is hard coded into scanf() below.
5602 char logfile[256]; /* Flawfinder: ignore */
5603 sscanf(argv[j], "%255s", logfile); /* Flawfinder: ignore */
5604 llinfos << "Setting log file to " << logfile << llendl;
5605 LLFile::remove(logfile);
5606 LLError::logToFile(logfile);
5607 }
5608 else if (!strcmp(argv[j], "-settings") && (++j < argc))
5609 {
5610 gSettingsFileName = gDirUtilp->getExpandedFilename(LL_PATH_USER_SETTINGS, argv[j]);
5611 }
5612 else if (!strcmp(argv[j], "-setdefault") && (j + 2 < argc))
5613 {
5614 std::string control_name;
5615 std::string control_value;
5616
5617 j++;
5618 if (argv[j]) control_name = std::string(argv[j]);
5619
5620 j++;
5621 if (argv[j]) control_value = std::string(argv[j]);
5622
5623 // grab control name and value
5624 if (!control_name.empty())
5625 {
5626 gCommandLineSettings[control_name] = control_value;
5627 }
5628 }
5629 else if (!strcmp(argv[j], "-set") && (j + 2 < argc))
5630 {
5631 std::string control_name;
5632 std::string control_value;
5633
5634 j++;
5635 if (argv[j]) control_name = std::string(argv[j]);
5636
5637 j++;
5638 if (argv[j]) control_value = std::string(argv[j]);
5639
5640 // grab control name and value
5641 if (!control_name.empty())
5642 {
5643 gCommandLineForcedSettings[control_name] = control_value;
5644 }
5645 }
5646 else if (!strcmp(argv[j], "-login"))
5647 {
5648 if (j + 3 < argc)
5649 {
5650 j++;
5651 gCmdLineFirstName = argv[j];
5652 j++;
5653 gCmdLineLastName = argv[j];
5654 j++;
5655 gCmdLinePassword = argv[j];
5656 }
5657 else
5658 {
5659 // only works if -login is last parameter on command line
5660 llerrs << "Not enough parameters to -login. Did you mean -loginuri?" << llendl;
5661 }
5662 }
5663 else if (!strcmp(argv[j], "-god"))
5664 {
5665 gGodConnect = TRUE;
5666 }
5667 else if (!strcmp(argv[j], "-noconsole"))
5668 {
5669 gUseConsole = FALSE;
5670 }
5671 else if (!strcmp(argv[j], "-safe"))
5672 {
5673 llinfos << "Setting viewer feature table to run in safe mode, resetting prefs" << llendl;
5674 gFeatureManagerp->setSafe(TRUE);
5675 }
5676 else if (!strcmp(argv[j], "-multiple"))
5677 {
5678 gMultipleViewersOK = TRUE;
5679 }
5680 else if (!strcmp(argv[j], "-nomultiple"))
5681 {
5682 gMultipleViewersOK = FALSE;
5683 }
5684 else if (!strcmp(argv[j], "-novoice"))
5685 {
5686 gDisableVoice = TRUE;
5687 }
5688 else if (!strcmp(argv[j], "-nothread"))
5689 {
5690 LLVFile::ALLOW_ASYNC = FALSE;
5691 llinfos << "Running VFS in nothread mode" << llendl;
5692 }
5693 // some programs don't respect the command line options in protocol handlers (I'm looking at you, Opera)
5694 // so this allows us to parse the URL straight off the command line without a "-url" paramater
5695 else if (LLURLDispatcher::isSLURL(argv[j])
5696 || !strcmp(argv[j], "-url") && (++j < argc))
5697 {
5698 std::string slurl = argv[j];
5699 if (LLURLDispatcher::isSLURLCommand(slurl))
5700 {
5701 LLStartUp::sSLURLCommand = slurl;
5702 }
5703 else
5704 {
5705 LLURLSimString::setString(slurl);
5706 }
5707 // *NOTE: After setting the url, bail. What can happen is
5708 // that someone can use IE (or potentially other browsers)
5709 // and do the rough equivalent of command injection and
5710 // steal passwords. Phoenix. SL-55321
5711 gArgs += argv[j];
5712 return 0;
5713 }
5714 else if (!strcmp(argv[j], "-ignorepixeldepth"))
5715 {
5716 gIgnorePixelDepth = TRUE;
5717 }
5718 else if (!strcmp(argv[j], "-cooperative"))
5719 {
5720 S32 ms_to_yield = 0;
5721 if(++j < argc)
5722 {
5723 S32 rv = sscanf(argv[j], "%d", &ms_to_yield);
5724 if(0 == rv)
5725 {
5726 --j;
5727 }
5728 }
5729 else
5730 {
5731 --j;
5732 }
5733 gYieldMS = ms_to_yield;
5734 gYieldTime = TRUE;
5735 }
5736 else if (!strncmp(argv[j], "-lang", 5))
5737 {
5738 j++;
5739 if (argv[j])
5740 gSavedSettings.setString("Language", std::string(argv[j]));
5741 }
5742 else if (!strcmp(argv[j], "-no-verify-ssl-cert"))
5743 {
5744 gVerifySSLCert = false;
5745 }
5746 else if ( (!strcmp(argv[j], "--channel") || !strcmp(argv[j], "-channel")) && (++j < argc))
5747 {
5748 gChannelName = argv[j];
5749 }
5750#if LL_DARWIN
5751 else if (!strncmp(argv[j], "-psn_", 5))
5752 {
5753 // this is the Finder passing the process session number
5754 // we ignore this
5755 }
5756#endif
5757 else
5758 {
5759
5760 // DBC - Mac OS X passes some stuff by default on the command line (e.g. psn).
5761 // Second Life URLs are passed this way as well?
5762 llwarns << "Possible unknown keyword " << argv[j] << llendl;
5763
5764 // print usage information
5765 llinfos << USAGE << llendl;
5766 // return 1;
5767 }
5768 }
5769 return 0;
5770}
5771
5772//============================================================================
5773
5774void load_name_cache()
5775{
5776 if (!gCacheName) return;
5777
5778 std::string name_cache;
5779 name_cache = gDirUtilp->getExpandedFilename(LL_PATH_CACHE, "name.cache");
5780 FILE* name_cache_fp = LLFile::fopen(name_cache.c_str(), "r"); /* Flawfinder: ignore*/
5781 if (name_cache_fp)
5782 {
5783 gCacheName->importFile(name_cache_fp);
5784 fclose(name_cache_fp);
5785 }
5786}
5787
5788void save_name_cache()
5789{
5790 if (!gCacheName) return;
5791
5792 std::string name_cache;
5793 name_cache = gDirUtilp->getExpandedFilename(LL_PATH_CACHE, "name.cache");
5794 FILE* name_cache_fp = LLFile::fopen(name_cache.c_str(), "w"); /* Flawfinder: ignore*/
5795 if (name_cache_fp)
5796 {
5797 gCacheName->exportFile(name_cache_fp);
5798 fclose(name_cache_fp);
5799 }
5800}
5801
5802
5803void login_alert_done(S32 option, void* user_data)
5804{
5805 LLPanelLogin::giveFocus();
5806}
5807
5808void disconnect_viewer(void *)
5809{
5810 if (gDisconnected)
5811 {
5812 return;
5813 }
5814 //
5815 // Cleanup after quitting.
5816 //
5817 // Save snapshot for next time, if we made it through initialization
5818
5819 llinfos << "Disconnecting viewer!" << llendl;
5820
5821 // Dump our frame statistics
5822 gFrameStats.dump();
5823
5824
5825 // Signal handlers may need resources that we're destroying here, so don't use them.
5826#if !LL_WINDOWS
5827 release_signals();
5828#endif
5829
5830 // Remember if we were flying
5831 gSavedSettings.setBOOL("FlyingAtExit", gAgent.getFlying() );
5832
5833 // Un-minimize all windows so they don't get saved minimized
5834 if (!gNoRender)
5835 {
5836 if (gFloaterView)
5837 {
5838 gFloaterView->restoreAll();
5839 }
5840 }
5841
5842 if (gSelectMgr)
5843 {
5844 gSelectMgr->deselectAll();
5845 }
5846
5847 if (!gNoRender)
5848 {
5849 // save inventory if appropriate
5850 gInventory.cache(gAgent.getInventoryRootID(), gAgent.getID());
5851 if(gInventoryLibraryRoot.notNull() && gInventoryLibraryOwner.notNull())
5852 {
5853 gInventory.cache(gInventoryLibraryRoot, gInventoryLibraryOwner);
5854 }
5855 }
5856
5857 save_name_cache();
5858
5859 // close inventory interface, close all windows
5860 LLInventoryView::cleanup();
5861
5862 // Log out of userserver
5863 user_logout();
5864
5865 // Delete stuff that would result in net connections
5866
5867 // Also writes cached agent settings to gSavedSettings
5868 gAgent.cleanup();
5869
5870 gObjectList.destroy();
5871 delete gWorldp;
5872 gWorldp = NULL;
5873
5874 cleanup_xfer_manager();
5875 gDisconnected = TRUE;
5876}
5877
5878// helper function for cleanup_app
5879void remove_cache_files(const char* file_mask)
5880{
5881 char mask[LL_MAX_PATH]; /* Flawfinder: ignore */
5882 snprintf(mask, LL_MAX_PATH, "%s%s", gDirUtilp->getDirDelimiter().c_str(), file_mask); /* Flawfinder: ignore */
5883 gDirUtilp->deleteFilesInDir(gDirUtilp->getExpandedFilename(LL_PATH_CACHE, "").c_str(), mask);
5884}
5885
5886
5887
5888
5889void finish_early_exit(S32 option, void* userdata)
5890{
5891 app_force_quit(NULL);
5892}
5893
5894void app_early_exit(const LLString& mesg)
5895{
5896 llwarns << "app_early_exit: " << mesg << llendl;
5897 gDoDisconnect = TRUE;
5898// LLStringBase<char>::format_map_t args;
5899// args["[MESSAGE]"] = mesg;
5900// gViewerWindow->alertXml("AppEarlyExit", args, finish_early_exit);
5901 LLAlertDialog::showCritical(mesg, finish_early_exit, NULL);
5902}
5903
5904// Used for drones, etc
5905void app_force_exit(S32 arg)
5906{
5907 remove_marker_file();
5908 exit(arg);
5909}
5910
5911// Callback from a dialog indicating user was logged out.
5912void finish_disconnect(S32 option, void* userdata)
5913{
5914 if (1 == option)
5915 {
5916 app_force_quit(NULL);
5917 }
5918}
5919
5920// Callback from an early disconnect dialog, force an exit
5921void finish_forced_disconnect(S32 /* option */, void* /* userdata */)
5922{
5923 app_force_quit(NULL);
5924}
5925
5926void send_logout_request()
5927{
5928 if(!gLogoutRequestSent)
5929 {
5930 LLMessageSystem* msg = gMessageSystem;
5931 msg->newMessageFast(_PREHASH_LogoutRequest);
5932 msg->nextBlockFast(_PREHASH_AgentData);
5933 msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID() );
5934 msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
5935 gAgent.sendReliableMessage();
5936
5937 gLogoutTimer.reset();
5938 gLogoutMaxTime = LOGOUT_REQUEST_TIME;
5939 gLogoutRequestSent = TRUE;
5940
5941 gVoiceClient->leaveChannel();
5942 }
5943}
5944
5945void do_disconnect(const LLString& mesg)
5946{
5947 if (gDoDisconnect)
5948 {
5949 // Already popped up one of these dialogs, don't
5950 // do this again.
5951 return;
5952 }
5953
5954 // Translate the message if possible
5955 LLString big_reason = LLAgent::sTeleportErrorMessages[mesg];
5956 if ( big_reason.size() == 0 )
5957 {
5958 big_reason = mesg;
5959 }
5960
5961 LLStringBase<char>::format_map_t args;
5962 gDoDisconnect = TRUE;
5963
5964 if (LLStartUp::getStartupState() < STATE_STARTED)
5965 {
5966 // Tell users what happened
5967 args["[ERROR_MESSAGE]"] = big_reason;
5968 gViewerWindow->alertXml("ErrorMessage", args, finish_forced_disconnect);
5969 }
5970 else
5971 {
5972 args["[MESSAGE]"] = big_reason;
5973 gViewerWindow->alertXml("YouHaveBeenLoggedOut", args, finish_disconnect );
5974 }
5975}
5976
5977const LLUUID& agent_get_id()
5978{
5979 return gAgent.getID();
5980}
5981
5982const LLUUID& agent_get_session_id()
5983{
5984 return gAgent.getSessionID();
5985}
5986
5987void agent_send_reliable_message()
5988{
5989 gAgent.sendReliableMessage();
5990}
5991
5992void bad_network_handler()
5993{
5994 // Dump the packet
5995 gMessageSystem->dumpPacketToLog();
5996
5997 // Flush all of our caches on exit in the case of disconnect due to
5998 // invalid packets.
5999
6000 gPurgeOnExit = TRUE;
6001
6002#if LL_WINDOWS
6003 LLWinDebug::handleException(NULL);
6004#else
6005 // Call the crash callback
6006 if (gCrashCallback)
6007 {
6008 gCrashCallback();
6009 }
6010#endif
6011
6012 std::ostringstream message;
6013 message <<
6014 "The viewer has detected mangled network data indicative\n"
6015 "of a bad upstream network connection or an incomplete\n"
6016 "local installation of " << gSecondLife << ". \n"
6017 " \n"
6018 "Try uninstalling and reinstalling to see if this resolves \n"
6019 "the issue. \n"
6020 " \n"
6021 "If the problem continues, see the Tech Support FAQ at: \n"
6022 "www.secondlife.com/support";
6023 do_disconnect(message.str());
6024}
6025
6026
6027// dump current avatar to .cal file
6028void save_avatar(void*)
6029{
6030 LLFloaterSaveAvatar::show();
6031}
6032
6033void cleanup_app()
6034{
6035 //flag all elements as needing to be destroyed immediately
6036 // to ensure shutdown order
6037 LLMortician::setZealous(TRUE);
6038
6039 LLVoiceClient::terminate();
6040
6041 disconnect_viewer(NULL);
6042
6043 llinfos << "Viewer disconnected" << llendflush;
6044
6045 gDisconnectedImagep = NULL;
6046 release_start_screen(); // just in case
6047
6048 LLError::logToFixedBuffer(NULL);
6049
6050 llinfos << "Cleaning Up" << llendflush;
6051
6052 LLKeyframeDataCache::clear();
6053
6054 // Must clean up texture references before viewer window is destroyed.
6055 LLHUDObject::cleanupHUDObjects();
6056 llinfos << "HUD Objects cleaned up" << llendflush;
6057
6058 // End TransferManager before deleting systems it depends on (Audio, VFS, AssetStorage)
6059#if 0 // this seems to get us stuck in an infinite loop...
6060 gTransferManager.cleanup();
6061#endif
6062
6063 // Clean up map data storage
6064 delete gWorldMap;
6065 gWorldMap = NULL;
6066
6067 delete gHUDManager;
6068 gHUDManager = NULL;
6069
6070 delete gToolMgr;
6071 gToolMgr = NULL;
6072
6073 delete gAssetStorage;
6074 gAssetStorage = NULL;
6075
6076 LLPolyMesh::freeAllMeshes();
6077
6078 delete gCacheName;
6079 gCacheName = NULL;
6080
6081 delete gGlobalEconomy;
6082 gGlobalEconomy = NULL;
6083
6084 delete gLocalSpeakerMgr;
6085 gLocalSpeakerMgr = NULL;
6086
6087 LLNotifyBox::cleanup();
6088
6089 llinfos << "Global stuff deleted" << llendflush;
6090
6091#if !LL_RELEASE_FOR_DOWNLOAD
6092 if (gAudiop)
6093 {
6094 gAudiop->shutdown();
6095 }
6096#else
6097 // This hack exists because fmod likes to occasionally hang forever
6098 // when shutting down for no apparent reason.
6099 llwarns << "Hack, skipping audio engine cleanup" << llendflush;
6100#endif
6101
6102
6103 // moved to main application shutdown for now because it's non-trivial and only needs to be done once
6104 // (even though it goes against the media framework design)
6105
6106 LLMediaEngine::cleanupClass();
6107
6108#if LL_QUICKTIME_ENABLED
6109 if (gQuickTimeInitialized)
6110 {
6111 // clean up media stuff
6112 llinfos << "Cleaning up QuickTime" << llendl;
6113 ExitMovies ();
6114 #if LL_WINDOWS
6115 // Only necessary/available on Windows.
6116 TerminateQTML ();
6117 #endif
6118 }
6119 llinfos << "Quicktime cleaned up" << llendflush;
6120#endif
6121
6122#if LL_GSTREAMER_ENABLED
6123 llinfos << "Cleaning up GStreamer" << llendl;
6124 UnloadGStreamer();
6125 llinfos << "GStreamer cleaned up" << llendflush;
6126#endif
6127
6128 llinfos << "Cleaning up feature manager" << llendflush;
6129 delete gFeatureManagerp;
6130 gFeatureManagerp = NULL;
6131
6132 // Patch up settings for next time
6133 // Must do this before we delete the viewer window,
6134 // such that we can suck rectangle information out of
6135 // it.
6136 cleanup_saved_settings();
6137 llinfos << "Settings patched up" << llendflush;
6138
6139 delete gAudiop;
6140 gAudiop = NULL;
6141
6142 // delete some of the files left around in the cache.
6143 remove_cache_files("*.wav");
6144 remove_cache_files("*.tmp");
6145 remove_cache_files("*.lso");
6146 remove_cache_files("*.out");
6147 remove_cache_files("*.dsf");
6148 remove_cache_files("*.bodypart");
6149 remove_cache_files("*.clothing");
6150
6151 llinfos << "Cache files removed" << llendflush;
6152
6153
6154 cleanup_menus();
6155
6156 // Wait for any pending VFS IO
6157 while (1)
6158 {
6159 S32 pending = LLVFSThread::updateClass(0);
6160 pending += LLLFSThread::updateClass(0);
6161 if (!pending)
6162 {
6163 break;
6164 }
6165 llinfos << "Waiting for pending IO to finish: " << pending << llendflush;
6166 ms_sleep(100);
6167 }
6168 llinfos << "Shutting down." << llendflush;
6169
6170 // Destroy Windows(R) window, and make sure we're not fullscreen
6171 // This may generate window reshape and activation events.
6172 // Therefore must do this before destroying the message system.
6173 delete gViewerWindow;
6174 gViewerWindow = NULL;
6175 llinfos << "ViewerWindow deleted" << llendflush;
6176
6177 // viewer UI relies on keyboard so keep it aound until viewer UI isa gone
6178 delete gKeyboard;
6179 gKeyboard = NULL;
6180
6181 // Clean up selection managers after UI is destroyed, as UI
6182 // may be observing them.
6183 LLSelectMgr::cleanupGlobals();
6184
6185 LLViewerObject::cleanupVOClasses();
6186
6187 LLTracker::cleanupInstance();
6188
6189#if LL_LIBXUL_ENABLED
6190 // this must be done after floater cleanup (delete gViewerWindow) since
6191 // floaters potentially need the manager to destroy their contents.
6192 LLMozLib::getInstance()->reset();
6193#endif
6194
6195#if LL_WINDOWS
6196 gDXHardware.cleanup();
6197#endif // LL_WINDOWS
6198
6199#if LL_WINDOWS && LL_LCD_COMPILE
6200 // shut down the LCD window on a logitech keyboard, if there is one
6201 delete gLcdScreen;
6202#endif
6203
6204 if (!gVolumeMgr->cleanup())
6205 {
6206 llwarns << "Remaining references in the volume manager!" << llendflush;
6207 }
6208
6209 LLViewerParcelMgr::cleanupGlobals();
6210
6211 delete gViewerStats;
6212 gViewerStats = NULL;
6213
6214 end_messaging_system();
6215
6216 LLFollowCamMgr::cleanupClass();
6217 LLVolumeMgr::cleanupClass();
6218 LLWorldMapView::cleanupClass();
6219 LLUI::cleanupClass();
6220
6221 //
6222 // Shut down the VFS's AFTER the decode manager cleans up (since it cleans up vfiles).
6223 // Also after viewerwindow is deleted, since it may have image pointers (which have vfiles)
6224 // Also after shutting down the messaging system since it has VFS dependencies
6225 //
6226 LLVFile::cleanupClass();
6227 llinfos << "VFS cleaned up" << llendflush;
6228
6229 // Store the time of our current logoff
6230 gSavedPerAccountSettings.setU32("LastLogoff", time_corrected());
6231
6232 // Must do this after all panels have been deleted because panels that have persistent rects
6233 // save their rects on delete.
6234 gSavedSettings.saveToFile(gSettingsFileName, TRUE);
6235 if (!gPerAccountSettingsFileName.empty())
6236 {
6237 gSavedPerAccountSettings.saveToFile(gPerAccountSettingsFileName, TRUE);
6238 }
6239 llinfos << "Saved settings" << llendflush;
6240
6241 std::string crash_settings_filename = gDirUtilp->getExpandedFilename(LL_PATH_USER_SETTINGS, CRASH_SETTINGS_FILE);
6242 // save all settings, even if equals defaults
6243 gCrashSettings.saveToFile(crash_settings_filename.c_str(), FALSE);
6244
6245 delete gUICtrlFactory;
6246 gUICtrlFactory = NULL;
6247
6248 gSavedSettings.cleanup();
6249 gViewerArt.cleanup();
6250 gColors.cleanup();
6251 gCrashSettings.cleanup();
6252
6253 if (gMuteListp)
6254 {
6255 // save mute list
6256 gMuteListp->cache(gAgent.getID());
6257
6258 delete gMuteListp;
6259 gMuteListp = NULL;
6260 }
6261
6262 if (gPurgeOnExit)
6263 {
6264 llinfos << "Purging all cache files on exit" << llendflush;
6265 char mask[LL_MAX_PATH]; /* Flawfinder: ignore */
6266 snprintf(mask, LL_MAX_PATH, "%s*.*", gDirUtilp->getDirDelimiter().c_str()); /* Flawfinder: ignore */
6267 gDirUtilp->deleteFilesInDir(gDirUtilp->getExpandedFilename(LL_PATH_CACHE,"").c_str(),mask);
6268 }
6269
6270 remove_marker_file(); // Any crashes from here on we'll just have to ignore
6271
6272 close_debug();
6273
6274 // Let threads finish
6275 LLTimer idleTimer;
6276 idleTimer.reset();
6277 const F64 max_idle_time = 5.f; // 5 seconds
6278 while(1)
6279 {
6280 S32 pending = 0;
6281 pending += gTextureCache->update(1); // unpauses the worker thread
6282 pending += gImageDecodeThread->update(1); // unpauses the image thread
6283 pending += gTextureFetch->update(1); // unpauses the texture fetch thread
6284 pending += LLVFSThread::updateClass(0);
6285 pending += LLLFSThread::updateClass(0);
6286 F64 idle_time = idleTimer.getElapsedTimeF64();
6287 if (!pending || idle_time >= max_idle_time)
6288 {
6289 llwarns << "Quitting with pending background tasks." << llendl;
6290 break;
6291 }
6292 }
6293
6294 // Delete workers first
6295 // shotdown all worker threads before deleting them in case of co-dependencies
6296 gTextureCache->shutdown();
6297 gTextureFetch->shutdown();
6298 gImageDecodeThread->shutdown();
6299 delete gTextureCache;
6300 delete gTextureFetch;
6301 delete gImageDecodeThread;
6302
6303 gImageList.shutdown(); // shutdown again in case a callback added something
6304
6305 // This should eventually be done in LLAppViewer
6306 LLImageJ2C::closeDSO();
6307 LLImageFormatted::cleanupClass();
6308 LLVFSThread::cleanupClass();
6309 LLLFSThread::cleanupClass();
6310
6311 llinfos << "VFS Thread finished" << llendflush;
6312
6313#ifndef LL_RELEASE_FOR_DOWNLOAD
6314 llinfos << "Auditing VFS" << llendl;
6315 gVFS->audit();
6316#endif
6317
6318 // For safety, the LLVFS has to be deleted *after* LLVFSThread. This should be cleaned up.
6319 // (LLVFS doesn't know about LLVFSThread so can't kill pending requests) -Steve
6320 delete gStaticVFS;
6321 gStaticVFS = NULL;
6322 delete gVFS;
6323 gVFS = NULL;
6324
6325 LLCurl::cleanup();
6326
6327 // This will eventually be done in LLApp
6328 LLCommon::cleanupClass();
6329
6330 end_messaging_system();
6331}
6332
6333// Clear URIs when picking a new server
6334void resetURIs()
6335{
6336 gLoginURIs.clear();
6337 gHelperURI.clear();
6338}
6339
6340const std::vector<std::string>& getLoginURIs()
6341{
6342 if (gLoginURIs.empty())
6343 {
6344 // not specified on the command line, use value from table
6345 gLoginURIs = LLSRV::rewriteURI(gUserServerDomainName[gUserServerChoice].mLoginURI);
6346 }
6347 return gLoginURIs;
6348}
6349
6350const std::string& getHelperURI()
6351{
6352 if (gHelperURI.empty())
6353 {
6354 // not specified on the command line, use value from table
6355 gHelperURI = gUserServerDomainName[gUserServerChoice].mHelperURI;
6356 }
6357 return gHelperURI;
6358}
6359
6360void errorCallback(const std::string &error_string)
6361{
6362#ifndef LL_RELEASE_FOR_DOWNLOAD
6363 OSMessageBox(error_string.c_str(), "Fatal Error", OSMB_OK);
6364#endif
6365 LLError::crashAndLoop(error_string);
6366}
6367// JC - Please don't put code here. Find the right file, perhaps
6368// llviewermessage.cpp, and put it there. Thanks!