Skip to content
Snippets Groups Projects
llappviewer.cpp 172 KiB
Newer Older
			LL_WARNS() << "Quitting with pending background tasks." << LL_ENDL;
    if (mPurgeUserDataOnExit)
    {
        // Ideally we should not save anything from this session since it is going to be purged now,
        // but this is a very 'rare' case (user deleting himself), not worth overcomplicating 'save&cleanup' code
        std::string user_path = gDirUtilp->getOSUserAppDir() + gDirUtilp->getDirDelimiter() + LLStartUp::getUserId();
        gDirUtilp->deleteDirAndContents(user_path);
    }

	// Delete workers first
	// shotdown all worker threads before deleting them in case of co-dependencies
	sTextureFetch->shutDownTextureCacheThread() ;
	sTextureFetch->shutDownImageDecodeThread() ;

	LL_INFOS() << "Shutting down message system" << LL_ENDL;
	// Non-LLCurl libcurl library
	mAppCoreHttp.cleanup();

	SUBSYSTEM_CLEANUP(LLFilePickerThread);
	//MUST happen AFTER SUBSYSTEM_CLEANUP(LLCurl)
	delete sTextureCache;
    sTextureCache = NULL;
	delete sTextureFetch;
    sTextureFetch = NULL;
	delete sImageDecodeThread;
    sImageDecodeThread = NULL;
	delete mFastTimerLogThread;
	mFastTimerLogThread = NULL;
	if (LLFastTimerView::sAnalyzePerformance)
	{
		LL_INFOS() << "Analyzing performance" << LL_ENDL;
		std::string baseline_name = LLTrace::BlockTimer::sLogName + "_baseline.slp";
		std::string current_name  = LLTrace::BlockTimer::sLogName + ".slp";
		std::string report_name   = LLTrace::BlockTimer::sLogName + "_report.csv";
			gDirUtilp->getExpandedFilename(LL_PATH_LOGS, baseline_name),
			gDirUtilp->getExpandedFilename(LL_PATH_LOGS, current_name),
			gDirUtilp->getExpandedFilename(LL_PATH_LOGS, report_name));
	SUBSYSTEM_CLEANUP(LLMetricPerformanceTesterBasic) ;
	LL_INFOS() << "Cleaning up Media and Textures" << LL_ENDL;
	//SUBSYSTEM_CLEANUP(LLViewerMedia) has to be put before gTextureList.shutdown()
	//because some new image might be generated during cleaning up media. --bao
	gTextureList.shutdown(); // shutdown again in case a callback added something
	LLUIImageList::getInstance()->cleanUp();
	// This should eventually be done in LLAppViewer
	SUBSYSTEM_CLEANUP(LLVFSThread);
	SUBSYSTEM_CLEANUP(LLLFSThread);
	LL_INFOS() << "Auditing VFS" << LL_ENDL;
	LL_INFOS() << "Misc Cleanup" << LL_ENDL;
	// For safety, the LLVFS has to be deleted *after* LLVFSThread. This should be cleaned up.
	// (LLVFS doesn't know about LLVFSThread so can't kill pending requests) -Steve
	delete gStaticVFS;
	gStaticVFS = NULL;
	delete gVFS;
	gVFS = NULL;
	gSavedSettings.cleanup();
	LLUIColorTable::instance().clear();
	LLViewerAssetStatsFF::cleanup();
	// If we're exiting to launch an URL, do that here so the screen
	// is at the right resolution before we launch IE.
	if (!gLaunchFileOnQuit.empty())
	{
		LL_INFOS() << "Launch file on quit." << LL_ENDL;
#if LL_WINDOWS
		// Indicate an application is starting.
		SetCursor(LoadCursor(NULL, IDC_WAIT));
#endif

		// HACK: Attempt to wait until the screen res. switch is complete.
		ms_sleep(1000);

		LLWeb::loadURLExternal( gLaunchFileOnQuit, false );
		LL_INFOS() << "File launched." << LL_ENDL;
	LL_INFOS() << "Cleaning up LLProxy." << LL_ENDL;
	SUBSYSTEM_CLEANUP(LLProxy);
    LLCore::LLHttp::cleanup();
	// It's not at first obvious where, in this long sequence, a generic cleanup
	// call OUGHT to go. So let's say this: as we migrate cleanup from
	// explicit hand-placed calls into the generic mechanism, eventually
	// all cleanup will get subsumed into the generic call. So the calls you
	// still see above are calls that MUST happen before the generic cleanup
	// kicks in.
	// This calls every remaining LLSingleton's cleanupSingleton() and
	// deleteSingleton() methods.
// A callback for LL_ERRS() to call during the watchdog error.
void watchdog_llerrs_callback(const std::string &error_string)
{
	gLLErrorActivated = true;

	gDebugInfo["FatalMessage"] = error_string;
	LLAppViewer::instance()->writeDebugInfo();

#ifdef LL_WINDOWS
	RaiseException(0,0,0,0);
#else
	raise(SIGQUIT);
#endif
}

// A callback for the watchdog to call.
void watchdog_killer_callback()
{
	LLError::setFatalFunction(watchdog_llerrs_callback);
	LL_ERRS() << "Watchdog killer event" << LL_ENDL;
bool LLAppViewer::initThreads()
{
	static const bool enable_threads = true;
	LLImage::initParamSingleton(gSavedSettings.getBOOL("TextureNewByteRange"),gSavedSettings.getS32("TextureReverseByteRange"));
	LLVFSThread::initClass(enable_threads && false);
	LLLFSThread::initClass(enable_threads && false);
	LLAppViewer::sImageDecodeThread = new LLImageDecodeThread(enable_threads && true);
	LLAppViewer::sTextureCache = new LLTextureCache(enable_threads && true);
	LLAppViewer::sTextureFetch = new LLTextureFetch(LLAppViewer::getTextureCache(),
													sImageDecodeThread,
													enable_threads && true,
	if (LLTrace::BlockTimer::sLog || LLTrace::BlockTimer::sMetricLog)
		LLTrace::BlockTimer::setLogLock(new LLMutex());
		mFastTimerLogThread = new LLFastTimerLogThread(LLTrace::BlockTimer::sLogName);
	// Mesh streaming and caching
	gMeshRepo.init();

David Parks's avatar
David Parks committed
	LLFilePickerThread::initClass();
void errorCallback(const std::string& error_string)
	static std::string last_message;
	if (last_message != error_string)
	{
#ifdef SHOW_ASSERT
		U32 response = OSMessageBox(error_string, LLTrans::getString("MBFatalError"), OSMB_YESNO);
		if (response == OSBTN_NO)
		{
			last_message = error_string;
			return;
		}
		//Set the ErrorActivated global so we know to create a marker file
		gLLErrorActivated = true;
		gDebugInfo["FatalMessage"] = error_string;
		// We're not already crashing -- we simply *intend* to crash. Since we
		// haven't actually trashed anything yet, we can afford to write the whole
		// static info file.
		LLAppViewer::instance()->writeDebugInfo();
		// [SL:KB] - Patch: Viewer-Build | Checked: Catznip-2.4
#if !LL_RELEASE_FOR_DOWNLOAD && LL_WINDOWS
		DebugBreak();
		LLError::crashAndLoop(error_string);
#endif // LL_RELEASE_WITH_DEBUG_INFO && LL_WINDOWS
		// [/SL:KB]
		//#ifndef SHADER_CRASH_NONFATAL
		//	LLError::crashAndLoop(error_string);
		//#endif
	}
	LLError::initForApplication( gDirUtilp->getExpandedFilename(LL_PATH_USER_SETTINGS, "")
                                ,gDirUtilp->getExpandedFilename(LL_PATH_APP_SETTINGS, "")
                                );
	LLError::setFatalFunction(errorCallback);
	//LLError::setTimeFunction(getRuntime);
	// Remove the last ".old" log file.
	std::string old_log_file = gDirUtilp->getExpandedFilename(LL_PATH_LOGS,
							     "Alchemy.old");
	LLFile::remove(old_log_file);
	// Get name of the log file
	std::string log_file = gDirUtilp->getExpandedFilename(LL_PATH_LOGS,
							     "Alchemy.log");
	 * Before touching any log files, compute the duration of the last run
	 * by comparing the ctime of the previous start marker file with the ctime
	 * of the last log file.
	 */
	std::string start_marker_file_name = gDirUtilp->getExpandedFilename(LL_PATH_LOGS, START_MARKER_FILE_NAME);
	llstat start_marker_stat;
	llstat log_file_stat;
	std::ostringstream duration_log_stream; // can't log yet, so save any message for when we can below
	int start_stat_result = LLFile::stat(start_marker_file_name, &start_marker_stat);
	int log_stat_result = LLFile::stat(log_file, &log_file_stat);
	if ( 0 == start_stat_result && 0 == log_stat_result )
	{
		int elapsed_seconds = log_file_stat.st_ctime - start_marker_stat.st_ctime;
		// only report a last run time if the last viewer was the same version
		// because this stat will be counted against this version
		if ( markerIsSameVersion(start_marker_file_name) )
		{
			gLastExecDuration = elapsed_seconds;
		}
		else
		{
			duration_log_stream << "start marker from some other version; duration is not reported";
			gLastExecDuration = -1;
		}
	}
	else
	{
		// at least one of the LLFile::stat calls failed, so we can't compute the run time
		duration_log_stream << "duration stat failure; start: "<< start_stat_result << " log: " << log_stat_result;
		gLastExecDuration = -1; // unknown
	}
	std::string duration_log_msg(duration_log_stream.str());
	// Create a new start marker file for comparison with log file time for the next run
	LLAPRFile start_marker_file ;
	start_marker_file.open(start_marker_file_name, LL_APR_WB);
	if (start_marker_file.getFileHandle())
	{
		recordMarkerVersion(start_marker_file);
		start_marker_file.close();
	}

	// Rename current log file to ".old"
	LLFile::rename(log_file, old_log_file);

	// Set the log file to SecondLife.log
	LLError::logToFile(log_file);
	if (!duration_log_msg.empty())
	{
		LL_WARNS("MarkerFile") << duration_log_msg << LL_ENDL;
	}
bool LLAppViewer::loadSettingsFromDirectory(const std::string& location_key,
					    bool set_defaults)
Richard Linden's avatar
Richard Linden committed
	if (!mSettingsLocationList)
		LL_ERRS() << "Invalid settings location list" << LL_ENDL;
	for (const SettingsGroup& group : mSettingsLocationList->groups)
Richard Linden's avatar
Richard Linden committed
		// skip settings groups that aren't the one we requested
		if (group.name() != location_key) continue;
		ELLPath path_index = (ELLPath)group.path_index();
Richard Linden's avatar
Richard Linden committed
		if(path_index <= LL_PATH_NONE || path_index >= LL_PATH_LAST)
			LL_ERRS() << "Out of range path index in app_settings/settings_files.xml" << LL_ENDL;
Richard Linden's avatar
Richard Linden committed
			return false;
		for (const SettingsFile& file : group.files)
			LL_INFOS("Settings") << "Attempting to load settings for the group " << file.name()
			    << " - from location " << location_key << LL_ENDL;
Richard Linden's avatar
Richard Linden committed

			LLControlGroup* settings_group = LLControlGroup::getInstance(file.name);
Richard Linden's avatar
Richard Linden committed
			if(!settings_group)
				LL_WARNS("Settings") << "No matching settings group for name " << file.name() << LL_ENDL;
Richard Linden's avatar
Richard Linden committed
				continue;
Richard Linden's avatar
Richard Linden committed
			std::string full_settings_path;
			if (file.file_name_setting.isProvided()
				&& gSavedSettings.controlExists(file.file_name_setting.getValue()))
Richard Linden's avatar
Richard Linden committed
				// try to find filename stored in file_name_setting control
				full_settings_path = gSavedSettings.getString(file.file_name_setting.getValue());
				if (full_settings_path.empty())
				{
					continue;
				}
				else if (!gDirUtilp->fileExists(full_settings_path))
Richard Linden's avatar
Richard Linden committed
				{
					// search in default path
					full_settings_path = gDirUtilp->getExpandedFilename((ELLPath)path_index, full_settings_path);
				}
Richard Linden's avatar
Richard Linden committed
				// by default, use specified file name
				full_settings_path = gDirUtilp->getExpandedFilename((ELLPath)path_index, file.file_name());
Richard Linden's avatar
Richard Linden committed
			}

			if(settings_group->loadFromFile(full_settings_path, set_defaults, file.persistent))
Richard Linden's avatar
Richard Linden committed
			{	// success!
				LL_INFOS("Settings") << "Loaded settings file " << full_settings_path << LL_ENDL;
Richard Linden's avatar
Richard Linden committed
			}
			else
			{	// failed to load
				if(file.required)
Richard Linden's avatar
Richard Linden committed
				{
					LL_ERRS() << "Error: Cannot load required settings file from: " << full_settings_path << LL_ENDL;
Richard Linden's avatar
Richard Linden committed
					return false;
				}
				else
				{
					// only complain if we actually have a filename at this point
					if (!full_settings_path.empty())
					{
						LL_INFOS("Settings") << "Cannot load " << full_settings_path << " - No settings found." << LL_ENDL;
Richard Linden's avatar
Richard Linden committed
					}
				}
	return true;
std::string LLAppViewer::getSettingsFilename(const std::string& location_key,
											 const std::string& file)
	for (const SettingsGroup& group : mSettingsLocationList->groups)
		if (group.name() == location_key)
			for (const SettingsFile& settings_file : group.files)
				if (settings_file.name() == file)
Richard Linden's avatar
Richard Linden committed
				{
					return settings_file.file_name;
Richard Linden's avatar
Richard Linden committed
				}
Richard Linden's avatar
Richard Linden committed

	return std::string();
}

void LLAppViewer::loadColorSettings()
{
	LLUIColorTable::instance().loadFromSettings();
namespace
{
    void handleCommandLineError(LLControlGroupCLP& clp)
    {
		LL_WARNS() << "Error parsing command line options. Command Line options ignored."  << LL_ENDL;
		LL_INFOS() << "Command line usage:\n" << clp << LL_ENDL;

		OSMessageBox(STRINGIZE(LLTrans::getString("MBCmdLineError") << clp.getErrorMessage()),
					 LLStringUtil::null,
					 OSMB_OK);
    }
} // anonymous namespace

	//Load settings files list
	std::string settings_file_list = gDirUtilp->getExpandedFilename(LL_PATH_APP_SETTINGS, "settings_files.xml");
Richard Linden's avatar
Richard Linden committed
	LLXMLNodePtr root;
	BOOL success  = LLXMLNode::parseFile(settings_file_list, root, NULL);
	if (!success)
        LL_ERRS() << "Cannot load default configuration file " << settings_file_list << LL_ENDL;
Richard Linden's avatar
Richard Linden committed
	mSettingsLocationList = new SettingsFiles();

	LLXUIParser parser;
	parser.readXUI(root, *mSettingsLocationList, settings_file_list);

	if (!mSettingsLocationList->validateBlock())
	{
        LL_ERRS() << "Invalid settings file list " << settings_file_list << LL_ENDL;
Richard Linden's avatar
Richard Linden committed
	}
	// The settings and command line parsing have a fragile
	// order-of-operation:
	// - load defaults from app_settings
	// - set procedural settings values
	// - read command line settings
	// - selectively apply settings needed to load user settings.
    // - load overrides from user_settings
	// - apply command line settings (to override the overrides)
	// - load per account settings (happens in llstartup
	// - load defaults
	bool set_defaults = true;
	if(!loadSettingsFromDirectory("Default", set_defaults))
		OSMessageBox(
			"Unable to load default settings file. The installation may be corrupted.",
			LLStringUtil::null,OSMB_OK);
		return false;
	}

	initStrings(); // setup paths for LLTrans based on settings files only
	// - set procedural settings
	// Note: can't use LL_PATH_PER_SL_ACCOUNT for any of these since we haven't logged in yet
	gSavedSettings.setString("ClientSettingsFile",
        gDirUtilp->getExpandedFilename(LL_PATH_USER_SETTINGS, getSettingsFilename("Default", "Global")));
#ifndef	LL_RELEASE_FOR_DOWNLOAD
	// provide developer build only overrides for these control variables that are not
	// persisted to settings.xml
	LLControlVariable* c = gSavedSettings.getControl("ShowConsoleWindow");
	if (c)
	{
		c->setValue(true, false);
	}
	c = gSavedSettings.getControl("AllowMultipleViewers");
	if (c)
	{
		c->setValue(true, false);
	}
	gSavedSettings.setBOOL("QAMode", TRUE );
	// These are warnings that appear on the first experience of that condition.
	// They are already set in the settings_default.xml file, but still need to be added to LLFirstUse
	// for disable/reset ability
//	LLFirstUse::addConfigVariable("FirstBalanceIncrease");
//	LLFirstUse::addConfigVariable("FirstBalanceDecrease");
//	LLFirstUse::addConfigVariable("FirstSit");
//	LLFirstUse::addConfigVariable("FirstMap");
//	LLFirstUse::addConfigVariable("FirstGoTo");
//	LLFirstUse::addConfigVariable("FirstBuild");
//	LLFirstUse::addConfigVariable("FirstLeftClickNoHit");
//	LLFirstUse::addConfigVariable("FirstTeleport");
//	LLFirstUse::addConfigVariable("FirstOverrideKeys");
//	LLFirstUse::addConfigVariable("FirstAttach");
//	LLFirstUse::addConfigVariable("FirstAppearance");
//	LLFirstUse::addConfigVariable("FirstInventory");
//	LLFirstUse::addConfigVariable("FirstSandbox");
//	LLFirstUse::addConfigVariable("FirstFlexible");
//	LLFirstUse::addConfigVariable("FirstDebugMenus");
//	LLFirstUse::addConfigVariable("FirstSculptedPrim");
//	LLFirstUse::addConfigVariable("FirstVoice");
//	LLFirstUse::addConfigVariable("FirstMedia");
	// - read command line settings.
	LLControlGroupCLP clp;
	std::string	cmd_line_config	= gDirUtilp->getExpandedFilename(LL_PATH_APP_SETTINGS,
														  "cmd_line.xml");
	clp.configure(cmd_line_config, &gSavedSettings);

	if(!initParseCommandLine(clp))
	{
		return false;

	// If the user has specified a alternate settings file name.
	// Load	it now before loading the user_settings/settings.xml
	if(clp.hasOption("settings"))
	{
		std::string	user_settings_filename =
			gDirUtilp->getExpandedFilename(LL_PATH_USER_SETTINGS,
										   clp.getOption("settings")[0]);
		gSavedSettings.setString("ClientSettingsFile", user_settings_filename);
		LL_INFOS("Settings")	<< "Using command line specified settings filename: "
			<< user_settings_filename << LL_ENDL;
	// - load overrides from user_settings
	loadSettingsFromDirectory("User");
Richard Linden's avatar
Richard Linden committed

	if (gSavedSettings.getBOOL("FirstRunThisInstall"))
	{
		// Set firstrun flag to indicate that some further init actiona should be taken
		// like determining screen DPI value and so on
		mIsFirstRun = true;

		gSavedSettings.setBOOL("FirstRunThisInstall", FALSE);
Richard Linden's avatar
Richard Linden committed
	}

	if (clp.hasOption("sessionsettings"))
	{
		std::string session_settings_filename = clp.getOption("sessionsettings")[0];
Richard Linden's avatar
Richard Linden committed
		gSavedSettings.setString("SessionSettingsFile", session_settings_filename);
		LL_INFOS("Settings")	<< "Using session settings filename: "
			<< session_settings_filename << LL_ENDL;
Richard Linden's avatar
Richard Linden committed
	}
	loadSettingsFromDirectory("Session");

	if (clp.hasOption("usersessionsettings"))
	{
		std::string user_session_settings_filename = clp.getOption("usersessionsettings")[0];
Richard Linden's avatar
Richard Linden committed
		gSavedSettings.setString("UserSessionSettingsFile", user_session_settings_filename);
		LL_INFOS("Settings") << "Using user session settings filename: "
			<< user_session_settings_filename << LL_ENDL;
Richard Linden's avatar
Richard Linden committed

	}
	loadSettingsFromDirectory("UserSession");

	// - apply command line settings
	if (! clp.notify())
	{
		handleCommandLineError(clp);
		return false;
	}
	// Register the core crash option as soon as we can
	// if we want gdb post-mortem on cores we need to be up and running
	// ASAP or we might miss init issue etc.
	if(gSavedSettings.getBOOL("DisableCrashLogger"))
		LL_WARNS() << "Crashes will be handled by system, stack trace logs and crash logger are both disabled" << LL_ENDL;
	// Handle initialization from settings.
	// Start up the debugging console before handling other options.
	if (gSavedSettings.getBOOL("ShowConsoleWindow"))
	{
		initConsole();
	}

	if(clp.hasOption("help"))
	{
		std::ostringstream msg;
		msg << LLTrans::getString("MBCmdLineUsg") << "\n" << clp;
		LL_INFOS()	<< msg.str() << LL_ENDL;

		OSMessageBox(
		return false;
	}

    if(clp.hasOption("set"))
    {
        const LLCommandLineParser::token_vector_t& set_values = clp.getOption("set");
        if(0x1 & set_values.size())
        {
            LL_WARNS() << "Invalid '--set' parameter count." << LL_ENDL;
        }
        else
        {
            LLCommandLineParser::token_vector_t::const_iterator itr = set_values.begin();
            for(; itr != set_values.end(); ++itr)
            {
                const std::string& name = *itr;
                const std::string& value = *(++itr);
                std::string name_part;
                std::string group_part;
				LLControlVariable* control = NULL;

				// Name can be further split into ControlGroup.Name, with the default control group being Global
				size_t pos = name.find('.');
				if (pos != std::string::npos)
				{
					group_part = name.substr(0, pos);
					name_part = name.substr(pos+1);
					LL_INFOS() << "Setting " << group_part << "." << name_part << " to " << value << LL_ENDL;
					LLControlGroup* g = LLControlGroup::getInstance(group_part);
					if (g) control = g->getControl(name_part);
				}
				else
				{
					LL_INFOS() << "Setting Global." << name << " to " << value << LL_ENDL;
					control = gSavedSettings.getControl(name);
				}

                if (control)
					LL_WARNS() << "Failed --set " << name << ": setting name unknown." << LL_ENDL;
    if  (clp.hasOption("logevents")) {
Aura Linden's avatar
Aura Linden committed
		LLViewerEventRecorder::instance().setEventLoggingOn();
	std::string CmdLineChannel(gSavedSettings.getString("CmdLineChannel"));
	if(! CmdLineChannel.empty())
		LLVersionInfo::instance().resetChannel(CmdLineChannel);
	// If we have specified crash on startup, set the global so we'll trigger the crash at the right time
	gCrashOnStartup = gSavedSettings.getBOOL("CrashOnStartup");
	if (gSavedSettings.getBOOL("LogPerformance"))
		LLTrace::BlockTimer::sLogName = std::string("performance");
	std::string test_name(gSavedSettings.getString("LogMetrics"));
	if (! test_name.empty())
Richard Linden's avatar
Richard Linden committed
		LLTrace::BlockTimer::sMetricLog = TRUE;
		// '--logmetrics' is specified with a named test metric argument so the data gathering is done only on that test
		// In the absence of argument, every metric would be gathered (makes for a rather slow run and hard to decipher report...)
		LL_INFOS() << "'--logmetrics' argument : " << test_name << LL_ENDL;
		LLTrace::BlockTimer::sLogName = test_name;
Richard Linden's avatar
Richard Linden committed
	}

	if (clp.hasOption("graphicslevel"))
	{
		// User explicitly requested --graphicslevel on the command line. We
		// expect this switch has already set RenderQualityPerformance. Check
		// that value for validity.
		U32 graphicslevel = gSavedSettings.getU32("RenderQualityPerformance");
		if (LLFeatureManager::instance().isValidGraphicsLevel(graphicslevel))
			// graphicslevel is valid: save it and engage it later. Capture
			// the requested value separately from the settings variable
			// because, if this is the first run, LLViewerWindow's constructor
			// will call LLFeatureManager::applyRecommendedSettings(), which
			// overwrites this settings variable!
			mForceGraphicsLevel = graphicslevel;
	LLFastTimerView::sAnalyzePerformance = gSavedSettings.getBOOL("AnalyzePerformance");
	gAgentPilot.setReplaySession(gSavedSettings.getBOOL("ReplaySession"));
Palmer Truelson's avatar
Palmer Truelson committed

	if (gSavedSettings.getBOOL("DebugSession"))
	{
		gDebugSession = TRUE;
		gDebugGL = TRUE;

		ll_init_fail_log(gDirUtilp->getExpandedFilename(LL_PATH_LOGS, "test_failures.log"));
	}

	const LLControlVariable* skinfolder = gSavedSettings.getControl("SkinCurrent");
	if(skinfolder && LLStringUtil::null != skinfolder->getValue().asString())
	{
		// Examining "Language" may not suffice -- see LLUI::getLanguage()
		// logic. Unfortunately LLUI::getLanguage() doesn't yet do us much
		// good because we haven't yet called LLUI::initClass().
		gDirUtilp->setSkinFolder(skinfolder->getValue().asString(),
								 gSavedSettings.getString("Language"));
	}

	if (gSavedSettings.getBOOL("SpellCheck"))
	{
		std::list<std::string> dict_list;
		std::string dict_setting = gSavedSettings.getString("SpellCheckDictionary");
		boost::split(dict_list, dict_setting, boost::is_any_of(std::string(",")));
		if (!dict_list.empty())
		{
			LLSpellChecker::setUseSpellCheck(dict_list.front());
			dict_list.pop_front();
			LLSpellChecker::instance().setSecondaryDictionaries(dict_list);
		}
	}

	// Handle slurl use. NOTE: Don't let SL-55321 reappear.
	// This initial-SLURL logic, up through the call to
	// sendURLToOtherInstance(), must precede LLSplashScreen::show() --
	// because if sendURLToOtherInstance() succeeds, we take a fast exit,
	// SKIPPING the splash screen and everything else.
    // *FIX: This init code should be made more robust to prevent
    // the issue SL-55321 from returning. One thought is to allow
    // only select options to be set from command line when a slurl
    // is specified. More work on the settings system is needed to
    // achieve this. For now...

    // *NOTE:Mani The command line parser parses tokens and is
    // setup to bail after parsing the '--url' option or the
    // first option specified without a '--option' flag (or
    // any other option that uses the 'last_option' setting -
    // see LLControlGroupCLP::configure())

    // What can happen is that someone can use IE (or potentially
    // other browsers) and do the rough equivalent of command
    // injection and steal passwords. Phoenix. SL-55321

	std::string starting_location;

	std::string cmd_line_login_location(gSavedSettings.getString("CmdLineLoginLocation"));
	if(! cmd_line_login_location.empty())
		starting_location = cmd_line_login_location;
	}
	else
		std::string default_login_location(gSavedSettings.getString("DefaultLoginLocation"));
		if (! default_login_location.empty())
		{
			starting_location = default_login_location;
Gilbert Gonzales's avatar
Gilbert Gonzales committed

	LLSLURL start_slurl;
	if (! starting_location.empty())
Richard Linden's avatar
Richard Linden committed
		start_slurl = starting_location;
		LLStartUp::setStartSLURL(start_slurl);
		if(start_slurl.getType() == LLSLURL::LOCATION)
		{
			LLGridManager::getInstance()->setGridChoice(start_slurl.getGrid());
	// NextLoginLocation is set as a side effect of LLStartUp::setStartSLURL()
	std::string nextLoginLocation = gSavedSettings.getString( "NextLoginLocation" );
	if ( !nextLoginLocation.empty() )
	{
		LL_DEBUGS("AppInit")<<"set start from NextLoginLocation: "<<nextLoginLocation<<LL_ENDL;
		LLStartUp::setStartSLURL(LLSLURL(nextLoginLocation));
	}
	else if (   (   clp.hasOption("login") || clp.hasOption("autologin"))
			 && gSavedSettings.getString("CmdLineLoginLocation").empty())
	{
		// If automatic login from command line with --login switch
		// init StartSLURL location.
		std::string start_slurl_setting = gSavedSettings.getString("LoginLocation");
		LL_DEBUGS("AppInit") << "start slurl setting '" << start_slurl_setting << "'" << LL_ENDL;
		LLStartUp::setStartSLURL(LLSLURL(start_slurl_setting));
	}
	else
	{
		// the login location will be set by the login panel (see LLPanelLogin)
	}

	//RN: if we received a URL, hand it off to the existing instance.
	// don't call anotherInstanceRunning() when doing URL handoff, as
	// it relies on checking a marker file which will not work when running
	// out of different directories

Gilbert Gonzales's avatar
Gilbert Gonzales committed
	if (start_slurl.isValid() &&
		(gSavedSettings.getBOOL("SLURLPassToOtherInstance")))
	{
Gilbert Gonzales's avatar
Gilbert Gonzales committed
		if (sendURLToOtherInstance(start_slurl.getSLURLString()))
			// successfully handed off URL to existing instance, exit
			return false;
		}
	// Display splash screen.  Must be after above check for previous
	// crash as this dialog is always frontmost.
	std::string splash_msg;
	LLStringUtil::format_map_t args;
	args["[APP_NAME]"] = LLTrans::getString("SECOND_LIFE");
	splash_msg = LLTrans::getString("StartupLoading", args);
	//LLVolumeMgr::initClass();
	LLVolumeMgr* volume_manager = new LLVolumeMgr();
	volume_manager->useMutex();	// LLApp and LLMutex magic must be manually enabled
	LLPrimitive::setVolumeManager(volume_manager);
	// Note: this is where we used to initialize gFeatureManagerp.
Rye Mutt's avatar
Rye Mutt committed
	if (LLVersionInfo::instance().getViewerMaturity() != LLVersionInfo::RELEASE_VIEWER)
Rye Mutt's avatar
Rye Mutt committed
		gWindowTitle = LLVersionInfo::instance().getChannelAndVersion();
	}
	else
	{
		gWindowTitle = LLTrans::getString("APP_NAME");
	}
	gWindowTitle += std::string(" [DEBUG]");
	gWindowTitle += std::string(" ") + gArgs;
	LLStringUtil::truncate(gWindowTitle, 255);
	//
	// Check for another instance of the app running
	// This happens AFTER LLSplashScreen::show(). That may or may not be
	// important.
	//
	if (mSecondInstance && !gSavedSettings.getBOOL("AllowMultipleViewers"))
		OSMessageBox(
			LLTrans::getString("MBAlreadyRunning"),
			LLStringUtil::null,
			OSMB_OK);
		return false;
	}
	if (mSecondInstance)
		// This is the second instance of SL. Turn off voice support,
		// but make sure the setting is *not* persisted.
		LLControlVariable* disable_voice = gSavedSettings.getControl("CmdLineDisableVoice");
		if(disable_voice)
			const BOOL DO_NOT_PERSIST = FALSE;
			disable_voice->setValue(LLSD(TRUE), DO_NOT_PERSIST);
	gLastRunVersion = gSavedSettings.getString("LastRunVersion");
	// Let anyone else who cares know that we've populated our settings
	// variables.
	for (const auto& key : LLControlGroup::key_snapshot())
	{
		// For each named instance of LLControlGroup, send an event saying
		// we've initialized an LLControlGroup instance by that name.
		LLEventPumps::instance().obtain("LLControlGroup").post(LLSDMap("init", key));
    if (LLControlVariable* pControl = gSavedSettings.getControl(RlvSettingNames::Main))
	{
		if ( (pControl->getValue().asBoolean()) && (pControl->hasUnsavedValue()) )
		{
			pControl->resetToDefault();
			pControl->setValue(false);

			std::ostringstream msg;
			msg << LLTrans::getString("RLVaToggleMessageLogin", LLSD().with("[STATE]", LLTrans::getString("RLVaToggleDisabled")));
			OSMessageBox(msg.str(), LLStringUtil::null, OSMB_OK);
		}
	}
// [/RLVa:KB]

	return true; // Config was successful.
// The following logic is replicated in initConfiguration() (to be able to get
// some initial strings before we've finished initializing enough to know the
// current language) and also in init() (to initialize for real). Somehow it
// keeps growing, necessitating a method all its own.
void LLAppViewer::initStrings()
{
	std::string strings_file = "strings.xml";
	std::string strings_path_full = gDirUtilp->findSkinnedFilenameBaseLang(LLDir::XUI, strings_file);
	if (strings_path_full.empty() || !LLFile::isfile(strings_path_full))
	{
		// initial check to make sure files are there failed
		gDirUtilp->dumpCurrentDirectories(LLError::LEVEL_WARN);
		LL_ERRS() << "Viewer failed to find localization and UI files. Please reinstall viewer from  https://secondlife.com/support/downloads/ and contact https://support.secondlife.com if issue persists after reinstall." << LL_ENDL;
	}
	LLTransUtil::parseStrings(strings_file, default_trans_args);
	LLTransUtil::parseLanguageStrings("language_settings.xml");

	// parseStrings() sets up the LLTrans substitution table. Add this one item.
	LLTrans::setDefaultArg("[sourceid]", gSavedSettings.getString("sourceid"));

	// Now that we've set "[sourceid]", have to go back through
	// default_trans_args and reinitialize all those other keys because some
	// of them, in turn, reference "[sourceid]".
	for (const std::string& key : default_trans_args)
	{
		std::string brackets(key), nobrackets(key);
		// Invalid to inspect key[0] if key is empty(). But then, the entire
		// body of this loop is pointless if key is empty().
		if (key.empty())
			continue;

		if (key[0] != '[')
		{
			// key was passed without brackets. That means that 'nobrackets'
			// is correct but 'brackets' is not.
			brackets = STRINGIZE('[' << brackets << ']');
		}
		else
		{
			// key was passed with brackets. That means that 'brackets' is
			// correct but 'nobrackets' is not. Erase the left bracket.
			nobrackets.erase(0, 1);
			std::string::size_type length(nobrackets.length());
			if (length && nobrackets[length - 1] == ']')
			{
				nobrackets.erase(length - 1);
			}
		}
		// Calling LLTrans::getString() is what embeds the other default
		// translation strings into this one.
		LLTrans::setDefaultArg(brackets, LLTrans::getString(nobrackets));
	}
}

//
// This function decides whether the client machine meets the minimum requirements to
// run in a maximized window, per the consensus of davep, boa and nyx on 3/30/2011.
//
bool LLAppViewer::meetsRequirementsForMaximizedStart()
{
	bool maximizedOk = (LLFeatureManager::getInstance()->getGPUClass() >= GPU_CLASS_2);

	maximizedOk &= (gSysMemory.getPhysicalMemoryKB() >= U32Gigabytes(1));
	LL_INFOS("AppInit") << "Initializing window..." << LL_ENDL;

	// store setting in a global for easy access and modification
	gHeadlessClient = gSavedSettings.getBOOL("HeadlessClient");
	BOOL ignorePixelDepth = gSavedSettings.getBOOL("IgnorePixelDepth");
	LLViewerWindow::Params window_params;
	window_params
		.title(gWindowTitle)
		.name(VIEWER_WINDOW_CLASSNAME)
		.x(gSavedSettings.getS32("WindowX"))
		.y(gSavedSettings.getS32("WindowY"))