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