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