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