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