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