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