BPicture: Fix archive constructor.
[haiku.git] / src / kits / app / Application.cpp
blobcda2c6d89117330d0951d42df045e89248e8d29c
1 /*
2 * Copyright 2001-2015 Haiku, inc. All rights reserved.
3 * Distributed under the terms of the MIT License.
5 * Authors:
6 * Axel Dörfler, axeld@pinc-software.de
7 * Jerome Duval
8 * Erik Jaesler, erik@cgsoftware.com
9 */
12 #include <Application.h>
14 #include <new>
15 #include <pthread.h>
16 #include <stdio.h>
17 #include <stdlib.h>
18 #include <string.h>
19 #include <strings.h>
20 #include <unistd.h>
22 #include <Alert.h>
23 #include <AppFileInfo.h>
24 #include <Cursor.h>
25 #include <Debug.h>
26 #include <Entry.h>
27 #include <File.h>
28 #include <Locker.h>
29 #include <MessageRunner.h>
30 #include <ObjectList.h>
31 #include <Path.h>
32 #include <PropertyInfo.h>
33 #include <RegistrarDefs.h>
34 #include <Resources.h>
35 #include <Roster.h>
36 #include <Window.h>
38 #include <AppMisc.h>
39 #include <AppServerLink.h>
40 #include <AutoLocker.h>
41 #include <BitmapPrivate.h>
42 #include <DraggerPrivate.h>
43 #include <LaunchDaemonDefs.h>
44 #include <LaunchRoster.h>
45 #include <LooperList.h>
46 #include <MenuWindow.h>
47 #include <PicturePrivate.h>
48 #include <PortLink.h>
49 #include <RosterPrivate.h>
50 #include <ServerMemoryAllocator.h>
51 #include <ServerProtocol.h>
54 using namespace BPrivate;
57 static const char* kDefaultLooperName = "AppLooperPort";
59 BApplication* be_app = NULL;
60 BMessenger be_app_messenger;
62 pthread_once_t sAppResourcesInitOnce = PTHREAD_ONCE_INIT;
63 BResources* BApplication::sAppResources = NULL;
66 enum {
67 kWindowByIndex,
68 kWindowByName,
69 kLooperByIndex,
70 kLooperByID,
71 kLooperByName,
72 kApplication
76 static property_info sPropertyInfo[] = {
78 "Window",
79 {},
80 {B_INDEX_SPECIFIER, B_REVERSE_INDEX_SPECIFIER},
81 NULL, kWindowByIndex,
82 {},
83 {},
87 "Window",
88 {},
89 {B_NAME_SPECIFIER},
90 NULL, kWindowByName,
91 {},
92 {},
96 "Looper",
97 {},
98 {B_INDEX_SPECIFIER, B_REVERSE_INDEX_SPECIFIER},
99 NULL, kLooperByIndex,
105 "Looper",
107 {B_ID_SPECIFIER},
108 NULL, kLooperByID,
114 "Looper",
116 {B_NAME_SPECIFIER},
117 NULL, kLooperByName,
123 "Name",
124 {B_GET_PROPERTY},
125 {B_DIRECT_SPECIFIER},
126 NULL, kApplication,
127 {B_STRING_TYPE},
132 "Window",
133 {B_COUNT_PROPERTIES},
134 {B_DIRECT_SPECIFIER},
135 NULL, kApplication,
136 {B_INT32_TYPE},
141 "Loopers",
142 {B_GET_PROPERTY},
143 {B_DIRECT_SPECIFIER},
144 NULL, kApplication,
145 {B_MESSENGER_TYPE},
150 "Windows",
151 {B_GET_PROPERTY},
152 {B_DIRECT_SPECIFIER},
153 NULL, kApplication,
154 {B_MESSENGER_TYPE},
159 "Looper",
160 {B_COUNT_PROPERTIES},
161 {B_DIRECT_SPECIFIER},
162 NULL, kApplication,
163 {B_INT32_TYPE},
171 // argc/argv
172 extern const int __libc_argc;
173 extern const char* const *__libc_argv;
176 // debugging
177 //#define DBG(x) x
178 #define DBG(x)
179 #define OUT printf
182 // #pragma mark - static helper functions
186 \brief Checks whether the supplied string is a valid application signature.
188 An error message is printed, if the string is no valid app signature.
190 \param signature The string to be checked.
192 \return A status code.
193 \retval B_OK \a signature is a valid app signature.
194 \retval B_BAD_VALUE \a signature is \c NULL or no valid app signature.
196 static status_t
197 check_app_signature(const char* signature)
199 bool isValid = false;
200 BMimeType type(signature);
202 if (type.IsValid() && !type.IsSupertypeOnly()
203 && BMimeType("application").Contains(&type)) {
204 isValid = true;
207 if (!isValid) {
208 printf("bad signature (%s), must begin with \"application/\" and "
209 "can't conflict with existing registered mime types inside "
210 "the \"application\" media type.\n", signature);
213 return (isValid ? B_OK : B_BAD_VALUE);
217 #ifndef RUN_WITHOUT_REGISTRAR
218 // Fills the passed BMessage with B_ARGV_RECEIVED infos.
219 static void
220 fill_argv_message(BMessage &message)
222 message.what = B_ARGV_RECEIVED;
224 int32 argc = __libc_argc;
225 const char* const *argv = __libc_argv;
227 // add argc
228 message.AddInt32("argc", argc);
230 // add argv
231 for (int32 i = 0; i < argc; i++) {
232 if (argv[i] != NULL)
233 message.AddString("argv", argv[i]);
236 // add current working directory
237 char cwd[B_PATH_NAME_LENGTH];
238 if (getcwd(cwd, B_PATH_NAME_LENGTH))
239 message.AddString("cwd", cwd);
241 #endif
244 // #pragma mark - BApplication
247 BApplication::BApplication(const char* signature)
249 BLooper(kDefaultLooperName)
251 _InitData(signature, true, NULL);
255 BApplication::BApplication(const char* signature, status_t* _error)
257 BLooper(kDefaultLooperName)
259 _InitData(signature, true, _error);
263 BApplication::BApplication(const char* signature, const char* looperName,
264 port_id port, bool initGUI, status_t* _error)
266 BLooper(B_NORMAL_PRIORITY + 1, port < 0 ? _GetPort(signature) : port,
267 looperName != NULL ? looperName : kDefaultLooperName)
269 _InitData(signature, initGUI, _error);
270 if (port < 0)
271 fOwnsPort = false;
275 BApplication::BApplication(BMessage* data)
276 // Note: BeOS calls the private BLooper(int32, port_id, const char*)
277 // constructor here, test if it's needed
279 BLooper(kDefaultLooperName)
281 const char* signature = NULL;
282 data->FindString("mime_sig", &signature);
284 _InitData(signature, true, NULL);
286 bigtime_t pulseRate;
287 if (data->FindInt64("_pulse", &pulseRate) == B_OK)
288 SetPulseRate(pulseRate);
292 BApplication::BApplication(uint32 signature)
297 BApplication::BApplication(const BApplication &rhs)
302 BApplication::~BApplication()
304 Lock();
306 // tell all loopers(usually windows) to quit. Also, wait for them.
307 _QuitAllWindows(true);
309 // unregister from the roster
310 BRoster::Private().RemoveApp(Team());
312 #ifndef RUN_WITHOUT_APP_SERVER
313 // tell app_server we're quitting...
314 if (be_app) {
315 // be_app can be NULL here if the application fails to initialize
316 // correctly. For example, if it's already running and it's set to
317 // exclusive launch.
318 BPrivate::AppServerLink link;
319 link.StartMessage(B_QUIT_REQUESTED);
320 link.Flush();
322 delete_port(fServerLink->SenderPort());
323 delete_port(fServerLink->ReceiverPort());
324 delete fServerLink;
325 #endif // RUN_WITHOUT_APP_SERVER
327 delete fServerAllocator;
329 // uninitialize be_app, the be_app_messenger is invalidated automatically
330 be_app = NULL;
334 BApplication&
335 BApplication::operator=(const BApplication &rhs)
337 return *this;
341 void
342 BApplication::_InitData(const char* signature, bool initGUI, status_t* _error)
344 DBG(OUT("BApplication::InitData(`%s', %p)\n", signature, _error));
345 // check whether there exists already an application
346 if (be_app != NULL)
347 debugger("2 BApplication objects were created. Only one is allowed.");
349 fServerLink = new BPrivate::PortLink(-1, -1);
350 fServerAllocator = NULL;
351 fServerReadOnlyMemory = NULL;
352 fInitialWorkspace = 0;
353 //fDraggedMessage = NULL;
354 fReadyToRunCalled = false;
356 // initially, there is no pulse
357 fPulseRunner = NULL;
358 fPulseRate = 0;
360 // check signature
361 fInitError = check_app_signature(signature);
362 fAppName = signature;
364 #ifndef RUN_WITHOUT_REGISTRAR
365 bool registerApp = signature == NULL
366 || (strcasecmp(signature, B_REGISTRAR_SIGNATURE) != 0
367 && strcasecmp(signature, kLaunchDaemonSignature) != 0);
368 // get team and thread
369 team_id team = Team();
370 thread_id thread = BPrivate::main_thread_for(team);
371 #endif
373 // get app executable ref
374 entry_ref ref;
375 if (fInitError == B_OK) {
376 fInitError = BPrivate::get_app_ref(&ref);
377 if (fInitError != B_OK) {
378 DBG(OUT("BApplication::InitData(): Failed to get app ref: %s\n",
379 strerror(fInitError)));
383 // get the BAppFileInfo and extract the information we need
384 uint32 appFlags = B_REG_DEFAULT_APP_FLAGS;
385 if (fInitError == B_OK) {
386 BAppFileInfo fileInfo;
387 BFile file(&ref, B_READ_ONLY);
388 fInitError = fileInfo.SetTo(&file);
389 if (fInitError == B_OK) {
390 fileInfo.GetAppFlags(&appFlags);
391 char appFileSignature[B_MIME_TYPE_LENGTH];
392 // compare the file signature and the supplied signature
393 if (fileInfo.GetSignature(appFileSignature) == B_OK
394 && strcasecmp(appFileSignature, signature) != 0) {
395 printf("Signature in rsrc doesn't match constructor arg. (%s, %s)\n",
396 signature, appFileSignature);
398 } else {
399 DBG(OUT("BApplication::InitData(): Failed to get info from: "
400 "BAppFileInfo: %s\n", strerror(fInitError)));
404 #ifndef RUN_WITHOUT_REGISTRAR
405 // check whether be_roster is valid
406 if (fInitError == B_OK && registerApp
407 && !BRoster::Private().IsMessengerValid(false)) {
408 printf("FATAL: be_roster is not valid. Is the registrar running?\n");
409 fInitError = B_NO_INIT;
412 // check whether or not we are pre-registered
413 bool preRegistered = false;
414 app_info appInfo;
415 if (fInitError == B_OK && registerApp) {
416 if (BRoster::Private().IsAppRegistered(&ref, team, 0, &preRegistered,
417 &appInfo) != B_OK) {
418 preRegistered = false;
421 if (preRegistered) {
422 // we are pre-registered => the app info has been filled in
423 // Check whether we need to replace the looper port with a port
424 // created by the roster.
425 if (appInfo.port >= 0 && appInfo.port != fMsgPort) {
426 delete_port(fMsgPort);
427 fMsgPort = appInfo.port;
428 } else
429 appInfo.port = fMsgPort;
430 // check the signature and correct it, if necessary, also the case
431 if (strcmp(appInfo.signature, fAppName))
432 BRoster::Private().SetSignature(team, fAppName);
433 // complete the registration
434 fInitError = BRoster::Private().CompleteRegistration(team, thread,
435 appInfo.port);
436 } else if (fInitError == B_OK) {
437 // not pre-registered -- try to register the application
438 team_id otherTeam = -1;
439 if (registerApp) {
440 fInitError = BRoster::Private().AddApplication(signature, &ref,
441 appFlags, team, thread, fMsgPort, true, NULL, &otherTeam);
442 if (fInitError != B_OK) {
443 DBG(OUT("BApplication::InitData(): Failed to add app: %s\n",
444 strerror(fInitError)));
447 if (fInitError == B_ALREADY_RUNNING) {
448 // An instance is already running and we asked for
449 // single/exclusive launch. Send our argv to the running app.
450 // Do that only, if the app is NOT B_ARGV_ONLY.
451 if (otherTeam >= 0) {
452 BMessenger otherApp(NULL, otherTeam);
453 app_info otherAppInfo;
454 bool argvOnly = be_roster->GetRunningAppInfo(otherTeam,
455 &otherAppInfo) == B_OK
456 && (otherAppInfo.flags & B_ARGV_ONLY) != 0;
458 if (__libc_argc > 1 && !argvOnly) {
459 // create an B_ARGV_RECEIVED message
460 BMessage argvMessage(B_ARGV_RECEIVED);
461 fill_argv_message(argvMessage);
463 // replace the first argv string with the path of the
464 // other application
465 BPath path;
466 if (path.SetTo(&otherAppInfo.ref) == B_OK)
467 argvMessage.ReplaceString("argv", 0, path.Path());
469 // send the message
470 otherApp.SendMessage(&argvMessage);
471 } else if (!argvOnly)
472 otherApp.SendMessage(B_SILENT_RELAUNCH);
474 } else if (fInitError == B_OK) {
475 // the registrations was successful
476 // Create a B_ARGV_RECEIVED message and send it to ourselves.
477 // Do that even, if we are B_ARGV_ONLY.
478 // TODO: When BLooper::AddMessage() is done, use that instead of
479 // PostMessage().
481 DBG(OUT("info: BApplication successfully registered.\n"));
483 if (__libc_argc > 1) {
484 BMessage argvMessage(B_ARGV_RECEIVED);
485 fill_argv_message(argvMessage);
486 PostMessage(&argvMessage, this);
488 // send a B_READY_TO_RUN message as well
489 PostMessage(B_READY_TO_RUN, this);
490 } else if (fInitError > B_ERRORS_END) {
491 // Registrar internal errors shouldn't fall into the user's hands.
492 fInitError = B_ERROR;
495 #else
496 // We need to have ReadyToRun called even when we're not using the registrar
497 PostMessage(B_READY_TO_RUN, this);
498 #endif // ifndef RUN_WITHOUT_REGISTRAR
500 if (fInitError == B_OK) {
501 // TODO: Not completely sure about the order, but this should be close.
503 // init be_app and be_app_messenger
504 be_app = this;
505 be_app_messenger = BMessenger(NULL, this);
507 // set the BHandler's name
508 SetName(ref.name);
510 // create meta MIME
511 BPath path;
512 if (registerApp && path.SetTo(&ref) == B_OK)
513 create_app_meta_mime(path.Path(), false, true, false);
515 #ifndef RUN_WITHOUT_APP_SERVER
516 // app server connection and IK initialization
517 if (initGUI)
518 fInitError = _InitGUIContext();
519 #endif // RUN_WITHOUT_APP_SERVER
522 // Return the error or exit, if there was an error and no error variable
523 // has been supplied.
524 if (_error != NULL) {
525 *_error = fInitError;
526 } else if (fInitError != B_OK) {
527 DBG(OUT("BApplication::InitData() failed: %s\n", strerror(fInitError)));
528 exit(0);
530 DBG(OUT("BApplication::InitData() done\n"));
534 port_id
535 BApplication::_GetPort(const char* signature)
537 return BLaunchRoster().GetPort(signature, NULL);
541 BArchivable*
542 BApplication::Instantiate(BMessage* data)
544 if (validate_instantiation(data, "BApplication"))
545 return new BApplication(data);
547 return NULL;
551 status_t
552 BApplication::Archive(BMessage* data, bool deep) const
554 status_t status = BLooper::Archive(data, deep);
555 if (status < B_OK)
556 return status;
558 app_info info;
559 status = GetAppInfo(&info);
560 if (status < B_OK)
561 return status;
563 status = data->AddString("mime_sig", info.signature);
564 if (status < B_OK)
565 return status;
567 return data->AddInt64("_pulse", fPulseRate);
571 status_t
572 BApplication::InitCheck() const
574 return fInitError;
578 thread_id
579 BApplication::Run()
581 if (fInitError != B_OK)
582 return fInitError;
584 AssertLocked();
586 if (fRunCalled)
587 debugger("BApplication::Run was already called. Can only be called once.");
589 fThread = find_thread(NULL);
590 fRunCalled = true;
592 task_looper();
594 delete fPulseRunner;
595 return fThread;
599 void
600 BApplication::Quit()
602 bool unlock = false;
603 if (!IsLocked()) {
604 const char* name = Name();
605 if (name == NULL)
606 name = "no-name";
608 printf("ERROR - you must Lock the application object before calling "
609 "Quit(), team=%" B_PRId32 ", looper=%s\n", Team(), name);
610 unlock = true;
611 if (!Lock())
612 return;
614 // Delete the object, if not running only.
615 if (!fRunCalled) {
616 delete this;
617 } else if (find_thread(NULL) != fThread) {
618 // ToDo: why shouldn't we set fTerminating to true directly in this case?
619 // We are not the looper thread.
620 // We push a _QUIT_ into the queue.
621 // TODO: When BLooper::AddMessage() is done, use that instead of
622 // PostMessage()??? This would overtake messages that are still at
623 // the port.
624 // NOTE: We must not unlock here -- otherwise we had to re-lock, which
625 // may not work. This is bad, since, if the port is full, it
626 // won't get emptier, as the looper thread needs to lock the object
627 // before dispatching messages.
628 while (PostMessage(_QUIT_, this) == B_WOULD_BLOCK)
629 snooze(10000);
630 } else {
631 // We are the looper thread.
632 // Just set fTerminating to true which makes us fall through the
633 // message dispatching loop and return from Run().
634 fTerminating = true;
637 // If we had to lock the object, unlock now.
638 if (unlock)
639 Unlock();
643 bool
644 BApplication::QuitRequested()
646 return _QuitAllWindows(false);
650 void
651 BApplication::Pulse()
653 // supposed to be implemented by subclasses
657 void
658 BApplication::ReadyToRun()
660 // supposed to be implemented by subclasses
664 void
665 BApplication::MessageReceived(BMessage* message)
667 switch (message->what) {
668 case B_COUNT_PROPERTIES:
669 case B_GET_PROPERTY:
670 case B_SET_PROPERTY:
672 int32 index;
673 BMessage specifier;
674 int32 what;
675 const char* property = NULL;
676 if (message->GetCurrentSpecifier(&index, &specifier, &what,
677 &property) < B_OK
678 || !ScriptReceived(message, index, &specifier, what,
679 property)) {
680 BLooper::MessageReceived(message);
682 break;
685 case B_SILENT_RELAUNCH:
686 // Sent to a B_SINGLE_LAUNCH application when it's launched again
687 // (see _InitData())
688 be_roster->ActivateApp(Team());
689 break;
691 case kMsgAppServerRestarted:
692 _ReconnectToServer();
693 break;
695 case kMsgDeleteServerMemoryArea:
697 int32 serverArea;
698 if (message->FindInt32("server area", &serverArea) == B_OK) {
699 // The link is not used, but we currently borrow its lock
700 BPrivate::AppServerLink link;
701 fServerAllocator->RemoveArea(serverArea);
703 break;
706 default:
707 BLooper::MessageReceived(message);
712 void
713 BApplication::ArgvReceived(int32 argc, char** argv)
715 // supposed to be implemented by subclasses
719 void
720 BApplication::AppActivated(bool active)
722 // supposed to be implemented by subclasses
726 void
727 BApplication::RefsReceived(BMessage* message)
729 // supposed to be implemented by subclasses
733 void
734 BApplication::AboutRequested()
736 // supposed to be implemented by subclasses
740 BHandler*
741 BApplication::ResolveSpecifier(BMessage* message, int32 index,
742 BMessage* specifier, int32 what, const char* property)
744 BPropertyInfo propInfo(sPropertyInfo);
745 status_t err = B_OK;
746 uint32 data;
748 if (propInfo.FindMatch(message, 0, specifier, what, property, &data) >= 0) {
749 switch (data) {
750 case kWindowByIndex:
752 int32 index;
753 err = specifier->FindInt32("index", &index);
754 if (err != B_OK)
755 break;
757 if (what == B_REVERSE_INDEX_SPECIFIER)
758 index = CountWindows() - index;
760 BWindow* window = WindowAt(index);
761 if (window != NULL) {
762 message->PopSpecifier();
763 BMessenger(window).SendMessage(message);
764 } else
765 err = B_BAD_INDEX;
766 break;
769 case kWindowByName:
771 const char* name;
772 err = specifier->FindString("name", &name);
773 if (err != B_OK)
774 break;
776 for (int32 i = 0;; i++) {
777 BWindow* window = WindowAt(i);
778 if (window == NULL) {
779 err = B_NAME_NOT_FOUND;
780 break;
782 if (window->Title() != NULL && !strcmp(window->Title(),
783 name)) {
784 message->PopSpecifier();
785 BMessenger(window).SendMessage(message);
786 break;
789 break;
792 case kLooperByIndex:
794 int32 index;
795 err = specifier->FindInt32("index", &index);
796 if (err != B_OK)
797 break;
799 if (what == B_REVERSE_INDEX_SPECIFIER)
800 index = CountLoopers() - index;
802 BLooper* looper = LooperAt(index);
803 if (looper != NULL) {
804 message->PopSpecifier();
805 BMessenger(looper).SendMessage(message);
806 } else
807 err = B_BAD_INDEX;
809 break;
812 case kLooperByID:
813 // TODO: implement getting looper by ID!
814 break;
816 case kLooperByName:
818 const char* name;
819 err = specifier->FindString("name", &name);
820 if (err != B_OK)
821 break;
823 for (int32 i = 0;; i++) {
824 BLooper* looper = LooperAt(i);
825 if (looper == NULL) {
826 err = B_NAME_NOT_FOUND;
827 break;
829 if (looper->Name() != NULL
830 && strcmp(looper->Name(), name) == 0) {
831 message->PopSpecifier();
832 BMessenger(looper).SendMessage(message);
833 break;
836 break;
839 case kApplication:
840 return this;
842 } else {
843 return BLooper::ResolveSpecifier(message, index, specifier, what,
844 property);
847 if (err != B_OK) {
848 BMessage reply(B_MESSAGE_NOT_UNDERSTOOD);
849 reply.AddInt32("error", err);
850 reply.AddString("message", strerror(err));
851 message->SendReply(&reply);
854 return NULL;
859 void
860 BApplication::ShowCursor()
862 BPrivate::AppServerLink link;
863 link.StartMessage(AS_SHOW_CURSOR);
864 link.Flush();
868 void
869 BApplication::HideCursor()
871 BPrivate::AppServerLink link;
872 link.StartMessage(AS_HIDE_CURSOR);
873 link.Flush();
877 void
878 BApplication::ObscureCursor()
880 BPrivate::AppServerLink link;
881 link.StartMessage(AS_OBSCURE_CURSOR);
882 link.Flush();
886 bool
887 BApplication::IsCursorHidden() const
889 BPrivate::AppServerLink link;
890 int32 status = B_ERROR;
891 link.StartMessage(AS_QUERY_CURSOR_HIDDEN);
892 link.FlushWithReply(status);
894 return status == B_OK;
898 void
899 BApplication::SetCursor(const void* cursorData)
901 BCursor cursor(cursorData);
902 SetCursor(&cursor, true);
903 // forces the cursor to be sync'ed
907 void
908 BApplication::SetCursor(const BCursor* cursor, bool sync)
910 BPrivate::AppServerLink link;
911 link.StartMessage(AS_SET_CURSOR);
912 link.Attach<bool>(sync);
913 link.Attach<int32>(cursor->fServerToken);
915 if (sync) {
916 int32 code;
917 link.FlushWithReply(code);
918 } else
919 link.Flush();
923 int32
924 BApplication::CountWindows() const
926 return _CountWindows(false);
927 // we're ignoring menu windows
931 BWindow*
932 BApplication::WindowAt(int32 index) const
934 return _WindowAt(index, false);
935 // we're ignoring menu windows
939 int32
940 BApplication::CountLoopers() const
942 AutoLocker<BLooperList> ListLock(gLooperList);
943 if (ListLock.IsLocked())
944 return gLooperList.CountLoopers();
946 // Some bad, non-specific thing has happened
947 return B_ERROR;
951 BLooper*
952 BApplication::LooperAt(int32 index) const
954 BLooper* looper = NULL;
955 AutoLocker<BLooperList> listLock(gLooperList);
956 if (listLock.IsLocked())
957 looper = gLooperList.LooperAt(index);
959 return looper;
963 bool
964 BApplication::IsLaunching() const
966 return !fReadyToRunCalled;
970 const char*
971 BApplication::Signature() const
973 return fAppName;
977 status_t
978 BApplication::GetAppInfo(app_info* info) const
980 if (be_app == NULL || be_roster == NULL)
981 return B_NO_INIT;
982 return be_roster->GetRunningAppInfo(be_app->Team(), info);
986 BResources*
987 BApplication::AppResources()
989 if (sAppResources == NULL)
990 pthread_once(&sAppResourcesInitOnce, &_InitAppResources);
992 return sAppResources;
996 void
997 BApplication::DispatchMessage(BMessage* message, BHandler* handler)
999 if (handler != this) {
1000 // it's not ours to dispatch
1001 BLooper::DispatchMessage(message, handler);
1002 return;
1005 switch (message->what) {
1006 case B_ARGV_RECEIVED:
1007 _ArgvReceived(message);
1008 break;
1010 case B_REFS_RECEIVED:
1012 // this adds the refs that are part of this message to the recent
1013 // lists, but only folders and documents are handled here
1014 entry_ref ref;
1015 int32 i = 0;
1016 while (message->FindRef("refs", i++, &ref) == B_OK) {
1017 BEntry entry(&ref, true);
1018 if (entry.InitCheck() != B_OK)
1019 continue;
1021 if (entry.IsDirectory())
1022 BRoster().AddToRecentFolders(&ref);
1023 else {
1024 // filter out applications, we only want to have documents
1025 // in the recent files list
1026 BNode node(&entry);
1027 BNodeInfo info(&node);
1029 char mimeType[B_MIME_TYPE_LENGTH];
1030 if (info.GetType(mimeType) != B_OK
1031 || strcasecmp(mimeType, B_APP_MIME_TYPE))
1032 BRoster().AddToRecentDocuments(&ref);
1036 RefsReceived(message);
1037 break;
1040 case B_READY_TO_RUN:
1041 if (!fReadyToRunCalled) {
1042 ReadyToRun();
1043 fReadyToRunCalled = true;
1045 break;
1047 case B_ABOUT_REQUESTED:
1048 AboutRequested();
1049 break;
1051 case B_PULSE:
1052 Pulse();
1053 break;
1055 case B_APP_ACTIVATED:
1057 bool active;
1058 if (message->FindBool("active", &active) == B_OK)
1059 AppActivated(active);
1060 break;
1063 case _SHOW_DRAG_HANDLES_:
1065 bool show;
1066 if (message->FindBool("show", &show) != B_OK)
1067 break;
1069 BDragger::Private::UpdateShowAllDraggers(show);
1070 break;
1073 // TODO: Handle these as well
1074 case _DISPOSE_DRAG_:
1075 case _PING_:
1076 puts("not yet handled message:");
1077 DBG(message->PrintToStream());
1078 break;
1080 default:
1081 BLooper::DispatchMessage(message, handler);
1082 break;
1087 void
1088 BApplication::SetPulseRate(bigtime_t rate)
1090 if (rate < 0)
1091 rate = 0;
1093 // BeBook states that we have only 100,000 microseconds granularity
1094 rate -= rate % 100000;
1096 if (!Lock())
1097 return;
1099 if (rate != 0) {
1100 // reset existing pulse runner, or create new one
1101 if (fPulseRunner == NULL) {
1102 BMessage pulse(B_PULSE);
1103 fPulseRunner = new BMessageRunner(be_app_messenger, &pulse, rate);
1104 } else
1105 fPulseRunner->SetInterval(rate);
1106 } else {
1107 // turn off pulse messages
1108 delete fPulseRunner;
1109 fPulseRunner = NULL;
1112 fPulseRate = rate;
1113 Unlock();
1117 status_t
1118 BApplication::GetSupportedSuites(BMessage* data)
1120 if (data == NULL)
1121 return B_BAD_VALUE;
1123 status_t status = data->AddString("suites", "suite/vnd.Be-application");
1124 if (status == B_OK) {
1125 BPropertyInfo propertyInfo(sPropertyInfo);
1126 status = data->AddFlat("messages", &propertyInfo);
1127 if (status == B_OK)
1128 status = BLooper::GetSupportedSuites(data);
1131 return status;
1135 status_t
1136 BApplication::Perform(perform_code d, void* arg)
1138 return BLooper::Perform(d, arg);
1142 void BApplication::_ReservedApplication1() {}
1143 void BApplication::_ReservedApplication2() {}
1144 void BApplication::_ReservedApplication3() {}
1145 void BApplication::_ReservedApplication4() {}
1146 void BApplication::_ReservedApplication5() {}
1147 void BApplication::_ReservedApplication6() {}
1148 void BApplication::_ReservedApplication7() {}
1149 void BApplication::_ReservedApplication8() {}
1152 bool
1153 BApplication::ScriptReceived(BMessage* message, int32 index,
1154 BMessage* specifier, int32 what, const char* property)
1156 BMessage reply(B_REPLY);
1157 status_t err = B_BAD_SCRIPT_SYNTAX;
1159 switch (message->what) {
1160 case B_GET_PROPERTY:
1161 if (strcmp("Loopers", property) == 0) {
1162 int32 count = CountLoopers();
1163 err = B_OK;
1164 for (int32 i=0; err == B_OK && i<count; i++) {
1165 BMessenger messenger(LooperAt(i));
1166 err = reply.AddMessenger("result", messenger);
1168 } else if (strcmp("Windows", property) == 0) {
1169 int32 count = CountWindows();
1170 err = B_OK;
1171 for (int32 i=0; err == B_OK && i<count; i++) {
1172 BMessenger messenger(WindowAt(i));
1173 err = reply.AddMessenger("result", messenger);
1175 } else if (strcmp("Window", property) == 0) {
1176 switch (what) {
1177 case B_INDEX_SPECIFIER:
1178 case B_REVERSE_INDEX_SPECIFIER:
1180 int32 index = -1;
1181 err = specifier->FindInt32("index", &index);
1182 if (err != B_OK)
1183 break;
1185 if (what == B_REVERSE_INDEX_SPECIFIER)
1186 index = CountWindows() - index;
1188 err = B_BAD_INDEX;
1189 BWindow* window = WindowAt(index);
1190 if (window == NULL)
1191 break;
1193 BMessenger messenger(window);
1194 err = reply.AddMessenger("result", messenger);
1195 break;
1198 case B_NAME_SPECIFIER:
1200 const char* name;
1201 err = specifier->FindString("name", &name);
1202 if (err != B_OK)
1203 break;
1204 err = B_NAME_NOT_FOUND;
1205 for (int32 i = 0; i < CountWindows(); i++) {
1206 BWindow* window = WindowAt(i);
1207 if (window && window->Name() != NULL
1208 && !strcmp(window->Name(), name)) {
1209 BMessenger messenger(window);
1210 err = reply.AddMessenger("result", messenger);
1211 break;
1214 break;
1217 } else if (strcmp("Looper", property) == 0) {
1218 switch (what) {
1219 case B_INDEX_SPECIFIER:
1220 case B_REVERSE_INDEX_SPECIFIER:
1222 int32 index = -1;
1223 err = specifier->FindInt32("index", &index);
1224 if (err != B_OK)
1225 break;
1227 if (what == B_REVERSE_INDEX_SPECIFIER)
1228 index = CountLoopers() - index;
1230 err = B_BAD_INDEX;
1231 BLooper* looper = LooperAt(index);
1232 if (looper == NULL)
1233 break;
1235 BMessenger messenger(looper);
1236 err = reply.AddMessenger("result", messenger);
1237 break;
1240 case B_NAME_SPECIFIER:
1242 const char* name;
1243 err = specifier->FindString("name", &name);
1244 if (err != B_OK)
1245 break;
1246 err = B_NAME_NOT_FOUND;
1247 for (int32 i = 0; i < CountLoopers(); i++) {
1248 BLooper* looper = LooperAt(i);
1249 if (looper != NULL && looper->Name()
1250 && strcmp(looper->Name(), name) == 0) {
1251 BMessenger messenger(looper);
1252 err = reply.AddMessenger("result", messenger);
1253 break;
1256 break;
1259 case B_ID_SPECIFIER:
1261 // TODO
1262 debug_printf("Looper's ID specifier used but not "
1263 "implemented.\n");
1264 break;
1267 } else if (strcmp("Name", property) == 0)
1268 err = reply.AddString("result", Name());
1270 break;
1272 case B_COUNT_PROPERTIES:
1273 if (strcmp("Looper", property) == 0)
1274 err = reply.AddInt32("result", CountLoopers());
1275 else if (strcmp("Window", property) == 0)
1276 err = reply.AddInt32("result", CountWindows());
1278 break;
1280 if (err == B_BAD_SCRIPT_SYNTAX)
1281 return false;
1283 if (err < B_OK) {
1284 reply.what = B_MESSAGE_NOT_UNDERSTOOD;
1285 reply.AddString("message", strerror(err));
1287 reply.AddInt32("error", err);
1288 message->SendReply(&reply);
1290 return true;
1294 void
1295 BApplication::BeginRectTracking(BRect rect, bool trackWhole)
1297 BPrivate::AppServerLink link;
1298 link.StartMessage(AS_BEGIN_RECT_TRACKING);
1299 link.Attach<BRect>(rect);
1300 link.Attach<int32>(trackWhole);
1301 link.Flush();
1305 void
1306 BApplication::EndRectTracking()
1308 BPrivate::AppServerLink link;
1309 link.StartMessage(AS_END_RECT_TRACKING);
1310 link.Flush();
1314 status_t
1315 BApplication::_SetupServerAllocator()
1317 fServerAllocator = new (std::nothrow) BPrivate::ServerMemoryAllocator();
1318 if (fServerAllocator == NULL)
1319 return B_NO_MEMORY;
1321 return fServerAllocator->InitCheck();
1325 status_t
1326 BApplication::_InitGUIContext()
1328 // An app_server connection is necessary for a lot of stuff, so get that first.
1329 status_t error = _ConnectToServer();
1330 if (error != B_OK)
1331 return error;
1333 // Initialize the IK after we have set be_app because of a construction
1334 // of a AppServerLink (which depends on be_app) nested inside the call
1335 // to get_menu_info.
1336 error = _init_interface_kit_();
1337 if (error != B_OK)
1338 return error;
1340 // create global system cursors
1341 B_CURSOR_SYSTEM_DEFAULT = new BCursor(B_HAND_CURSOR);
1342 B_CURSOR_I_BEAM = new BCursor(B_I_BEAM_CURSOR);
1344 // TODO: would be nice to get the workspace at launch time from the registrar
1345 fInitialWorkspace = current_workspace();
1347 return B_OK;
1351 status_t
1352 BApplication::_ConnectToServer()
1354 status_t status
1355 = create_desktop_connection(fServerLink, "a<app_server", 100);
1356 if (status != B_OK)
1357 return status;
1359 // AS_CREATE_APP:
1361 // Attach data:
1362 // 1) port_id - receiver port of a regular app
1363 // 2) port_id - looper port for this BApplication
1364 // 3) team_id - team identification field
1365 // 4) int32 - handler ID token of the app
1366 // 5) char* - signature of the regular app
1368 fServerLink->StartMessage(AS_CREATE_APP);
1369 fServerLink->Attach<port_id>(fServerLink->ReceiverPort());
1370 fServerLink->Attach<port_id>(_get_looper_port_(this));
1371 fServerLink->Attach<team_id>(Team());
1372 fServerLink->Attach<int32>(_get_object_token_(this));
1373 fServerLink->AttachString(fAppName);
1375 area_id sharedReadOnlyArea;
1376 team_id serverTeam;
1377 port_id serverPort;
1379 int32 code;
1380 if (fServerLink->FlushWithReply(code) == B_OK
1381 && code == B_OK) {
1382 // We don't need to contact the main app_server anymore
1383 // directly; we now talk to our server alter ego only.
1384 fServerLink->Read<port_id>(&serverPort);
1385 fServerLink->Read<area_id>(&sharedReadOnlyArea);
1386 fServerLink->Read<team_id>(&serverTeam);
1387 } else {
1388 fServerLink->SetSenderPort(-1);
1389 debugger("BApplication: couldn't obtain new app_server comm port");
1390 return B_ERROR;
1392 fServerLink->SetTargetTeam(serverTeam);
1393 fServerLink->SetSenderPort(serverPort);
1395 status = _SetupServerAllocator();
1396 if (status != B_OK)
1397 return status;
1399 area_id area;
1400 uint8* base;
1401 status = fServerAllocator->AddArea(sharedReadOnlyArea, area, base, true);
1402 if (status < B_OK)
1403 return status;
1405 fServerReadOnlyMemory = base;
1407 return B_OK;
1411 void
1412 BApplication::_ReconnectToServer()
1414 delete_port(fServerLink->SenderPort());
1415 delete_port(fServerLink->ReceiverPort());
1417 if (_ConnectToServer() != B_OK)
1418 debugger("Can't reconnect to app server!");
1420 AutoLocker<BLooperList> listLock(gLooperList);
1421 if (!listLock.IsLocked())
1422 return;
1424 uint32 count = gLooperList.CountLoopers();
1425 for (uint32 i = 0; i < count ; i++) {
1426 BWindow* window = dynamic_cast<BWindow*>(gLooperList.LooperAt(i));
1427 if (window == NULL)
1428 continue;
1429 BMessenger windowMessenger(window);
1430 windowMessenger.SendMessage(kMsgAppServerRestarted);
1433 reconnect_bitmaps_to_app_server();
1434 reconnect_pictures_to_app_server();
1438 #if 0
1439 void
1440 BApplication::send_drag(BMessage* message, int32 vs_token, BPoint offset,
1441 BRect dragRect, BHandler* replyTo)
1443 // TODO: implement
1447 void
1448 BApplication::send_drag(BMessage* message, int32 vs_token, BPoint offset,
1449 int32 bitmapToken, drawing_mode dragMode, BHandler* replyTo)
1451 // TODO: implement
1455 void
1456 BApplication::write_drag(_BSession_* session, BMessage* message)
1458 // TODO: implement
1460 #endif
1463 bool
1464 BApplication::_WindowQuitLoop(bool quitFilePanels, bool force)
1466 int32 index = 0;
1467 while (true) {
1468 BWindow* window = WindowAt(index);
1469 if (window == NULL)
1470 break;
1472 // NOTE: the window pointer might be stale, in case the looper
1473 // was already quit by quitting an earlier looper... but fortunately,
1474 // we can still call Lock() on the invalid pointer, and it
1475 // will return false...
1476 if (!window->Lock())
1477 continue;
1479 // don't quit file panels if we haven't been asked for it
1480 if (!quitFilePanels && window->IsFilePanel()) {
1481 window->Unlock();
1482 index++;
1483 continue;
1486 if (!force && !window->QuitRequested()
1487 && !(quitFilePanels && window->IsFilePanel())) {
1488 // the window does not want to quit, so we don't either
1489 window->Unlock();
1490 return false;
1493 // Re-lock, just to make sure that the user hasn't done nasty
1494 // things in QuitRequested(). Quit() unlocks fully, thus
1495 // double-locking is harmless.
1496 if (window->Lock())
1497 window->Quit();
1499 index = 0;
1500 // we need to continue at the start of the list again - it
1501 // might have changed
1504 return true;
1508 bool
1509 BApplication::_QuitAllWindows(bool force)
1511 AssertLocked();
1513 // We need to unlock here because BWindow::QuitRequested() must be
1514 // allowed to lock the application - which would cause a deadlock
1515 Unlock();
1517 bool quit = _WindowQuitLoop(false, force);
1518 if (quit)
1519 quit = _WindowQuitLoop(true, force);
1521 Lock();
1523 return quit;
1527 void
1528 BApplication::_ArgvReceived(BMessage* message)
1530 ASSERT(message != NULL);
1532 // build the argv vector
1533 status_t error = B_OK;
1534 int32 argc = 0;
1535 char** argv = NULL;
1536 if (message->FindInt32("argc", &argc) == B_OK && argc > 0) {
1537 // allocate a NULL terminated array
1538 argv = new(std::nothrow) char*[argc + 1];
1539 if (argv == NULL)
1540 return;
1542 // copy the arguments
1543 for (int32 i = 0; error == B_OK && i < argc; i++) {
1544 const char* arg = NULL;
1545 error = message->FindString("argv", i, &arg);
1546 if (error == B_OK && arg) {
1547 argv[i] = strdup(arg);
1548 if (argv[i] == NULL)
1549 error = B_NO_MEMORY;
1550 } else
1551 argc = i;
1554 argv[argc] = NULL;
1557 // call the hook
1558 if (error == B_OK && argc > 0)
1559 ArgvReceived(argc, argv);
1561 if (error != B_OK) {
1562 printf("Error parsing B_ARGV_RECEIVED message. Message:\n");
1563 message->PrintToStream();
1566 // cleanup
1567 if (argv) {
1568 for (int32 i = 0; i < argc; i++)
1569 free(argv[i]);
1570 delete[] argv;
1575 uint32
1576 BApplication::InitialWorkspace()
1578 return fInitialWorkspace;
1582 int32
1583 BApplication::_CountWindows(bool includeMenus) const
1585 uint32 count = 0;
1586 for (int32 i = 0; i < gLooperList.CountLoopers(); i++) {
1587 BWindow* window = dynamic_cast<BWindow*>(gLooperList.LooperAt(i));
1588 if (window != NULL && !window->fOffscreen && (includeMenus
1589 || dynamic_cast<BMenuWindow*>(window) == NULL)) {
1590 count++;
1594 return count;
1598 BWindow*
1599 BApplication::_WindowAt(uint32 index, bool includeMenus) const
1601 AutoLocker<BLooperList> listLock(gLooperList);
1602 if (!listLock.IsLocked())
1603 return NULL;
1605 uint32 count = gLooperList.CountLoopers();
1606 for (uint32 i = 0; i < count && index < count; i++) {
1607 BWindow* window = dynamic_cast<BWindow*>(gLooperList.LooperAt(i));
1608 if (window == NULL || (window != NULL && window->fOffscreen)
1609 || (!includeMenus && dynamic_cast<BMenuWindow*>(window) != NULL)) {
1610 index++;
1611 continue;
1614 if (i == index)
1615 return window;
1618 return NULL;
1622 /*static*/ void
1623 BApplication::_InitAppResources()
1625 entry_ref ref;
1626 bool found = false;
1628 // App is already running. Get its entry ref with
1629 // GetAppInfo()
1630 app_info appInfo;
1631 if (be_app && be_app->GetAppInfo(&appInfo) == B_OK) {
1632 ref = appInfo.ref;
1633 found = true;
1634 } else {
1635 // Run() hasn't been called yet
1636 found = BPrivate::get_app_ref(&ref) == B_OK;
1639 if (!found)
1640 return;
1642 BFile file(&ref, B_READ_ONLY);
1643 if (file.InitCheck() != B_OK)
1644 return;
1646 BResources* resources = new (std::nothrow) BResources(&file, false);
1647 if (resources == NULL || resources->InitCheck() != B_OK) {
1648 delete resources;
1649 return;
1652 sAppResources = resources;