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