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