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