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