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