/indra/newview/llfloaterabout.cpp
C++ | 502 lines | 363 code | 62 blank | 77 comment | 24 complexity | 9b4cacada8d2ab20ebf62c63fdc371be MD5 | raw file
Possible License(s): LGPL-2.1
1/** 2 * @file llfloaterabout.cpp 3 * @author James Cook 4 * @brief The about box from Help->About 5 * 6 * $LicenseInfo:firstyear=2001&license=viewerlgpl$ 7 * Second Life Viewer Source Code 8 * Copyright (C) 2010, Linden Research, Inc. 9 * 10 * This library is free software; you can redistribute it and/or 11 * modify it under the terms of the GNU Lesser General Public 12 * License as published by the Free Software Foundation; 13 * version 2.1 of the License only. 14 * 15 * This library is distributed in the hope that it will be useful, 16 * but WITHOUT ANY WARRANTY; without even the implied warranty of 17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 18 * Lesser General Public License for more details. 19 * 20 * You should have received a copy of the GNU Lesser General Public 21 * License along with this library; if not, write to the Free Software 22 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 23 * 24 * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA 25 * $/LicenseInfo$ 26 */ 27 28#include "llviewerprecompiledheaders.h" 29#include <iostream> 30#include <fstream> 31 32#include "llfloaterabout.h" 33 34// Viewer includes 35#include "llagent.h" 36#include "llappviewer.h" 37#include "llsecondlifeurls.h" 38#include "llvoiceclient.h" 39#include "lluictrlfactory.h" 40#include "llviewertexteditor.h" 41#include "llviewercontrol.h" 42#include "llviewerstats.h" 43#include "llviewerregion.h" 44#include "llversioninfo.h" 45#include "llweb.h" 46 47// Linden library includes 48#include "llaudioengine.h" 49#include "llbutton.h" 50#include "llcurl.h" 51#include "llglheaders.h" 52#include "llfloater.h" 53#include "llfloaterreg.h" 54#include "llimagej2c.h" 55#include "llsys.h" 56#include "lltrans.h" 57#include "lluri.h" 58#include "v3dmath.h" 59#include "llwindow.h" 60#include "stringize.h" 61#include "llsdutil_math.h" 62#include "lleventapi.h" 63 64#if LL_WINDOWS 65#include "lldxhardware.h" 66#endif 67 68extern LLMemoryInfo gSysMemory; 69extern U32 gPacketsIn; 70 71static std::string get_viewer_release_notes_url(); 72 73///---------------------------------------------------------------------------- 74/// Class LLServerReleaseNotesURLFetcher 75///---------------------------------------------------------------------------- 76class LLServerReleaseNotesURLFetcher : public LLHTTPClient::Responder 77{ 78 LOG_CLASS(LLServerReleaseNotesURLFetcher); 79public: 80 81 static void startFetch(); 82 /*virtual*/ void completedHeader(U32 status, const std::string& reason, const LLSD& content); 83 /*virtual*/ void completedRaw( 84 U32 status, 85 const std::string& reason, 86 const LLChannelDescriptors& channels, 87 const LLIOPipe::buffer_ptr_t& buffer); 88}; 89 90///---------------------------------------------------------------------------- 91/// Class LLFloaterAbout 92///---------------------------------------------------------------------------- 93class LLFloaterAbout 94 : public LLFloater 95{ 96 friend class LLFloaterReg; 97private: 98 LLFloaterAbout(const LLSD& key); 99 virtual ~LLFloaterAbout(); 100 101public: 102 /*virtual*/ BOOL postBuild(); 103 104 /// Obtain the data used to fill out the contents string. This is 105 /// separated so that we can programmatically access the same info. 106 static LLSD getInfo(); 107 void onClickCopyToClipboard(); 108 109 void updateServerReleaseNotesURL(const std::string& url); 110 111private: 112 void setSupportText(const std::string& server_release_notes_url); 113}; 114 115 116// Default constructor 117LLFloaterAbout::LLFloaterAbout(const LLSD& key) 118: LLFloater(key) 119{ 120 121} 122 123// Destroys the object 124LLFloaterAbout::~LLFloaterAbout() 125{ 126} 127 128BOOL LLFloaterAbout::postBuild() 129{ 130 center(); 131 LLViewerTextEditor *support_widget = 132 getChild<LLViewerTextEditor>("support_editor", true); 133 134 LLViewerTextEditor *linden_names_widget = 135 getChild<LLViewerTextEditor>("linden_names", true); 136 137 LLViewerTextEditor *contrib_names_widget = 138 getChild<LLViewerTextEditor>("contrib_names", true); 139 140 LLViewerTextEditor *trans_names_widget = 141 getChild<LLViewerTextEditor>("trans_names", true); 142 143 getChild<LLUICtrl>("copy_btn")->setCommitCallback( 144 boost::bind(&LLFloaterAbout::onClickCopyToClipboard, this)); 145 146 if (gAgent.getRegion()) 147 { 148 // start fetching server release notes URL 149 setSupportText(LLTrans::getString("RetrievingData")); 150 LLServerReleaseNotesURLFetcher::startFetch(); 151 } 152 else // not logged in 153 { 154 setSupportText(LLStringUtil::null); 155 } 156 157 support_widget->blockUndo(); 158 159 // Fix views 160 support_widget->setEnabled(FALSE); 161 support_widget->startOfDoc(); 162 163 // Get the names of Lindens, added by viewer_manifest.py at build time 164 std::string lindens_path = gDirUtilp->getExpandedFilename(LL_PATH_APP_SETTINGS,"lindens.txt"); 165 llifstream linden_file; 166 std::string lindens; 167 linden_file.open(lindens_path); /* Flawfinder: ignore */ 168 if (linden_file.is_open()) 169 { 170 std::getline(linden_file, lindens); // all names are on a single line 171 linden_file.close(); 172 linden_names_widget->setText(lindens); 173 } 174 else 175 { 176 LL_INFOS("AboutInit") << "Could not read lindens file at " << lindens_path << LL_ENDL; 177 } 178 linden_names_widget->setEnabled(FALSE); 179 linden_names_widget->startOfDoc(); 180 181 // Get the names of contributors, extracted from .../doc/contributions.txt by viewer_manifest.py at build time 182 std::string contributors_path = gDirUtilp->getExpandedFilename(LL_PATH_APP_SETTINGS,"contributors.txt"); 183 llifstream contrib_file; 184 std::string contributors; 185 contrib_file.open(contributors_path); /* Flawfinder: ignore */ 186 if (contrib_file.is_open()) 187 { 188 std::getline(contrib_file, contributors); // all names are on a single line 189 contrib_file.close(); 190 } 191 else 192 { 193 LL_WARNS("AboutInit") << "Could not read contributors file at " << contributors_path << LL_ENDL; 194 } 195 contrib_names_widget->setText(contributors); 196 contrib_names_widget->setEnabled(FALSE); 197 contrib_names_widget->startOfDoc(); 198 199 // Get the names of translators, extracted from .../doc/tranlations.txt by viewer_manifest.py at build time 200 std::string translators_path = gDirUtilp->getExpandedFilename(LL_PATH_APP_SETTINGS,"translators.txt"); 201 llifstream trans_file; 202 std::string translators; 203 trans_file.open(translators_path); /* Flawfinder: ignore */ 204 if (trans_file.is_open()) 205 { 206 std::getline(trans_file, translators); // all names are on a single line 207 trans_file.close(); 208 } 209 else 210 { 211 LL_WARNS("AboutInit") << "Could not read translators file at " << translators_path << LL_ENDL; 212 } 213 trans_names_widget->setText(translators); 214 trans_names_widget->setEnabled(FALSE); 215 trans_names_widget->startOfDoc(); 216 217 return TRUE; 218} 219 220// static 221LLSD LLFloaterAbout::getInfo() 222{ 223 // The point of having one method build an LLSD info block and the other 224 // construct the user-visible About string is to ensure that the same info 225 // is available to a getInfo() caller as to the user opening 226 // LLFloaterAbout. 227 LLSD info; 228 LLSD version; 229 version.append(LLVersionInfo::getMajor()); 230 version.append(LLVersionInfo::getMinor()); 231 version.append(LLVersionInfo::getPatch()); 232 version.append(LLVersionInfo::getBuild()); 233 info["VIEWER_VERSION"] = version; 234 info["VIEWER_VERSION_STR"] = LLVersionInfo::getVersion(); 235 info["BUILD_DATE"] = __DATE__; 236 info["BUILD_TIME"] = __TIME__; 237 info["CHANNEL"] = LLVersionInfo::getChannel(); 238 239 info["VIEWER_RELEASE_NOTES_URL"] = get_viewer_release_notes_url(); 240 241#if LL_MSVC 242 info["COMPILER"] = "MSVC"; 243 info["COMPILER_VERSION"] = _MSC_VER; 244#elif LL_GNUC 245 info["COMPILER"] = "GCC"; 246 info["COMPILER_VERSION"] = GCC_VERSION; 247#endif 248 249 // Position 250 LLViewerRegion* region = gAgent.getRegion(); 251 if (region) 252 { 253 const LLVector3d &pos = gAgent.getPositionGlobal(); 254 info["POSITION"] = ll_sd_from_vector3d(pos); 255 info["REGION"] = gAgent.getRegion()->getName(); 256 info["HOSTNAME"] = gAgent.getRegion()->getHost().getHostName(); 257 info["HOSTIP"] = gAgent.getRegion()->getHost().getString(); 258 info["SERVER_VERSION"] = gLastVersionChannel; 259 } 260 261 // CPU 262 info["CPU"] = gSysCPU.getCPUString(); 263 info["MEMORY_MB"] = LLSD::Integer(gSysMemory.getPhysicalMemoryKB() / 1024); 264 // Moved hack adjustment to Windows memory size into llsys.cpp 265 info["OS_VERSION"] = LLAppViewer::instance()->getOSInfo().getOSString(); 266 info["GRAPHICS_CARD_VENDOR"] = (const char*)(glGetString(GL_VENDOR)); 267 info["GRAPHICS_CARD"] = (const char*)(glGetString(GL_RENDERER)); 268 269#if LL_WINDOWS 270 LLSD driver_info = gDXHardware.getDisplayInfo(); 271 if (driver_info.has("DriverVersion")) 272 { 273 info["GRAPHICS_DRIVER_VERSION"] = driver_info["DriverVersion"]; 274 } 275#endif 276 277 info["OPENGL_VERSION"] = (const char*)(glGetString(GL_VERSION)); 278 info["LIBCURL_VERSION"] = LLCurl::getVersionString(); 279 info["J2C_VERSION"] = LLImageJ2C::getEngineInfo(); 280 bool want_fullname = true; 281 info["AUDIO_DRIVER_VERSION"] = gAudiop ? LLSD(gAudiop->getDriverName(want_fullname)) : LLSD(); 282 if(LLVoiceClient::getInstance()->voiceEnabled()) 283 { 284 LLVoiceVersionInfo version = LLVoiceClient::getInstance()->getVersion(); 285 std::ostringstream version_string; 286 version_string << version.serverType << " " << version.serverVersion << std::endl; 287 info["VOICE_VERSION"] = version_string.str(); 288 } 289 else 290 { 291 info["VOICE_VERSION"] = LLTrans::getString("NotConnected"); 292 } 293 294 // TODO: Implement media plugin version query 295 info["QT_WEBKIT_VERSION"] = "4.7.1 (version number hard-coded)"; 296 297 if (gPacketsIn > 0) 298 { 299 info["PACKETS_LOST"] = LLViewerStats::getInstance()->mPacketsLostStat.getCurrent(); 300 info["PACKETS_IN"] = F32(gPacketsIn); 301 info["PACKETS_PCT"] = 100.f*info["PACKETS_LOST"].asReal() / info["PACKETS_IN"].asReal(); 302 } 303 304 return info; 305} 306 307static std::string get_viewer_release_notes_url() 308{ 309 // return a URL to the release notes for this viewer, such as: 310 // http://wiki.secondlife.com/wiki/Release_Notes/Second Life Beta Viewer/2.1.0 311 std::string url = LLTrans::getString("RELEASE_NOTES_BASE_URL"); 312 if (! LLStringUtil::endsWith(url, "/")) 313 url += "/"; 314 url += LLVersionInfo::getChannel() + "/"; 315 url += LLVersionInfo::getShortVersion(); 316 return LLWeb::escapeURL(url); 317} 318 319class LLFloaterAboutListener: public LLEventAPI 320{ 321public: 322 LLFloaterAboutListener(): 323 LLEventAPI("LLFloaterAbout", 324 "LLFloaterAbout listener to retrieve About box info") 325 { 326 add("getInfo", 327 "Request an LLSD::Map containing information used to populate About box", 328 &LLFloaterAboutListener::getInfo, 329 LLSD().with("reply", LLSD())); 330 } 331 332private: 333 void getInfo(const LLSD& request) const 334 { 335 LLReqID reqid(request); 336 LLSD reply(LLFloaterAbout::getInfo()); 337 reqid.stamp(reply); 338 LLEventPumps::instance().obtain(request["reply"]).post(reply); 339 } 340}; 341 342static LLFloaterAboutListener floaterAboutListener; 343 344void LLFloaterAbout::onClickCopyToClipboard() 345{ 346 LLViewerTextEditor *support_widget = 347 getChild<LLViewerTextEditor>("support_editor", true); 348 support_widget->selectAll(); 349 support_widget->copy(); 350 support_widget->deselect(); 351} 352 353void LLFloaterAbout::updateServerReleaseNotesURL(const std::string& url) 354{ 355 setSupportText(url); 356} 357 358void LLFloaterAbout::setSupportText(const std::string& server_release_notes_url) 359{ 360#if LL_WINDOWS 361 getWindow()->incBusyCount(); 362 getWindow()->setCursor(UI_CURSOR_ARROW); 363#endif 364 LLSD info(getInfo()); 365#if LL_WINDOWS 366 getWindow()->decBusyCount(); 367 getWindow()->setCursor(UI_CURSOR_ARROW); 368#endif 369 370 if (LLStringUtil::startsWith(server_release_notes_url, "http")) // it's an URL 371 { 372 info["SERVER_RELEASE_NOTES_URL"] = "[" + LLWeb::escapeURL(server_release_notes_url) + " " + LLTrans::getString("ReleaseNotes") + "]"; 373 } 374 else 375 { 376 info["SERVER_RELEASE_NOTES_URL"] = server_release_notes_url; 377 } 378 379 LLViewerTextEditor *support_widget = 380 getChild<LLViewerTextEditor>("support_editor", true); 381 382 std::ostringstream support; 383 384 // Render the LLSD from getInfo() as a format_map_t 385 LLStringUtil::format_map_t args; 386 387 // allow the "Release Notes" URL label to be localized 388 args["ReleaseNotes"] = LLTrans::getString("ReleaseNotes"); 389 390 for (LLSD::map_const_iterator ii(info.beginMap()), iend(info.endMap()); 391 ii != iend; ++ii) 392 { 393 if (! ii->second.isArray()) 394 { 395 // Scalar value 396 if (ii->second.isUndefined()) 397 { 398 args[ii->first] = getString("none"); 399 } 400 else 401 { 402 // don't forget to render value asString() 403 args[ii->first] = ii->second.asString(); 404 } 405 } 406 else 407 { 408 // array value: build KEY_0, KEY_1 etc. entries 409 for (LLSD::Integer n(0), size(ii->second.size()); n < size; ++n) 410 { 411 args[STRINGIZE(ii->first << '_' << n)] = ii->second[n].asString(); 412 } 413 } 414 } 415 416 // Now build the various pieces 417 support << getString("AboutHeader", args); 418 if (info.has("REGION")) 419 { 420 support << "\n\n" << getString("AboutPosition", args); 421 } 422 support << "\n\n" << getString("AboutSystem", args); 423 support << "\n"; 424 if (info.has("GRAPHICS_DRIVER_VERSION")) 425 { 426 support << "\n" << getString("AboutDriver", args); 427 } 428 support << "\n" << getString("AboutLibs", args); 429 if (info.has("COMPILER")) 430 { 431 support << "\n" << getString("AboutCompiler", args); 432 } 433 if (info.has("PACKETS_IN")) 434 { 435 support << '\n' << getString("AboutTraffic", args); 436 } 437 438 support_widget->clear(); 439 support_widget->appendText(support.str(), 440 FALSE, 441 LLStyle::Params() 442 .color(LLUIColorTable::instance().getColor("TextFgReadOnlyColor"))); 443} 444 445///---------------------------------------------------------------------------- 446/// LLFloaterAboutUtil 447///---------------------------------------------------------------------------- 448void LLFloaterAboutUtil::registerFloater() 449{ 450 LLFloaterReg::add("sl_about", "floater_about.xml", 451 &LLFloaterReg::build<LLFloaterAbout>); 452 453} 454 455///---------------------------------------------------------------------------- 456/// Class LLServerReleaseNotesURLFetcher implementation 457///---------------------------------------------------------------------------- 458// static 459void LLServerReleaseNotesURLFetcher::startFetch() 460{ 461 LLViewerRegion* region = gAgent.getRegion(); 462 if (!region) return; 463 464 // We cannot display the URL returned by the ServerReleaseNotes capability 465 // because opening it in an external browser will trigger a warning about untrusted 466 // SSL certificate. 467 // So we query the URL ourselves, expecting to find 468 // an URL suitable for external browsers in the "Location:" HTTP header. 469 std::string cap_url = region->getCapability("ServerReleaseNotes"); 470 LLHTTPClient::get(cap_url, new LLServerReleaseNotesURLFetcher); 471} 472 473// virtual 474void LLServerReleaseNotesURLFetcher::completedHeader(U32 status, const std::string& reason, const LLSD& content) 475{ 476 lldebugs << "Status: " << status << llendl; 477 lldebugs << "Reason: " << reason << llendl; 478 lldebugs << "Headers: " << content << llendl; 479 480 LLFloaterAbout* floater_about = LLFloaterReg::getTypedInstance<LLFloaterAbout>("sl_about"); 481 if (floater_about) 482 { 483 std::string location = content["location"].asString(); 484 if (location.empty()) 485 { 486 location = floater_about->getString("ErrorFetchingServerReleaseNotesURL"); 487 } 488 floater_about->updateServerReleaseNotesURL(location); 489 } 490} 491 492// virtual 493void LLServerReleaseNotesURLFetcher::completedRaw( 494 U32 status, 495 const std::string& reason, 496 const LLChannelDescriptors& channels, 497 const LLIOPipe::buffer_ptr_t& buffer) 498{ 499 // Do nothing. 500 // We're overriding just because the base implementation tries to 501 // deserialize LLSD which triggers warnings. 502}