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