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