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