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