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