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