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