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