xref: /haiku/src/servers/app/AppServer.cpp (revision 2ae568931fcac7deb9f1e6ff4e47213fbfe4029b)
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  *		Stephan Aßmus <superstippi@gmx.de>
9  */
10 
11 
12 #include "AppServer.h"
13 
14 #include "BitmapManager.h"
15 #include "ColorSet.h"
16 #include "CursorManager.h"
17 #include "DecorManager.h"
18 #include "DefaultDecorator.h"
19 #include "Desktop.h"
20 #include "FontManager.h"
21 #include "HWInterface.h"
22 #include "InputManager.h"
23 #include "Layer.h"
24 #include "RGBColor.h"
25 #include "RegistrarDefs.h"
26 #include "RootLayer.h"
27 #include "ScreenManager.h"
28 #include "ServerApp.h"
29 #include "ServerConfig.h"
30 #include "ServerCursor.h"
31 #include "ServerProtocol.h"
32 #include "ServerWindow.h"
33 #include "SystemPalette.h"
34 
35 #include <Accelerant.h>
36 #include <AppDefs.h>
37 #include <Autolock.h>
38 #include <Directory.h>
39 #include <Entry.h>
40 #include <File.h>
41 #include <Message.h>
42 #include <Path.h>
43 #include <PortLink.h>
44 #include <RosterPrivate.h>
45 #include <StopWatch.h>
46 
47 #include <unistd.h>
48 
49 
50 //#define DEBUG_SERVER
51 #ifdef DEBUG_SERVER
52 #	include <stdio.h>
53 #	define STRACE(x) printf x
54 #else
55 #	define STRACE(x) ;
56 #endif
57 
58 
59 // Globals
60 port_id gAppServerPort;
61 static AppServer *sAppServer;
62 BTokenSpace gTokenSpace;
63 
64 //! System-wide GUI color object
65 ColorSet gGUIColorSet;
66 
67 
68 /*!
69 	\brief Constructor
70 
71 	This loads the default fonts, allocates all the major global variables, spawns the main housekeeping
72 	threads, loads user preferences for the UI and decorator, and allocates various locks.
73 */
74 AppServer::AppServer()
75 	: MessageLooper("app_server"),
76 	fMessagePort(-1),
77 	fDesktops(),
78 	fDesktopLock("AppServerDesktopLock")
79 {
80 	fMessagePort = create_port(DEFAULT_MONITOR_PORT_SIZE, SERVER_PORT_NAME);
81 	if (fMessagePort < B_OK)
82 		debugger("app_server could not create message port");
83 
84 	fLink.SetReceiverPort(fMessagePort);
85 
86 	sAppServer = this;
87 
88 	gInputManager = new InputManager();
89 
90 	// Create the font server and scan the proper directories.
91 	gFontManager = new FontManager;
92 	if (gFontManager->InitCheck() != B_OK)
93 		debugger("font manager could not be initialized!");
94 
95 	gFontManager->Run();
96 
97 	// Load the GUI colors here and set the global set to the values contained therein. If this
98 	// is not possible, set colors to the defaults
99 	if (LoadColorSet(SERVER_SETTINGS_DIR COLOR_SETTINGS_NAME, &gGUIColorSet) != B_OK)
100 		gGUIColorSet.SetToDefaults();
101 
102 	gScreenManager = new ScreenManager();
103 	gScreenManager->Run();
104 
105 	// the system palette needs to be initialized before the desktop,
106 	// since it is used there already
107 	InitializeColorMap();
108 
109 	// Create the bitmap allocator. Object declared in BitmapManager.cpp
110 	gBitmapManager = new BitmapManager();
111 
112 #if 0
113 	_LaunchCursorThread();
114 #endif
115 }
116 
117 /*!
118 	\brief Destructor
119 	Reached only when the server is asked to shut down in Test mode.
120 */
121 AppServer::~AppServer()
122 {
123 	delete gBitmapManager;
124 
125 	gScreenManager->Lock();
126 	gScreenManager->Quit();
127 
128 	gFontManager->Lock();
129 	gFontManager->Quit();
130 }
131 
132 
133 /*!
134 	\brief The call that starts it all...
135 */
136 void
137 AppServer::RunLooper()
138 {
139 	rename_thread(find_thread(NULL), "picasso");
140 	_message_thread((void *)this);
141 }
142 
143 
144 #if 0
145 /*!
146 	\brief Starts Input Server
147 */
148 void
149 AppServer::_LaunchInputServer()
150 {
151 	// We are supposed to start the input_server, but it's a BApplication
152 	// that depends on the registrar running, which is started after app_server
153 	// so we wait on roster thread to launch the input server
154 
155 	fISThreadID = B_ERROR;
156 
157 	while (!BRoster::Private().IsMessengerValid(false) && !fQuitting) {
158 		snooze(250000);
159 		BRoster::Private::DeleteBeRoster();
160 		BRoster::Private::InitBeRoster();
161 	}
162 
163 	if (fQuitting)
164 		return;
165 
166 	// we use an area for cursor data communication with input_server
167 	// area id and sem id are sent to the input_server
168 
169 	if (fCursorArea < B_OK)
170 		fCursorArea = create_area("isCursor", (void**) &fCursorAddr, B_ANY_ADDRESS, B_PAGE_SIZE, B_FULL_LOCK, B_READ_AREA | B_WRITE_AREA);
171 	if (fCursorSem < B_OK)
172 		fCursorSem = create_sem(0, "isSem");
173 
174 	int32 arg_c = 1;
175 	char **arg_v = (char **)malloc(sizeof(char *) * (arg_c + 1));
176 #if TEST_MODE
177 	arg_v[0] = strdup("/boot/home/svnhaiku/trunk/distro/x86.R1/beos/system/servers/input_server");
178 #else
179 	arg_v[0] = strdup("/system/servers/input_server");
180 #endif
181 	arg_v[1] = NULL;
182 	fISThreadID = load_image(arg_c, (const char**)arg_v, (const char **)environ);
183 	free(arg_v[0]);
184 
185 	int32 tmpbuf[2] = {fCursorSem, fCursorArea};
186 	int32 code = 0;
187 	send_data(fISThreadID, code, (void *)tmpbuf, sizeof(tmpbuf));
188 
189 	resume_thread(fISThreadID);
190 	setpgid(fISThreadID, 0);
191 
192 	// we receive
193 
194 	thread_id sender;
195 	code = receive_data(&sender, (void *)tmpbuf, sizeof(tmpbuf));
196 	fISASPort = tmpbuf[0];
197 	fISPort = tmpbuf[1];
198 
199 	// if at any time, one of these ports is error prone, it might mean input_server is gone
200 	// then relaunch input_server
201 }
202 #endif
203 
204 
205 /*!
206 	\brief Creates a desktop object for an authorized user
207 */
208 Desktop *
209 AppServer::_CreateDesktop(uid_t userID)
210 {
211 	BAutolock locker(fDesktopLock);
212 	Desktop* desktop = NULL;
213 	try {
214 		desktop = new Desktop(userID);
215 
216 		desktop->Init();
217 		desktop->Run();
218 
219 		if (!fDesktops.AddItem(desktop)) {
220 			delete desktop;
221 			return NULL;
222 		}
223 	} catch (...) {
224 		// there is obviously no memory left
225 		return NULL;
226 	}
227 
228 	return desktop;
229 }
230 
231 
232 /*!
233 	\brief Finds the desktop object that belongs to a certain user
234 */
235 Desktop *
236 AppServer::_FindDesktop(uid_t userID)
237 {
238 	BAutolock locker(fDesktopLock);
239 
240 	for (int32 i = 0; i < fDesktops.CountItems(); i++) {
241 		Desktop* desktop = fDesktops.ItemAt(i);
242 
243 		if (desktop->UserID() == userID)
244 			return desktop;
245 	}
246 
247 	return NULL;
248 }
249 
250 
251 /*!
252 	\brief Message handling function for all messages sent to the app_server
253 	\param code ID of the message sent
254 	\param buffer Attachment buffer for the message.
255 
256 */
257 void
258 AppServer::_DispatchMessage(int32 code, BPrivate::LinkReceiver& msg)
259 {
260 	switch (code) {
261 		case AS_GET_DESKTOP:
262 		{
263 			port_id replyPort;
264 			if (msg.Read<port_id>(&replyPort) < B_OK)
265 				break;
266 
267 			int32 userID;
268 			msg.Read<int32>(&userID);
269 
270 			Desktop* desktop = _FindDesktop(userID);
271 			if (desktop == NULL) {
272 				// we need to create a new desktop object for this user
273 				// ToDo: test if the user exists on the system
274 				// ToDo: maybe have a separate AS_START_DESKTOP_SESSION for authorizing the user
275 				desktop = _CreateDesktop(userID);
276 			}
277 
278 			BPrivate::LinkSender reply(replyPort);
279 			reply.StartMessage(B_OK);
280 			reply.Attach<port_id>(desktop->MessagePort());
281 			reply.Flush();
282 			break;
283 		}
284 
285 #if TEST_MODE
286 		case B_QUIT_REQUESTED:
287 		{
288 			// We've been asked to quit, so (for now) broadcast to all
289 			// desktops to quit. This situation will occur only when the server
290 			// is compiled as a regular Be application.
291 
292 			fQuitting = true;
293 
294 			while (fDesktops.CountItems() > 0) {
295 				Desktop *desktop = fDesktops.RemoveItemAt(0);
296 
297 				thread_id thread = desktop->Thread();
298 				desktop->PostMessage(B_QUIT_REQUESTED);
299 
300 				// we just wait for the desktop to kill itself
301 				status_t status;
302 				wait_for_thread(thread, &status);
303 			}
304 
305 			delete this;
306 
307 			// we are now clear to exit
308 			exit(0);
309 			break;
310 		}
311 #endif
312 
313 		default:
314 			STRACE(("Server::MainLoop received unexpected code %ld (offset %ld)\n",
315 				code, code - SERVER_TRUE));
316 			break;
317 	}
318 }
319 
320 
321 //	#pragma mark -
322 
323 
324 /*!
325 	\brief Entry function to run the entire server
326 	\param argc Number of command-line arguments present
327 	\param argv String array of the command-line arguments
328 	\return -1 if the app_server is already running, 0 if everything's OK.
329 */
330 int
331 main(int argc, char** argv)
332 {
333 	// There can be only one....
334 	if (find_port(SERVER_PORT_NAME) >= B_OK)
335 		return -1;
336 
337 	srand(real_time_clock_usecs());
338 
339 	AppServer* server = new AppServer;
340 	server->RunLooper();
341 
342 	return 0;
343 }
344