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