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