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