xref: /haiku/src/tests/apps/fake_app_server/AppServer.cpp (revision 68ea01249e1e2088933cb12f9c28d4e5c5d1c9ef)
1 //------------------------------------------------------------------------------
2 //	Copyright (c) 2001-2005, Haiku, Inc.
3 //
4 //	Permission is hereby granted, free of charge, to any person obtaining a
5 //	copy of this software and associated documentation files (the "Software"),
6 //	to deal in the Software without restriction, including without limitation
7 //	the rights to use, copy, modify, merge, publish, distribute, sublicense,
8 //	and/or sell copies of the Software, and to permit persons to whom the
9 //	Software is furnished to do so, subject to the following conditions:
10 //
11 //	The above copyright notice and this permission notice shall be included in
12 //	all copies or substantial portions of the Software.
13 //
14 //	THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 //	IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 //	FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17 //	AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 //	LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
19 //	FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
20 //	DEALINGS IN THE SOFTWARE.
21 //
22 //	File Name:		AppServer.cpp
23 //	Author:			DarkWyrm <bpmagic@columbus.rr.com>
24 //	Description:	main manager object for the app_server
25 //
26 //------------------------------------------------------------------------------
27 #include "AppServer.h"
28 #include "ServerApp.h"
29 #include "ServerProtocol.h"
30 
31 #include <Accelerant.h>
32 #include <AppDefs.h>
33 #include <Directory.h>
34 #include <Entry.h>
35 #include <File.h>
36 #include <Message.h>
37 #include <Path.h>
38 #include <PortLink.h>
39 #include <StopWatch.h>
40 
41 #include <stdio.h>
42 #include <stdlib.h>
43 
44 //#define DEBUG_KEYHANDLING
45 //#define DEBUG_SERVER
46 
47 #ifdef DEBUG_KEYHANDLING
48 #	define KBTRACE(x) printf x
49 #else
50 #	define KBTRACE(x) ;
51 #endif
52 
53 #ifdef DEBUG_SERVER
54 #	define STRACE(x) printf x
55 #else
56 #	define STRACE(x) ;
57 #endif
58 
59 // Globals
60 
61 port_id gAppServerPort;
62 
63 //! Used to access the app_server from new_decorator
64 static AppServer *sAppServer = NULL;
65 
66 //! Default background color for workspaces
67 //RGBColor workspace_default_color(51,102,160);
68 
69 
70 /*!
71 	\brief Constructor
72 
73 	This loads the default fonts, allocates all the major global variables, spawns the main housekeeping
74 	threads, loads user preferences for the UI and decorator, and allocates various locks.
75 */
76 AppServer::AppServer(void)
77 	:
78 	fCursorSem(-1),
79 	fCursorArea(-1)
80 {
81 	fMessagePort = create_port(200, SERVER_PORT_NAME);
82 	if (fMessagePort == B_NO_MORE_PORTS)
83 		debugger("app_server could not create message port");
84 
85 	gAppServerPort = fMessagePort;
86 
87 	fAppList = new BList();
88 	fQuittingServer = false;
89 
90 	// We need this in order for new_decorator to be able to instantiate new decorators
91 	sAppServer = this;
92 
93 	// This is necessary to mediate access between the Poller and app_server threads
94 	fActiveAppLock = create_sem(1,"app_server_active_sem");
95 
96 	// This locker is for app_server and Picasso to vy for control of the ServerApp list
97 	fAppListLock = create_sem(1,"app_server_applist_sem");
98 
99 	// Spawn our thread-monitoring thread
100 	fPicassoThreadID = spawn_thread(PicassoThread, "picasso", B_NORMAL_PRIORITY, this);
101 	if (fPicassoThreadID >= 0)
102 		resume_thread(fPicassoThreadID);
103 }
104 
105 /*!
106 	\brief Destructor
107 
108 	Reached only when the server is asked to shut down in Test mode. Kills all apps, shuts down the
109 	desktop, kills the housekeeping threads, etc.
110 */
111 AppServer::~AppServer()
112 {
113 }
114 
115 
116 /*!
117 	\brief Thread function for watching for dead apps
118 	\param data Pointer to the app_server to which the thread belongs
119 	\return Throwaway value - always 0
120 */
121 int32
122 AppServer::PicassoThread(void *data)
123 {
124 	for (;;) {
125 		acquire_sem(sAppServer->fAppListLock);
126 		for (int32 i = 0;;) {
127 			ServerApp *app = (ServerApp *)sAppServer->fAppList->ItemAt(i++);
128 			if (!app)
129 				break;
130 
131 			app->PingTarget();
132 		}
133 		release_sem(sAppServer->fAppListLock);
134 		// we do this every other second so as not to suck *too* many CPU cycles
135 		snooze(1000000);
136 	}
137 	return 0;
138 }
139 
140 
141 /*!
142 	\brief Starts Input Server
143 */
144 void
145 AppServer::LaunchInputServer()
146 {
147 }
148 
149 
150 /*!
151 	\brief Starts the Cursor Thread
152 */
153 void
154 AppServer::LaunchCursorThread()
155 {
156 }
157 
158 
159 /*!
160 	\brief The call that starts it all...
161 	\return Always 0
162 */
163 thread_id
164 AppServer::Run(void)
165 {
166 	MainLoop();
167 	kill_thread(fPicassoThreadID);
168 	return 0;
169 }
170 
171 
172 //! Main message-monitoring loop for the regular message port - no input messages!
173 void
174 AppServer::MainLoop(void)
175 {
176 	BPrivate::PortLink pmsg(-1, fMessagePort);
177 
178 	while (1) {
179 		STRACE(("info: AppServer::MainLoop listening on port %ld.\n", fMessagePort));
180 
181 		int32 code;
182 		status_t err = pmsg.GetNextMessage(code);
183 		if (err < B_OK) {
184 			STRACE(("MainLoop:pmsg.GetNextMessage() failed\n"));
185 			continue;
186 		}
187 
188 		switch (code) {
189 			case B_QUIT_REQUESTED:
190 			case AS_GET_DESKTOP:
191 			case AS_CREATE_APP:
192 			case AS_DELETE_APP:
193 				DispatchMessage(code, pmsg);
194 				break;
195 			default:
196 			{
197 				STRACE(("Server::MainLoop received unexpected code %ld\n",
198 					code));
199 				break;
200 			}
201 		}
202 	}
203 }
204 
205 /*!
206 	\brief Message handling function for all messages sent to the app_server
207 	\param code ID of the message sent
208 	\param buffer Attachment buffer for the message.
209 
210 */
211 void
212 AppServer::DispatchMessage(int32 code, BPrivate::PortLink &msg)
213 {
214 	switch (code) {
215 		case AS_GET_DESKTOP:
216 		{
217 			port_id replyPort;
218 			if (msg.Read<port_id>(&replyPort) < B_OK)
219 				break;
220 
221 			int32 userID;
222 			msg.Read<int32>(&userID);
223 
224 			BPrivate::LinkSender reply(replyPort);
225 			reply.StartMessage(B_OK);
226 			reply.Attach<port_id>(fMessagePort);
227 			reply.Flush();
228 			break;
229 		}
230 
231 		case AS_CREATE_APP:
232 		{
233 			// Create the ServerApp to node monitor a new BApplication
234 
235 			// Attached data:
236 			// 1) port_id - receiver port of a regular app
237 			// 2) port_id - client looper port - for send messages to the client
238 			// 2) team_id - app's team ID
239 			// 3) int32 - handler token of the regular app
240 			// 4) char * - signature of the regular app
241 
242 			// Find the necessary data
243 			team_id	clientTeamID = -1;
244 			port_id	clientLooperPort = -1;
245 			port_id clientReplyPort = -1;
246 			int32 clientToken;
247 			char *appSignature = NULL;
248 
249 			msg.Read<port_id>(&clientReplyPort);
250 			msg.Read<port_id>(&clientLooperPort);
251 			msg.Read<team_id>(&clientTeamID);
252 			msg.Read<int32>(&clientToken);
253 			if (msg.ReadString(&appSignature) != B_OK)
254 				break;
255 
256 			port_id serverListen = create_port(DEFAULT_MONITOR_PORT_SIZE, appSignature);
257 			if (serverListen < B_OK) {
258 				printf("No more ports left. Time to crash. Have a nice day! :)\n");
259 				break;
260 			}
261 
262 			// we let the application own the port, so that we get aware when it's gone
263 			if (set_port_owner(serverListen, clientTeamID) < B_OK) {
264 				delete_port(serverListen);
265 				printf("Could not transfer port ownership to client %ld!\n", clientTeamID);
266 				break;
267 			}
268 
269 			// Create the ServerApp subthread for this app
270 			acquire_sem(fAppListLock);
271 
272 			ServerApp *app = new ServerApp(clientReplyPort, serverListen, clientLooperPort,
273 				clientTeamID, clientToken, appSignature);
274 
275 			// add the new ServerApp to the known list of ServerApps
276 			fAppList->AddItem(app);
277 
278 			release_sem(fAppListLock);
279 
280 			BPrivate::PortLink replylink(clientReplyPort);
281 			replylink.StartMessage(B_OK);
282 			replylink.Attach<int32>(serverListen);
283 			replylink.Flush();
284 
285 			// This is necessary because BPortLink::ReadString allocates memory
286 			free(appSignature);
287 			break;
288 		}
289 		case AS_DELETE_APP:
290 		{
291 			// Delete a ServerApp. Received only from the respective ServerApp when a
292 			// BApplication asks it to quit.
293 
294 			// Attached Data:
295 			// 1) thread_id - thread ID of the ServerApp to be deleted
296 
297 			int32 i = 0, appnum = fAppList->CountItems();
298 			ServerApp *srvapp = NULL;
299 			thread_id srvapp_id = -1;
300 
301 			if (msg.Read<thread_id>(&srvapp_id) < B_OK)
302 				break;
303 
304 			acquire_sem(fAppListLock);
305 
306 			// Run through the list of apps and nuke the proper one
307 			for (i = 0; i < appnum; i++) {
308 				srvapp = (ServerApp *)fAppList->ItemAt(i);
309 
310 				if (srvapp != NULL && srvapp->MonitorThreadID() == srvapp_id) {
311 					srvapp = (ServerApp *)fAppList->RemoveItem(i);
312 					if (srvapp) {
313 						delete srvapp;
314 						srvapp = NULL;
315 					}
316 					break;	// jump out of our for() loop
317 				}
318 			}
319 
320 			release_sem(fAppListLock);
321 			break;
322 		}
323 
324 		case B_QUIT_REQUESTED:
325 			break;
326 
327 		default:
328 			// we should never get here.
329 			break;
330 	}
331 }
332 
333 /*!
334 	\brief Send a quick (no attachments) message to all applications
335 
336 	Quite useful for notification for things like server shutdown, system
337 	color changes, etc.
338 */
339 void
340 AppServer::Broadcast(int32 code)
341 {
342 	acquire_sem(fAppListLock);
343 
344 	for (int32 i = 0; i < fAppList->CountItems(); i++) {
345 		ServerApp *app = (ServerApp *)fAppList->ItemAt(i);
346 
347 		if (!app)
348 			{ printf("PANIC in AppServer::Broadcast()\n"); continue; }
349 		app->PostMessage(code);
350 	}
351 
352 	release_sem(fAppListLock);
353 }
354 
355 /*!
356 	\brief Finds the application with the given signature
357 	\param sig MIME signature of the application to find
358 	\return the corresponding ServerApp or NULL if not found
359 
360 	This call should be made only when necessary because it locks the app list
361 	while it does its searching.
362 */
363 ServerApp *
364 AppServer::FindApp(const char *sig)
365 {
366 	if (!sig)
367 		return NULL;
368 
369 	ServerApp *foundapp=NULL;
370 
371 	acquire_sem(fAppListLock);
372 
373 	for(int32 i=0; i<fAppList->CountItems();i++)
374 	{
375 		foundapp=(ServerApp*)fAppList->ItemAt(i);
376 		if(foundapp && foundapp->Title() == sig)
377 		{
378 			release_sem(fAppListLock);
379 			return foundapp;
380 		}
381 	}
382 
383 	release_sem(fAppListLock);
384 
385 	// couldn't find a match
386 	return NULL;
387 }
388 
389 
390 //	#pragma mark -
391 
392 
393 /*!
394 	\brief Entry function to run the entire server
395 	\param argc Number of command-line arguments present
396 	\param argv String array of the command-line arguments
397 	\return -1 if the app_server is already running, 0 if everything's OK.
398 */
399 int
400 main(int argc, char** argv)
401 {
402 	// There can be only one....
403 	if (find_port(SERVER_PORT_NAME) >= B_OK)
404 		return -1;
405 
406 	AppServer app_server;
407 	app_server.Run();
408 	return 0;
409 }
410