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