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