xref: /haiku/src/servers/app/AppServer.cpp (revision 94978fa6a71bdb289ed6a14fa0292adaeceb5380)
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 #include <unistd.h>
11 
12 #include <Accelerant.h>
13 #include <AppDefs.h>
14 #include <Directory.h>
15 #include <Entry.h>
16 #include <File.h>
17 #include <Message.h>
18 #include <Path.h>
19 #include <PortLink.h>
20 #include <StopWatch.h>
21 #include <RosterPrivate.h>
22 #include <Autolock.h>
23 
24 #include "BitmapManager.h"
25 #include "ColorSet.h"
26 #include "CursorManager.h"
27 #include "DecorManager.h"
28 #include "DefaultDecorator.h"
29 #include "Desktop.h"
30 #include "FontServer.h"
31 #include "HWInterface.h"
32 #include "RegistrarDefs.h"
33 #include "RGBColor.h"
34 #include "Layer.h"
35 #include "WinBorder.h"
36 #include "RootLayer.h"
37 #include "ScreenManager.h"
38 #include "ServerApp.h"
39 #include "ServerConfig.h"
40 #include "ServerCursor.h"
41 #include "ServerProtocol.h"
42 #include "ServerWindow.h"
43 #include "SystemPalette.h"
44 #include "Utils.h"
45 
46 #include "AppServer.h"
47 
48 //#define DEBUG_KEYHANDLING
49 //#define DEBUG_SERVER
50 
51 #ifdef DEBUG_KEYHANDLING
52 #	include <stdio.h>
53 #	define KBTRACE(x) printf x
54 #else
55 #	define KBTRACE(x) ;
56 #endif
57 
58 #ifdef DEBUG_SERVER
59 #	include <stdio.h>
60 #	define STRACE(x) printf x
61 #else
62 #	define STRACE(x) ;
63 #endif
64 
65 
66 // Globals
67 static Desktop *sDesktop;
68 
69 port_id gAppServerPort;
70 
71 static AppServer *sAppServer;
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 AppServer::AppServer()
83 	: MessageLooper("app_server"),
84 	fCursorSem(-1),
85 	fCursorArea(-1)
86 {
87 	fMessagePort = create_port(DEFAULT_MONITOR_PORT_SIZE, SERVER_PORT_NAME);
88 	if (fMessagePort < B_OK)
89 		debugger("app_server could not create message port");
90 
91 	fLink.SetReceiverPort(fMessagePort);
92 	gAppServerPort = fMessagePort;
93 
94 	// Create the input port. The input_server will send it's messages here.
95 	// TODO: If we want multiple user support we would need an individual
96 	// port for each user and do the following for each RootLayer.
97 	fServerInputPort = create_port(200, SERVER_INPUT_PORT);
98 	if (fServerInputPort < B_OK)
99 		debugger("app_server could not create input port");
100 
101 	sAppServer = this;
102 
103 	// Create the font server and scan the proper directories.
104 	gFontServer = new FontServer;
105 	gFontServer->Lock();
106 	gFontServer->ScanSystemFolders();
107 	gFontServer->SaveList();
108 
109 	if (!gFontServer->SetSystemPlain(DEFAULT_PLAIN_FONT_FAMILY,
110 									DEFAULT_PLAIN_FONT_STYLE,
111 									DEFAULT_PLAIN_FONT_SIZE) &&
112 		!gFontServer->SetSystemPlain(FALLBACK_PLAIN_FONT_FAMILY,
113 									DEFAULT_PLAIN_FONT_STYLE,
114 									DEFAULT_PLAIN_FONT_SIZE)) {
115 		printf("Couldn't set plain to %s (fallback: %s), %s %d pt\n",
116 				DEFAULT_PLAIN_FONT_FAMILY,
117 				FALLBACK_PLAIN_FONT_FAMILY,
118 				DEFAULT_PLAIN_FONT_STYLE,
119 				DEFAULT_PLAIN_FONT_SIZE);
120 	}
121 
122 	if (!gFontServer->SetSystemBold(DEFAULT_BOLD_FONT_FAMILY,
123 								   DEFAULT_BOLD_FONT_STYLE,
124 								   DEFAULT_BOLD_FONT_SIZE) &&
125 		!gFontServer->SetSystemBold(FALLBACK_BOLD_FONT_FAMILY,
126 								   DEFAULT_BOLD_FONT_STYLE,
127 								   DEFAULT_BOLD_FONT_SIZE)) {
128 		printf("Couldn't set bold to %s (fallback: %s), %s %d pt\n",
129 				DEFAULT_BOLD_FONT_FAMILY,
130 				FALLBACK_BOLD_FONT_FAMILY,
131 				DEFAULT_BOLD_FONT_STYLE,
132 				DEFAULT_BOLD_FONT_SIZE);
133 	}
134 
135 	if (!gFontServer->SetSystemFixed(DEFAULT_FIXED_FONT_FAMILY,
136 									DEFAULT_FIXED_FONT_STYLE,
137 									DEFAULT_FIXED_FONT_SIZE) &&
138 		!gFontServer->SetSystemFixed(FALLBACK_FIXED_FONT_FAMILY,
139 									DEFAULT_FIXED_FONT_STYLE,
140 									DEFAULT_FIXED_FONT_SIZE)) {
141 		printf("Couldn't set fixed to %s (fallback: %s), %s %d pt\n",
142 				DEFAULT_FIXED_FONT_FAMILY,
143 				FALLBACK_FIXED_FONT_FAMILY,
144 				DEFAULT_FIXED_FONT_STYLE,
145 				DEFAULT_FIXED_FONT_SIZE);
146 	}
147 
148 	gFontServer->Unlock();
149 
150 	// Load the GUI colors here and set the global set to the values contained therein. If this
151 	// is not possible, set colors to the defaults
152 	if (LoadColorSet(SERVER_SETTINGS_DIR COLOR_SETTINGS_NAME, &gGUIColorSet) != B_OK)
153 		gGUIColorSet.SetToDefaults();
154 
155 	gScreenManager = new ScreenManager();
156 	gScreenManager->Run();
157 
158 	// the system palette needs to be initialized before the desktop,
159 	// since it is used there already
160 	InitializeColorMap();
161 
162 	// Create the bitmap allocator. Object declared in BitmapManager.cpp
163 	gBitmapManager = new BitmapManager();
164 
165 	// Set up the Desktop
166 	sDesktop = new Desktop();
167 	sDesktop->Init();
168 	sDesktop->Run();
169 
170 #if 0
171 	LaunchCursorThread();
172 #endif
173 }
174 
175 /*!
176 	\brief Destructor
177 	Reached only when the server is asked to shut down in Test mode.
178 */
179 AppServer::~AppServer()
180 {
181 	delete gBitmapManager;
182 
183 	gScreenManager->Lock();
184 	gScreenManager->Quit();
185 
186 	delete gFontServer;
187 }
188 
189 
190 /*!
191 	\brief Starts Input Server
192 */
193 void
194 AppServer::LaunchInputServer()
195 {
196 	// We are supposed to start the input_server, but it's a BApplication
197 	// that depends on the registrar running, which is started after app_server
198 	// so we wait on roster thread to launch the input server
199 
200 	fISThreadID = B_ERROR;
201 
202 	while (!BRoster::Private().IsMessengerValid(false) && !fQuitting) {
203 		snooze(250000);
204 		BRoster::Private::DeleteBeRoster();
205 		BRoster::Private::InitBeRoster();
206 	}
207 
208 	if (fQuitting)
209 		return;
210 
211 	// we use an area for cursor data communication with input_server
212 	// area id and sem id are sent to the input_server
213 
214 	if (fCursorArea < B_OK)
215 		fCursorArea = create_area("isCursor", (void**) &fCursorAddr, B_ANY_ADDRESS, B_PAGE_SIZE, B_FULL_LOCK, B_READ_AREA | B_WRITE_AREA);
216 	if (fCursorSem < B_OK)
217 		fCursorSem = create_sem(0, "isSem");
218 
219 	int32 arg_c = 1;
220 	char **arg_v = (char **)malloc(sizeof(char *) * (arg_c + 1));
221 #if TEST_MODE
222 	arg_v[0] = strdup("/boot/home/svnhaiku/trunk/distro/x86.R1/beos/system/servers/input_server");
223 #else
224 	arg_v[0] = strdup("/system/servers/input_server");
225 #endif
226 	arg_v[1] = NULL;
227 	fISThreadID = load_image(arg_c, (const char**)arg_v, (const char **)environ);
228 	free(arg_v[0]);
229 
230 	int32 tmpbuf[2] = {fCursorSem, fCursorArea};
231 	int32 code = 0;
232 	send_data(fISThreadID, code, (void *)tmpbuf, sizeof(tmpbuf));
233 
234 	resume_thread(fISThreadID);
235 	setpgid(fISThreadID, 0);
236 
237 	// we receive
238 
239 	thread_id sender;
240 	code = receive_data(&sender, (void *)tmpbuf, sizeof(tmpbuf));
241 	fISASPort = tmpbuf[0];
242 	fISPort = tmpbuf[1];
243 
244 	// if at any time, one of these ports is error prone, it might mean input_server is gone
245 	// then relaunch input_server
246 }
247 
248 
249 /*!
250 	\brief Starts the Cursor Thread
251 */
252 void
253 AppServer::LaunchCursorThread()
254 {
255 	// Spawn our cursor thread
256 	fCursorThreadID = spawn_thread(CursorThread, "CursorThreadOfTheDeath", B_REAL_TIME_DISPLAY_PRIORITY - 1, this);
257 	if (fCursorThreadID >= 0)
258 		resume_thread(fCursorThreadID);
259 
260 }
261 
262 /*!
263 	\brief The Cursor Thread task
264 */
265 int32
266 AppServer::CursorThread(void* data)
267 {
268 	AppServer *server = (AppServer *)data;
269 
270 	server->LaunchInputServer();
271 
272 	do {
273 		while (acquire_sem(server->fCursorSem) == B_OK) {
274 			BPoint p;
275 			p.y = *server->fCursorAddr & 0x7fff;
276 			p.x = *server->fCursorAddr >> 15 & 0x7fff;
277 
278 			sDesktop->GetHWInterface()->MoveCursorTo(p.x, p.y);
279 			STRACE(("CursorThread : %f, %f\n", p.x, p.y));
280 		}
281 
282 		snooze(100000);
283 	} while (!server->IsQuitting());
284 
285 	return B_OK;
286 }
287 
288 
289 /*!
290 	\brief The call that starts it all...
291 */
292 void
293 AppServer::RunLooper()
294 {
295 	rename_thread(find_thread(NULL), "picasso");
296 	_message_thread((void *)this);
297 }
298 
299 
300 /*!
301 	\brief Message handling function for all messages sent to the app_server
302 	\param code ID of the message sent
303 	\param buffer Attachment buffer for the message.
304 
305 */
306 void
307 AppServer::_DispatchMessage(int32 code, BPrivate::LinkReceiver& msg)
308 {
309 	switch (code) {
310 		case AS_GET_DESKTOP:
311 		{
312 			port_id replyPort;
313 			if (msg.Read<port_id>(&replyPort) < B_OK)
314 				break;
315 
316 			int32 userID;
317 			msg.Read<int32>(&userID);
318 
319 			BPrivate::LinkSender reply(replyPort);
320 			reply.StartMessage(B_OK);
321 			reply.Attach<port_id>(sDesktop->MessagePort());
322 			reply.Flush();
323 			break;
324 		}
325 
326 		case AS_QUERY_FONTS_CHANGED:
327 		{
328 			// Client application is asking if the font list has changed since
329 			// the last client-side refresh
330 
331 			// Attached data:
332 			// 1) port_id reply port
333 
334 			gFontServer->Lock();
335 			bool needsUpdate = gFontServer->FontsNeedUpdated();
336 			gFontServer->Unlock();
337 
338 			// Seeing how the client merely wants an answer, we'll skip the BPortLink
339 			// and all its overhead and just write the code to port.
340 			port_id replyPort;
341 			if (msg.Read<port_id>(&replyPort) < B_OK)
342 				break;
343 
344 			BPrivate::PortLink reply(replyPort);
345 			reply.StartMessage(needsUpdate ? SERVER_TRUE : SERVER_FALSE);
346 			reply.Flush();
347 			break;
348 		}
349 
350 #if TEST_MODE
351 		case B_QUIT_REQUESTED:
352 		{
353 			thread_id thread = sDesktop->Thread();
354 
355 			// We've been asked to quit, so (for now) broadcast to all
356 			// test apps to quit. This situation will occur only when the server
357 			// is compiled as a regular Be application.
358 
359 			sDesktop->PostMessage(B_QUIT_REQUESTED);
360 			fQuitting = true;
361 
362 			// we just wait for the desktop to kill itself
363 			status_t status;
364 			wait_for_thread(thread, &status);
365 
366 			kill_thread(fCursorThreadID);
367 			delete this;
368 
369 			// we are now clear to exit
370 			exit(0);
371 			break;
372 		}
373 #endif
374 
375 		case AS_SET_SYSCURSOR_DEFAULTS:
376 		{
377 			sDesktop->GetCursorManager().SetDefaults();
378 			break;
379 		}
380 
381 		case AS_ACTIVATE_APP:
382 		{
383 			// Someone is requesting to activation of a certain app.
384 
385 			// Attached data:
386 			// 1) port_id reply port
387 			// 2) team_id team
388 
389 			status_t error = B_OK;
390 
391 			// get the parameters
392 			port_id replyPort;
393 			team_id team;
394 			if (msg.Read(&replyPort) < B_OK
395 				|| msg.Read(&team) < B_OK) {
396 				error = B_ERROR;
397 			}
398 
399 			// activate one of the app's windows.
400 			if (error == B_OK) {
401 				error = B_BAD_TEAM_ID;
402 				if (sDesktop->Lock()) {
403 					// search for an unhidden window to give focus to
404 					int32 windowCount = sDesktop->WindowList().CountItems();
405 					Layer *layer;
406 					for (int32 i = 0; i < windowCount; ++i)
407 						// is this layer in fact a WinBorder?
408 						if ((layer = static_cast<Layer*>(sDesktop->WindowList().ItemAtFast(i)))) {
409 							WinBorder *winBorder = dynamic_cast<WinBorder*>(layer);
410 							// if winBorder is valid and not hidden, then we've found our target
411 							if (winBorder && !winBorder->IsHidden()
412 									&& winBorder->App()->ClientTeam() == team) {
413 								if (sDesktop->ActiveRootLayer()
414 										&& sDesktop->ActiveRootLayer()->Lock()) {
415 									sDesktop->ActiveRootLayer()->SetActive(winBorder);
416 									sDesktop->ActiveRootLayer()->Unlock();
417 
418 									if (sDesktop->ActiveRootLayer()->Active() == winBorder)
419 										error = B_OK;
420 									else
421 										error = B_ERROR;
422 								}
423 							}
424 						}
425 					sDesktop->Unlock();
426 				}
427 			}
428 
429 			// send the reply
430 			BPrivate::PortLink replyLink(replyPort);
431 			replyLink.StartMessage(error);
432 			replyLink.Flush();
433 			break;
434 		}
435 
436 		default:
437 			STRACE(("Server::MainLoop received unexpected code %ld (offset %ld)\n",
438 				code, code - SERVER_TRUE));
439 			break;
440 	}
441 }
442 
443 
444 //	#pragma mark -
445 
446 
447 /*!
448 	\brief Entry function to run the entire server
449 	\param argc Number of command-line arguments present
450 	\param argv String array of the command-line arguments
451 	\return -1 if the app_server is already running, 0 if everything's OK.
452 */
453 int
454 main(int argc, char** argv)
455 {
456 	// There can be only one....
457 	if (find_port(SERVER_PORT_NAME) >= B_OK)
458 		return -1;
459 
460 	srand(real_time_clock_usecs());
461 
462 	AppServer* server = new AppServer;
463 	server->RunLooper();
464 
465 	return 0;
466 }
467