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