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 AssertLocked(); 594 595 if (fRunCalled) 596 debugger("BApplication::Run was already called. Can only be called once."); 597 598 fThread = find_thread(NULL); 599 fRunCalled = true; 600 601 task_looper(); 602 603 delete fPulseRunner; 604 return fThread; 605 } 606 607 608 void 609 BApplication::Quit() 610 { 611 bool unlock = false; 612 if (!IsLocked()) { 613 const char* name = Name(); 614 if (name == NULL) 615 name = "no-name"; 616 617 printf("ERROR - you must Lock the application object before calling " 618 "Quit(), team=%" B_PRId32 ", looper=%s\n", Team(), name); 619 unlock = true; 620 if (!Lock()) 621 return; 622 } 623 // Delete the object, if not running only. 624 if (!fRunCalled) { 625 delete this; 626 } else if (find_thread(NULL) != fThread) { 627 // ToDo: why shouldn't we set fTerminating to true directly in this case? 628 // We are not the looper thread. 629 // We push a _QUIT_ into the queue. 630 // TODO: When BLooper::AddMessage() is done, use that instead of 631 // PostMessage()??? This would overtake messages that are still at 632 // the port. 633 // NOTE: We must not unlock here -- otherwise we had to re-lock, which 634 // may not work. This is bad, since, if the port is full, it 635 // won't get emptier, as the looper thread needs to lock the object 636 // before dispatching messages. 637 while (PostMessage(_QUIT_, this) == B_WOULD_BLOCK) 638 snooze(10000); 639 } else { 640 // We are the looper thread. 641 // Just set fTerminating to true which makes us fall through the 642 // message dispatching loop and return from Run(). 643 fTerminating = true; 644 } 645 646 // If we had to lock the object, unlock now. 647 if (unlock) 648 Unlock(); 649 } 650 651 652 bool 653 BApplication::QuitRequested() 654 { 655 return _QuitAllWindows(false); 656 } 657 658 659 void 660 BApplication::Pulse() 661 { 662 // supposed to be implemented by subclasses 663 } 664 665 666 void 667 BApplication::ReadyToRun() 668 { 669 // supposed to be implemented by subclasses 670 } 671 672 673 void 674 BApplication::MessageReceived(BMessage* message) 675 { 676 switch (message->what) { 677 case B_COUNT_PROPERTIES: 678 case B_GET_PROPERTY: 679 case B_SET_PROPERTY: 680 { 681 int32 index; 682 BMessage specifier; 683 int32 what; 684 const char* property = NULL; 685 if (message->GetCurrentSpecifier(&index, &specifier, &what, 686 &property) < B_OK 687 || !ScriptReceived(message, index, &specifier, what, 688 property)) { 689 BLooper::MessageReceived(message); 690 } 691 break; 692 } 693 694 case B_SILENT_RELAUNCH: 695 // Sent to a B_SINGLE_LAUNCH application when it's launched again 696 // (see _InitData()) 697 be_roster->ActivateApp(Team()); 698 break; 699 700 case kMsgAppServerRestarted: 701 _ReconnectToServer(); 702 break; 703 704 case kMsgDeleteServerMemoryArea: 705 { 706 int32 serverArea; 707 if (message->FindInt32("server area", &serverArea) == B_OK) { 708 // The link is not used, but we currently borrow its lock 709 BPrivate::AppServerLink link; 710 fServerAllocator->RemoveArea(serverArea); 711 } 712 break; 713 } 714 715 default: 716 BLooper::MessageReceived(message); 717 } 718 } 719 720 721 void 722 BApplication::ArgvReceived(int32 argc, char** argv) 723 { 724 // supposed to be implemented by subclasses 725 } 726 727 728 void 729 BApplication::AppActivated(bool active) 730 { 731 // supposed to be implemented by subclasses 732 } 733 734 735 void 736 BApplication::RefsReceived(BMessage* message) 737 { 738 // supposed to be implemented by subclasses 739 } 740 741 742 void 743 BApplication::AboutRequested() 744 { 745 // supposed to be implemented by subclasses 746 } 747 748 749 BHandler* 750 BApplication::ResolveSpecifier(BMessage* message, int32 index, 751 BMessage* specifier, int32 what, const char* property) 752 { 753 BPropertyInfo propInfo(sPropertyInfo); 754 status_t err = B_OK; 755 uint32 data; 756 757 if (propInfo.FindMatch(message, 0, specifier, what, property, &data) >= 0) { 758 switch (data) { 759 case kWindowByIndex: 760 { 761 int32 index; 762 err = specifier->FindInt32("index", &index); 763 if (err != B_OK) 764 break; 765 766 if (what == B_REVERSE_INDEX_SPECIFIER) 767 index = CountWindows() - index; 768 769 BWindow* window = WindowAt(index); 770 if (window != NULL) { 771 message->PopSpecifier(); 772 BMessenger(window).SendMessage(message); 773 } else 774 err = B_BAD_INDEX; 775 break; 776 } 777 778 case kWindowByName: 779 { 780 const char* name; 781 err = specifier->FindString("name", &name); 782 if (err != B_OK) 783 break; 784 785 for (int32 i = 0;; i++) { 786 BWindow* window = WindowAt(i); 787 if (window == NULL) { 788 err = B_NAME_NOT_FOUND; 789 break; 790 } 791 if (window->Title() != NULL && !strcmp(window->Title(), 792 name)) { 793 message->PopSpecifier(); 794 BMessenger(window).SendMessage(message); 795 break; 796 } 797 } 798 break; 799 } 800 801 case kLooperByIndex: 802 { 803 int32 index; 804 err = specifier->FindInt32("index", &index); 805 if (err != B_OK) 806 break; 807 808 if (what == B_REVERSE_INDEX_SPECIFIER) 809 index = CountLoopers() - index; 810 811 BLooper* looper = LooperAt(index); 812 if (looper != NULL) { 813 message->PopSpecifier(); 814 BMessenger(looper).SendMessage(message); 815 } else 816 err = B_BAD_INDEX; 817 818 break; 819 } 820 821 case kLooperByID: 822 // TODO: implement getting looper by ID! 823 break; 824 825 case kLooperByName: 826 { 827 const char* name; 828 err = specifier->FindString("name", &name); 829 if (err != B_OK) 830 break; 831 832 for (int32 i = 0;; i++) { 833 BLooper* looper = LooperAt(i); 834 if (looper == NULL) { 835 err = B_NAME_NOT_FOUND; 836 break; 837 } 838 if (looper->Name() != NULL 839 && strcmp(looper->Name(), name) == 0) { 840 message->PopSpecifier(); 841 BMessenger(looper).SendMessage(message); 842 break; 843 } 844 } 845 break; 846 } 847 848 case kApplication: 849 return this; 850 } 851 } else { 852 return BLooper::ResolveSpecifier(message, index, specifier, what, 853 property); 854 } 855 856 if (err != B_OK) { 857 BMessage reply(B_MESSAGE_NOT_UNDERSTOOD); 858 reply.AddInt32("error", err); 859 reply.AddString("message", strerror(err)); 860 message->SendReply(&reply); 861 } 862 863 return NULL; 864 865 } 866 867 868 void 869 BApplication::ShowCursor() 870 { 871 BPrivate::AppServerLink link; 872 link.StartMessage(AS_SHOW_CURSOR); 873 link.Flush(); 874 } 875 876 877 void 878 BApplication::HideCursor() 879 { 880 BPrivate::AppServerLink link; 881 link.StartMessage(AS_HIDE_CURSOR); 882 link.Flush(); 883 } 884 885 886 void 887 BApplication::ObscureCursor() 888 { 889 BPrivate::AppServerLink link; 890 link.StartMessage(AS_OBSCURE_CURSOR); 891 link.Flush(); 892 } 893 894 895 bool 896 BApplication::IsCursorHidden() const 897 { 898 BPrivate::AppServerLink link; 899 int32 status = B_ERROR; 900 link.StartMessage(AS_QUERY_CURSOR_HIDDEN); 901 link.FlushWithReply(status); 902 903 return status == B_OK; 904 } 905 906 907 void 908 BApplication::SetCursor(const void* cursorData) 909 { 910 BCursor cursor(cursorData); 911 SetCursor(&cursor, true); 912 // forces the cursor to be sync'ed 913 } 914 915 916 void 917 BApplication::SetCursor(const BCursor* cursor, bool sync) 918 { 919 BPrivate::AppServerLink link; 920 link.StartMessage(AS_SET_CURSOR); 921 link.Attach<bool>(sync); 922 link.Attach<int32>(cursor->fServerToken); 923 924 if (sync) { 925 int32 code; 926 link.FlushWithReply(code); 927 } else 928 link.Flush(); 929 } 930 931 932 int32 933 BApplication::CountWindows() const 934 { 935 return _CountWindows(false); 936 // we're ignoring menu windows 937 } 938 939 940 BWindow* 941 BApplication::WindowAt(int32 index) const 942 { 943 return _WindowAt(index, false); 944 // we're ignoring menu windows 945 } 946 947 948 int32 949 BApplication::CountLoopers() const 950 { 951 AutoLocker<BLooperList> ListLock(gLooperList); 952 if (ListLock.IsLocked()) 953 return gLooperList.CountLoopers(); 954 955 // Some bad, non-specific thing has happened 956 return B_ERROR; 957 } 958 959 960 BLooper* 961 BApplication::LooperAt(int32 index) const 962 { 963 BLooper* looper = NULL; 964 AutoLocker<BLooperList> listLock(gLooperList); 965 if (listLock.IsLocked()) 966 looper = gLooperList.LooperAt(index); 967 968 return looper; 969 } 970 971 972 status_t 973 BApplication::RegisterLooper(BLooper* looper) 974 { 975 BWindow* window = dynamic_cast<BWindow*>(looper); 976 if (window != NULL) 977 return B_BAD_VALUE; 978 979 if (sOnQuitLooperList.HasItem(looper)) 980 return B_ERROR; 981 982 if (sOnQuitLooperList.AddItem(looper) != true) 983 return B_ERROR; 984 985 return B_OK; 986 } 987 988 989 status_t 990 BApplication::UnregisterLooper(BLooper* looper) 991 { 992 BWindow* window = dynamic_cast<BWindow*>(looper); 993 if (window != NULL) 994 return B_BAD_VALUE; 995 996 if (!sOnQuitLooperList.HasItem(looper)) 997 return B_ERROR; 998 999 if (sOnQuitLooperList.RemoveItem(looper) != true) 1000 return B_ERROR; 1001 1002 return B_OK; 1003 } 1004 1005 1006 bool 1007 BApplication::IsLaunching() const 1008 { 1009 return !fReadyToRunCalled; 1010 } 1011 1012 1013 const char* 1014 BApplication::Signature() const 1015 { 1016 return fAppName; 1017 } 1018 1019 1020 status_t 1021 BApplication::GetAppInfo(app_info* info) const 1022 { 1023 if (be_app == NULL || be_roster == NULL) 1024 return B_NO_INIT; 1025 return be_roster->GetRunningAppInfo(be_app->Team(), info); 1026 } 1027 1028 1029 BResources* 1030 BApplication::AppResources() 1031 { 1032 if (sAppResources == NULL) 1033 pthread_once(&sAppResourcesInitOnce, &_InitAppResources); 1034 1035 return sAppResources; 1036 } 1037 1038 1039 void 1040 BApplication::DispatchMessage(BMessage* message, BHandler* handler) 1041 { 1042 if (handler != this) { 1043 // it's not ours to dispatch 1044 BLooper::DispatchMessage(message, handler); 1045 return; 1046 } 1047 1048 switch (message->what) { 1049 case B_ARGV_RECEIVED: 1050 _ArgvReceived(message); 1051 break; 1052 1053 case B_REFS_RECEIVED: 1054 { 1055 // this adds the refs that are part of this message to the recent 1056 // lists, but only folders and documents are handled here 1057 entry_ref ref; 1058 int32 i = 0; 1059 while (message->FindRef("refs", i++, &ref) == B_OK) { 1060 BEntry entry(&ref, true); 1061 if (entry.InitCheck() != B_OK) 1062 continue; 1063 1064 if (entry.IsDirectory()) 1065 BRoster().AddToRecentFolders(&ref); 1066 else { 1067 // filter out applications, we only want to have documents 1068 // in the recent files list 1069 BNode node(&entry); 1070 BNodeInfo info(&node); 1071 1072 char mimeType[B_MIME_TYPE_LENGTH]; 1073 if (info.GetType(mimeType) != B_OK 1074 || strcasecmp(mimeType, B_APP_MIME_TYPE)) 1075 BRoster().AddToRecentDocuments(&ref); 1076 } 1077 } 1078 1079 RefsReceived(message); 1080 break; 1081 } 1082 1083 case B_READY_TO_RUN: 1084 if (!fReadyToRunCalled) { 1085 ReadyToRun(); 1086 fReadyToRunCalled = true; 1087 } 1088 break; 1089 1090 case B_ABOUT_REQUESTED: 1091 AboutRequested(); 1092 break; 1093 1094 case B_PULSE: 1095 Pulse(); 1096 break; 1097 1098 case B_APP_ACTIVATED: 1099 { 1100 bool active; 1101 if (message->FindBool("active", &active) == B_OK) 1102 AppActivated(active); 1103 break; 1104 } 1105 1106 case B_COLORS_UPDATED: 1107 { 1108 AutoLocker<BLooperList> listLock(gLooperList); 1109 if (!listLock.IsLocked()) 1110 break; 1111 1112 BWindow* window = NULL; 1113 uint32 count = gLooperList.CountLoopers(); 1114 for (uint32 index = 0; index < count; ++index) { 1115 window = dynamic_cast<BWindow*>(gLooperList.LooperAt(index)); 1116 if (window == NULL || (window != NULL && window->fOffscreen)) 1117 continue; 1118 window->PostMessage(message); 1119 } 1120 break; 1121 } 1122 1123 case _SHOW_DRAG_HANDLES_: 1124 { 1125 bool show; 1126 if (message->FindBool("show", &show) != B_OK) 1127 break; 1128 1129 BDragger::Private::UpdateShowAllDraggers(show); 1130 break; 1131 } 1132 1133 // TODO: Handle these as well 1134 case _DISPOSE_DRAG_: 1135 case _PING_: 1136 puts("not yet handled message:"); 1137 DBG(message->PrintToStream()); 1138 break; 1139 1140 default: 1141 BLooper::DispatchMessage(message, handler); 1142 break; 1143 } 1144 } 1145 1146 1147 void 1148 BApplication::SetPulseRate(bigtime_t rate) 1149 { 1150 if (rate < 0) 1151 rate = 0; 1152 1153 // BeBook states that we have only 100,000 microseconds granularity 1154 rate -= rate % 100000; 1155 1156 if (!Lock()) 1157 return; 1158 1159 if (rate != 0) { 1160 // reset existing pulse runner, or create new one 1161 if (fPulseRunner == NULL) { 1162 BMessage pulse(B_PULSE); 1163 fPulseRunner = new BMessageRunner(be_app_messenger, &pulse, rate); 1164 } else 1165 fPulseRunner->SetInterval(rate); 1166 } else { 1167 // turn off pulse messages 1168 delete fPulseRunner; 1169 fPulseRunner = NULL; 1170 } 1171 1172 fPulseRate = rate; 1173 Unlock(); 1174 } 1175 1176 1177 status_t 1178 BApplication::GetSupportedSuites(BMessage* data) 1179 { 1180 if (data == NULL) 1181 return B_BAD_VALUE; 1182 1183 status_t status = data->AddString("suites", "suite/vnd.Be-application"); 1184 if (status == B_OK) { 1185 BPropertyInfo propertyInfo(sPropertyInfo); 1186 status = data->AddFlat("messages", &propertyInfo); 1187 if (status == B_OK) 1188 status = BLooper::GetSupportedSuites(data); 1189 } 1190 1191 return status; 1192 } 1193 1194 1195 status_t 1196 BApplication::Perform(perform_code d, void* arg) 1197 { 1198 return BLooper::Perform(d, arg); 1199 } 1200 1201 1202 void BApplication::_ReservedApplication1() {} 1203 void BApplication::_ReservedApplication2() {} 1204 void BApplication::_ReservedApplication3() {} 1205 void BApplication::_ReservedApplication4() {} 1206 void BApplication::_ReservedApplication5() {} 1207 void BApplication::_ReservedApplication6() {} 1208 void BApplication::_ReservedApplication7() {} 1209 void BApplication::_ReservedApplication8() {} 1210 1211 1212 bool 1213 BApplication::ScriptReceived(BMessage* message, int32 index, 1214 BMessage* specifier, int32 what, const char* property) 1215 { 1216 BMessage reply(B_REPLY); 1217 status_t err = B_BAD_SCRIPT_SYNTAX; 1218 1219 switch (message->what) { 1220 case B_GET_PROPERTY: 1221 if (strcmp("Loopers", property) == 0) { 1222 int32 count = CountLoopers(); 1223 err = B_OK; 1224 for (int32 i=0; err == B_OK && i<count; i++) { 1225 BMessenger messenger(LooperAt(i)); 1226 err = reply.AddMessenger("result", messenger); 1227 } 1228 } else if (strcmp("Windows", property) == 0) { 1229 int32 count = CountWindows(); 1230 err = B_OK; 1231 for (int32 i=0; err == B_OK && i<count; i++) { 1232 BMessenger messenger(WindowAt(i)); 1233 err = reply.AddMessenger("result", messenger); 1234 } 1235 } else if (strcmp("Window", property) == 0) { 1236 switch (what) { 1237 case B_INDEX_SPECIFIER: 1238 case B_REVERSE_INDEX_SPECIFIER: 1239 { 1240 int32 index = -1; 1241 err = specifier->FindInt32("index", &index); 1242 if (err != B_OK) 1243 break; 1244 1245 if (what == B_REVERSE_INDEX_SPECIFIER) 1246 index = CountWindows() - index; 1247 1248 err = B_BAD_INDEX; 1249 BWindow* window = WindowAt(index); 1250 if (window == NULL) 1251 break; 1252 1253 BMessenger messenger(window); 1254 err = reply.AddMessenger("result", messenger); 1255 break; 1256 } 1257 1258 case B_NAME_SPECIFIER: 1259 { 1260 const char* name; 1261 err = specifier->FindString("name", &name); 1262 if (err != B_OK) 1263 break; 1264 err = B_NAME_NOT_FOUND; 1265 for (int32 i = 0; i < CountWindows(); i++) { 1266 BWindow* window = WindowAt(i); 1267 if (window && window->Name() != NULL 1268 && !strcmp(window->Name(), name)) { 1269 BMessenger messenger(window); 1270 err = reply.AddMessenger("result", messenger); 1271 break; 1272 } 1273 } 1274 break; 1275 } 1276 } 1277 } else if (strcmp("Looper", property) == 0) { 1278 switch (what) { 1279 case B_INDEX_SPECIFIER: 1280 case B_REVERSE_INDEX_SPECIFIER: 1281 { 1282 int32 index = -1; 1283 err = specifier->FindInt32("index", &index); 1284 if (err != B_OK) 1285 break; 1286 1287 if (what == B_REVERSE_INDEX_SPECIFIER) 1288 index = CountLoopers() - index; 1289 1290 err = B_BAD_INDEX; 1291 BLooper* looper = LooperAt(index); 1292 if (looper == NULL) 1293 break; 1294 1295 BMessenger messenger(looper); 1296 err = reply.AddMessenger("result", messenger); 1297 break; 1298 } 1299 1300 case B_NAME_SPECIFIER: 1301 { 1302 const char* name; 1303 err = specifier->FindString("name", &name); 1304 if (err != B_OK) 1305 break; 1306 err = B_NAME_NOT_FOUND; 1307 for (int32 i = 0; i < CountLoopers(); i++) { 1308 BLooper* looper = LooperAt(i); 1309 if (looper != NULL && looper->Name() 1310 && strcmp(looper->Name(), name) == 0) { 1311 BMessenger messenger(looper); 1312 err = reply.AddMessenger("result", messenger); 1313 break; 1314 } 1315 } 1316 break; 1317 } 1318 1319 case B_ID_SPECIFIER: 1320 { 1321 // TODO 1322 debug_printf("Looper's ID specifier used but not " 1323 "implemented.\n"); 1324 break; 1325 } 1326 } 1327 } else if (strcmp("Name", property) == 0) 1328 err = reply.AddString("result", Name()); 1329 1330 break; 1331 1332 case B_COUNT_PROPERTIES: 1333 if (strcmp("Looper", property) == 0) 1334 err = reply.AddInt32("result", CountLoopers()); 1335 else if (strcmp("Window", property) == 0) 1336 err = reply.AddInt32("result", CountWindows()); 1337 1338 break; 1339 } 1340 if (err == B_BAD_SCRIPT_SYNTAX) 1341 return false; 1342 1343 if (err < B_OK) { 1344 reply.what = B_MESSAGE_NOT_UNDERSTOOD; 1345 reply.AddString("message", strerror(err)); 1346 } 1347 reply.AddInt32("error", err); 1348 message->SendReply(&reply); 1349 1350 return true; 1351 } 1352 1353 1354 void 1355 BApplication::BeginRectTracking(BRect rect, bool trackWhole) 1356 { 1357 BPrivate::AppServerLink link; 1358 link.StartMessage(AS_BEGIN_RECT_TRACKING); 1359 link.Attach<BRect>(rect); 1360 link.Attach<int32>(trackWhole); 1361 link.Flush(); 1362 } 1363 1364 1365 void 1366 BApplication::EndRectTracking() 1367 { 1368 BPrivate::AppServerLink link; 1369 link.StartMessage(AS_END_RECT_TRACKING); 1370 link.Flush(); 1371 } 1372 1373 1374 status_t 1375 BApplication::_SetupServerAllocator() 1376 { 1377 fServerAllocator = new (std::nothrow) BPrivate::ServerMemoryAllocator(); 1378 if (fServerAllocator == NULL) 1379 return B_NO_MEMORY; 1380 1381 return fServerAllocator->InitCheck(); 1382 } 1383 1384 1385 status_t 1386 BApplication::_InitGUIContext() 1387 { 1388 // An app_server connection is necessary for a lot of stuff, so get that first. 1389 status_t error = _ConnectToServer(); 1390 if (error != B_OK) 1391 return error; 1392 1393 // Initialize the IK after we have set be_app because of a construction 1394 // of a AppServerLink (which depends on be_app) nested inside the call 1395 // to get_menu_info. 1396 error = _init_interface_kit_(); 1397 if (error != B_OK) 1398 return error; 1399 1400 // create global system cursors 1401 B_CURSOR_SYSTEM_DEFAULT = new BCursor(B_HAND_CURSOR); 1402 B_CURSOR_I_BEAM = new BCursor(B_I_BEAM_CURSOR); 1403 1404 // TODO: would be nice to get the workspace at launch time from the registrar 1405 fInitialWorkspace = current_workspace(); 1406 1407 return B_OK; 1408 } 1409 1410 1411 status_t 1412 BApplication::_ConnectToServer() 1413 { 1414 status_t status 1415 = create_desktop_connection(fServerLink, "a<app_server", 100); 1416 if (status != B_OK) 1417 return status; 1418 1419 // AS_CREATE_APP: 1420 // 1421 // Attach data: 1422 // 1) port_id - receiver port of a regular app 1423 // 2) port_id - looper port for this BApplication 1424 // 3) team_id - team identification field 1425 // 4) int32 - handler ID token of the app 1426 // 5) char* - signature of the regular app 1427 1428 fServerLink->StartMessage(AS_CREATE_APP); 1429 fServerLink->Attach<port_id>(fServerLink->ReceiverPort()); 1430 fServerLink->Attach<port_id>(_get_looper_port_(this)); 1431 fServerLink->Attach<team_id>(Team()); 1432 fServerLink->Attach<int32>(_get_object_token_(this)); 1433 fServerLink->AttachString(fAppName); 1434 1435 area_id sharedReadOnlyArea; 1436 team_id serverTeam; 1437 port_id serverPort; 1438 1439 int32 code; 1440 if (fServerLink->FlushWithReply(code) == B_OK 1441 && code == B_OK) { 1442 // We don't need to contact the main app_server anymore 1443 // directly; we now talk to our server alter ego only. 1444 fServerLink->Read<port_id>(&serverPort); 1445 fServerLink->Read<area_id>(&sharedReadOnlyArea); 1446 fServerLink->Read<team_id>(&serverTeam); 1447 } else { 1448 fServerLink->SetSenderPort(-1); 1449 debugger("BApplication: couldn't obtain new app_server comm port"); 1450 return B_ERROR; 1451 } 1452 fServerLink->SetTargetTeam(serverTeam); 1453 fServerLink->SetSenderPort(serverPort); 1454 1455 status = _SetupServerAllocator(); 1456 if (status != B_OK) 1457 return status; 1458 1459 area_id area; 1460 uint8* base; 1461 status = fServerAllocator->AddArea(sharedReadOnlyArea, area, base, true); 1462 if (status < B_OK) 1463 return status; 1464 1465 fServerReadOnlyMemory = base; 1466 1467 return B_OK; 1468 } 1469 1470 1471 void 1472 BApplication::_ReconnectToServer() 1473 { 1474 delete_port(fServerLink->SenderPort()); 1475 delete_port(fServerLink->ReceiverPort()); 1476 1477 if (_ConnectToServer() != B_OK) 1478 debugger("Can't reconnect to app server!"); 1479 1480 AutoLocker<BLooperList> listLock(gLooperList); 1481 if (!listLock.IsLocked()) 1482 return; 1483 1484 uint32 count = gLooperList.CountLoopers(); 1485 for (uint32 i = 0; i < count ; i++) { 1486 BWindow* window = dynamic_cast<BWindow*>(gLooperList.LooperAt(i)); 1487 if (window == NULL) 1488 continue; 1489 BMessenger windowMessenger(window); 1490 windowMessenger.SendMessage(kMsgAppServerRestarted); 1491 } 1492 1493 reconnect_bitmaps_to_app_server(); 1494 reconnect_pictures_to_app_server(); 1495 } 1496 1497 1498 #if 0 1499 void 1500 BApplication::send_drag(BMessage* message, int32 vs_token, BPoint offset, 1501 BRect dragRect, BHandler* replyTo) 1502 { 1503 // TODO: implement 1504 } 1505 1506 1507 void 1508 BApplication::send_drag(BMessage* message, int32 vs_token, BPoint offset, 1509 int32 bitmapToken, drawing_mode dragMode, BHandler* replyTo) 1510 { 1511 // TODO: implement 1512 } 1513 1514 1515 void 1516 BApplication::write_drag(_BSession_* session, BMessage* message) 1517 { 1518 // TODO: implement 1519 } 1520 #endif 1521 1522 1523 bool 1524 BApplication::_WindowQuitLoop(bool quitFilePanels, bool force) 1525 { 1526 int32 index = 0; 1527 while (true) { 1528 BWindow* window = WindowAt(index); 1529 if (window == NULL) 1530 break; 1531 1532 // NOTE: the window pointer might be stale, in case the looper 1533 // was already quit by quitting an earlier looper... but fortunately, 1534 // we can still call Lock() on the invalid pointer, and it 1535 // will return false... 1536 if (!window->Lock()) 1537 continue; 1538 1539 // don't quit file panels if we haven't been asked for it 1540 if (!quitFilePanels && window->IsFilePanel()) { 1541 window->Unlock(); 1542 index++; 1543 continue; 1544 } 1545 1546 if (!force && !window->QuitRequested() 1547 && !(quitFilePanels && window->IsFilePanel())) { 1548 // the window does not want to quit, so we don't either 1549 window->Unlock(); 1550 return false; 1551 } 1552 1553 // Re-lock, just to make sure that the user hasn't done nasty 1554 // things in QuitRequested(). Quit() unlocks fully, thus 1555 // double-locking is harmless. 1556 if (window->Lock()) 1557 window->Quit(); 1558 1559 index = 0; 1560 // we need to continue at the start of the list again - it 1561 // might have changed 1562 } 1563 1564 return true; 1565 } 1566 1567 1568 bool 1569 BApplication::_QuitAllWindows(bool force) 1570 { 1571 AssertLocked(); 1572 1573 // We need to unlock here because BWindow::QuitRequested() must be 1574 // allowed to lock the application - which would cause a deadlock 1575 Unlock(); 1576 1577 bool quit = _WindowQuitLoop(false, force); 1578 if (quit) 1579 quit = _WindowQuitLoop(true, force); 1580 1581 Lock(); 1582 1583 return quit; 1584 } 1585 1586 1587 void 1588 BApplication::_ArgvReceived(BMessage* message) 1589 { 1590 ASSERT(message != NULL); 1591 1592 // build the argv vector 1593 status_t error = B_OK; 1594 int32 argc = 0; 1595 char** argv = NULL; 1596 if (message->FindInt32("argc", &argc) == B_OK && argc > 0) { 1597 // allocate a NULL terminated array 1598 argv = new(std::nothrow) char*[argc + 1]; 1599 if (argv == NULL) 1600 return; 1601 1602 // copy the arguments 1603 for (int32 i = 0; error == B_OK && i < argc; i++) { 1604 const char* arg = NULL; 1605 error = message->FindString("argv", i, &arg); 1606 if (error == B_OK && arg) { 1607 argv[i] = strdup(arg); 1608 if (argv[i] == NULL) 1609 error = B_NO_MEMORY; 1610 } else 1611 argc = i; 1612 } 1613 1614 argv[argc] = NULL; 1615 } 1616 1617 // call the hook 1618 if (error == B_OK && argc > 0) 1619 ArgvReceived(argc, argv); 1620 1621 if (error != B_OK) { 1622 printf("Error parsing B_ARGV_RECEIVED message. Message:\n"); 1623 message->PrintToStream(); 1624 } 1625 1626 // cleanup 1627 if (argv) { 1628 for (int32 i = 0; i < argc; i++) 1629 free(argv[i]); 1630 delete[] argv; 1631 } 1632 } 1633 1634 1635 uint32 1636 BApplication::InitialWorkspace() 1637 { 1638 return fInitialWorkspace; 1639 } 1640 1641 1642 int32 1643 BApplication::_CountWindows(bool includeMenus) const 1644 { 1645 uint32 count = 0; 1646 for (int32 i = 0; i < gLooperList.CountLoopers(); i++) { 1647 BWindow* window = dynamic_cast<BWindow*>(gLooperList.LooperAt(i)); 1648 if (window != NULL && !window->fOffscreen && (includeMenus 1649 || dynamic_cast<BMenuWindow*>(window) == NULL)) { 1650 count++; 1651 } 1652 } 1653 1654 return count; 1655 } 1656 1657 1658 BWindow* 1659 BApplication::_WindowAt(uint32 index, bool includeMenus) const 1660 { 1661 AutoLocker<BLooperList> listLock(gLooperList); 1662 if (!listLock.IsLocked()) 1663 return NULL; 1664 1665 uint32 count = gLooperList.CountLoopers(); 1666 for (uint32 i = 0; i < count && index < count; i++) { 1667 BWindow* window = dynamic_cast<BWindow*>(gLooperList.LooperAt(i)); 1668 if (window == NULL || (window != NULL && window->fOffscreen) 1669 || (!includeMenus && dynamic_cast<BMenuWindow*>(window) != NULL)) { 1670 index++; 1671 continue; 1672 } 1673 1674 if (i == index) 1675 return window; 1676 } 1677 1678 return NULL; 1679 } 1680 1681 1682 /*static*/ void 1683 BApplication::_InitAppResources() 1684 { 1685 entry_ref ref; 1686 bool found = false; 1687 1688 // App is already running. Get its entry ref with 1689 // GetAppInfo() 1690 app_info appInfo; 1691 if (be_app && be_app->GetAppInfo(&appInfo) == B_OK) { 1692 ref = appInfo.ref; 1693 found = true; 1694 } else { 1695 // Run() hasn't been called yet 1696 found = BPrivate::get_app_ref(&ref) == B_OK; 1697 } 1698 1699 if (!found) 1700 return; 1701 1702 BFile file(&ref, B_READ_ONLY); 1703 if (file.InitCheck() != B_OK) 1704 return; 1705 1706 BResources* resources = new (std::nothrow) BResources(&file, false); 1707 if (resources == NULL || resources->InitCheck() != B_OK) { 1708 delete resources; 1709 return; 1710 } 1711 1712 sAppResources = resources; 1713 } 1714