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