xref: /haiku/src/servers/app/AppServer.cpp (revision 0b2dbe7d46ee888392907c60131b7f7652314175)
1 /*
2  * Copyright (c) 2001-2005, Haiku, Inc.
3  * Distributed under the terms of the MIT license.
4  *
5  * Authors:
6  *		DarkWyrm <bpmagic@columbus.rr.com>
7  *		Axel Dörfler, axeld@pinc-software.de
8  */
9 
10 
11 #include <Accelerant.h>
12 #include <AppDefs.h>
13 #include <Directory.h>
14 #include <Entry.h>
15 #include <File.h>
16 #include <Message.h>
17 #include <Path.h>
18 #include <PortLink.h>
19 #include <StopWatch.h>
20 #include <RosterPrivate.h>
21 
22 #include "BitmapManager.h"
23 #include "ColorSet.h"
24 #include "CursorManager.h"
25 #include "DecorManager.h"
26 #include "DefaultDecorator.h"
27 #include "Desktop.h"
28 #include "FontServer.h"
29 #include "HWInterface.h"
30 #include "RegistrarDefs.h"
31 #include "RGBColor.h"
32 #include "RootLayer.h"
33 #include "ServerApp.h"
34 #include "ServerCursor.h"
35 #include "ServerProtocol.h"
36 #include "ServerWindow.h"
37 #include "SystemPalette.h"
38 #include "Utils.h"
39 
40 #include "AppServer.h"
41 
42 //#define DEBUG_KEYHANDLING
43 //#define DEBUG_SERVER
44 
45 #ifdef DEBUG_KEYHANDLING
46 #	include <stdio.h>
47 #	define KBTRACE(x) printf x
48 #else
49 #	define KBTRACE(x) ;
50 #endif
51 
52 #ifdef DEBUG_SERVER
53 #	include <stdio.h>
54 #	define STRACE(x) printf x
55 #else
56 #	define STRACE(x) ;
57 #endif
58 
59 
60 static const uint32 kMsgShutdownServer = 'shuT';
61 
62 
63 // Globals
64 Desktop *gDesktop;
65 
66 port_id gAppServerPort;
67 
68 static AppServer *sAppServer;
69 
70 //! Default background color for workspaces
71 //RGBColor workspace_default_color(51,102,160);
72 
73 //! System-wide GUI color object
74 ColorSet gGUIColorSet;
75 
76 /*!
77 	\brief Constructor
78 
79 	This loads the default fonts, allocates all the major global variables, spawns the main housekeeping
80 	threads, loads user preferences for the UI and decorator, and allocates various locks.
81 */
82 #if TEST_MODE
83 AppServer::AppServer(void) : BApplication (SERVER_SIGNATURE),
84 #else
85 AppServer::AppServer(void) :
86 #endif
87 	fCursorSem(-1),
88 	fCursorArea(-1)
89 {
90 	fMessagePort = create_port(200, SERVER_PORT_NAME);
91 	if (fMessagePort == B_NO_MORE_PORTS)
92 		debugger("app_server could not create message port");
93 
94 	gAppServerPort = fMessagePort;
95 
96 	// Create the input port. The input_server will send it's messages here.
97 	// TODO: If we want multiple user support we would need an individual
98 	// port for each user and do the following for each RootLayer.
99 	fServerInputPort = create_port(200, SERVER_INPUT_PORT);
100 	if (fServerInputPort == B_NO_MORE_PORTS)
101 		debugger("app_server could not create input port");
102 
103 	fQuitting = false;
104 	sAppServer = this;
105 
106 	// Create the font server and scan the proper directories.
107 	gFontServer = new FontServer;
108 	gFontServer->Lock();
109 
110 	// Used for testing purposes
111 
112 	// TODO: Re-enable scanning of all font directories when server is actually put to use
113 	gFontServer->ScanDirectory("/boot/beos/etc/fonts/ttfonts/");
114 //	gFontServer->ScanDirectory("/boot/beos/etc/fonts/PS-Type1/");
115 //	gFontServer->ScanDirectory("/boot/home/config/fonts/ttfonts/");
116 //	gFontServer->ScanDirectory("/boot/home/config/fonts/psfonts/");
117 	gFontServer->SaveList();
118 
119 	if (!gFontServer->SetSystemPlain(DEFAULT_PLAIN_FONT_FAMILY,
120 									DEFAULT_PLAIN_FONT_STYLE,
121 									DEFAULT_PLAIN_FONT_SIZE) &&
122 		!gFontServer->SetSystemPlain(FALLBACK_PLAIN_FONT_FAMILY,
123 									DEFAULT_PLAIN_FONT_STYLE,
124 									DEFAULT_PLAIN_FONT_SIZE)) {
125 		printf("Couldn't set plain to %s (fallback: %s), %s %d pt\n",
126 				DEFAULT_PLAIN_FONT_FAMILY,
127 				FALLBACK_PLAIN_FONT_FAMILY,
128 				DEFAULT_PLAIN_FONT_STYLE,
129 				DEFAULT_PLAIN_FONT_SIZE);
130 	}
131 
132 	if (!gFontServer->SetSystemBold(DEFAULT_BOLD_FONT_FAMILY,
133 								   DEFAULT_BOLD_FONT_STYLE,
134 								   DEFAULT_BOLD_FONT_SIZE) &&
135 		!gFontServer->SetSystemBold(FALLBACK_BOLD_FONT_FAMILY,
136 								   DEFAULT_BOLD_FONT_STYLE,
137 								   DEFAULT_BOLD_FONT_SIZE)) {
138 		printf("Couldn't set bold to %s (fallback: %s), %s %d pt\n",
139 				DEFAULT_BOLD_FONT_FAMILY,
140 				FALLBACK_BOLD_FONT_FAMILY,
141 				DEFAULT_BOLD_FONT_STYLE,
142 				DEFAULT_BOLD_FONT_SIZE);
143 	}
144 
145 	if (!gFontServer->SetSystemFixed(DEFAULT_FIXED_FONT_FAMILY,
146 									DEFAULT_FIXED_FONT_STYLE,
147 									DEFAULT_FIXED_FONT_SIZE) &&
148 		!gFontServer->SetSystemFixed(FALLBACK_FIXED_FONT_FAMILY,
149 									DEFAULT_FIXED_FONT_STYLE,
150 									DEFAULT_FIXED_FONT_SIZE)) {
151 		printf("Couldn't set fixed to %s (fallback: %s), %s %d pt\n",
152 				DEFAULT_FIXED_FONT_FAMILY,
153 				FALLBACK_FIXED_FONT_FAMILY,
154 				DEFAULT_FIXED_FONT_STYLE,
155 				DEFAULT_FIXED_FONT_SIZE);
156 	}
157 
158 	gFontServer->Unlock();
159 
160 	// Load the GUI colors here and set the global set to the values contained therein. If this
161 	// is not possible, set colors to the defaults
162 	if (LoadColorSet(SERVER_SETTINGS_DIR COLOR_SETTINGS_NAME, &gGUIColorSet) != B_OK)
163 		gGUIColorSet.SetToDefaults();
164 
165 	// Set up the Desktop
166 	gDesktop = new Desktop();
167 	gDesktop->Init();
168 
169 	// TODO: Maybe this is not the best place for this
170 	InitializeColorMap();
171 
172 	// Create the bitmap allocator. Object declared in BitmapManager.cpp
173 	gBitmapManager = new BitmapManager();
174 
175 	// This is necessary to mediate access between the Poller and app_server threads
176 	fActiveAppLock = create_sem(1,"app_server_active_sem");
177 
178 	// This locker is for app_server and Picasso to vy for control of the ServerApp list
179 	fAppListLock = create_sem(1,"app_server_applist_sem");
180 
181 	// Spawn our thread-monitoring thread
182 	fPicassoThreadID = spawn_thread(PicassoThread, "picasso", B_NORMAL_PRIORITY, this);
183 	if (fPicassoThreadID >= 0)
184 		resume_thread(fPicassoThreadID);
185 
186 #if 0
187 	LaunchCursorThread();
188 #endif
189 }
190 
191 /*!
192 	\brief Destructor
193 
194 	Reached only when the server is asked to shut down in Test mode. Kills all apps, shuts down the
195 	desktop, kills the housekeeping threads, etc.
196 */
197 AppServer::~AppServer()
198 {
199 	debugger("We shouldn't be here! MainLoop()::B_QUIT_REQUESTED should see if we can exit the server.\n");
200 /*
201 	ServerApp *tempapp;
202 	int32 i;
203 	acquire_sem(fAppListLock);
204 	for(i=0;i<fAppList->CountItems();i++)
205 	{
206 		tempapp=(ServerApp *)fAppList->ItemAt(i);
207 		if(tempapp!=NULL)
208 			delete tempapp;
209 	}
210 	delete fAppList;
211 	release_sem(fAppListLock);
212 
213 	delete gBitmapManager;
214 
215 	delete gDesktop;
216 
217 	// If these threads are still running, kill them - after this, if exit_poller
218 	// is deleted, who knows what will happen... These things will just return an
219 	// error and fail if the threads have already exited.
220 	kill_thread(fPicassoThreadID);
221 	kill_thread(fCursorThreadID);
222 	kill_thread(fISThreadID);
223 
224 	delete gFontServer;
225 
226 	make_decorator=NULL;
227 */
228 }
229 
230 /*!
231 	\brief Thread function for watching for dead apps
232 	\param data Pointer to the app_server to which the thread belongs
233 	\return Throwaway value - always 0
234 */
235 int32
236 AppServer::PicassoThread(void *data)
237 {
238 	while (!sAppServer->fQuitting) {
239 		acquire_sem(sAppServer->fAppListLock);
240 		for (int32 i = 0;;) {
241 			ServerApp *app = (ServerApp *)sAppServer->fAppList.ItemAt(i++);
242 			if (!app)
243 				break;
244 
245 			app->PingTarget();
246 		}
247 		release_sem(sAppServer->fAppListLock);
248 		// we do this every second so as not to suck *too* many CPU cycles
249 		snooze(1000000);
250 	}
251 
252 	// app_server is about to quit
253 	// wait some more, and then make sure it'll shutdown everything
254 	snooze(2000000);
255 	sAppServer->PostMessage(kMsgShutdownServer);
256 
257 	return 0;
258 }
259 
260 
261 /*!
262 	\brief Starts Input Server
263 */
264 void
265 AppServer::LaunchInputServer()
266 {
267 	// We are supposed to start the input_server, but it's a BApplication
268 	// that depends on the registrar running, which is started after app_server
269 	// so we wait on roster thread to launch the input server
270 
271 	fISThreadID = B_ERROR;
272 
273 	while (!BRoster::Private().IsMessengerValid(false) && !fQuitting) {
274 		snooze(250000);
275 		BRoster::Private::DeleteBeRoster();
276 		BRoster::Private::InitBeRoster();
277 	}
278 
279 	if (fQuitting)
280 		return;
281 
282 	// we use an area for cursor data communication with input_server
283 	// area id and sem id are sent to the input_server
284 
285 	if (fCursorArea < B_OK)
286 		fCursorArea = create_area("isCursor", (void**) &fCursorAddr, B_ANY_ADDRESS, B_PAGE_SIZE, B_FULL_LOCK, B_READ_AREA | B_WRITE_AREA);
287 	if (fCursorSem < B_OK)
288 		fCursorSem = create_sem(0, "isSem");
289 
290 	int32 arg_c = 1;
291 	char **arg_v = (char **)malloc(sizeof(char *) * (arg_c + 1));
292 #if TEST_MODE
293 	arg_v[0] = strdup("/boot/home/svnhaiku/trunk/distro/x86.R1/beos/system/servers/input_server");
294 #else
295 	arg_v[0] = strdup("/system/servers/input_server");
296 #endif
297 	arg_v[1] = NULL;
298 	fISThreadID = load_image(arg_c, (const char**)arg_v, (const char **)environ);
299 	free(arg_v[0]);
300 
301 	int32 tmpbuf[2] = {fCursorSem, fCursorArea};
302 	int32 code = 0;
303 	send_data(fISThreadID, code, (void *)tmpbuf, sizeof(tmpbuf));
304 
305 	resume_thread(fISThreadID);
306 	setpgid(fISThreadID, 0);
307 
308 	// we receive
309 
310 	thread_id sender;
311 	code = receive_data(&sender, (void *)tmpbuf, sizeof(tmpbuf));
312 	fISASPort = tmpbuf[0];
313 	fISPort = tmpbuf[1];
314 
315 	// if at any time, one of these ports is error prone, it might mean input_server is gone
316 	// then relaunch input_server
317 }
318 
319 
320 /*!
321 	\brief Starts the Cursor Thread
322 */
323 void
324 AppServer::LaunchCursorThread()
325 {
326 	// Spawn our cursor thread
327 	fCursorThreadID = spawn_thread(CursorThread, "CursorThreadOfTheDeath", B_REAL_TIME_DISPLAY_PRIORITY - 1, this);
328 	if (fCursorThreadID >= 0)
329 		resume_thread(fCursorThreadID);
330 
331 }
332 
333 /*!
334 	\brief The Cursor Thread task
335 */
336 int32
337 AppServer::CursorThread(void* data)
338 {
339 	AppServer *server = (AppServer *)data;
340 
341 	server->LaunchInputServer();
342 
343 	do {
344 		while (acquire_sem(server->fCursorSem) == B_OK) {
345 			BPoint p;
346 			p.y = *server->fCursorAddr & 0x7fff;
347 			p.x = *server->fCursorAddr >> 15 & 0x7fff;
348 
349 			gDesktop->GetHWInterface()->MoveCursorTo(p.x, p.y);
350 			STRACE(("CursorThread : %f, %f\n", p.x, p.y));
351 		}
352 
353 		snooze(100000);
354 
355 	} while (!server->fQuitting);
356 
357 	return B_OK;
358 }
359 
360 
361 /*!
362 	\brief Send a message to the AppServer with no attachments
363 	\param code ID code of the message to post
364 */
365 void
366 AppServer::PostMessage(int32 code)
367 {
368 	BPrivate::LinkSender link(fMessagePort);
369 	link.StartMessage(code);
370 	link.Flush();
371 }
372 
373 
374 /*!
375 	\brief The call that starts it all...
376 	\return Always 0
377 */
378 thread_id
379 AppServer::Run(void)
380 {
381 	MainLoop();
382 	kill_thread(fPicassoThreadID);
383 	return 0;
384 }
385 
386 
387 //! Main message-monitoring loop for the regular message port - no input messages!
388 void
389 AppServer::MainLoop(void)
390 {
391 	BPrivate::PortLink link(-1, fMessagePort);
392 
393 	while (1) {
394 		STRACE(("info: AppServer::MainLoop listening on port %ld.\n", fMessagePort));
395 
396 		int32 code;
397 		status_t err = link.GetNextMessage(code);
398 		if (err < B_OK) {
399 			STRACE(("MainLoop:link.GetNextMessage() failed\n"));
400 			continue;
401 		}
402 
403 		DispatchMessage(code, link);
404 	}
405 }
406 
407 /*!
408 	\brief Message handling function for all messages sent to the app_server
409 	\param code ID of the message sent
410 	\param buffer Attachment buffer for the message.
411 
412 */
413 void
414 AppServer::DispatchMessage(int32 code, BPrivate::PortLink &msg)
415 {
416 	switch (code) {
417 		case AS_CREATE_APP:
418 		{
419 			// Create the ServerApp to node monitor a new BApplication
420 
421 			// Attached data:
422 			// 1) port_id - receiver port of a regular app
423 			// 2) port_id - client looper port - for send messages to the client
424 			// 2) team_id - app's team ID
425 			// 3) int32 - handler token of the regular app
426 			// 4) char * - signature of the regular app
427 
428 			// Find the necessary data
429 			team_id	clientTeamID = -1;
430 			port_id	clientLooperPort = -1;
431 			port_id clientReplyPort = -1;
432 			int32 htoken = B_NULL_TOKEN;
433 			char *appSignature = NULL;
434 
435 			msg.Read<port_id>(&clientReplyPort);
436 			msg.Read<port_id>(&clientLooperPort);
437 			msg.Read<team_id>(&clientTeamID);
438 			msg.Read<int32>(&htoken);
439 			if (msg.ReadString(&appSignature) != B_OK)
440 				break;
441 
442 			ServerApp *app = new ServerApp(clientReplyPort, clientLooperPort,
443 				clientTeamID, htoken, appSignature);
444 			if (app->InitCheck() == B_OK
445 				&& app->Run()) {
446 				// add the new ServerApp to the known list of ServerApps
447 				acquire_sem(fAppListLock);
448 				fAppList.AddItem(app);
449 				release_sem(fAppListLock);
450 			} else {
451 				delete app;
452 
453 				// if everything went well, ServerApp::Run() will notify
454 				// the client - but since it didn't, we do it here
455 				BPrivate::LinkSender reply(clientReplyPort);
456 				reply.StartMessage(SERVER_FALSE);
457 				reply.Flush();
458 			}
459 
460 			// This is necessary because BPortLink::ReadString allocates memory
461 			free(appSignature);
462 			break;
463 		}
464 
465 		case AS_DELETE_APP:
466 		{
467 			// Delete a ServerApp. Received only from the respective ServerApp when a
468 			// BApplication asks it to quit.
469 
470 			// Attached Data:
471 			// 1) thread_id - thread ID of the ServerApp to be deleted
472 
473 			thread_id thread = -1;
474 			if (msg.Read<thread_id>(&thread) < B_OK)
475 				break;
476 
477 			acquire_sem(fAppListLock);
478 
479 			// Run through the list of apps and nuke the proper one
480 
481 			int32 count = fAppList.CountItems();
482 			ServerApp *removeApp = NULL;
483 
484 			for (int32 i = 0; i < count; i++) {
485 				ServerApp *app = (ServerApp *)fAppList.ItemAt(i);
486 
487 				if (app != NULL && app->Thread() == thread) {
488 					fAppList.RemoveItem(i);
489 					removeApp = app;
490 					break;
491 				}
492 			}
493 
494 			release_sem(fAppListLock);
495 
496 			if (removeApp != NULL)
497 				removeApp->Quit();
498 
499 			if (fQuitting && count <= 1)
500 				PostMessage(kMsgShutdownServer);
501 			break;
502 		}
503 
504 		case AS_UPDATED_CLIENT_FONTLIST:
505 			// received when the client-side global font list has been
506 			// refreshed
507 			gFontServer->Lock();
508 			gFontServer->FontsUpdated();
509 			gFontServer->Unlock();
510 			break;
511 
512 		case AS_QUERY_FONTS_CHANGED:
513 		{
514 			// Client application is asking if the font list has changed since
515 			// the last client-side refresh
516 
517 			// Attached data:
518 			// 1) port_id reply port
519 
520 			gFontServer->Lock();
521 			bool needsUpdate = gFontServer->FontsNeedUpdated();
522 			gFontServer->Unlock();
523 
524 			// Seeing how the client merely wants an answer, we'll skip the BPortLink
525 			// and all its overhead and just write the code to port.
526 			port_id replyport;
527 			if (msg.Read<port_id>(&replyport) < B_OK)
528 				break;
529 			BPrivate::PortLink replylink(replyport);
530 			replylink.StartMessage(needsUpdate ? SERVER_TRUE : SERVER_FALSE);
531 			replylink.Flush();
532 			break;
533 		}
534 
535 #if TEST_MODE
536 		case B_QUIT_REQUESTED:
537 			// We've been asked to quit, so (for now) broadcast to all
538 			// test apps to quit. This situation will occur only when the server
539 			// is compiled as a regular Be application.
540 
541 			fQuitting = true;
542 			BroadcastToAllApps(AS_QUIT_APP);
543 
544 			// We now need to process the remaining AS_DELETE_APP messages and
545 			// wait for the kMsgShutdownServer message.
546 			// If an application does not quit as asked, the picasso thread
547 			// will send us this message in 2-3 seconds.
548 			break;
549 
550 		case kMsgShutdownServer:
551 			// let's kill all remaining applications
552 
553 			acquire_sem(fAppListLock);
554 
555 			for (int32 i = 0; i < fAppList.CountItems(); i++) {
556 				ServerApp *app = (ServerApp*)fAppList.ItemAt(i);
557 
558 				kill_thread(app->Thread());
559 				kill_team(app->ClientTeam());
560 			}
561 
562 			kill_thread(fPicassoThreadID);
563 
564 			release_sem(fAppListLock);
565 
566 			delete gDesktop;
567 			delete gBitmapManager;
568 			delete gFontServer;
569 
570 			// we are now clear to exit
571 			exit_thread(0);
572 			break;
573 #endif
574 
575 		case AS_SET_SYSCURSOR_DEFAULTS:
576 		{
577 			// although this isn't pretty, ATM we only have RootLayer.
578 			// this messages should be handled somewhere into a RootLayer
579 			// specific area - this set is intended for a RootLayer.
580 			gDesktop->ActiveRootLayer()->GetCursorManager().SetDefaults();
581 			break;
582 		}
583 
584 		default:
585 			STRACE(("Server::MainLoop received unexpected code %ld (offset %ld)\n",
586 				code, code - SERVER_TRUE));
587 			break;
588 	}
589 }
590 
591 /*!
592 	\brief Finds the application with the given signature
593 	\param sig MIME signature of the application to find
594 	\return the corresponding ServerApp or NULL if not found
595 
596 	This call should be made only when necessary because it locks the app list
597 	while it does its searching.
598 */
599 ServerApp *
600 AppServer::FindApp(const char *signature)
601 {
602 	if (!signature)
603 		return NULL;
604 
605 	acquire_sem(fAppListLock);
606 
607 	ServerApp *foundApp = NULL;
608 	for (int32 i = 0; i < fAppList.CountItems(); i++) {
609 		ServerApp *app = (ServerApp*)fAppList.ItemAt(i);
610 		if (app && !strcasecmp(app->Signature(), signature)) {
611 			foundApp = app;
612 			break;
613 		}
614 	}
615 
616 	release_sem(fAppListLock);
617 
618 	return foundApp;
619 }
620 
621 
622 //	#pragma mark -
623 
624 
625 /*!
626 	\brief Send a quick (no attachments) message to all applications
627 
628 	Quite useful for notification for things like server shutdown, system
629 	color changes, etc.
630 */
631 void
632 BroadcastToAllApps(const int32 &code)
633 {
634 	acquire_sem(sAppServer->fAppListLock);
635 
636 	for (int32 i = 0; i < sAppServer->fAppList.CountItems(); i++) {
637 		ServerApp *app = (ServerApp *)sAppServer->fAppList.ItemAt(i);
638 
639 		if (!app) {
640 			printf("PANIC in Broadcast()\n");
641 			continue;
642 		}
643 		app->PostMessage(code);
644 	}
645 
646 	release_sem(sAppServer->fAppListLock);
647 }
648 
649 
650 /*!
651 	\brief Entry function to run the entire server
652 	\param argc Number of command-line arguments present
653 	\param argv String array of the command-line arguments
654 	\return -1 if the app_server is already running, 0 if everything's OK.
655 */
656 int
657 main(int argc, char** argv)
658 {
659 	// There can be only one....
660 	if (find_port(SERVER_PORT_NAME) >= B_OK)
661 		return -1;
662 
663 	srand(real_time_clock_usecs());
664 
665 	AppServer server;
666 	server.Run();
667 
668 	return 0;
669 }
670