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