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