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