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