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