llappviewermacosx.cpp
上传用户:king477883
上传日期:2021-03-01
资源大小:9553k
文件大小:17k
源码类别:

游戏引擎

开发平台:

C++ Builder

  1. /**
  2.  * @file llappviewermacosx.cpp
  3.  * @brief The LLAppViewerMacOSX class definitions
  4.  *
  5.  * $LicenseInfo:firstyear=2007&license=viewergpl$
  6.  * 
  7.  * Copyright (c) 2007-2010, 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://secondlifegrid.net/programs/open_source/licensing/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
  21.  * http://secondlifegrid.net/programs/open_source/licensing/flossexception
  22.  * 
  23.  * By copying, modifying or distributing this software, you acknowledge
  24.  * that you have read and understood your obligations described above,
  25.  * and agree to abide by those obligations.
  26.  * 
  27.  * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
  28.  * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
  29.  * COMPLETENESS OR PERFORMANCE.
  30.  * $/LicenseInfo$
  31.  */ 
  32. #include "llviewerprecompiledheaders.h"
  33. #if !defined LL_DARWIN
  34. #error "Use only with Mac OS X"
  35. #endif
  36. #include "llappviewermacosx.h"
  37. #include "llcommandlineparser.h"
  38. #include "llmemtype.h"
  39. #include "llviewernetwork.h"
  40. #include "llviewercontrol.h"
  41. #include "llmd5.h"
  42. #include "llurlsimstring.h"
  43. #include "llfloaterworldmap.h"
  44. #include "llurldispatcher.h"
  45. #include <Carbon/Carbon.h>
  46. #include "lldir.h"
  47. #include <signal.h>
  48. #include <CoreAudio/CoreAudio.h> // for systemwide mute
  49. class LLMediaCtrl; // for LLURLDispatcher
  50. namespace 
  51. {
  52. // The command line args stored.
  53. // They are not used immediately by the app.
  54. int gArgC;
  55. char** gArgV;
  56. bool sCrashReporterIsRunning = false;
  57. OSErr AEQuitHandler(const AppleEvent *messagein, AppleEvent *reply, long refIn)
  58. {
  59. OSErr result = noErr;
  60. LLAppViewer::instance()->userQuit();
  61. return(result);
  62. }
  63. }
  64. int main( int argc, char **argv ) 
  65. {
  66. LLMemType mt1(LLMemType::MTYPE_STARTUP);
  67. #if LL_SOLARIS && defined(__sparc)
  68. asm ("tat6");  // NOTE:  Make sure memory alignment is enforced on SPARC
  69. #endif
  70. // Set the working dir to <bundle>/Contents/Resources
  71. if (chdir(gDirUtilp->getAppRODataDir().c_str()) == -1)
  72. {
  73. llwarns << "Could not change directory to "
  74. << gDirUtilp->getAppRODataDir() << ": " << strerror(errno)
  75. << llendl;
  76. }
  77. LLAppViewerMacOSX* viewer_app_ptr = new LLAppViewerMacOSX();
  78. viewer_app_ptr->setErrorHandler(LLAppViewer::handleViewerCrash);
  79. // Store off the command line args for use later.
  80. gArgC = argc;
  81. gArgV = argv;
  82. bool ok = viewer_app_ptr->init();
  83. if(!ok)
  84. {
  85. llwarns << "Application init failed." << llendl;
  86. return -1;
  87. }
  88. // Run the application main loop
  89. if(!LLApp::isQuitting()) 
  90. {
  91. viewer_app_ptr->mainLoop();
  92. }
  93. if (!LLApp::isError())
  94. {
  95. //
  96. // We don't want to do cleanup here if the error handler got called -
  97. // the assumption is that the error handler is responsible for doing
  98. // app cleanup if there was a problem.
  99. //
  100. viewer_app_ptr->cleanup();
  101. }
  102. delete viewer_app_ptr;
  103. viewer_app_ptr = NULL;
  104. return 0;
  105. }
  106. LLAppViewerMacOSX::LLAppViewerMacOSX()
  107. {
  108. }
  109. LLAppViewerMacOSX::~LLAppViewerMacOSX()
  110. {
  111. }
  112. bool LLAppViewerMacOSX::init()
  113. {
  114. return LLAppViewer::init();
  115. }
  116. // MacOSX may add and addition command line arguement for the process serial number.
  117. // The option takes a form like '-psn_0_12345'. The following method should be able to recognize
  118. // and either ignore or return a pair of values for the option.
  119. // look for this method to be added to the parser in parseAndStoreResults.
  120. std::pair<std::string, std::string> parse_psn(const std::string& s)
  121. {
  122.     if (s.find("-psn_") == 0) 
  123. {
  124. // *FIX:Mani Not sure that the value makes sense.
  125. // fix it once the actual -psn_XXX syntax is known.
  126. return std::make_pair("psn", s.substr(5));
  127.     }
  128. else 
  129. {
  130.         return std::make_pair(std::string(), std::string());
  131.     }
  132. }
  133. bool LLAppViewerMacOSX::initParseCommandLine(LLCommandLineParser& clp)
  134. {
  135. // The next two lines add the support for parsing the mac -psn_XXX arg.
  136. clp.addOptionDesc("psn", NULL, 1, "MacOSX process serial number");
  137. clp.setCustomParser(parse_psn);
  138.     // First read in the args from arguments txt.
  139.     const char* filename = "arguments.txt";
  140. llifstream ifs(filename, llifstream::binary);
  141. if (!ifs.is_open())
  142. {
  143. llwarns << "Unable to open file" << filename << llendl;
  144. return false;
  145. }
  146. if(clp.parseCommandLineFile(ifs) == false)
  147. {
  148. return false;
  149. }
  150. // Then parse the user's command line, so that any --url arg can appear last
  151. // Succesive calls to clp.parse... will NOT override earlier options. 
  152. if(clp.parseCommandLine(gArgC, gArgV) == false)
  153. {
  154. return false;
  155. }
  156.     
  157. // Get the user's preferred language string based on the Mac OS localization mechanism.
  158. // To add a new localization:
  159. // go to the "Resources" section of the project
  160. // get info on "language.txt"
  161. // in the "General" tab, click the "Add Localization" button
  162. // create a new localization for the language you're adding
  163. // set the contents of the new localization of the file to the string corresponding to our localization
  164. //   (i.e. "en", "ja", etc.  Use the existing ones as a guide.)
  165. CFURLRef url = CFBundleCopyResourceURL(CFBundleGetMainBundle(), CFSTR("language"), CFSTR("txt"), NULL);
  166. char path[MAX_PATH];
  167. if(CFURLGetFileSystemRepresentation(url, false, (UInt8 *)path, sizeof(path)))
  168. {
  169. std::string lang;
  170. if(_read_file_into_string(lang, path)) /* Flawfinder: ignore*/
  171. {
  172.             LLControlVariable* c = gSavedSettings.getControl("SystemLanguage");
  173.             if(c)
  174.             {
  175.                 c->setValue(lang, false);
  176.             }
  177. }
  178. }
  179. CFRelease(url);
  180.     return true;
  181. }
  182. // *FIX:Mani It would be nice to provide a clean interface to get the
  183. // default_unix_signal_handler for the LLApp class.
  184. extern void default_unix_signal_handler(int, siginfo_t *, void *);
  185. bool LLAppViewerMacOSX::restoreErrorTrap()
  186. {
  187. // This method intends to reinstate signal handlers.
  188. // *NOTE:Mani It was found that the first execution of a shader was overriding
  189. // our initial signal handlers somehow.
  190. // This method will be called (at least) once per mainloop execution.
  191. // *NOTE:Mani The signals used below are copied over from the 
  192. // setup_signals() func in LLApp.cpp
  193. // LLApp could use some way of overriding that func, but for this viewer
  194. // fix I opt to avoid affecting the server code.
  195. // Set up signal handlers that may result in program termination
  196. //
  197. struct sigaction act;
  198. struct sigaction old_act;
  199. act.sa_sigaction = default_unix_signal_handler;
  200. sigemptyset( &act.sa_mask );
  201. act.sa_flags = SA_SIGINFO;
  202. unsigned int reset_count = 0;
  203. #define SET_SIG(S)  sigaction(SIGABRT, &act, &old_act); 
  204. if((unsigned int)act.sa_sigaction != (unsigned int) old_act.sa_sigaction) 
  205. ++reset_count;
  206. // Synchronous signals
  207. SET_SIG(SIGABRT)
  208. SET_SIG(SIGALRM)
  209. SET_SIG(SIGBUS)
  210. SET_SIG(SIGFPE)
  211. SET_SIG(SIGHUP) 
  212. SET_SIG(SIGILL)
  213. SET_SIG(SIGPIPE)
  214. SET_SIG(SIGSEGV)
  215. SET_SIG(SIGSYS)
  216. SET_SIG(LL_HEARTBEAT_SIGNAL)
  217. SET_SIG(LL_SMACKDOWN_SIGNAL)
  218. // Asynchronous signals that are normally ignored
  219. SET_SIG(SIGCHLD)
  220. SET_SIG(SIGUSR2)
  221. // Asynchronous signals that result in attempted graceful exit
  222. SET_SIG(SIGHUP)
  223. SET_SIG(SIGTERM)
  224. SET_SIG(SIGINT)
  225. // Asynchronous signals that result in core
  226. SET_SIG(SIGQUIT)
  227. #undef SET_SIG
  228. return reset_count == 0;
  229. }
  230. void LLAppViewerMacOSX::handleSyncCrashTrace()
  231. {
  232. // do nothing
  233. }
  234. static OSStatus CarbonEventHandler(EventHandlerCallRef inHandlerCallRef, 
  235.    EventRef inEvent, 
  236.    void* inUserData)
  237. {
  238.     ProcessSerialNumber psn;
  239.     GetEventParameter(inEvent, 
  240.   kEventParamProcessID, 
  241.   typeProcessSerialNumber, 
  242.   NULL, 
  243.   sizeof(psn), 
  244.   NULL, 
  245.   &psn);
  246.     if( GetEventKind(inEvent) == kEventAppTerminated ) 
  247. {
  248. Boolean matching_psn = FALSE;
  249. OSErr os_result = SameProcess(&psn, (ProcessSerialNumber*)inUserData, &matching_psn);
  250. if(os_result >= 0 && matching_psn)
  251. {
  252. sCrashReporterIsRunning = false;
  253. }
  254.     }
  255.     return noErr;
  256. }
  257. void LLAppViewerMacOSX::handleCrashReporting(bool reportFreeze)
  258. {
  259. // This used to use fork&exec, but is switched to LSOpenApplication to 
  260. // Make sure the crash reporter launches in front of the SL window.
  261. std::string command_str;
  262. //command_str = "open Second Life.app/Contents/Resources/mac-crash-logger.app";
  263. command_str = "mac-crash-logger.app/Contents/MacOS/mac-crash-logger";
  264. FSRef appRef;
  265. Boolean isDir = 0;
  266. OSStatus os_result = FSPathMakeRef((UInt8*)command_str.c_str(),
  267.    &appRef,
  268.    &isDir);
  269. if(os_result >= 0)
  270. {
  271. LSApplicationParameters appParams;
  272. memset(&appParams, 0, sizeof(appParams));
  273.   appParams.version = 0;
  274. appParams.flags = kLSLaunchNoParams | kLSLaunchStartClassic;
  275. appParams.application = &appRef;
  276. if(reportFreeze)
  277. {
  278. // Make sure freeze reporting launches the crash logger synchronously, lest 
  279. // Log files get changed by SL while the logger is running.
  280. // *NOTE:Mani A better way - make a copy of the data that the crash reporter will send
  281. // and let SL go about its business. This way makes the mac work like windows and linux
  282. // and is the smallest patch for the issue. 
  283. sCrashReporterIsRunning = true;
  284. ProcessSerialNumber o_psn;
  285. static EventHandlerRef sCarbonEventsRef = NULL;
  286. static const EventTypeSpec kEvents[] = 
  287. {
  288. { kEventClassApplication, kEventAppTerminated }
  289. };
  290. // Install the handler to detect crash logger termination
  291. InstallEventHandler(GetApplicationEventTarget(), 
  292. (EventHandlerUPP) CarbonEventHandler,
  293. GetEventTypeCount(kEvents),
  294. kEvents,
  295. &o_psn,
  296. &sCarbonEventsRef
  297. );
  298. // Remove, temporarily the quit handler - which has *crash* behavior before 
  299. // the mainloop gets running!
  300. AERemoveEventHandler(kCoreEventClass, 
  301.  kAEQuitApplication, 
  302.  NewAEEventHandlerUPP(AEQuitHandler),
  303.  false);
  304. // Launch the crash reporter.
  305. os_result = LSOpenApplication(&appParams, &o_psn);
  306. if(os_result >= 0)
  307. {
  308. EventRecord evt;
  309. while(sCrashReporterIsRunning)
  310. {
  311. while(WaitNextEvent(osMask, &evt, 0, NULL))
  312. {
  313. // null op!?!
  314. }
  315. }
  316. }
  317. // Re-install the apps quit handler.
  318. AEInstallEventHandler(kCoreEventClass, 
  319.   kAEQuitApplication, 
  320.   NewAEEventHandlerUPP(AEQuitHandler),
  321.   0, 
  322.   false);
  323. // Remove the crash reporter quit handler.
  324. RemoveEventHandler(sCarbonEventsRef);
  325. }
  326. else
  327. {
  328. appParams.flags |= kLSLaunchAsync;
  329. clear_signals();
  330. ProcessSerialNumber o_psn;
  331. os_result = LSOpenApplication(&appParams, &o_psn);
  332. }
  333. }
  334. if(!reportFreeze)
  335. {
  336. _exit(1);
  337. }
  338. // TODO:palmer REMOVE THIS VERY SOON.  THIS WILL NOT BE IN VIEWER 2.0
  339. // Remove the crash stack log from previous executions.
  340. // Since we've started logging a new instance of the app, we can assume 
  341. // The old crash stack is invalid for the next crash report.
  342. char path[MAX_PATH];
  343. FSRef folder;
  344. if(FSFindFolder(kUserDomain, kLogsFolderType, false, &folder) == noErr)
  345. {
  346. // folder is an FSRef to ~/Library/Logs/
  347. if(FSRefMakePath(&folder, (UInt8*)&path, sizeof(path)) == noErr)
  348. {
  349. std::string pathname = std::string(path) + std::string("/CrashReporter/");
  350. std::string mask = "Second Life*";
  351. std::string file_name;
  352. while(gDirUtilp->getNextFileInDir(pathname, mask, file_name, false))
  353. {
  354. LLFile::remove(pathname + file_name);
  355. }
  356. }
  357. }
  358. }
  359. std::string LLAppViewerMacOSX::generateSerialNumber()
  360. {
  361. char serial_md5[MD5HEX_STR_SIZE]; // Flawfinder: ignore
  362. serial_md5[0] = 0;
  363. // JC: Sample code from http://developer.apple.com/technotes/tn/tn1103.html
  364. CFStringRef serialNumber = NULL;
  365. io_service_t    platformExpert = IOServiceGetMatchingService(kIOMasterPortDefault,
  366.  IOServiceMatching("IOPlatformExpertDevice"));
  367. if (platformExpert) {
  368. serialNumber = (CFStringRef) IORegistryEntryCreateCFProperty(platformExpert,
  369.  CFSTR(kIOPlatformSerialNumberKey),
  370.  kCFAllocatorDefault, 0);
  371. IOObjectRelease(platformExpert);
  372. }
  373. if (serialNumber)
  374. {
  375. char buffer[MAX_STRING]; // Flawfinder: ignore
  376. if (CFStringGetCString(serialNumber, buffer, MAX_STRING, kCFStringEncodingASCII))
  377. {
  378. LLMD5 md5( (unsigned char*)buffer );
  379. md5.hex_digest(serial_md5);
  380. }
  381. CFRelease(serialNumber);
  382. }
  383. return serial_md5;
  384. }
  385. static AudioDeviceID get_default_audio_output_device(void)
  386. {
  387. AudioDeviceID device = 0;
  388. UInt32 size;
  389. OSStatus err;
  390. size = sizeof(device);
  391. err = AudioHardwareGetProperty(kAudioHardwarePropertyDefaultOutputDevice, &size, &device);
  392. if(err != noErr)
  393. {
  394. LL_DEBUGS("SystemMute") << "Couldn't get default audio output device (0x" << std::hex << err << ")" << LL_ENDL;
  395. }
  396. return device;
  397. }
  398. //virtual
  399. void LLAppViewerMacOSX::setMasterSystemAudioMute(bool new_mute)
  400. {
  401. AudioDeviceID device = get_default_audio_output_device();
  402. if(device != 0)
  403. {
  404. UInt32 mute = new_mute;
  405. OSStatus err = AudioDeviceSetProperty(device, NULL, 0, false, kAudioDevicePropertyMute, sizeof(mute), &mute);
  406. if(err != noErr)
  407. {
  408. LL_INFOS("SystemMute") << "Couldn't set audio mute property (0x" << std::hex << err << ")" << LL_ENDL;
  409. }
  410. }
  411. }
  412. //virtual
  413. bool LLAppViewerMacOSX::getMasterSystemAudioMute()
  414. {
  415. // Assume the system isn't muted 
  416. UInt32 mute = 0;
  417. AudioDeviceID device = get_default_audio_output_device();
  418. if(device != 0)
  419. {
  420. UInt32 size = sizeof(mute);
  421. OSStatus err = AudioDeviceGetProperty(device, 0, false, kAudioDevicePropertyMute, &size, &mute);
  422. if(err != noErr)
  423. {
  424. LL_DEBUGS("SystemMute") << "Couldn't get audio mute property (0x" << std::hex << err << ")" << LL_ENDL;
  425. }
  426. }
  427. return (mute != 0);
  428. }
  429. OSErr AEGURLHandler(const AppleEvent *messagein, AppleEvent *reply, long refIn)
  430. {
  431. OSErr result = noErr;
  432. DescType actualType;
  433. char buffer[1024]; // Flawfinder: ignore
  434. Size size;
  435. result = AEGetParamPtr (
  436. messagein,
  437. keyDirectObject,
  438. typeCString,
  439. &actualType,
  440. (Ptr)buffer,
  441. sizeof(buffer),
  442. &size);
  443. if(result == noErr)
  444. {
  445. std::string url = buffer;
  446. // Safari 3.2 silently mangles secondlife:///app/ URLs into
  447. // secondlife:/app/ (only one leading slash).
  448. // Fix them up to meet the URL specification. JC
  449. const std::string prefix = "secondlife:/app/";
  450. std::string test_prefix = url.substr(0, prefix.length());
  451. LLStringUtil::toLower(test_prefix);
  452. if (test_prefix == prefix)
  453. {
  454. url.replace(0, prefix.length(), "secondlife:///app/");
  455. }
  456. LLMediaCtrl* web = NULL;
  457. const bool trusted_browser = false;
  458. LLURLDispatcher::dispatch(url, web, trusted_browser);
  459. }
  460. return(result);
  461. }
  462. OSStatus simpleDialogHandler(EventHandlerCallRef handler, EventRef event, void *userdata)
  463. {
  464. OSStatus result = eventNotHandledErr;
  465. OSStatus err;
  466. UInt32 evtClass = GetEventClass(event);
  467. UInt32 evtKind = GetEventKind(event);
  468. WindowRef window = (WindowRef)userdata;
  469. if((evtClass == kEventClassCommand) && (evtKind == kEventCommandProcess))
  470. {
  471. HICommand cmd;
  472. err = GetEventParameter(event, kEventParamDirectObject, typeHICommand, NULL, sizeof(cmd), NULL, &cmd);
  473. if(err == noErr)
  474. {
  475. switch(cmd.commandID)
  476. {
  477. case kHICommandOK:
  478. QuitAppModalLoopForWindow(window);
  479. result = noErr;
  480. break;
  481. case kHICommandCancel:
  482. QuitAppModalLoopForWindow(window);
  483. result = userCanceledErr;
  484. break;
  485. }
  486. }
  487. }
  488. return(result);
  489. }
  490. void init_apple_menu(const char* product)
  491. {
  492. // Load up a proper menu bar.
  493. {
  494. OSStatus err;
  495. IBNibRef nib = NULL;
  496. // NOTE: DO NOT translate or brand this string.  It's an internal name in the .nib file, and MUST match exactly.
  497. err = CreateNibReference(CFSTR("SecondLife"), &nib);
  498. if(err == noErr)
  499. {
  500. // NOTE: DO NOT translate or brand this string.  It's an internal name in the .nib file, and MUST match exactly.
  501. SetMenuBarFromNib(nib, CFSTR("MenuBar"));
  502. }
  503. if(nib != NULL)
  504. {
  505. DisposeNibReference(nib);
  506. }
  507. }
  508. // Install a handler for 'gurl' AppleEvents.  This is how secondlife:// URLs get passed to the viewer.
  509. if(AEInstallEventHandler('GURL', 'GURL', NewAEEventHandlerUPP(AEGURLHandler),0, false) != noErr)
  510. {
  511. // Couldn't install AppleEvent handler.  This error shouldn't be fatal.
  512. llinfos << "Couldn't install 'GURL' AppleEvent handler.  Continuing..." << llendl;
  513. }
  514. // Install a handler for 'quit' AppleEvents.  This makes quitting the application from the dock work.
  515. if(AEInstallEventHandler(kCoreEventClass, kAEQuitApplication, NewAEEventHandlerUPP(AEQuitHandler),0, false) != noErr)
  516. {
  517. // Couldn't install AppleEvent handler.  This error shouldn't be fatal.
  518. llinfos << "Couldn't install Quit AppleEvent handler.  Continuing..." << llendl;
  519. }
  520. }