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