vfs: check userland buffers before reading them.
[haiku.git] / src / kits / app / Application.cpp
blob4598a765f368e1b444357ca6d60d17e1138bab01
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;
64 BObjectList<BLooper> sOnQuitLooperList;
67 enum {
68 kWindowByIndex,
69 kWindowByName,
70 kLooperByIndex,
71 kLooperByID,
72 kLooperByName,
73 kApplication
77 static property_info sPropertyInfo[] = {
79 "Window",
80 {},
81 {B_INDEX_SPECIFIER, B_REVERSE_INDEX_SPECIFIER},
82 NULL, kWindowByIndex,
83 {},
84 {},
88 "Window",
89 {},
90 {B_NAME_SPECIFIER},
91 NULL, kWindowByName,
92 {},
93 {},
97 "Looper",
98 {},
99 {B_INDEX_SPECIFIER, B_REVERSE_INDEX_SPECIFIER},
100 NULL, kLooperByIndex,
106 "Looper",
108 {B_ID_SPECIFIER},
109 NULL, kLooperByID,
115 "Looper",
117 {B_NAME_SPECIFIER},
118 NULL, kLooperByName,
124 "Name",
125 {B_GET_PROPERTY},
126 {B_DIRECT_SPECIFIER},
127 NULL, kApplication,
128 {B_STRING_TYPE},
133 "Window",
134 {B_COUNT_PROPERTIES},
135 {B_DIRECT_SPECIFIER},
136 NULL, kApplication,
137 {B_INT32_TYPE},
142 "Loopers",
143 {B_GET_PROPERTY},
144 {B_DIRECT_SPECIFIER},
145 NULL, kApplication,
146 {B_MESSENGER_TYPE},
151 "Windows",
152 {B_GET_PROPERTY},
153 {B_DIRECT_SPECIFIER},
154 NULL, kApplication,
155 {B_MESSENGER_TYPE},
160 "Looper",
161 {B_COUNT_PROPERTIES},
162 {B_DIRECT_SPECIFIER},
163 NULL, kApplication,
164 {B_INT32_TYPE},
169 { 0 }
173 // argc/argv
174 extern const int __libc_argc;
175 extern const char* const *__libc_argv;
178 // debugging
179 //#define DBG(x) x
180 #define DBG(x)
181 #define OUT printf
184 // #pragma mark - static helper functions
188 \brief Checks whether the supplied string is a valid application signature.
190 An error message is printed, if the string is no valid app signature.
192 \param signature The string to be checked.
194 \return A status code.
195 \retval B_OK \a signature is a valid app signature.
196 \retval B_BAD_VALUE \a signature is \c NULL or no valid app signature.
198 static status_t
199 check_app_signature(const char* signature)
201 bool isValid = false;
202 BMimeType type(signature);
204 if (type.IsValid() && !type.IsSupertypeOnly()
205 && BMimeType("application").Contains(&type)) {
206 isValid = true;
209 if (!isValid) {
210 printf("bad signature (%s), must begin with \"application/\" and "
211 "can't conflict with existing registered mime types inside "
212 "the \"application\" media type.\n", signature);
215 return (isValid ? B_OK : B_BAD_VALUE);
219 #ifndef RUN_WITHOUT_REGISTRAR
220 // Fills the passed BMessage with B_ARGV_RECEIVED infos.
221 static void
222 fill_argv_message(BMessage &message)
224 message.what = B_ARGV_RECEIVED;
226 int32 argc = __libc_argc;
227 const char* const *argv = __libc_argv;
229 // add argc
230 message.AddInt32("argc", argc);
232 // add argv
233 for (int32 i = 0; i < argc; i++) {
234 if (argv[i] != NULL)
235 message.AddString("argv", argv[i]);
238 // add current working directory
239 char cwd[B_PATH_NAME_LENGTH];
240 if (getcwd(cwd, B_PATH_NAME_LENGTH))
241 message.AddString("cwd", cwd);
243 #endif
246 // #pragma mark - BApplication
249 BApplication::BApplication(const char* signature)
251 BLooper(kDefaultLooperName)
253 _InitData(signature, true, NULL);
257 BApplication::BApplication(const char* signature, status_t* _error)
259 BLooper(kDefaultLooperName)
261 _InitData(signature, true, _error);
265 BApplication::BApplication(const char* signature, const char* looperName,
266 port_id port, bool initGUI, status_t* _error)
268 BLooper(B_NORMAL_PRIORITY + 1, port < 0 ? _GetPort(signature) : port,
269 looperName != NULL ? looperName : kDefaultLooperName)
271 _InitData(signature, initGUI, _error);
272 if (port < 0)
273 fOwnsPort = false;
277 BApplication::BApplication(BMessage* data)
278 // Note: BeOS calls the private BLooper(int32, port_id, const char*)
279 // constructor here, test if it's needed
281 BLooper(kDefaultLooperName)
283 const char* signature = NULL;
284 data->FindString("mime_sig", &signature);
286 _InitData(signature, true, NULL);
288 bigtime_t pulseRate;
289 if (data->FindInt64("_pulse", &pulseRate) == B_OK)
290 SetPulseRate(pulseRate);
294 BApplication::BApplication(uint32 signature)
299 BApplication::BApplication(const BApplication &rhs)
304 BApplication::~BApplication()
306 Lock();
308 // tell all loopers(usually windows) to quit. Also, wait for them.
309 _QuitAllWindows(true);
311 // quit registered loopers
312 for (int32 i = 0; i < sOnQuitLooperList.CountItems(); i++) {
313 BLooper* looper = sOnQuitLooperList.ItemAt(i);
314 if (looper->Lock())
315 looper->Quit();
318 // unregister from the roster
319 BRoster::Private().RemoveApp(Team());
321 #ifndef RUN_WITHOUT_APP_SERVER
322 // tell app_server we're quitting...
323 if (be_app) {
324 // be_app can be NULL here if the application fails to initialize
325 // correctly. For example, if it's already running and it's set to
326 // exclusive launch.
327 BPrivate::AppServerLink link;
328 link.StartMessage(B_QUIT_REQUESTED);
329 link.Flush();
331 delete_port(fServerLink->SenderPort());
332 delete_port(fServerLink->ReceiverPort());
333 delete fServerLink;
334 #endif // RUN_WITHOUT_APP_SERVER
336 delete fServerAllocator;
338 // uninitialize be_app, the be_app_messenger is invalidated automatically
339 be_app = NULL;
343 BApplication&
344 BApplication::operator=(const BApplication &rhs)
346 return *this;
350 void
351 BApplication::_InitData(const char* signature, bool initGUI, status_t* _error)
353 DBG(OUT("BApplication::InitData(`%s', %p)\n", signature, _error));
354 // check whether there exists already an application
355 if (be_app != NULL)
356 debugger("2 BApplication objects were created. Only one is allowed.");
358 fServerLink = new BPrivate::PortLink(-1, -1);
359 fServerAllocator = NULL;
360 fServerReadOnlyMemory = NULL;
361 fInitialWorkspace = 0;
362 //fDraggedMessage = NULL;
363 fReadyToRunCalled = false;
365 // initially, there is no pulse
366 fPulseRunner = NULL;
367 fPulseRate = 0;
369 // check signature
370 fInitError = check_app_signature(signature);
371 fAppName = signature;
373 #ifndef RUN_WITHOUT_REGISTRAR
374 bool registerApp = signature == NULL
375 || (strcasecmp(signature, B_REGISTRAR_SIGNATURE) != 0
376 && strcasecmp(signature, kLaunchDaemonSignature) != 0);
377 // get team and thread
378 team_id team = Team();
379 thread_id thread = BPrivate::main_thread_for(team);
380 #endif
382 // get app executable ref
383 entry_ref ref;
384 if (fInitError == B_OK) {
385 fInitError = BPrivate::get_app_ref(&ref);
386 if (fInitError != B_OK) {
387 DBG(OUT("BApplication::InitData(): Failed to get app ref: %s\n",
388 strerror(fInitError)));
392 // get the BAppFileInfo and extract the information we need
393 uint32 appFlags = B_REG_DEFAULT_APP_FLAGS;
394 if (fInitError == B_OK) {
395 BAppFileInfo fileInfo;
396 BFile file(&ref, B_READ_ONLY);
397 fInitError = fileInfo.SetTo(&file);
398 if (fInitError == B_OK) {
399 fileInfo.GetAppFlags(&appFlags);
400 char appFileSignature[B_MIME_TYPE_LENGTH];
401 // compare the file signature and the supplied signature
402 if (fileInfo.GetSignature(appFileSignature) == B_OK
403 && strcasecmp(appFileSignature, signature) != 0) {
404 printf("Signature in rsrc doesn't match constructor arg. (%s, %s)\n",
405 signature, appFileSignature);
407 } else {
408 DBG(OUT("BApplication::InitData(): Failed to get info from: "
409 "BAppFileInfo: %s\n", strerror(fInitError)));
413 #ifndef RUN_WITHOUT_REGISTRAR
414 // check whether be_roster is valid
415 if (fInitError == B_OK && registerApp
416 && !BRoster::Private().IsMessengerValid(false)) {
417 printf("FATAL: be_roster is not valid. Is the registrar running?\n");
418 fInitError = B_NO_INIT;
421 // check whether or not we are pre-registered
422 bool preRegistered = false;
423 app_info appInfo;
424 if (fInitError == B_OK && registerApp) {
425 if (BRoster::Private().IsAppRegistered(&ref, team, 0, &preRegistered,
426 &appInfo) != B_OK) {
427 preRegistered = false;
430 if (preRegistered) {
431 // we are pre-registered => the app info has been filled in
432 // Check whether we need to replace the looper port with a port
433 // created by the roster.
434 if (appInfo.port >= 0 && appInfo.port != fMsgPort) {
435 delete_port(fMsgPort);
436 fMsgPort = appInfo.port;
437 } else
438 appInfo.port = fMsgPort;
439 // check the signature and correct it, if necessary, also the case
440 if (strcmp(appInfo.signature, fAppName))
441 BRoster::Private().SetSignature(team, fAppName);
442 // complete the registration
443 fInitError = BRoster::Private().CompleteRegistration(team, thread,
444 appInfo.port);
445 } else if (fInitError == B_OK) {
446 // not pre-registered -- try to register the application
447 team_id otherTeam = -1;
448 if (registerApp) {
449 fInitError = BRoster::Private().AddApplication(signature, &ref,
450 appFlags, team, thread, fMsgPort, true, NULL, &otherTeam);
451 if (fInitError != B_OK) {
452 DBG(OUT("BApplication::InitData(): Failed to add app: %s\n",
453 strerror(fInitError)));
456 if (fInitError == B_ALREADY_RUNNING) {
457 // An instance is already running and we asked for
458 // single/exclusive launch. Send our argv to the running app.
459 // Do that only, if the app is NOT B_ARGV_ONLY.
460 if (otherTeam >= 0) {
461 BMessenger otherApp(NULL, otherTeam);
462 app_info otherAppInfo;
463 bool argvOnly = be_roster->GetRunningAppInfo(otherTeam,
464 &otherAppInfo) == B_OK
465 && (otherAppInfo.flags & B_ARGV_ONLY) != 0;
467 if (__libc_argc > 1 && !argvOnly) {
468 // create an B_ARGV_RECEIVED message
469 BMessage argvMessage(B_ARGV_RECEIVED);
470 fill_argv_message(argvMessage);
472 // replace the first argv string with the path of the
473 // other application
474 BPath path;
475 if (path.SetTo(&otherAppInfo.ref) == B_OK)
476 argvMessage.ReplaceString("argv", 0, path.Path());
478 // send the message
479 otherApp.SendMessage(&argvMessage);
480 } else if (!argvOnly)
481 otherApp.SendMessage(B_SILENT_RELAUNCH);
483 } else if (fInitError == B_OK) {
484 // the registrations was successful
485 // Create a B_ARGV_RECEIVED message and send it to ourselves.
486 // Do that even, if we are B_ARGV_ONLY.
487 // TODO: When BLooper::AddMessage() is done, use that instead of
488 // PostMessage().
490 DBG(OUT("info: BApplication successfully registered.\n"));
492 if (__libc_argc > 1) {
493 BMessage argvMessage(B_ARGV_RECEIVED);
494 fill_argv_message(argvMessage);
495 PostMessage(&argvMessage, this);
497 // send a B_READY_TO_RUN message as well
498 PostMessage(B_READY_TO_RUN, this);
499 } else if (fInitError > B_ERRORS_END) {
500 // Registrar internal errors shouldn't fall into the user's hands.
501 fInitError = B_ERROR;
504 #else
505 // We need to have ReadyToRun called even when we're not using the registrar
506 PostMessage(B_READY_TO_RUN, this);
507 #endif // ifndef RUN_WITHOUT_REGISTRAR
509 if (fInitError == B_OK) {
510 // TODO: Not completely sure about the order, but this should be close.
512 // init be_app and be_app_messenger
513 be_app = this;
514 be_app_messenger = BMessenger(NULL, this);
516 // set the BHandler's name
517 SetName(ref.name);
519 // create meta MIME
520 BPath path;
521 if (registerApp && path.SetTo(&ref) == B_OK)
522 create_app_meta_mime(path.Path(), false, true, false);
524 #ifndef RUN_WITHOUT_APP_SERVER
525 // app server connection and IK initialization
526 if (initGUI)
527 fInitError = _InitGUIContext();
528 #endif // RUN_WITHOUT_APP_SERVER
531 // Return the error or exit, if there was an error and no error variable
532 // has been supplied.
533 if (_error != NULL) {
534 *_error = fInitError;
535 } else if (fInitError != B_OK) {
536 DBG(OUT("BApplication::InitData() failed: %s\n", strerror(fInitError)));
537 exit(0);
539 DBG(OUT("BApplication::InitData() done\n"));
543 port_id
544 BApplication::_GetPort(const char* signature)
546 return BLaunchRoster().GetPort(signature, NULL);
550 BArchivable*
551 BApplication::Instantiate(BMessage* data)
553 if (validate_instantiation(data, "BApplication"))
554 return new BApplication(data);
556 return NULL;
560 status_t
561 BApplication::Archive(BMessage* data, bool deep) const
563 status_t status = BLooper::Archive(data, deep);
564 if (status < B_OK)
565 return status;
567 app_info info;
568 status = GetAppInfo(&info);
569 if (status < B_OK)
570 return status;
572 status = data->AddString("mime_sig", info.signature);
573 if (status < B_OK)
574 return status;
576 return data->AddInt64("_pulse", fPulseRate);
580 status_t
581 BApplication::InitCheck() const
583 return fInitError;
587 thread_id
588 BApplication::Run()
590 if (fInitError != B_OK)
591 return fInitError;
593 AssertLocked();
595 if (fRunCalled)
596 debugger("BApplication::Run was already called. Can only be called once.");
598 fThread = find_thread(NULL);
599 fRunCalled = true;
601 task_looper();
603 delete fPulseRunner;
604 return fThread;
608 void
609 BApplication::Quit()
611 bool unlock = false;
612 if (!IsLocked()) {
613 const char* name = Name();
614 if (name == NULL)
615 name = "no-name";
617 printf("ERROR - you must Lock the application object before calling "
618 "Quit(), team=%" B_PRId32 ", looper=%s\n", Team(), name);
619 unlock = true;
620 if (!Lock())
621 return;
623 // Delete the object, if not running only.
624 if (!fRunCalled) {
625 delete this;
626 } else if (find_thread(NULL) != fThread) {
627 // ToDo: why shouldn't we set fTerminating to true directly in this case?
628 // We are not the looper thread.
629 // We push a _QUIT_ into the queue.
630 // TODO: When BLooper::AddMessage() is done, use that instead of
631 // PostMessage()??? This would overtake messages that are still at
632 // the port.
633 // NOTE: We must not unlock here -- otherwise we had to re-lock, which
634 // may not work. This is bad, since, if the port is full, it
635 // won't get emptier, as the looper thread needs to lock the object
636 // before dispatching messages.
637 while (PostMessage(_QUIT_, this) == B_WOULD_BLOCK)
638 snooze(10000);
639 } else {
640 // We are the looper thread.
641 // Just set fTerminating to true which makes us fall through the
642 // message dispatching loop and return from Run().
643 fTerminating = true;
646 // If we had to lock the object, unlock now.
647 if (unlock)
648 Unlock();
652 bool
653 BApplication::QuitRequested()
655 return _QuitAllWindows(false);
659 void
660 BApplication::Pulse()
662 // supposed to be implemented by subclasses
666 void
667 BApplication::ReadyToRun()
669 // supposed to be implemented by subclasses
673 void
674 BApplication::MessageReceived(BMessage* message)
676 switch (message->what) {
677 case B_COUNT_PROPERTIES:
678 case B_GET_PROPERTY:
679 case B_SET_PROPERTY:
681 int32 index;
682 BMessage specifier;
683 int32 what;
684 const char* property = NULL;
685 if (message->GetCurrentSpecifier(&index, &specifier, &what,
686 &property) < B_OK
687 || !ScriptReceived(message, index, &specifier, what,
688 property)) {
689 BLooper::MessageReceived(message);
691 break;
694 case B_SILENT_RELAUNCH:
695 // Sent to a B_SINGLE_LAUNCH application when it's launched again
696 // (see _InitData())
697 be_roster->ActivateApp(Team());
698 break;
700 case kMsgAppServerRestarted:
701 _ReconnectToServer();
702 break;
704 case kMsgDeleteServerMemoryArea:
706 int32 serverArea;
707 if (message->FindInt32("server area", &serverArea) == B_OK) {
708 // The link is not used, but we currently borrow its lock
709 BPrivate::AppServerLink link;
710 fServerAllocator->RemoveArea(serverArea);
712 break;
715 default:
716 BLooper::MessageReceived(message);
721 void
722 BApplication::ArgvReceived(int32 argc, char** argv)
724 // supposed to be implemented by subclasses
728 void
729 BApplication::AppActivated(bool active)
731 // supposed to be implemented by subclasses
735 void
736 BApplication::RefsReceived(BMessage* message)
738 // supposed to be implemented by subclasses
742 void
743 BApplication::AboutRequested()
745 // supposed to be implemented by subclasses
749 BHandler*
750 BApplication::ResolveSpecifier(BMessage* message, int32 index,
751 BMessage* specifier, int32 what, const char* property)
753 BPropertyInfo propInfo(sPropertyInfo);
754 status_t err = B_OK;
755 uint32 data;
757 if (propInfo.FindMatch(message, 0, specifier, what, property, &data) >= 0) {
758 switch (data) {
759 case kWindowByIndex:
761 int32 index;
762 err = specifier->FindInt32("index", &index);
763 if (err != B_OK)
764 break;
766 if (what == B_REVERSE_INDEX_SPECIFIER)
767 index = CountWindows() - index;
769 BWindow* window = WindowAt(index);
770 if (window != NULL) {
771 message->PopSpecifier();
772 BMessenger(window).SendMessage(message);
773 } else
774 err = B_BAD_INDEX;
775 break;
778 case kWindowByName:
780 const char* name;
781 err = specifier->FindString("name", &name);
782 if (err != B_OK)
783 break;
785 for (int32 i = 0;; i++) {
786 BWindow* window = WindowAt(i);
787 if (window == NULL) {
788 err = B_NAME_NOT_FOUND;
789 break;
791 if (window->Title() != NULL && !strcmp(window->Title(),
792 name)) {
793 message->PopSpecifier();
794 BMessenger(window).SendMessage(message);
795 break;
798 break;
801 case kLooperByIndex:
803 int32 index;
804 err = specifier->FindInt32("index", &index);
805 if (err != B_OK)
806 break;
808 if (what == B_REVERSE_INDEX_SPECIFIER)
809 index = CountLoopers() - index;
811 BLooper* looper = LooperAt(index);
812 if (looper != NULL) {
813 message->PopSpecifier();
814 BMessenger(looper).SendMessage(message);
815 } else
816 err = B_BAD_INDEX;
818 break;
821 case kLooperByID:
822 // TODO: implement getting looper by ID!
823 break;
825 case kLooperByName:
827 const char* name;
828 err = specifier->FindString("name", &name);
829 if (err != B_OK)
830 break;
832 for (int32 i = 0;; i++) {
833 BLooper* looper = LooperAt(i);
834 if (looper == NULL) {
835 err = B_NAME_NOT_FOUND;
836 break;
838 if (looper->Name() != NULL
839 && strcmp(looper->Name(), name) == 0) {
840 message->PopSpecifier();
841 BMessenger(looper).SendMessage(message);
842 break;
845 break;
848 case kApplication:
849 return this;
851 } else {
852 return BLooper::ResolveSpecifier(message, index, specifier, what,
853 property);
856 if (err != B_OK) {
857 BMessage reply(B_MESSAGE_NOT_UNDERSTOOD);
858 reply.AddInt32("error", err);
859 reply.AddString("message", strerror(err));
860 message->SendReply(&reply);
863 return NULL;
868 void
869 BApplication::ShowCursor()
871 BPrivate::AppServerLink link;
872 link.StartMessage(AS_SHOW_CURSOR);
873 link.Flush();
877 void
878 BApplication::HideCursor()
880 BPrivate::AppServerLink link;
881 link.StartMessage(AS_HIDE_CURSOR);
882 link.Flush();
886 void
887 BApplication::ObscureCursor()
889 BPrivate::AppServerLink link;
890 link.StartMessage(AS_OBSCURE_CURSOR);
891 link.Flush();
895 bool
896 BApplication::IsCursorHidden() const
898 BPrivate::AppServerLink link;
899 int32 status = B_ERROR;
900 link.StartMessage(AS_QUERY_CURSOR_HIDDEN);
901 link.FlushWithReply(status);
903 return status == B_OK;
907 void
908 BApplication::SetCursor(const void* cursorData)
910 BCursor cursor(cursorData);
911 SetCursor(&cursor, true);
912 // forces the cursor to be sync'ed
916 void
917 BApplication::SetCursor(const BCursor* cursor, bool sync)
919 BPrivate::AppServerLink link;
920 link.StartMessage(AS_SET_CURSOR);
921 link.Attach<bool>(sync);
922 link.Attach<int32>(cursor->fServerToken);
924 if (sync) {
925 int32 code;
926 link.FlushWithReply(code);
927 } else
928 link.Flush();
932 int32
933 BApplication::CountWindows() const
935 return _CountWindows(false);
936 // we're ignoring menu windows
940 BWindow*
941 BApplication::WindowAt(int32 index) const
943 return _WindowAt(index, false);
944 // we're ignoring menu windows
948 int32
949 BApplication::CountLoopers() const
951 AutoLocker<BLooperList> ListLock(gLooperList);
952 if (ListLock.IsLocked())
953 return gLooperList.CountLoopers();
955 // Some bad, non-specific thing has happened
956 return B_ERROR;
960 BLooper*
961 BApplication::LooperAt(int32 index) const
963 BLooper* looper = NULL;
964 AutoLocker<BLooperList> listLock(gLooperList);
965 if (listLock.IsLocked())
966 looper = gLooperList.LooperAt(index);
968 return looper;
972 status_t
973 BApplication::RegisterLooper(BLooper* looper)
975 BWindow* window = dynamic_cast<BWindow*>(looper);
976 if (window != NULL)
977 return B_BAD_VALUE;
979 if (sOnQuitLooperList.HasItem(looper))
980 return B_ERROR;
982 if (sOnQuitLooperList.AddItem(looper) != true)
983 return B_ERROR;
985 return B_OK;
989 status_t
990 BApplication::UnregisterLooper(BLooper* looper)
992 BWindow* window = dynamic_cast<BWindow*>(looper);
993 if (window != NULL)
994 return B_BAD_VALUE;
996 if (!sOnQuitLooperList.HasItem(looper))
997 return B_ERROR;
999 if (sOnQuitLooperList.RemoveItem(looper) != true)
1000 return B_ERROR;
1002 return B_OK;
1006 bool
1007 BApplication::IsLaunching() const
1009 return !fReadyToRunCalled;
1013 const char*
1014 BApplication::Signature() const
1016 return fAppName;
1020 status_t
1021 BApplication::GetAppInfo(app_info* info) const
1023 if (be_app == NULL || be_roster == NULL)
1024 return B_NO_INIT;
1025 return be_roster->GetRunningAppInfo(be_app->Team(), info);
1029 BResources*
1030 BApplication::AppResources()
1032 if (sAppResources == NULL)
1033 pthread_once(&sAppResourcesInitOnce, &_InitAppResources);
1035 return sAppResources;
1039 void
1040 BApplication::DispatchMessage(BMessage* message, BHandler* handler)
1042 if (handler != this) {
1043 // it's not ours to dispatch
1044 BLooper::DispatchMessage(message, handler);
1045 return;
1048 switch (message->what) {
1049 case B_ARGV_RECEIVED:
1050 _ArgvReceived(message);
1051 break;
1053 case B_REFS_RECEIVED:
1055 // this adds the refs that are part of this message to the recent
1056 // lists, but only folders and documents are handled here
1057 entry_ref ref;
1058 int32 i = 0;
1059 while (message->FindRef("refs", i++, &ref) == B_OK) {
1060 BEntry entry(&ref, true);
1061 if (entry.InitCheck() != B_OK)
1062 continue;
1064 if (entry.IsDirectory())
1065 BRoster().AddToRecentFolders(&ref);
1066 else {
1067 // filter out applications, we only want to have documents
1068 // in the recent files list
1069 BNode node(&entry);
1070 BNodeInfo info(&node);
1072 char mimeType[B_MIME_TYPE_LENGTH];
1073 if (info.GetType(mimeType) != B_OK
1074 || strcasecmp(mimeType, B_APP_MIME_TYPE))
1075 BRoster().AddToRecentDocuments(&ref);
1079 RefsReceived(message);
1080 break;
1083 case B_READY_TO_RUN:
1084 if (!fReadyToRunCalled) {
1085 ReadyToRun();
1086 fReadyToRunCalled = true;
1088 break;
1090 case B_ABOUT_REQUESTED:
1091 AboutRequested();
1092 break;
1094 case B_PULSE:
1095 Pulse();
1096 break;
1098 case B_APP_ACTIVATED:
1100 bool active;
1101 if (message->FindBool("active", &active) == B_OK)
1102 AppActivated(active);
1103 break;
1106 case B_COLORS_UPDATED:
1108 AutoLocker<BLooperList> listLock(gLooperList);
1109 if (!listLock.IsLocked())
1110 break;
1112 BWindow* window = NULL;
1113 uint32 count = gLooperList.CountLoopers();
1114 for (uint32 index = 0; index < count; ++index) {
1115 window = dynamic_cast<BWindow*>(gLooperList.LooperAt(index));
1116 if (window == NULL || (window != NULL && window->fOffscreen))
1117 continue;
1118 window->PostMessage(message);
1120 break;
1123 case _SHOW_DRAG_HANDLES_:
1125 bool show;
1126 if (message->FindBool("show", &show) != B_OK)
1127 break;
1129 BDragger::Private::UpdateShowAllDraggers(show);
1130 break;
1133 // TODO: Handle these as well
1134 case _DISPOSE_DRAG_:
1135 case _PING_:
1136 puts("not yet handled message:");
1137 DBG(message->PrintToStream());
1138 break;
1140 default:
1141 BLooper::DispatchMessage(message, handler);
1142 break;
1147 void
1148 BApplication::SetPulseRate(bigtime_t rate)
1150 if (rate < 0)
1151 rate = 0;
1153 // BeBook states that we have only 100,000 microseconds granularity
1154 rate -= rate % 100000;
1156 if (!Lock())
1157 return;
1159 if (rate != 0) {
1160 // reset existing pulse runner, or create new one
1161 if (fPulseRunner == NULL) {
1162 BMessage pulse(B_PULSE);
1163 fPulseRunner = new BMessageRunner(be_app_messenger, &pulse, rate);
1164 } else
1165 fPulseRunner->SetInterval(rate);
1166 } else {
1167 // turn off pulse messages
1168 delete fPulseRunner;
1169 fPulseRunner = NULL;
1172 fPulseRate = rate;
1173 Unlock();
1177 status_t
1178 BApplication::GetSupportedSuites(BMessage* data)
1180 if (data == NULL)
1181 return B_BAD_VALUE;
1183 status_t status = data->AddString("suites", "suite/vnd.Be-application");
1184 if (status == B_OK) {
1185 BPropertyInfo propertyInfo(sPropertyInfo);
1186 status = data->AddFlat("messages", &propertyInfo);
1187 if (status == B_OK)
1188 status = BLooper::GetSupportedSuites(data);
1191 return status;
1195 status_t
1196 BApplication::Perform(perform_code d, void* arg)
1198 return BLooper::Perform(d, arg);
1202 void BApplication::_ReservedApplication1() {}
1203 void BApplication::_ReservedApplication2() {}
1204 void BApplication::_ReservedApplication3() {}
1205 void BApplication::_ReservedApplication4() {}
1206 void BApplication::_ReservedApplication5() {}
1207 void BApplication::_ReservedApplication6() {}
1208 void BApplication::_ReservedApplication7() {}
1209 void BApplication::_ReservedApplication8() {}
1212 bool
1213 BApplication::ScriptReceived(BMessage* message, int32 index,
1214 BMessage* specifier, int32 what, const char* property)
1216 BMessage reply(B_REPLY);
1217 status_t err = B_BAD_SCRIPT_SYNTAX;
1219 switch (message->what) {
1220 case B_GET_PROPERTY:
1221 if (strcmp("Loopers", property) == 0) {
1222 int32 count = CountLoopers();
1223 err = B_OK;
1224 for (int32 i=0; err == B_OK && i<count; i++) {
1225 BMessenger messenger(LooperAt(i));
1226 err = reply.AddMessenger("result", messenger);
1228 } else if (strcmp("Windows", property) == 0) {
1229 int32 count = CountWindows();
1230 err = B_OK;
1231 for (int32 i=0; err == B_OK && i<count; i++) {
1232 BMessenger messenger(WindowAt(i));
1233 err = reply.AddMessenger("result", messenger);
1235 } else if (strcmp("Window", property) == 0) {
1236 switch (what) {
1237 case B_INDEX_SPECIFIER:
1238 case B_REVERSE_INDEX_SPECIFIER:
1240 int32 index = -1;
1241 err = specifier->FindInt32("index", &index);
1242 if (err != B_OK)
1243 break;
1245 if (what == B_REVERSE_INDEX_SPECIFIER)
1246 index = CountWindows() - index;
1248 err = B_BAD_INDEX;
1249 BWindow* window = WindowAt(index);
1250 if (window == NULL)
1251 break;
1253 BMessenger messenger(window);
1254 err = reply.AddMessenger("result", messenger);
1255 break;
1258 case B_NAME_SPECIFIER:
1260 const char* name;
1261 err = specifier->FindString("name", &name);
1262 if (err != B_OK)
1263 break;
1264 err = B_NAME_NOT_FOUND;
1265 for (int32 i = 0; i < CountWindows(); i++) {
1266 BWindow* window = WindowAt(i);
1267 if (window && window->Name() != NULL
1268 && !strcmp(window->Name(), name)) {
1269 BMessenger messenger(window);
1270 err = reply.AddMessenger("result", messenger);
1271 break;
1274 break;
1277 } else if (strcmp("Looper", property) == 0) {
1278 switch (what) {
1279 case B_INDEX_SPECIFIER:
1280 case B_REVERSE_INDEX_SPECIFIER:
1282 int32 index = -1;
1283 err = specifier->FindInt32("index", &index);
1284 if (err != B_OK)
1285 break;
1287 if (what == B_REVERSE_INDEX_SPECIFIER)
1288 index = CountLoopers() - index;
1290 err = B_BAD_INDEX;
1291 BLooper* looper = LooperAt(index);
1292 if (looper == NULL)
1293 break;
1295 BMessenger messenger(looper);
1296 err = reply.AddMessenger("result", messenger);
1297 break;
1300 case B_NAME_SPECIFIER:
1302 const char* name;
1303 err = specifier->FindString("name", &name);
1304 if (err != B_OK)
1305 break;
1306 err = B_NAME_NOT_FOUND;
1307 for (int32 i = 0; i < CountLoopers(); i++) {
1308 BLooper* looper = LooperAt(i);
1309 if (looper != NULL && looper->Name()
1310 && strcmp(looper->Name(), name) == 0) {
1311 BMessenger messenger(looper);
1312 err = reply.AddMessenger("result", messenger);
1313 break;
1316 break;
1319 case B_ID_SPECIFIER:
1321 // TODO
1322 debug_printf("Looper's ID specifier used but not "
1323 "implemented.\n");
1324 break;
1327 } else if (strcmp("Name", property) == 0)
1328 err = reply.AddString("result", Name());
1330 break;
1332 case B_COUNT_PROPERTIES:
1333 if (strcmp("Looper", property) == 0)
1334 err = reply.AddInt32("result", CountLoopers());
1335 else if (strcmp("Window", property) == 0)
1336 err = reply.AddInt32("result", CountWindows());
1338 break;
1340 if (err == B_BAD_SCRIPT_SYNTAX)
1341 return false;
1343 if (err < B_OK) {
1344 reply.what = B_MESSAGE_NOT_UNDERSTOOD;
1345 reply.AddString("message", strerror(err));
1347 reply.AddInt32("error", err);
1348 message->SendReply(&reply);
1350 return true;
1354 void
1355 BApplication::BeginRectTracking(BRect rect, bool trackWhole)
1357 BPrivate::AppServerLink link;
1358 link.StartMessage(AS_BEGIN_RECT_TRACKING);
1359 link.Attach<BRect>(rect);
1360 link.Attach<int32>(trackWhole);
1361 link.Flush();
1365 void
1366 BApplication::EndRectTracking()
1368 BPrivate::AppServerLink link;
1369 link.StartMessage(AS_END_RECT_TRACKING);
1370 link.Flush();
1374 status_t
1375 BApplication::_SetupServerAllocator()
1377 fServerAllocator = new (std::nothrow) BPrivate::ServerMemoryAllocator();
1378 if (fServerAllocator == NULL)
1379 return B_NO_MEMORY;
1381 return fServerAllocator->InitCheck();
1385 status_t
1386 BApplication::_InitGUIContext()
1388 // An app_server connection is necessary for a lot of stuff, so get that first.
1389 status_t error = _ConnectToServer();
1390 if (error != B_OK)
1391 return error;
1393 // Initialize the IK after we have set be_app because of a construction
1394 // of a AppServerLink (which depends on be_app) nested inside the call
1395 // to get_menu_info.
1396 error = _init_interface_kit_();
1397 if (error != B_OK)
1398 return error;
1400 // create global system cursors
1401 B_CURSOR_SYSTEM_DEFAULT = new BCursor(B_HAND_CURSOR);
1402 B_CURSOR_I_BEAM = new BCursor(B_I_BEAM_CURSOR);
1404 // TODO: would be nice to get the workspace at launch time from the registrar
1405 fInitialWorkspace = current_workspace();
1407 return B_OK;
1411 status_t
1412 BApplication::_ConnectToServer()
1414 status_t status
1415 = create_desktop_connection(fServerLink, "a<app_server", 100);
1416 if (status != B_OK)
1417 return status;
1419 // AS_CREATE_APP:
1421 // Attach data:
1422 // 1) port_id - receiver port of a regular app
1423 // 2) port_id - looper port for this BApplication
1424 // 3) team_id - team identification field
1425 // 4) int32 - handler ID token of the app
1426 // 5) char* - signature of the regular app
1428 fServerLink->StartMessage(AS_CREATE_APP);
1429 fServerLink->Attach<port_id>(fServerLink->ReceiverPort());
1430 fServerLink->Attach<port_id>(_get_looper_port_(this));
1431 fServerLink->Attach<team_id>(Team());
1432 fServerLink->Attach<int32>(_get_object_token_(this));
1433 fServerLink->AttachString(fAppName);
1435 area_id sharedReadOnlyArea;
1436 team_id serverTeam;
1437 port_id serverPort;
1439 int32 code;
1440 if (fServerLink->FlushWithReply(code) == B_OK
1441 && code == B_OK) {
1442 // We don't need to contact the main app_server anymore
1443 // directly; we now talk to our server alter ego only.
1444 fServerLink->Read<port_id>(&serverPort);
1445 fServerLink->Read<area_id>(&sharedReadOnlyArea);
1446 fServerLink->Read<team_id>(&serverTeam);
1447 } else {
1448 fServerLink->SetSenderPort(-1);
1449 debugger("BApplication: couldn't obtain new app_server comm port");
1450 return B_ERROR;
1452 fServerLink->SetTargetTeam(serverTeam);
1453 fServerLink->SetSenderPort(serverPort);
1455 status = _SetupServerAllocator();
1456 if (status != B_OK)
1457 return status;
1459 area_id area;
1460 uint8* base;
1461 status = fServerAllocator->AddArea(sharedReadOnlyArea, area, base, true);
1462 if (status < B_OK)
1463 return status;
1465 fServerReadOnlyMemory = base;
1467 return B_OK;
1471 void
1472 BApplication::_ReconnectToServer()
1474 delete_port(fServerLink->SenderPort());
1475 delete_port(fServerLink->ReceiverPort());
1477 if (_ConnectToServer() != B_OK)
1478 debugger("Can't reconnect to app server!");
1480 AutoLocker<BLooperList> listLock(gLooperList);
1481 if (!listLock.IsLocked())
1482 return;
1484 uint32 count = gLooperList.CountLoopers();
1485 for (uint32 i = 0; i < count ; i++) {
1486 BWindow* window = dynamic_cast<BWindow*>(gLooperList.LooperAt(i));
1487 if (window == NULL)
1488 continue;
1489 BMessenger windowMessenger(window);
1490 windowMessenger.SendMessage(kMsgAppServerRestarted);
1493 reconnect_bitmaps_to_app_server();
1494 reconnect_pictures_to_app_server();
1498 #if 0
1499 void
1500 BApplication::send_drag(BMessage* message, int32 vs_token, BPoint offset,
1501 BRect dragRect, BHandler* replyTo)
1503 // TODO: implement
1507 void
1508 BApplication::send_drag(BMessage* message, int32 vs_token, BPoint offset,
1509 int32 bitmapToken, drawing_mode dragMode, BHandler* replyTo)
1511 // TODO: implement
1515 void
1516 BApplication::write_drag(_BSession_* session, BMessage* message)
1518 // TODO: implement
1520 #endif
1523 bool
1524 BApplication::_WindowQuitLoop(bool quitFilePanels, bool force)
1526 int32 index = 0;
1527 while (true) {
1528 BWindow* window = WindowAt(index);
1529 if (window == NULL)
1530 break;
1532 // NOTE: the window pointer might be stale, in case the looper
1533 // was already quit by quitting an earlier looper... but fortunately,
1534 // we can still call Lock() on the invalid pointer, and it
1535 // will return false...
1536 if (!window->Lock())
1537 continue;
1539 // don't quit file panels if we haven't been asked for it
1540 if (!quitFilePanels && window->IsFilePanel()) {
1541 window->Unlock();
1542 index++;
1543 continue;
1546 if (!force && !window->QuitRequested()
1547 && !(quitFilePanels && window->IsFilePanel())) {
1548 // the window does not want to quit, so we don't either
1549 window->Unlock();
1550 return false;
1553 // Re-lock, just to make sure that the user hasn't done nasty
1554 // things in QuitRequested(). Quit() unlocks fully, thus
1555 // double-locking is harmless.
1556 if (window->Lock())
1557 window->Quit();
1559 index = 0;
1560 // we need to continue at the start of the list again - it
1561 // might have changed
1564 return true;
1568 bool
1569 BApplication::_QuitAllWindows(bool force)
1571 AssertLocked();
1573 // We need to unlock here because BWindow::QuitRequested() must be
1574 // allowed to lock the application - which would cause a deadlock
1575 Unlock();
1577 bool quit = _WindowQuitLoop(false, force);
1578 if (quit)
1579 quit = _WindowQuitLoop(true, force);
1581 Lock();
1583 return quit;
1587 void
1588 BApplication::_ArgvReceived(BMessage* message)
1590 ASSERT(message != NULL);
1592 // build the argv vector
1593 status_t error = B_OK;
1594 int32 argc = 0;
1595 char** argv = NULL;
1596 if (message->FindInt32("argc", &argc) == B_OK && argc > 0) {
1597 // allocate a NULL terminated array
1598 argv = new(std::nothrow) char*[argc + 1];
1599 if (argv == NULL)
1600 return;
1602 // copy the arguments
1603 for (int32 i = 0; error == B_OK && i < argc; i++) {
1604 const char* arg = NULL;
1605 error = message->FindString("argv", i, &arg);
1606 if (error == B_OK && arg) {
1607 argv[i] = strdup(arg);
1608 if (argv[i] == NULL)
1609 error = B_NO_MEMORY;
1610 } else
1611 argc = i;
1614 argv[argc] = NULL;
1617 // call the hook
1618 if (error == B_OK && argc > 0)
1619 ArgvReceived(argc, argv);
1621 if (error != B_OK) {
1622 printf("Error parsing B_ARGV_RECEIVED message. Message:\n");
1623 message->PrintToStream();
1626 // cleanup
1627 if (argv) {
1628 for (int32 i = 0; i < argc; i++)
1629 free(argv[i]);
1630 delete[] argv;
1635 uint32
1636 BApplication::InitialWorkspace()
1638 return fInitialWorkspace;
1642 int32
1643 BApplication::_CountWindows(bool includeMenus) const
1645 uint32 count = 0;
1646 for (int32 i = 0; i < gLooperList.CountLoopers(); i++) {
1647 BWindow* window = dynamic_cast<BWindow*>(gLooperList.LooperAt(i));
1648 if (window != NULL && !window->fOffscreen && (includeMenus
1649 || dynamic_cast<BMenuWindow*>(window) == NULL)) {
1650 count++;
1654 return count;
1658 BWindow*
1659 BApplication::_WindowAt(uint32 index, bool includeMenus) const
1661 AutoLocker<BLooperList> listLock(gLooperList);
1662 if (!listLock.IsLocked())
1663 return NULL;
1665 uint32 count = gLooperList.CountLoopers();
1666 for (uint32 i = 0; i < count && index < count; i++) {
1667 BWindow* window = dynamic_cast<BWindow*>(gLooperList.LooperAt(i));
1668 if (window == NULL || (window != NULL && window->fOffscreen)
1669 || (!includeMenus && dynamic_cast<BMenuWindow*>(window) != NULL)) {
1670 index++;
1671 continue;
1674 if (i == index)
1675 return window;
1678 return NULL;
1682 /*static*/ void
1683 BApplication::_InitAppResources()
1685 entry_ref ref;
1686 bool found = false;
1688 // App is already running. Get its entry ref with
1689 // GetAppInfo()
1690 app_info appInfo;
1691 if (be_app && be_app->GetAppInfo(&appInfo) == B_OK) {
1692 ref = appInfo.ref;
1693 found = true;
1694 } else {
1695 // Run() hasn't been called yet
1696 found = BPrivate::get_app_ref(&ref) == B_OK;
1699 if (!found)
1700 return;
1702 BFile file(&ref, B_READ_ONLY);
1703 if (file.InitCheck() != B_OK)
1704 return;
1706 BResources* resources = new (std::nothrow) BResources(&file, false);
1707 if (resources == NULL || resources->InitCheck() != B_OK) {
1708 delete resources;
1709 return;
1712 sAppResources = resources;