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