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