1 /* 2 * Copyright 2001-2015 Haiku, inc. All rights reserved. 3 * Distributed under the terms of the MIT License. 4 * 5 * Authors: 6 * Axel Dörfler, axeld@pinc-software.de 7 * Jerome Duval 8 * Erik Jaesler, erik@cgsoftware.com 9 */ 10 11 12 #include <Application.h> 13 14 #include <new> 15 #include <pthread.h> 16 #include <stdio.h> 17 #include <stdlib.h> 18 #include <string.h> 19 #include <strings.h> 20 #include <unistd.h> 21 22 #include <Alert.h> 23 #include <AppFileInfo.h> 24 #include <Cursor.h> 25 #include <Debug.h> 26 #include <Entry.h> 27 #include <File.h> 28 #include <Locker.h> 29 #include <MessageRunner.h> 30 #include <ObjectList.h> 31 #include <Path.h> 32 #include <PropertyInfo.h> 33 #include <RegistrarDefs.h> 34 #include <Resources.h> 35 #include <Roster.h> 36 #include <Window.h> 37 38 #include <AppMisc.h> 39 #include <AppServerLink.h> 40 #include <AutoLocker.h> 41 #include <BitmapPrivate.h> 42 #include <DraggerPrivate.h> 43 #include <LaunchDaemonDefs.h> 44 #include <LaunchRoster.h> 45 #include <LooperList.h> 46 #include <MenuWindow.h> 47 #include <PicturePrivate.h> 48 #include <PortLink.h> 49 #include <RosterPrivate.h> 50 #include <ServerMemoryAllocator.h> 51 #include <ServerProtocol.h> 52 53 54 using namespace BPrivate; 55 56 57 static const char* kDefaultLooperName = "AppLooperPort"; 58 59 BApplication* be_app = NULL; 60 BMessenger be_app_messenger; 61 62 pthread_once_t sAppResourcesInitOnce = PTHREAD_ONCE_INIT; 63 BResources* BApplication::sAppResources = NULL; 64 BObjectList<BLooper> sOnQuitLooperList; 65 66 67 enum { 68 kWindowByIndex, 69 kWindowByName, 70 kLooperByIndex, 71 kLooperByID, 72 kLooperByName, 73 kApplication 74 }; 75 76 77 static property_info sPropertyInfo[] = { 78 { 79 "Window", 80 {}, 81 {B_INDEX_SPECIFIER, B_REVERSE_INDEX_SPECIFIER}, 82 NULL, kWindowByIndex, 83 {}, 84 {}, 85 {} 86 }, 87 { 88 "Window", 89 {}, 90 {B_NAME_SPECIFIER}, 91 NULL, kWindowByName, 92 {}, 93 {}, 94 {} 95 }, 96 { 97 "Looper", 98 {}, 99 {B_INDEX_SPECIFIER, B_REVERSE_INDEX_SPECIFIER}, 100 NULL, kLooperByIndex, 101 {}, 102 {}, 103 {} 104 }, 105 { 106 "Looper", 107 {}, 108 {B_ID_SPECIFIER}, 109 NULL, kLooperByID, 110 {}, 111 {}, 112 {} 113 }, 114 { 115 "Looper", 116 {}, 117 {B_NAME_SPECIFIER}, 118 NULL, kLooperByName, 119 {}, 120 {}, 121 {} 122 }, 123 { 124 "Name", 125 {B_GET_PROPERTY}, 126 {B_DIRECT_SPECIFIER}, 127 NULL, kApplication, 128 {B_STRING_TYPE}, 129 {}, 130 {} 131 }, 132 { 133 "Window", 134 {B_COUNT_PROPERTIES}, 135 {B_DIRECT_SPECIFIER}, 136 NULL, kApplication, 137 {B_INT32_TYPE}, 138 {}, 139 {} 140 }, 141 { 142 "Loopers", 143 {B_GET_PROPERTY}, 144 {B_DIRECT_SPECIFIER}, 145 NULL, kApplication, 146 {B_MESSENGER_TYPE}, 147 {}, 148 {} 149 }, 150 { 151 "Windows", 152 {B_GET_PROPERTY}, 153 {B_DIRECT_SPECIFIER}, 154 NULL, kApplication, 155 {B_MESSENGER_TYPE}, 156 {}, 157 {} 158 }, 159 { 160 "Looper", 161 {B_COUNT_PROPERTIES}, 162 {B_DIRECT_SPECIFIER}, 163 NULL, kApplication, 164 {B_INT32_TYPE}, 165 {}, 166 {} 167 }, 168 169 { 0 } 170 }; 171 172 173 // argc/argv 174 extern const int __libc_argc; 175 extern const char* const *__libc_argv; 176 177 178 // debugging 179 //#define DBG(x) x 180 #define DBG(x) 181 #define OUT printf 182 183 184 // #pragma mark - static helper functions 185 186 187 /*! 188 \brief Checks whether the supplied string is a valid application signature. 189 190 An error message is printed, if the string is no valid app signature. 191 192 \param signature The string to be checked. 193 194 \return A status code. 195 \retval B_OK \a signature is a valid app signature. 196 \retval B_BAD_VALUE \a signature is \c NULL or no valid app signature. 197 */ 198 static status_t 199 check_app_signature(const char* signature) 200 { 201 bool isValid = false; 202 BMimeType type(signature); 203 204 if (type.IsValid() && !type.IsSupertypeOnly() 205 && BMimeType("application").Contains(&type)) { 206 isValid = true; 207 } 208 209 if (!isValid) { 210 printf("bad signature (%s), must begin with \"application/\" and " 211 "can't conflict with existing registered mime types inside " 212 "the \"application\" media type.\n", signature); 213 } 214 215 return (isValid ? B_OK : B_BAD_VALUE); 216 } 217 218 219 #ifndef RUN_WITHOUT_REGISTRAR 220 // Fills the passed BMessage with B_ARGV_RECEIVED infos. 221 static void 222 fill_argv_message(BMessage &message) 223 { 224 message.what = B_ARGV_RECEIVED; 225 226 int32 argc = __libc_argc; 227 const char* const *argv = __libc_argv; 228 229 // add argc 230 message.AddInt32("argc", argc); 231 232 // add argv 233 for (int32 i = 0; i < argc; i++) { 234 if (argv[i] != NULL) 235 message.AddString("argv", argv[i]); 236 } 237 238 // add current working directory 239 char cwd[B_PATH_NAME_LENGTH]; 240 if (getcwd(cwd, B_PATH_NAME_LENGTH)) 241 message.AddString("cwd", cwd); 242 } 243 #endif 244 245 246 // #pragma mark - BApplication 247 248 249 BApplication::BApplication(const char* signature) 250 : 251 BLooper(kDefaultLooperName) 252 { 253 _InitData(signature, true, NULL); 254 } 255 256 257 BApplication::BApplication(const char* signature, status_t* _error) 258 : 259 BLooper(kDefaultLooperName) 260 { 261 _InitData(signature, true, _error); 262 } 263 264 265 BApplication::BApplication(const char* signature, const char* looperName, 266 port_id port, bool initGUI, status_t* _error) 267 : 268 BLooper(B_NORMAL_PRIORITY + 1, port < 0 ? _GetPort(signature) : port, 269 looperName != NULL ? looperName : kDefaultLooperName) 270 { 271 _InitData(signature, initGUI, _error); 272 if (port < 0) 273 fOwnsPort = false; 274 } 275 276 277 BApplication::BApplication(BMessage* data) 278 // Note: BeOS calls the private BLooper(int32, port_id, const char*) 279 // constructor here, test if it's needed 280 : 281 BLooper(kDefaultLooperName) 282 { 283 const char* signature = NULL; 284 data->FindString("mime_sig", &signature); 285 286 _InitData(signature, true, NULL); 287 288 bigtime_t pulseRate; 289 if (data->FindInt64("_pulse", &pulseRate) == B_OK) 290 SetPulseRate(pulseRate); 291 } 292 293 294 BApplication::BApplication(uint32 signature) 295 { 296 } 297 298 299 BApplication::BApplication(const BApplication &rhs) 300 { 301 } 302 303 304 BApplication::~BApplication() 305 { 306 Lock(); 307 308 // tell all loopers(usually windows) to quit. Also, wait for them. 309 _QuitAllWindows(true); 310 311 // quit registered loopers 312 for (int32 i = 0; i < sOnQuitLooperList.CountItems(); i++) { 313 BLooper* looper = sOnQuitLooperList.ItemAt(i); 314 if (looper->Lock()) 315 looper->Quit(); 316 } 317 318 // unregister from the roster 319 BRoster::Private().RemoveApp(Team()); 320 321 #ifndef RUN_WITHOUT_APP_SERVER 322 // tell app_server we're quitting... 323 if (be_app) { 324 // be_app can be NULL here if the application fails to initialize 325 // correctly. For example, if it's already running and it's set to 326 // exclusive launch. 327 BPrivate::AppServerLink link; 328 link.StartMessage(B_QUIT_REQUESTED); 329 link.Flush(); 330 } 331 delete_port(fServerLink->SenderPort()); 332 delete_port(fServerLink->ReceiverPort()); 333 delete fServerLink; 334 #endif // RUN_WITHOUT_APP_SERVER 335 336 delete fServerAllocator; 337 338 // uninitialize be_app, the be_app_messenger is invalidated automatically 339 be_app = NULL; 340 } 341 342 343 BApplication& 344 BApplication::operator=(const BApplication &rhs) 345 { 346 return *this; 347 } 348 349 350 void 351 BApplication::_InitData(const char* signature, bool initGUI, status_t* _error) 352 { 353 DBG(OUT("BApplication::InitData(`%s', %p)\n", signature, _error)); 354 // check whether there exists already an application 355 if (be_app != NULL) 356 debugger("2 BApplication objects were created. Only one is allowed."); 357 358 fServerLink = new BPrivate::PortLink(-1, -1); 359 fServerAllocator = NULL; 360 fServerReadOnlyMemory = NULL; 361 fInitialWorkspace = 0; 362 //fDraggedMessage = NULL; 363 fReadyToRunCalled = false; 364 365 // initially, there is no pulse 366 fPulseRunner = NULL; 367 fPulseRate = 0; 368 369 // check signature 370 fInitError = check_app_signature(signature); 371 fAppName = signature; 372 373 #ifndef RUN_WITHOUT_REGISTRAR 374 bool registerApp = signature == NULL 375 || (strcasecmp(signature, B_REGISTRAR_SIGNATURE) != 0 376 && strcasecmp(signature, kLaunchDaemonSignature) != 0); 377 // get team and thread 378 team_id team = Team(); 379 thread_id thread = BPrivate::main_thread_for(team); 380 #endif 381 382 // get app executable ref 383 entry_ref ref; 384 if (fInitError == B_OK) { 385 fInitError = BPrivate::get_app_ref(&ref); 386 if (fInitError != B_OK) { 387 DBG(OUT("BApplication::InitData(): Failed to get app ref: %s\n", 388 strerror(fInitError))); 389 } 390 } 391 392 // get the BAppFileInfo and extract the information we need 393 uint32 appFlags = B_REG_DEFAULT_APP_FLAGS; 394 if (fInitError == B_OK) { 395 BAppFileInfo fileInfo; 396 BFile file(&ref, B_READ_ONLY); 397 fInitError = fileInfo.SetTo(&file); 398 if (fInitError == B_OK) { 399 fileInfo.GetAppFlags(&appFlags); 400 char appFileSignature[B_MIME_TYPE_LENGTH]; 401 // compare the file signature and the supplied signature 402 if (fileInfo.GetSignature(appFileSignature) == B_OK 403 && strcasecmp(appFileSignature, signature) != 0) { 404 printf("Signature in rsrc doesn't match constructor arg. (%s, %s)\n", 405 signature, appFileSignature); 406 } 407 } else { 408 DBG(OUT("BApplication::InitData(): Failed to get info from: " 409 "BAppFileInfo: %s\n", strerror(fInitError))); 410 } 411 } 412 413 #ifndef RUN_WITHOUT_REGISTRAR 414 // check whether be_roster is valid 415 if (fInitError == B_OK && registerApp 416 && !BRoster::Private().IsMessengerValid(false)) { 417 printf("FATAL: be_roster is not valid. Is the registrar running?\n"); 418 fInitError = B_NO_INIT; 419 } 420 421 // check whether or not we are pre-registered 422 bool preRegistered = false; 423 app_info appInfo; 424 if (fInitError == B_OK && registerApp) { 425 if (BRoster::Private().IsAppRegistered(&ref, team, 0, &preRegistered, 426 &appInfo) != B_OK) { 427 preRegistered = false; 428 } 429 } 430 if (preRegistered) { 431 // we are pre-registered => the app info has been filled in 432 // Check whether we need to replace the looper port with a port 433 // created by the roster. 434 if (appInfo.port >= 0 && appInfo.port != fMsgPort) { 435 delete_port(fMsgPort); 436 fMsgPort = appInfo.port; 437 } else 438 appInfo.port = fMsgPort; 439 // check the signature and correct it, if necessary, also the case 440 if (strcmp(appInfo.signature, fAppName)) 441 BRoster::Private().SetSignature(team, fAppName); 442 // complete the registration 443 fInitError = BRoster::Private().CompleteRegistration(team, thread, 444 appInfo.port); 445 } else if (fInitError == B_OK) { 446 // not pre-registered -- try to register the application 447 team_id otherTeam = -1; 448 if (registerApp) { 449 fInitError = BRoster::Private().AddApplication(signature, &ref, 450 appFlags, team, thread, fMsgPort, true, NULL, &otherTeam); 451 if (fInitError != B_OK) { 452 DBG(OUT("BApplication::InitData(): Failed to add app: %s\n", 453 strerror(fInitError))); 454 } 455 } 456 if (fInitError == B_ALREADY_RUNNING) { 457 // An instance is already running and we asked for 458 // single/exclusive launch. Send our argv to the running app. 459 // Do that only, if the app is NOT B_ARGV_ONLY. 460 if (otherTeam >= 0) { 461 BMessenger otherApp(NULL, otherTeam); 462 app_info otherAppInfo; 463 bool argvOnly = be_roster->GetRunningAppInfo(otherTeam, 464 &otherAppInfo) == B_OK 465 && (otherAppInfo.flags & B_ARGV_ONLY) != 0; 466 467 if (__libc_argc > 1 && !argvOnly) { 468 // create an B_ARGV_RECEIVED message 469 BMessage argvMessage(B_ARGV_RECEIVED); 470 fill_argv_message(argvMessage); 471 472 // replace the first argv string with the path of the 473 // other application 474 BPath path; 475 if (path.SetTo(&otherAppInfo.ref) == B_OK) 476 argvMessage.ReplaceString("argv", 0, path.Path()); 477 478 // send the message 479 otherApp.SendMessage(&argvMessage); 480 } else if (!argvOnly) 481 otherApp.SendMessage(B_SILENT_RELAUNCH); 482 } 483 } else if (fInitError == B_OK) { 484 // the registrations was successful 485 // Create a B_ARGV_RECEIVED message and send it to ourselves. 486 // Do that even, if we are B_ARGV_ONLY. 487 // TODO: When BLooper::AddMessage() is done, use that instead of 488 // PostMessage(). 489 490 DBG(OUT("info: BApplication successfully registered.\n")); 491 492 if (__libc_argc > 1) { 493 BMessage argvMessage(B_ARGV_RECEIVED); 494 fill_argv_message(argvMessage); 495 PostMessage(&argvMessage, this); 496 } 497 // send a B_READY_TO_RUN message as well 498 PostMessage(B_READY_TO_RUN, this); 499 } else if (fInitError > B_ERRORS_END) { 500 // Registrar internal errors shouldn't fall into the user's hands. 501 fInitError = B_ERROR; 502 } 503 } 504 #else 505 // We need to have ReadyToRun called even when we're not using the registrar 506 PostMessage(B_READY_TO_RUN, this); 507 #endif // ifndef RUN_WITHOUT_REGISTRAR 508 509 if (fInitError == B_OK) { 510 // TODO: Not completely sure about the order, but this should be close. 511 512 // init be_app and be_app_messenger 513 be_app = this; 514 be_app_messenger = BMessenger(NULL, this); 515 516 // set the BHandler's name 517 SetName(ref.name); 518 519 // create meta MIME 520 BPath path; 521 if (registerApp && path.SetTo(&ref) == B_OK) 522 create_app_meta_mime(path.Path(), false, true, false); 523 524 #ifndef RUN_WITHOUT_APP_SERVER 525 // app server connection and IK initialization 526 if (initGUI) 527 fInitError = _InitGUIContext(); 528 #endif // RUN_WITHOUT_APP_SERVER 529 } 530 531 // Return the error or exit, if there was an error and no error variable 532 // has been supplied. 533 if (_error != NULL) { 534 *_error = fInitError; 535 } else if (fInitError != B_OK) { 536 DBG(OUT("BApplication::InitData() failed: %s\n", strerror(fInitError))); 537 exit(0); 538 } 539 DBG(OUT("BApplication::InitData() done\n")); 540 } 541 542 543 port_id 544 BApplication::_GetPort(const char* signature) 545 { 546 return BLaunchRoster().GetPort(signature, NULL); 547 } 548 549 550 BArchivable* 551 BApplication::Instantiate(BMessage* data) 552 { 553 if (validate_instantiation(data, "BApplication")) 554 return new BApplication(data); 555 556 return NULL; 557 } 558 559 560 status_t 561 BApplication::Archive(BMessage* data, bool deep) const 562 { 563 status_t status = BLooper::Archive(data, deep); 564 if (status < B_OK) 565 return status; 566 567 app_info info; 568 status = GetAppInfo(&info); 569 if (status < B_OK) 570 return status; 571 572 status = data->AddString("mime_sig", info.signature); 573 if (status < B_OK) 574 return status; 575 576 return data->AddInt64("_pulse", fPulseRate); 577 } 578 579 580 status_t 581 BApplication::InitCheck() const 582 { 583 return fInitError; 584 } 585 586 587 thread_id 588 BApplication::Run() 589 { 590 if (fInitError != B_OK) 591 return fInitError; 592 593 Loop(); 594 595 delete fPulseRunner; 596 return fThread; 597 } 598 599 600 void 601 BApplication::Quit() 602 { 603 bool unlock = false; 604 if (!IsLocked()) { 605 const char* name = Name(); 606 if (name == NULL) 607 name = "no-name"; 608 609 printf("ERROR - you must Lock the application object before calling " 610 "Quit(), team=%" B_PRId32 ", looper=%s\n", Team(), name); 611 unlock = true; 612 if (!Lock()) 613 return; 614 } 615 // Delete the object, if not running only. 616 if (!fRunCalled) { 617 delete this; 618 } else if (find_thread(NULL) != fThread) { 619 // ToDo: why shouldn't we set fTerminating to true directly in this case? 620 // We are not the looper thread. 621 // We push a _QUIT_ into the queue. 622 // TODO: When BLooper::AddMessage() is done, use that instead of 623 // PostMessage()??? This would overtake messages that are still at 624 // the port. 625 // NOTE: We must not unlock here -- otherwise we had to re-lock, which 626 // may not work. This is bad, since, if the port is full, it 627 // won't get emptier, as the looper thread needs to lock the object 628 // before dispatching messages. 629 while (PostMessage(_QUIT_, this) == B_WOULD_BLOCK) 630 snooze(10000); 631 } else { 632 // We are the looper thread. 633 // Just set fTerminating to true which makes us fall through the 634 // message dispatching loop and return from Run(). 635 fTerminating = true; 636 } 637 638 // If we had to lock the object, unlock now. 639 if (unlock) 640 Unlock(); 641 } 642 643 644 bool 645 BApplication::QuitRequested() 646 { 647 return _QuitAllWindows(false); 648 } 649 650 651 void 652 BApplication::Pulse() 653 { 654 // supposed to be implemented by subclasses 655 } 656 657 658 void 659 BApplication::ReadyToRun() 660 { 661 // supposed to be implemented by subclasses 662 } 663 664 665 void 666 BApplication::MessageReceived(BMessage* message) 667 { 668 switch (message->what) { 669 case B_COUNT_PROPERTIES: 670 case B_GET_PROPERTY: 671 case B_SET_PROPERTY: 672 { 673 int32 index; 674 BMessage specifier; 675 int32 what; 676 const char* property = NULL; 677 if (message->GetCurrentSpecifier(&index, &specifier, &what, 678 &property) < B_OK 679 || !ScriptReceived(message, index, &specifier, what, 680 property)) { 681 BLooper::MessageReceived(message); 682 } 683 break; 684 } 685 686 case B_SILENT_RELAUNCH: 687 // Sent to a B_SINGLE_LAUNCH application when it's launched again 688 // (see _InitData()) 689 be_roster->ActivateApp(Team()); 690 break; 691 692 case kMsgAppServerRestarted: 693 _ReconnectToServer(); 694 break; 695 696 case kMsgDeleteServerMemoryArea: 697 { 698 int32 serverArea; 699 if (message->FindInt32("server area", &serverArea) == B_OK) { 700 // The link is not used, but we currently borrow its lock 701 BPrivate::AppServerLink link; 702 fServerAllocator->RemoveArea(serverArea); 703 } 704 break; 705 } 706 707 default: 708 BLooper::MessageReceived(message); 709 } 710 } 711 712 713 void 714 BApplication::ArgvReceived(int32 argc, char** argv) 715 { 716 // supposed to be implemented by subclasses 717 } 718 719 720 void 721 BApplication::AppActivated(bool active) 722 { 723 // supposed to be implemented by subclasses 724 } 725 726 727 void 728 BApplication::RefsReceived(BMessage* message) 729 { 730 // supposed to be implemented by subclasses 731 } 732 733 734 void 735 BApplication::AboutRequested() 736 { 737 // supposed to be implemented by subclasses 738 } 739 740 741 BHandler* 742 BApplication::ResolveSpecifier(BMessage* message, int32 index, 743 BMessage* specifier, int32 what, const char* property) 744 { 745 BPropertyInfo propInfo(sPropertyInfo); 746 status_t err = B_OK; 747 uint32 data; 748 749 if (propInfo.FindMatch(message, 0, specifier, what, property, &data) >= 0) { 750 switch (data) { 751 case kWindowByIndex: 752 { 753 int32 index; 754 err = specifier->FindInt32("index", &index); 755 if (err != B_OK) 756 break; 757 758 if (what == B_REVERSE_INDEX_SPECIFIER) 759 index = CountWindows() - index; 760 761 BWindow* window = WindowAt(index); 762 if (window != NULL) { 763 message->PopSpecifier(); 764 BMessenger(window).SendMessage(message); 765 } else 766 err = B_BAD_INDEX; 767 break; 768 } 769 770 case kWindowByName: 771 { 772 const char* name; 773 err = specifier->FindString("name", &name); 774 if (err != B_OK) 775 break; 776 777 for (int32 i = 0;; i++) { 778 BWindow* window = WindowAt(i); 779 if (window == NULL) { 780 err = B_NAME_NOT_FOUND; 781 break; 782 } 783 if (window->Title() != NULL && !strcmp(window->Title(), 784 name)) { 785 message->PopSpecifier(); 786 BMessenger(window).SendMessage(message); 787 break; 788 } 789 } 790 break; 791 } 792 793 case kLooperByIndex: 794 { 795 int32 index; 796 err = specifier->FindInt32("index", &index); 797 if (err != B_OK) 798 break; 799 800 if (what == B_REVERSE_INDEX_SPECIFIER) 801 index = CountLoopers() - index; 802 803 BLooper* looper = LooperAt(index); 804 if (looper != NULL) { 805 message->PopSpecifier(); 806 BMessenger(looper).SendMessage(message); 807 } else 808 err = B_BAD_INDEX; 809 810 break; 811 } 812 813 case kLooperByID: 814 // TODO: implement getting looper by ID! 815 break; 816 817 case kLooperByName: 818 { 819 const char* name; 820 err = specifier->FindString("name", &name); 821 if (err != B_OK) 822 break; 823 824 for (int32 i = 0;; i++) { 825 BLooper* looper = LooperAt(i); 826 if (looper == NULL) { 827 err = B_NAME_NOT_FOUND; 828 break; 829 } 830 if (looper->Name() != NULL 831 && strcmp(looper->Name(), name) == 0) { 832 message->PopSpecifier(); 833 BMessenger(looper).SendMessage(message); 834 break; 835 } 836 } 837 break; 838 } 839 840 case kApplication: 841 return this; 842 } 843 } else { 844 return BLooper::ResolveSpecifier(message, index, specifier, what, 845 property); 846 } 847 848 if (err != B_OK) { 849 BMessage reply(B_MESSAGE_NOT_UNDERSTOOD); 850 reply.AddInt32("error", err); 851 reply.AddString("message", strerror(err)); 852 message->SendReply(&reply); 853 } 854 855 return NULL; 856 857 } 858 859 860 void 861 BApplication::ShowCursor() 862 { 863 BPrivate::AppServerLink link; 864 link.StartMessage(AS_SHOW_CURSOR); 865 link.Flush(); 866 } 867 868 869 void 870 BApplication::HideCursor() 871 { 872 BPrivate::AppServerLink link; 873 link.StartMessage(AS_HIDE_CURSOR); 874 link.Flush(); 875 } 876 877 878 void 879 BApplication::ObscureCursor() 880 { 881 BPrivate::AppServerLink link; 882 link.StartMessage(AS_OBSCURE_CURSOR); 883 link.Flush(); 884 } 885 886 887 bool 888 BApplication::IsCursorHidden() const 889 { 890 BPrivate::AppServerLink link; 891 int32 status = B_ERROR; 892 link.StartMessage(AS_QUERY_CURSOR_HIDDEN); 893 link.FlushWithReply(status); 894 895 return status == B_OK; 896 } 897 898 899 void 900 BApplication::SetCursor(const void* cursorData) 901 { 902 BCursor cursor(cursorData); 903 SetCursor(&cursor, true); 904 // forces the cursor to be sync'ed 905 } 906 907 908 void 909 BApplication::SetCursor(const BCursor* cursor, bool sync) 910 { 911 BPrivate::AppServerLink link; 912 link.StartMessage(AS_SET_CURSOR); 913 link.Attach<bool>(sync); 914 link.Attach<int32>(cursor->fServerToken); 915 916 if (sync) { 917 int32 code; 918 link.FlushWithReply(code); 919 } else 920 link.Flush(); 921 } 922 923 924 int32 925 BApplication::CountWindows() const 926 { 927 return _CountWindows(false); 928 // we're ignoring menu windows 929 } 930 931 932 BWindow* 933 BApplication::WindowAt(int32 index) const 934 { 935 return _WindowAt(index, false); 936 // we're ignoring menu windows 937 } 938 939 940 int32 941 BApplication::CountLoopers() const 942 { 943 AutoLocker<BLooperList> ListLock(gLooperList); 944 if (ListLock.IsLocked()) 945 return gLooperList.CountLoopers(); 946 947 // Some bad, non-specific thing has happened 948 return B_ERROR; 949 } 950 951 952 BLooper* 953 BApplication::LooperAt(int32 index) const 954 { 955 BLooper* looper = NULL; 956 AutoLocker<BLooperList> listLock(gLooperList); 957 if (listLock.IsLocked()) 958 looper = gLooperList.LooperAt(index); 959 960 return looper; 961 } 962 963 964 status_t 965 BApplication::RegisterLooper(BLooper* looper) 966 { 967 BWindow* window = dynamic_cast<BWindow*>(looper); 968 if (window != NULL) 969 return B_BAD_VALUE; 970 971 if (sOnQuitLooperList.HasItem(looper)) 972 return B_ERROR; 973 974 if (sOnQuitLooperList.AddItem(looper) != true) 975 return B_ERROR; 976 977 return B_OK; 978 } 979 980 981 status_t 982 BApplication::UnregisterLooper(BLooper* looper) 983 { 984 BWindow* window = dynamic_cast<BWindow*>(looper); 985 if (window != NULL) 986 return B_BAD_VALUE; 987 988 if (!sOnQuitLooperList.HasItem(looper)) 989 return B_ERROR; 990 991 if (sOnQuitLooperList.RemoveItem(looper) != true) 992 return B_ERROR; 993 994 return B_OK; 995 } 996 997 998 bool 999 BApplication::IsLaunching() const 1000 { 1001 return !fReadyToRunCalled; 1002 } 1003 1004 1005 const char* 1006 BApplication::Signature() const 1007 { 1008 return fAppName; 1009 } 1010 1011 1012 status_t 1013 BApplication::GetAppInfo(app_info* info) const 1014 { 1015 if (be_app == NULL || be_roster == NULL) 1016 return B_NO_INIT; 1017 return be_roster->GetRunningAppInfo(be_app->Team(), info); 1018 } 1019 1020 1021 BResources* 1022 BApplication::AppResources() 1023 { 1024 if (sAppResources == NULL) 1025 pthread_once(&sAppResourcesInitOnce, &_InitAppResources); 1026 1027 return sAppResources; 1028 } 1029 1030 1031 void 1032 BApplication::DispatchMessage(BMessage* message, BHandler* handler) 1033 { 1034 if (handler != this) { 1035 // it's not ours to dispatch 1036 BLooper::DispatchMessage(message, handler); 1037 return; 1038 } 1039 1040 switch (message->what) { 1041 case B_ARGV_RECEIVED: 1042 _ArgvReceived(message); 1043 break; 1044 1045 case B_REFS_RECEIVED: 1046 { 1047 // this adds the refs that are part of this message to the recent 1048 // lists, but only folders and documents are handled here 1049 entry_ref ref; 1050 int32 i = 0; 1051 while (message->FindRef("refs", i++, &ref) == B_OK) { 1052 BEntry entry(&ref, true); 1053 if (entry.InitCheck() != B_OK) 1054 continue; 1055 1056 if (entry.IsDirectory()) 1057 BRoster().AddToRecentFolders(&ref); 1058 else { 1059 // filter out applications, we only want to have documents 1060 // in the recent files list 1061 BNode node(&entry); 1062 BNodeInfo info(&node); 1063 1064 char mimeType[B_MIME_TYPE_LENGTH]; 1065 if (info.GetType(mimeType) != B_OK 1066 || strcasecmp(mimeType, B_APP_MIME_TYPE)) 1067 BRoster().AddToRecentDocuments(&ref); 1068 } 1069 } 1070 1071 RefsReceived(message); 1072 break; 1073 } 1074 1075 case B_READY_TO_RUN: 1076 if (!fReadyToRunCalled) { 1077 ReadyToRun(); 1078 fReadyToRunCalled = true; 1079 } 1080 break; 1081 1082 case B_ABOUT_REQUESTED: 1083 AboutRequested(); 1084 break; 1085 1086 case B_PULSE: 1087 Pulse(); 1088 break; 1089 1090 case B_APP_ACTIVATED: 1091 { 1092 bool active; 1093 if (message->FindBool("active", &active) == B_OK) 1094 AppActivated(active); 1095 break; 1096 } 1097 1098 case B_COLORS_UPDATED: 1099 { 1100 AutoLocker<BLooperList> listLock(gLooperList); 1101 if (!listLock.IsLocked()) 1102 break; 1103 1104 BWindow* window = NULL; 1105 uint32 count = gLooperList.CountLoopers(); 1106 for (uint32 index = 0; index < count; ++index) { 1107 window = dynamic_cast<BWindow*>(gLooperList.LooperAt(index)); 1108 if (window == NULL || (window != NULL && window->fOffscreen)) 1109 continue; 1110 window->PostMessage(message); 1111 } 1112 break; 1113 } 1114 1115 case _SHOW_DRAG_HANDLES_: 1116 { 1117 bool show; 1118 if (message->FindBool("show", &show) != B_OK) 1119 break; 1120 1121 BDragger::Private::UpdateShowAllDraggers(show); 1122 break; 1123 } 1124 1125 // TODO: Handle these as well 1126 case _DISPOSE_DRAG_: 1127 case _PING_: 1128 puts("not yet handled message:"); 1129 DBG(message->PrintToStream()); 1130 break; 1131 1132 default: 1133 BLooper::DispatchMessage(message, handler); 1134 break; 1135 } 1136 } 1137 1138 1139 void 1140 BApplication::SetPulseRate(bigtime_t rate) 1141 { 1142 if (rate < 0) 1143 rate = 0; 1144 1145 // BeBook states that we have only 100,000 microseconds granularity 1146 rate -= rate % 100000; 1147 1148 if (!Lock()) 1149 return; 1150 1151 if (rate != 0) { 1152 // reset existing pulse runner, or create new one 1153 if (fPulseRunner == NULL) { 1154 BMessage pulse(B_PULSE); 1155 fPulseRunner = new BMessageRunner(be_app_messenger, &pulse, rate); 1156 } else 1157 fPulseRunner->SetInterval(rate); 1158 } else { 1159 // turn off pulse messages 1160 delete fPulseRunner; 1161 fPulseRunner = NULL; 1162 } 1163 1164 fPulseRate = rate; 1165 Unlock(); 1166 } 1167 1168 1169 status_t 1170 BApplication::GetSupportedSuites(BMessage* data) 1171 { 1172 if (data == NULL) 1173 return B_BAD_VALUE; 1174 1175 status_t status = data->AddString("suites", "suite/vnd.Be-application"); 1176 if (status == B_OK) { 1177 BPropertyInfo propertyInfo(sPropertyInfo); 1178 status = data->AddFlat("messages", &propertyInfo); 1179 if (status == B_OK) 1180 status = BLooper::GetSupportedSuites(data); 1181 } 1182 1183 return status; 1184 } 1185 1186 1187 status_t 1188 BApplication::Perform(perform_code d, void* arg) 1189 { 1190 return BLooper::Perform(d, arg); 1191 } 1192 1193 1194 void BApplication::_ReservedApplication1() {} 1195 void BApplication::_ReservedApplication2() {} 1196 void BApplication::_ReservedApplication3() {} 1197 void BApplication::_ReservedApplication4() {} 1198 void BApplication::_ReservedApplication5() {} 1199 void BApplication::_ReservedApplication6() {} 1200 void BApplication::_ReservedApplication7() {} 1201 void BApplication::_ReservedApplication8() {} 1202 1203 1204 bool 1205 BApplication::ScriptReceived(BMessage* message, int32 index, 1206 BMessage* specifier, int32 what, const char* property) 1207 { 1208 BMessage reply(B_REPLY); 1209 status_t err = B_BAD_SCRIPT_SYNTAX; 1210 1211 switch (message->what) { 1212 case B_GET_PROPERTY: 1213 if (strcmp("Loopers", property) == 0) { 1214 int32 count = CountLoopers(); 1215 err = B_OK; 1216 for (int32 i=0; err == B_OK && i<count; i++) { 1217 BMessenger messenger(LooperAt(i)); 1218 err = reply.AddMessenger("result", messenger); 1219 } 1220 } else if (strcmp("Windows", property) == 0) { 1221 int32 count = CountWindows(); 1222 err = B_OK; 1223 for (int32 i=0; err == B_OK && i<count; i++) { 1224 BMessenger messenger(WindowAt(i)); 1225 err = reply.AddMessenger("result", messenger); 1226 } 1227 } else if (strcmp("Window", property) == 0) { 1228 switch (what) { 1229 case B_INDEX_SPECIFIER: 1230 case B_REVERSE_INDEX_SPECIFIER: 1231 { 1232 int32 index = -1; 1233 err = specifier->FindInt32("index", &index); 1234 if (err != B_OK) 1235 break; 1236 1237 if (what == B_REVERSE_INDEX_SPECIFIER) 1238 index = CountWindows() - index; 1239 1240 err = B_BAD_INDEX; 1241 BWindow* window = WindowAt(index); 1242 if (window == NULL) 1243 break; 1244 1245 BMessenger messenger(window); 1246 err = reply.AddMessenger("result", messenger); 1247 break; 1248 } 1249 1250 case B_NAME_SPECIFIER: 1251 { 1252 const char* name; 1253 err = specifier->FindString("name", &name); 1254 if (err != B_OK) 1255 break; 1256 err = B_NAME_NOT_FOUND; 1257 for (int32 i = 0; i < CountWindows(); i++) { 1258 BWindow* window = WindowAt(i); 1259 if (window && window->Name() != NULL 1260 && !strcmp(window->Name(), name)) { 1261 BMessenger messenger(window); 1262 err = reply.AddMessenger("result", messenger); 1263 break; 1264 } 1265 } 1266 break; 1267 } 1268 } 1269 } else if (strcmp("Looper", property) == 0) { 1270 switch (what) { 1271 case B_INDEX_SPECIFIER: 1272 case B_REVERSE_INDEX_SPECIFIER: 1273 { 1274 int32 index = -1; 1275 err = specifier->FindInt32("index", &index); 1276 if (err != B_OK) 1277 break; 1278 1279 if (what == B_REVERSE_INDEX_SPECIFIER) 1280 index = CountLoopers() - index; 1281 1282 err = B_BAD_INDEX; 1283 BLooper* looper = LooperAt(index); 1284 if (looper == NULL) 1285 break; 1286 1287 BMessenger messenger(looper); 1288 err = reply.AddMessenger("result", messenger); 1289 break; 1290 } 1291 1292 case B_NAME_SPECIFIER: 1293 { 1294 const char* name; 1295 err = specifier->FindString("name", &name); 1296 if (err != B_OK) 1297 break; 1298 err = B_NAME_NOT_FOUND; 1299 for (int32 i = 0; i < CountLoopers(); i++) { 1300 BLooper* looper = LooperAt(i); 1301 if (looper != NULL && looper->Name() 1302 && strcmp(looper->Name(), name) == 0) { 1303 BMessenger messenger(looper); 1304 err = reply.AddMessenger("result", messenger); 1305 break; 1306 } 1307 } 1308 break; 1309 } 1310 1311 case B_ID_SPECIFIER: 1312 { 1313 // TODO 1314 debug_printf("Looper's ID specifier used but not " 1315 "implemented.\n"); 1316 break; 1317 } 1318 } 1319 } else if (strcmp("Name", property) == 0) 1320 err = reply.AddString("result", Name()); 1321 1322 break; 1323 1324 case B_COUNT_PROPERTIES: 1325 if (strcmp("Looper", property) == 0) 1326 err = reply.AddInt32("result", CountLoopers()); 1327 else if (strcmp("Window", property) == 0) 1328 err = reply.AddInt32("result", CountWindows()); 1329 1330 break; 1331 } 1332 if (err == B_BAD_SCRIPT_SYNTAX) 1333 return false; 1334 1335 if (err < B_OK) { 1336 reply.what = B_MESSAGE_NOT_UNDERSTOOD; 1337 reply.AddString("message", strerror(err)); 1338 } 1339 reply.AddInt32("error", err); 1340 message->SendReply(&reply); 1341 1342 return true; 1343 } 1344 1345 1346 void 1347 BApplication::BeginRectTracking(BRect rect, bool trackWhole) 1348 { 1349 BPrivate::AppServerLink link; 1350 link.StartMessage(AS_BEGIN_RECT_TRACKING); 1351 link.Attach<BRect>(rect); 1352 link.Attach<int32>(trackWhole); 1353 link.Flush(); 1354 } 1355 1356 1357 void 1358 BApplication::EndRectTracking() 1359 { 1360 BPrivate::AppServerLink link; 1361 link.StartMessage(AS_END_RECT_TRACKING); 1362 link.Flush(); 1363 } 1364 1365 1366 status_t 1367 BApplication::_SetupServerAllocator() 1368 { 1369 fServerAllocator = new (std::nothrow) BPrivate::ServerMemoryAllocator(); 1370 if (fServerAllocator == NULL) 1371 return B_NO_MEMORY; 1372 1373 return fServerAllocator->InitCheck(); 1374 } 1375 1376 1377 status_t 1378 BApplication::_InitGUIContext() 1379 { 1380 // An app_server connection is necessary for a lot of stuff, so get that first. 1381 status_t error = _ConnectToServer(); 1382 if (error != B_OK) 1383 return error; 1384 1385 // Initialize the IK after we have set be_app because of a construction 1386 // of a AppServerLink (which depends on be_app) nested inside the call 1387 // to get_menu_info. 1388 error = _init_interface_kit_(); 1389 if (error != B_OK) 1390 return error; 1391 1392 // create global system cursors 1393 B_CURSOR_SYSTEM_DEFAULT = new BCursor(B_HAND_CURSOR); 1394 B_CURSOR_I_BEAM = new BCursor(B_I_BEAM_CURSOR); 1395 1396 // TODO: would be nice to get the workspace at launch time from the registrar 1397 fInitialWorkspace = current_workspace(); 1398 1399 return B_OK; 1400 } 1401 1402 1403 status_t 1404 BApplication::_ConnectToServer() 1405 { 1406 status_t status 1407 = create_desktop_connection(fServerLink, "a<app_server", 100); 1408 if (status != B_OK) 1409 return status; 1410 1411 // AS_CREATE_APP: 1412 // 1413 // Attach data: 1414 // 1) port_id - receiver port of a regular app 1415 // 2) port_id - looper port for this BApplication 1416 // 3) team_id - team identification field 1417 // 4) int32 - handler ID token of the app 1418 // 5) char* - signature of the regular app 1419 1420 fServerLink->StartMessage(AS_CREATE_APP); 1421 fServerLink->Attach<port_id>(fServerLink->ReceiverPort()); 1422 fServerLink->Attach<port_id>(_get_looper_port_(this)); 1423 fServerLink->Attach<team_id>(Team()); 1424 fServerLink->Attach<int32>(_get_object_token_(this)); 1425 fServerLink->AttachString(fAppName); 1426 1427 area_id sharedReadOnlyArea; 1428 team_id serverTeam; 1429 port_id serverPort; 1430 1431 int32 code; 1432 if (fServerLink->FlushWithReply(code) == B_OK 1433 && code == B_OK) { 1434 // We don't need to contact the main app_server anymore 1435 // directly; we now talk to our server alter ego only. 1436 fServerLink->Read<port_id>(&serverPort); 1437 fServerLink->Read<area_id>(&sharedReadOnlyArea); 1438 fServerLink->Read<team_id>(&serverTeam); 1439 } else { 1440 fServerLink->SetSenderPort(-1); 1441 debugger("BApplication: couldn't obtain new app_server comm port"); 1442 return B_ERROR; 1443 } 1444 fServerLink->SetTargetTeam(serverTeam); 1445 fServerLink->SetSenderPort(serverPort); 1446 1447 status = _SetupServerAllocator(); 1448 if (status != B_OK) 1449 return status; 1450 1451 area_id area; 1452 uint8* base; 1453 status = fServerAllocator->AddArea(sharedReadOnlyArea, area, base, true); 1454 if (status < B_OK) 1455 return status; 1456 1457 fServerReadOnlyMemory = base; 1458 1459 return B_OK; 1460 } 1461 1462 1463 void 1464 BApplication::_ReconnectToServer() 1465 { 1466 delete_port(fServerLink->SenderPort()); 1467 delete_port(fServerLink->ReceiverPort()); 1468 1469 if (_ConnectToServer() != B_OK) 1470 debugger("Can't reconnect to app server!"); 1471 1472 AutoLocker<BLooperList> listLock(gLooperList); 1473 if (!listLock.IsLocked()) 1474 return; 1475 1476 uint32 count = gLooperList.CountLoopers(); 1477 for (uint32 i = 0; i < count ; i++) { 1478 BWindow* window = dynamic_cast<BWindow*>(gLooperList.LooperAt(i)); 1479 if (window == NULL) 1480 continue; 1481 BMessenger windowMessenger(window); 1482 windowMessenger.SendMessage(kMsgAppServerRestarted); 1483 } 1484 1485 reconnect_bitmaps_to_app_server(); 1486 reconnect_pictures_to_app_server(); 1487 } 1488 1489 1490 #if 0 1491 void 1492 BApplication::send_drag(BMessage* message, int32 vs_token, BPoint offset, 1493 BRect dragRect, BHandler* replyTo) 1494 { 1495 // TODO: implement 1496 } 1497 1498 1499 void 1500 BApplication::send_drag(BMessage* message, int32 vs_token, BPoint offset, 1501 int32 bitmapToken, drawing_mode dragMode, BHandler* replyTo) 1502 { 1503 // TODO: implement 1504 } 1505 1506 1507 void 1508 BApplication::write_drag(_BSession_* session, BMessage* message) 1509 { 1510 // TODO: implement 1511 } 1512 #endif 1513 1514 1515 bool 1516 BApplication::_WindowQuitLoop(bool quitFilePanels, bool force) 1517 { 1518 int32 index = 0; 1519 while (true) { 1520 BWindow* window = WindowAt(index); 1521 if (window == NULL) 1522 break; 1523 1524 // NOTE: the window pointer might be stale, in case the looper 1525 // was already quit by quitting an earlier looper... but fortunately, 1526 // we can still call Lock() on the invalid pointer, and it 1527 // will return false... 1528 if (!window->Lock()) 1529 continue; 1530 1531 // don't quit file panels if we haven't been asked for it 1532 if (!quitFilePanels && window->IsFilePanel()) { 1533 window->Unlock(); 1534 index++; 1535 continue; 1536 } 1537 1538 if (!force && !window->QuitRequested() 1539 && !(quitFilePanels && window->IsFilePanel())) { 1540 // the window does not want to quit, so we don't either 1541 window->Unlock(); 1542 return false; 1543 } 1544 1545 // Re-lock, just to make sure that the user hasn't done nasty 1546 // things in QuitRequested(). Quit() unlocks fully, thus 1547 // double-locking is harmless. 1548 if (window->Lock()) 1549 window->Quit(); 1550 1551 index = 0; 1552 // we need to continue at the start of the list again - it 1553 // might have changed 1554 } 1555 1556 return true; 1557 } 1558 1559 1560 bool 1561 BApplication::_QuitAllWindows(bool force) 1562 { 1563 AssertLocked(); 1564 1565 // We need to unlock here because BWindow::QuitRequested() must be 1566 // allowed to lock the application - which would cause a deadlock 1567 Unlock(); 1568 1569 bool quit = _WindowQuitLoop(false, force); 1570 if (quit) 1571 quit = _WindowQuitLoop(true, force); 1572 1573 Lock(); 1574 1575 return quit; 1576 } 1577 1578 1579 void 1580 BApplication::_ArgvReceived(BMessage* message) 1581 { 1582 ASSERT(message != NULL); 1583 1584 // build the argv vector 1585 status_t error = B_OK; 1586 int32 argc = 0; 1587 char** argv = NULL; 1588 if (message->FindInt32("argc", &argc) == B_OK && argc > 0) { 1589 // allocate a NULL terminated array 1590 argv = new(std::nothrow) char*[argc + 1]; 1591 if (argv == NULL) 1592 return; 1593 1594 // copy the arguments 1595 for (int32 i = 0; error == B_OK && i < argc; i++) { 1596 const char* arg = NULL; 1597 error = message->FindString("argv", i, &arg); 1598 if (error == B_OK && arg) { 1599 argv[i] = strdup(arg); 1600 if (argv[i] == NULL) 1601 error = B_NO_MEMORY; 1602 } else 1603 argc = i; 1604 } 1605 1606 argv[argc] = NULL; 1607 } 1608 1609 // call the hook 1610 if (error == B_OK && argc > 0) 1611 ArgvReceived(argc, argv); 1612 1613 if (error != B_OK) { 1614 printf("Error parsing B_ARGV_RECEIVED message. Message:\n"); 1615 message->PrintToStream(); 1616 } 1617 1618 // cleanup 1619 if (argv) { 1620 for (int32 i = 0; i < argc; i++) 1621 free(argv[i]); 1622 delete[] argv; 1623 } 1624 } 1625 1626 1627 uint32 1628 BApplication::InitialWorkspace() 1629 { 1630 return fInitialWorkspace; 1631 } 1632 1633 1634 int32 1635 BApplication::_CountWindows(bool includeMenus) const 1636 { 1637 uint32 count = 0; 1638 for (int32 i = 0; i < gLooperList.CountLoopers(); i++) { 1639 BWindow* window = dynamic_cast<BWindow*>(gLooperList.LooperAt(i)); 1640 if (window != NULL && !window->fOffscreen && (includeMenus 1641 || dynamic_cast<BMenuWindow*>(window) == NULL)) { 1642 count++; 1643 } 1644 } 1645 1646 return count; 1647 } 1648 1649 1650 BWindow* 1651 BApplication::_WindowAt(uint32 index, bool includeMenus) const 1652 { 1653 AutoLocker<BLooperList> listLock(gLooperList); 1654 if (!listLock.IsLocked()) 1655 return NULL; 1656 1657 uint32 count = gLooperList.CountLoopers(); 1658 for (uint32 i = 0; i < count && index < count; i++) { 1659 BWindow* window = dynamic_cast<BWindow*>(gLooperList.LooperAt(i)); 1660 if (window == NULL || (window != NULL && window->fOffscreen) 1661 || (!includeMenus && dynamic_cast<BMenuWindow*>(window) != NULL)) { 1662 index++; 1663 continue; 1664 } 1665 1666 if (i == index) 1667 return window; 1668 } 1669 1670 return NULL; 1671 } 1672 1673 1674 /*static*/ void 1675 BApplication::_InitAppResources() 1676 { 1677 entry_ref ref; 1678 bool found = false; 1679 1680 // App is already running. Get its entry ref with 1681 // GetAppInfo() 1682 app_info appInfo; 1683 if (be_app && be_app->GetAppInfo(&appInfo) == B_OK) { 1684 ref = appInfo.ref; 1685 found = true; 1686 } else { 1687 // Run() hasn't been called yet 1688 found = BPrivate::get_app_ref(&ref) == B_OK; 1689 } 1690 1691 if (!found) 1692 return; 1693 1694 BFile file(&ref, B_READ_ONLY); 1695 if (file.InitCheck() != B_OK) 1696 return; 1697 1698 BResources* resources = new (std::nothrow) BResources(&file, false); 1699 if (resources == NULL || resources->InitCheck() != B_OK) { 1700 delete resources; 1701 return; 1702 } 1703 1704 sAppResources = resources; 1705 } 1706