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