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