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