diff options
Diffstat (limited to 'linden/indra/newview/viewer.cpp')
-rw-r--r-- | linden/indra/newview/viewer.cpp | 6570 |
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 | |||
241 | extern LLErrorBuffer gErrorBuffer; | ||
242 | |||
243 | // Support for crash handling. | ||
244 | void errorCallback(const std::string &error_string); | ||
245 | S32 gCrashBehavior = CRASH_BEHAVIOR_ASK; | ||
246 | void (*gCrashCallback)(void) = NULL; | ||
247 | BOOL gReportedCrash = FALSE; | ||
248 | bool gVerifySSLCert = true; | ||
249 | |||
250 | BOOL gHandleKeysAsync = FALSE; | ||
251 | |||
252 | // Use DirectX 9 to probe for hardware | ||
253 | BOOL gProbeHardware = TRUE; | ||
254 | std::string gSerialNumber; | ||
255 | |||
256 | // | ||
257 | // Application constants | ||
258 | // | ||
259 | S32 gStartupState = STATE_FIRST; | ||
260 | |||
261 | BOOL gHaveSavedSnapshot = FALSE; | ||
262 | |||
263 | S32 gYieldMS = 0; | ||
264 | BOOL gYieldTime = FALSE; | ||
265 | |||
266 | const U32 COLLISION_LIST_DEPTH = 300; // only looks at the first so many collisionable objects | ||
267 | const S32 MAX_CONSOLE_LINES = 500; | ||
268 | const S32 NUM_SESSIONS_BEFORE_SHOW_PROFILE = 5; | ||
269 | const F32 DEFAULT_AFK_TIMEOUT = 5.f * 60.f; // time with no input before user flagged as Away From Keyboard | ||
270 | |||
271 | const char *VFS_DATA_FILE_BASE = "data.db2.x."; | ||
272 | const char *VFS_INDEX_FILE_BASE = "index.db2.x."; | ||
273 | const U32 VFS_SIZE_MAP[4] = {52428800, 209715200, 524288000, 1048576000}; | ||
274 | |||
275 | const F32 MAX_USER_FOG_RATIO = 4.f; | ||
276 | const F32 MIN_USER_FOG_RATIO = 0.5f; | ||
277 | |||
278 | F32 gSimLastTime; | ||
279 | F32 gSimFrames; | ||
280 | |||
281 | const S32 MAX_USER_COMPOSITE_LIMIT = 100; | ||
282 | const 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 | |||
294 | LLString gSecondLife; | ||
295 | LLString gWindowTitle; | ||
296 | LLString gWindowName("Second Life"); | ||
297 | LLString gDisabledMessage; | ||
298 | BOOL gHideLinks = FALSE; | ||
299 | |||
300 | // This is whether or not we are connect to a production grid. | ||
301 | BOOL 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 | ||
307 | static EUserServerDomain UserServerDefaultChoice = USERSERVER_AGNI; | ||
308 | #else | ||
309 | static EUserServerDomain UserServerDefaultChoice = USERSERVER_ADITI; | ||
310 | #endif | ||
311 | #else | ||
312 | // Default userserver for development builds is dmz | ||
313 | static EUserServerDomain UserServerDefaultChoice = USERSERVER_DMZ; | ||
314 | #endif | ||
315 | |||
316 | #ifdef TOGGLE_HACKED_GODLIKE_VIEWER | ||
317 | BOOL gHackGodmode = FALSE; | ||
318 | #endif | ||
319 | |||
320 | LLUUID gTemplateToken; | ||
321 | |||
322 | // Only used if not empty. Otherwise uses value from table above. | ||
323 | static std::string gLoginURI; | ||
324 | static std::string gHelperURI; | ||
325 | |||
326 | LLAgent gAgent; | ||
327 | LLPipeline gPipeline; | ||
328 | |||
329 | // Set true when the viewer has been disconnected from the server, for example, | ||
330 | // by the user being kicked. | ||
331 | BOOL gDoDisconnect = FALSE; | ||
332 | BOOL gDisconnected = FALSE; | ||
333 | |||
334 | // Tells us to clean up the cache directory in the case of network corruption | ||
335 | BOOL gPurgeOnExit = FALSE; | ||
336 | |||
337 | // Allow multiple viewers in ReleaseForDownload | ||
338 | #if LL_RELEASE_FOR_DOWNLOAD | ||
339 | BOOL gMultipleViewersOK = FALSE; | ||
340 | #else | ||
341 | BOOL gMultipleViewersOK = TRUE; | ||
342 | #endif | ||
343 | |||
344 | LLString gArgs; | ||
345 | |||
346 | // Setting this true will cause the app to exit cleanly at the end of the frame. | ||
347 | BOOL gQuit = FALSE; | ||
348 | // Set when user has indicated desire to quit, but may have modified documents open | ||
349 | BOOL gQuitRequested = FALSE; | ||
350 | LLString gLaunchFileOnQuit; | ||
351 | BOOL gDoneLogout = FALSE; | ||
352 | |||
353 | BOOL gInitializationComplete = FALSE; // used in windows handlers to determine if OK to call idle() | ||
354 | BOOL gAutoLogin = FALSE; | ||
355 | char gOldSettingsFileName[LL_MAX_PATH]; | ||
356 | BOOL gPrintMessagesThisFrame = FALSE; | ||
357 | const char* DEFAULT_SETTINGS_FILE = "settings.xml"; | ||
358 | const char* LEGACY_DEFAULT_SETTINGS_FILE = "settings.ini"; | ||
359 | BOOL gRenderLightGlows = FALSE; // off by default for speed | ||
360 | BOOL gUseWireframe = FALSE; | ||
361 | BOOL gRunLocal = FALSE; | ||
362 | LLUUID gViewerDigest; // MD5 digest of the viewer's executable file. | ||
363 | S32 gNumSessions = 0; | ||
364 | |||
365 | BOOL gAllowAFK = TRUE; | ||
366 | F32 gAFKTimeout = DEFAULT_AFK_TIMEOUT; | ||
367 | F32 gMouseSensitivity = 3.f; | ||
368 | BOOL gInvertMouse = FALSE; | ||
369 | BOOL gLogoutRequestSent = FALSE; | ||
370 | LLTimer gLogoutTimer; | ||
371 | BOOL gShowObjectUpdates = FALSE; | ||
372 | |||
373 | const F32 LOGOUT_REQUEST_TIME = 6.f; // this will be cut short by the LogoutReply msg. | ||
374 | F32 gLogoutMaxTime = LOGOUT_REQUEST_TIME; | ||
375 | |||
376 | // Map scale in pixels per region | ||
377 | F32 gMapScale = 128.f; | ||
378 | F32 gMiniMapScale = 128.f; | ||
379 | |||
380 | // User interface/rendering globals | ||
381 | |||
382 | extern LLPointer<LLImageGL> gStartImageGL; | ||
383 | extern BOOL gDebugWindowProc; | ||
384 | extern BOOL gAvatarBacklight; | ||
385 | |||
386 | // Sky object, globals | ||
387 | LLSky gSky; | ||
388 | |||
389 | // HUD display lines in lower right | ||
390 | BOOL gDisplayWindInfo = FALSE; | ||
391 | BOOL gDisplayCameraPos = FALSE; | ||
392 | BOOL gDisplayNearestWater = FALSE; | ||
393 | BOOL gDoNearestWaterSearch = FALSE; | ||
394 | BOOL gDisplayFOV = FALSE; | ||
395 | |||
396 | // used to restore texture state after a mode switch | ||
397 | LLFrameTimer gRestoreGLTimer; | ||
398 | BOOL gRestoreGL = FALSE; | ||
399 | |||
400 | |||
401 | LLGlobalEconomy *gGlobalEconomy = NULL; | ||
402 | |||
403 | // VFS globals - see viewer.h | ||
404 | LLVFS *gStaticVFS = NULL; | ||
405 | |||
406 | // Debugging | ||
407 | FILE *gDebugFile = NULL; // File pointer used by the function which writes debug data. | ||
408 | BOOL gRandomizeFramerate = FALSE; | ||
409 | BOOL gPeriodicSlowFrame = FALSE; | ||
410 | |||
411 | //LLVector3 gCameraVelocitySmoothed; | ||
412 | // | ||
413 | // Timing/Performance/statistics globals | ||
414 | // | ||
415 | |||
416 | // Frame timing | ||
417 | U64 gFrameTime = 0; | ||
418 | F32 gFrameTimeSeconds = 0.f; | ||
419 | F32 gFrameIntervalSeconds = 0.f; | ||
420 | U32 gFrameCount = 0; | ||
421 | U64 gStartTime = 0; // gStartTime is "private", used only to calculate gFrameTimeSeconds | ||
422 | U64 gSpaceTime = 0; // gSpaceTime is the time, according to the spaceserver. | ||
423 | |||
424 | // Timing Globals | ||
425 | LLTimer gRenderStartTime; | ||
426 | F32 gQuitAfterSeconds = 0.f; | ||
427 | BOOL gRotateRight = FALSE; | ||
428 | |||
429 | // Is the Pacific time zone (aka server time zone) | ||
430 | // current in daylight savings time? | ||
431 | BOOL gPacificDaylightTime = FALSE; | ||
432 | |||
433 | // | ||
434 | // Simulator/SpaceServer configuration information | ||
435 | // | ||
436 | U32 gSecondsPerDay = 0; | ||
437 | U32 gSecondsPerYear = 0; | ||
438 | |||
439 | // | ||
440 | // Region/Object globals | ||
441 | // | ||
442 | |||
443 | |||
444 | LLVector3 gWindVec(3.0, 3.0, 0.0); | ||
445 | LLVector3 gRelativeWindVec(0.0, 0.0, 0.0); | ||
446 | |||
447 | // | ||
448 | // Globals for controls... | ||
449 | // | ||
450 | |||
451 | BOOL gVelocityInterpolate = TRUE; // (These are written once/frame with the data from gSavedSettings) | ||
452 | BOOL gPingInterpolate = TRUE; | ||
453 | |||
454 | //static | ||
455 | LLURLSimString LLURLSimString::sInstance; | ||
456 | |||
457 | // | ||
458 | // System info | ||
459 | // | ||
460 | |||
461 | LLMemoryInfo gSysMemory; | ||
462 | LLOSInfo gSysOS; | ||
463 | |||
464 | // file globals | ||
465 | static 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 | ||
513 | BOOL gGodConnect = FALSE; | ||
514 | BOOL gUseConsole = TRUE; | ||
515 | BOOL gUseAudio = TRUE; | ||
516 | BOOL gUseFMOD = TRUE; | ||
517 | BOOL gConnectToSomething = TRUE; | ||
518 | BOOL gLogMessages = FALSE; | ||
519 | BOOL gLogUTC = TRUE; | ||
520 | BOOL gRequestInventoryLibrary = TRUE; | ||
521 | BOOL gAcceptTOS = FALSE; | ||
522 | BOOL gAcceptCriticalMessage = FALSE; | ||
523 | LLUUID gInventoryLibraryOwner; | ||
524 | LLUUID gInventoryLibraryRoot; | ||
525 | |||
526 | LLString gCmdLineFirstName; | ||
527 | LLString gCmdLineLastName; | ||
528 | LLString gCmdLinePassword; | ||
529 | std::map<std::string, std::string> gCommandLineSettings; | ||
530 | std::map<std::string, std::string> gCommandLineForcedSettings; | ||
531 | BOOL gLastExecFroze = FALSE; | ||
532 | BOOL gIgnorePixelDepth = FALSE; | ||
533 | |||
534 | |||
535 | LLPointer<LLImageGL> gDisconnectedImagep = NULL; | ||
536 | |||
537 | |||
538 | /* | ||
539 | class 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. | ||
544 | public: | ||
545 | virtual void changed(U32 mask); | ||
546 | void observe(LLInventoryModel* model); | ||
547 | protected: | ||
548 | LLInventoryModel* mInventory; | ||
549 | }; | ||
550 | */ | ||
551 | |||
552 | /////////////////////// | ||
553 | // | ||
554 | // Forward declarations | ||
555 | // | ||
556 | |||
557 | // | ||
558 | // Application initialization and cleanup | ||
559 | // | ||
560 | void init_marker_file(); | ||
561 | void init_crash_logging(); | ||
562 | void create_console(); | ||
563 | void write_system_info(); | ||
564 | int parse_args(int argc, char **argv); | ||
565 | void saved_settings_to_globals(); | ||
566 | BOOL init_vfs_viewer(); | ||
567 | void purge_cache(); | ||
568 | void cleanup_app(); | ||
569 | void disconnect_viewer(void *); // Don't use directly - use do_disconnect() | ||
570 | |||
571 | // | ||
572 | // Debugging, logging, and error reporting | ||
573 | // | ||
574 | void write_debug(const char *str); // Write a string to the debug log | ||
575 | void write_debug(const std::string& str); | ||
576 | void close_debug(); // Close the debug log | ||
577 | void catch_signals(); | ||
578 | void viewer_crash_callback(); | ||
579 | void remove_marker_file(); | ||
580 | #if !LL_WINDOWS | ||
581 | void release_signals(); | ||
582 | #endif | ||
583 | |||
584 | void bad_network_handler(); | ||
585 | |||
586 | #if LL_WINDOWS | ||
587 | void disable_win_error_reporting(); | ||
588 | #endif | ||
589 | std::string get_serial_number(); | ||
590 | BOOL another_instance_running(); | ||
591 | void main_loop(); | ||
592 | |||
593 | // | ||
594 | // Callbacks | ||
595 | // | ||
596 | // Callbacks and other stuff that's not directly used in main | ||
597 | // | ||
598 | void uuid_table_request_file_callback(void **user_data, S32 result); | ||
599 | void send_stats(); | ||
600 | |||
601 | // | ||
602 | // Apple specific stuff | ||
603 | // | ||
604 | #if LL_DARWIN | ||
605 | void init_apple_menu(const char* product); | ||
606 | OSErr AEGURLHandler(const AppleEvent *messagein, AppleEvent *reply, long refIn); | ||
607 | OSErr AEQuitHandler(const AppleEvent *messagein, AppleEvent *reply, long refIn); | ||
608 | OSStatus simpleDialogHandler(EventHandlerCallRef handler, EventRef event, void *userdata); | ||
609 | OSStatus DisplayReleaseNotes(void); | ||
610 | #endif // LL_DARWIN | ||
611 | |||
612 | #ifdef LL_WINDOWS | ||
613 | extern LPWSTR gIconResource; | ||
614 | #endif | ||
615 | |||
616 | void 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 | ||
625 | BOOL 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 | |||
736 | MEM_BOOL MEM_CALLBACK second_mem_error_handler(MEM_ERROR_INFO *errorInfo); | ||
737 | |||
738 | MEM_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 | |||
755 | MEM_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 | ||
767 | int APIENTRY WinMain(HINSTANCE hInstance, | ||
768 | HINSTANCE hPrevInstance, | ||
769 | LPSTR lpCmdLine, | ||
770 | int nCmdShow) | ||
771 | #else | ||
772 | int 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 | |||
1704 | BOOL 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 | |||
1742 | void 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" | ||
1756 | extern BOOL gbCapturing; | ||
1757 | extern MovieMaker gMovieMaker; | ||
1758 | |||
1759 | void 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 | |||
1902 | void 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 | |||
1947 | FILE *gMarkerFile = NULL; | ||
1948 | |||
1949 | void 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 | |||
1964 | void 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 | |||
2043 | void 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 | |||
2081 | void 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 | ||
2122 | void 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. | ||
2158 | std::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. | ||
2224 | static 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). | ||
2264 | static 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 | |||
2381 | void 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 | |||
2526 | BOOL 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 | |||
2693 | OSErr 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 | |||
2734 | OSErr AEQuitHandler(const AppleEvent *messagein, AppleEvent *reply, long refIn) | ||
2735 | { | ||
2736 | OSErr result = noErr; | ||
2737 | |||
2738 | app_request_quit(); | ||
2739 | |||
2740 | return(result); | ||
2741 | } | ||
2742 | |||
2743 | OSStatus 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 | |||
2776 | OSStatus 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 | |||
2891 | void 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 | |||
2929 | void 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. | ||
2975 | void 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. | ||
2996 | void 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. | ||
3011 | void 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. | ||
3049 | void app_abort_quit() | ||
3050 | { | ||
3051 | llinfos << "app_abort_quit()" << llendl; | ||
3052 | gQuitRequested = FALSE; | ||
3053 | } | ||
3054 | |||
3055 | void 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 | |||
3120 | U32 gTotalLandIn = 0, gTotalLandOut = 0; | ||
3121 | U32 gTotalWaterIn = 0, gTotalWaterOut = 0; | ||
3122 | |||
3123 | F32 gAveLandCompression = 0.f, gAveWaterCompression = 0.f; | ||
3124 | F32 gBestLandCompression = 1.f, gBestWaterCompression = 1.f; | ||
3125 | F32 gWorstLandCompression = 0.f, gWorstWaterCompression = 0.f; | ||
3126 | |||
3127 | F32 gFPSClamped = 10.f; // Pretend we start at target rate. | ||
3128 | F32 gFrameDTClamped = 0.f; // Time between adjacent checks to network for packets | ||
3129 | F32 gFrameDT = 0.f; | ||
3130 | |||
3131 | //S32 gDecodedBits = 0; | ||
3132 | U32 gPacketsIn = 0; | ||
3133 | |||
3134 | U32 gTotalWorldBytes = 0, gTotalObjectBytes = 0, gTotalTextureBytes = 0, gSimPingCount = 0; | ||
3135 | U32 gObjectBits = 0; | ||
3136 | F32 gAvgSimPing = 0.f; | ||
3137 | |||
3138 | |||
3139 | extern U32 gVisCompared; | ||
3140 | extern U32 gVisTested; | ||
3141 | |||
3142 | |||
3143 | void 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!) | ||
3392 | static F32 CheckMessagesMaxTime = CHECK_MESSAGES_DEFAULT_MAX_TIME; | ||
3393 | #endif | ||
3394 | |||
3395 | void 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 | |||
3489 | void idle_afk_check() | ||
3490 | { | ||
3491 | // check idle timers | ||
3492 | if (gAwayTriggerTimer.getElapsedTimeF32() > gAFKTimeout) | ||
3493 | { | ||
3494 | gAgent.setAFK(); | ||
3495 | } | ||
3496 | } | ||
3497 | void 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 | |||
3525 | void 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 | |||
4007 | F32 mouse_x_from_center(S32 x) | ||
4008 | { | ||
4009 | return ((F32) x / (F32) gViewerWindow->getWindowWidth() ) - 0.5f; | ||
4010 | } | ||
4011 | |||
4012 | |||
4013 | F32 mouse_y_from_center(S32 y) | ||
4014 | { | ||
4015 | return ((F32) y / (F32) gViewerWindow->getWindowHeight() ) - 0.5f; | ||
4016 | } | ||
4017 | |||
4018 | |||
4019 | ///////////////////////////////////////////////////////// | ||
4020 | |||
4021 | void 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 | |||
4105 | BOOL 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 | |||
4178 | const LLVector3 DEFAULT_OBJECT_SCALE(0.5f, 0.5f, 0.5f); | ||
4179 | |||
4180 | BOOL 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, ®ionp ); | ||
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 | ||
4460 | void 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 | |||
4499 | class 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 | }; | ||
4507 | static LLAFKTimeoutListener afk_timeout_listener; | ||
4508 | |||
4509 | class 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 | }; | ||
4517 | static LLMouseSensitivityListener mouse_sensitivity_listener; | ||
4518 | |||
4519 | |||
4520 | class LLInvertMouseListener: public LLSimpleListener | ||
4521 | { | ||
4522 | bool handleEvent(LLPointer<LLEvent> event, const LLSD& userdata) | ||
4523 | { | ||
4524 | gInvertMouse = event->getValue().asBoolean(); | ||
4525 | return true; | ||
4526 | } | ||
4527 | }; | ||
4528 | static LLInvertMouseListener invert_mouse_listener; | ||
4529 | |||
4530 | class 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 | }; | ||
4538 | static LLRenderAvatarMouselookListener render_avatar_mouselook_listener; | ||
4539 | |||
4540 | class 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 | }; | ||
4553 | static LLRenderFarClipListener render_far_clip_listener; | ||
4554 | |||
4555 | class 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 | }; | ||
4563 | static LLTerrainDetailListener terrain_detail_listener; | ||
4564 | |||
4565 | |||
4566 | class LLSetShaderListener: public LLSimpleListener | ||
4567 | { | ||
4568 | bool handleEvent(LLPointer<LLEvent> event, const LLSD& userdata) | ||
4569 | { | ||
4570 | gPipeline.setShaders(); | ||
4571 | return true; | ||
4572 | } | ||
4573 | }; | ||
4574 | static LLSetShaderListener set_shader_listener; | ||
4575 | |||
4576 | class 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 | }; | ||
4585 | static LLVolumeLODListener volume_lod_listener; | ||
4586 | |||
4587 | class 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 | }; | ||
4595 | static LLAvatarLODListener avatar_lod_listener; | ||
4596 | |||
4597 | class 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 | }; | ||
4605 | static LLTreeLODListener tree_lod_listener; | ||
4606 | |||
4607 | class 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 | }; | ||
4615 | static LLFlexLODListener flex_lod_listener; | ||
4616 | |||
4617 | class 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 | }; | ||
4638 | static LLGammaListener gamma_listener; | ||
4639 | |||
4640 | class 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 | }; | ||
4648 | static LLNightBrightnessListener night_brightness_listener; | ||
4649 | |||
4650 | class 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 | }; | ||
4658 | static LLUseAGPListener use_agp_listener; | ||
4659 | |||
4660 | class 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 | }; | ||
4671 | static LLFogRatioListener fog_ratio_listener; | ||
4672 | |||
4673 | class 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 | }; | ||
4681 | static LLMaxPartCountListener max_partCount_listener; | ||
4682 | |||
4683 | class 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 | }; | ||
4694 | static LLCompositeLimitListener composite_limit_listener; | ||
4695 | |||
4696 | class 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 | }; | ||
4704 | static LLVideoMemoryListener video_memory_listener; | ||
4705 | |||
4706 | class 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 | }; | ||
4714 | static LLBandwidthListener bandwidth_listener; | ||
4715 | |||
4716 | class 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 | }; | ||
4724 | static LLChatFontSizeListener chat_font_size_listener; | ||
4725 | |||
4726 | class 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 | }; | ||
4734 | static LLChatPersistTimeListener chat_persist_time_listener; | ||
4735 | |||
4736 | class 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 | }; | ||
4744 | static LLConsoleMaxLinesListener console_max_lines_listener; | ||
4745 | |||
4746 | |||
4747 | class 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 | |||
4766 | void stop_video(); | ||
4767 | void prepare_video(const LLParcel *parcel); | ||
4768 | |||
4769 | static LLMasterAudioListener master_audio_listener; | ||
4770 | |||
4771 | |||
4772 | class 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 | |||
4802 | static LLAudioStreamMusicListener audio_stream_music_listener; | ||
4803 | |||
4804 | |||
4805 | |||
4806 | class 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 | |||
4844 | static LLAudioStreamMediaListener audio_stream_media_listener; | ||
4845 | |||
4846 | |||
4847 | class 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 | |||
4859 | static LLAudioMuteListener audio_mute_listener; | ||
4860 | |||
4861 | |||
4862 | class 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 | |||
4874 | static 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 | ||
4878 | void 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 | |||
4971 | void 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 | |||
5030 | void 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 | |||
5042 | void write_debug(const std::string& str) | ||
5043 | { | ||
5044 | write_debug(str.c_str()); | ||
5045 | } | ||
5046 | |||
5047 | void 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 | |||
5064 | void close_debug() | ||
5065 | { | ||
5066 | if (gDebugFile) | ||
5067 | { | ||
5068 | fclose(gDebugFile); | ||
5069 | } | ||
5070 | gDebugFile = NULL; | ||
5071 | } | ||
5072 | |||
5073 | void 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 | |||
5093 | void 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 | |||
5105 | void 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 | |||
5299 | void 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(<ime); | ||
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 | ||
5376 | void 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. | ||
5402 | void 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 | ||
5433 | void 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 | |||
5451 | void 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 | |||
5458 | int 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 | |||
5891 | LLString LLURLSimString::sLocationStringHome("My Home"); | ||
5892 | LLString LLURLSimString::sLocationStringLast("My Last Location"); | ||
5893 | |||
5894 | // "secondlife://simname/x/y/z" -> "simname/x/y/z" | ||
5895 | // (actually .*//foo -> foo) | ||
5896 | // static | ||
5897 | void 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 | ||
5922 | S32 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 | ||
5939 | bool 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 | ||
5979 | bool 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 | ||
6003 | bool 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 | |||
6041 | void 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 | |||
6055 | void 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 | |||
6070 | void login_alert_done(S32 option, void* user_data) | ||
6071 | { | ||
6072 | LLPanelLogin::giveFocus(); | ||
6073 | } | ||
6074 | |||
6075 | void 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 | ||
6153 | void 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 | |||
6163 | void finish_early_exit(S32 option, void* userdata) | ||
6164 | { | ||
6165 | app_force_quit(NULL); | ||
6166 | } | ||
6167 | |||
6168 | void 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 | ||
6179 | void app_force_exit(S32 arg) | ||
6180 | { | ||
6181 | remove_marker_file(); | ||
6182 | exit(arg); | ||
6183 | } | ||
6184 | |||
6185 | void finish_disconnect(S32 option, void* userdata) | ||
6186 | { | ||
6187 | if (1 == option) | ||
6188 | { | ||
6189 | app_force_quit(NULL); | ||
6190 | } | ||
6191 | } | ||
6192 | |||
6193 | void 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 | |||
6210 | void 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 | |||
6232 | const LLUUID& agent_get_id() | ||
6233 | { | ||
6234 | return gAgent.getID(); | ||
6235 | } | ||
6236 | |||
6237 | const LLUUID& agent_get_session_id() | ||
6238 | { | ||
6239 | return gAgent.getSessionID(); | ||
6240 | } | ||
6241 | |||
6242 | void agent_send_reliable_message() | ||
6243 | { | ||
6244 | gAgent.sendReliableMessage(); | ||
6245 | } | ||
6246 | |||
6247 | void 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 | ||
6283 | void save_avatar(void*) | ||
6284 | { | ||
6285 | LLFloaterSaveAvatar::show(); | ||
6286 | } | ||
6287 | |||
6288 | void 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 | |||
6543 | const 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 | |||
6553 | const 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 | |||
6563 | void 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! | ||