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