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