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