xref: /haiku/src/tests/apps/fake_app_server/ServerApp.cpp (revision 93aeb8c3bc3f13cb1f282e3e749258a23790d947)
1 /*
2  * Copyright 2001-2005, Haiku.
3  * Distributed under the terms of the MIT License.
4  *
5  * Authors:
6  *		DarkWyrm <bpmagic@columbus.rr.com>
7  *		Adrian Oanca <adioanca@cotty.iren.ro>
8  *		Stephan Aßmus <superstippi@gmx.de>
9  *		Stefano Ceccherini (burton666@libero.it)
10  *		Axel Dörfler, axeld@pinc-software.de
11  */
12 
13 
14 #include <AppDefs.h>
15 #include <List.h>
16 #include <String.h>
17 #include <ColorSet.h>
18 #include <RGBColor.h>
19 #include <stdio.h>
20 #include <string.h>
21 #include <ScrollBar.h>
22 #include <Shape.h>
23 #include <ServerProtocol.h>
24 
25 #include "AppServer.h"
26 #include "BitmapManager.h"
27 #include "BGet++.h"
28 
29 #include "ServerApp.h"
30 #include "ServerCursor.h"
31 #include "ServerBitmap.h"
32 #include "ServerConfig.h"
33 #include "SystemPalette.h"
34 #include "Utils.h"
35 
36 //#define DEBUG_SERVERAPP
37 
38 #ifdef DEBUG_SERVERAPP
39 #	include <stdio.h>
40 #	define STRACE(x) printf x
41 #else
42 #	define STRACE(x) ;
43 #endif
44 
45 //#define DEBUG_SERVERAPP_FONT
46 
47 #ifdef DEBUG_SERVERAPP_FONT
48 #	include <stdio.h>
49 #	define FTRACE(x) printf x
50 #else
51 #	define FTRACE(x) ;
52 #endif
53 
54 /*!
55 	\brief Constructor
56 	\param sendport port ID for the BApplication which will receive the ServerApp's messages
57 	\param rcvport port by which the ServerApp will receive messages from its BApplication.
58 	\param fSignature NULL-terminated string which contains the BApplication's
59 	MIME fSignature.
60 */
61 ServerApp::ServerApp(port_id sendport, port_id rcvport, port_id clientLooperPort,
62 	team_id clientTeamID, int32 handlerID, const char* signature)
63 	:
64 	fClientAppPort(sendport),
65 	fMessagePort(rcvport),
66 	fClientLooperPort(clientLooperPort),
67 	fSignature(signature),
68 	fMonitorThreadID(-1),
69 	fClientTeamID(clientTeamID),
70 	fLink(fClientAppPort, fMessagePort),
71 	fSWindowList(new BList()),
72 	fBitmapList(new BList()),
73 	fPictureList(new BList()),
74 	fAppCursor(NULL),
75 	fLockSem(create_sem(1, "ServerApp sem")),
76 	fCursorHidden(false),
77 	fIsActive(false),
78 	//fHandlerToken(handlerID),
79 	fSharedMem(new AreaPool),
80 	fQuitting(false)
81 {
82 	if (fSignature == "")
83 		fSignature = "application/x-vnd.NULL-application-signature";
84 
85 	Run();
86 
87 	STRACE(("ServerApp %s:\n", fSignature.String()));
88 	STRACE(("\tBApp port: %ld\n", fClientAppPort));
89 	STRACE(("\tReceiver port: %ld\n", fMessagePort));
90 }
91 
92 
93 //! Does all necessary teardown for application
94 ServerApp::~ServerApp(void)
95 {
96 	STRACE(("*ServerApp %s:~ServerApp()\n",fSignature.String()));
97 
98 	fQuitting = true;
99 
100 	for (int32 i = 0; i< fBitmapList->CountItems(); i++) {
101 		delete static_cast<ServerBitmap *>(fBitmapList->ItemAt(i));
102 	}
103 	delete fBitmapList;
104 
105 	delete fPictureList;
106 
107 	// This shouldn't be necessary -- all cursors owned by the app
108 	// should be cleaned up by RemoveAppCursors
109 //	if(fAppCursor)
110 //		delete fAppCursor;
111 
112 	delete_sem(fLockSem);
113 
114 	STRACE(("#ServerApp %s:~ServerApp()\n", fSignature.String()));
115 
116 	// TODO: Is this the right place for this ?
117 	// From what I've understood, this is the port created by
118 	// the BApplication (?), but if I delete it in there, GetNextMessage()
119 	// in the MonitorApp thread never returns. Cleanup.
120 	delete_port(fMessagePort);
121 
122 	status_t dummyStatus;
123 	wait_for_thread(fMonitorThreadID, &dummyStatus);
124 
125 	delete fSharedMem;
126 
127 	STRACE(("ServerApp %s::~ServerApp(): Exiting\n", fSignature.String()));
128 }
129 
130 /*!
131 	\brief Starts the ServerApp monitoring for messages
132 	\return false if the application couldn't start, true if everything went OK.
133 */
134 bool
135 ServerApp::Run(void)
136 {
137 	// Unlike a BApplication, a ServerApp is *supposed* to return immediately
138 	// when its Run() function is called.
139 	fMonitorThreadID = spawn_thread(MonitorApp, fSignature.String(), B_NORMAL_PRIORITY, this);
140 	if (fMonitorThreadID < B_OK)
141 		return false;
142 
143 	return resume_thread(fMonitorThreadID) == B_OK;
144 }
145 
146 /*!
147 	\brief Pings the target app to make sure it's still working
148 	\return true if target is still "alive" and false if "He's dead, Jim."
149 	"But that's impossible..."
150 
151 	This function is called by the app_server thread to ensure that
152 	the target app still exists. We do this not by sending a message
153 	but by calling get_port_info. We don't want to send ping messages
154 	just because the app might simply be hung. If this is the case, it
155 	should be up to the user to kill it. If the app has been killed, its
156 	ports will be invalid. Thus, if get_port_info returns an error, we
157 	tell the app_server to delete the respective ServerApp.
158 */
159 bool
160 ServerApp::PingTarget(void)
161 {
162 	team_info tinfo;
163 	if (get_team_info(fClientTeamID, &tinfo) == B_BAD_TEAM_ID) {
164 		BPrivate::LinkSender link(gAppServerPort);
165 		link.StartMessage(AS_DELETE_APP);
166 		link.Attach(&fMonitorThreadID, sizeof(thread_id));
167 		link.Flush();
168 		return false;
169 	}
170 	return true;
171 }
172 
173 /*!
174 	\brief Send a message to the ServerApp with no attachments
175 	\param code ID code of the message to post
176 */
177 void
178 ServerApp::PostMessage(int32 code)
179 {
180 	BPrivate::LinkSender link(fMessagePort);
181 	link.StartMessage(code);
182 	link.Flush();
183 }
184 
185 /*!
186 	\brief Send a message to the ServerApp's BApplication
187 	\param msg The message to send
188 */
189 void
190 ServerApp::SendMessageToClient(const BMessage *msg) const
191 {
192 	ssize_t size = msg->FlattenedSize();
193 	char *buffer = new char[size];
194 
195 	if (msg->Flatten(buffer, size) == B_OK)
196 		write_port(fClientLooperPort, msg->what, buffer, size);
197 	else
198 		printf("PANIC: ServerApp: '%s': can't flatten message in 'SendMessageToClient()'\n", fSignature.String());
199 
200 	delete [] buffer;
201 }
202 
203 /*!
204 	\brief Sets the ServerApp's active status
205 	\param value The new status of the ServerApp.
206 
207 	This changes an internal flag and also sets the current cursor to the one specified by
208 	the application
209 */
210 void
211 ServerApp::Activate(bool value)
212 {
213 	fIsActive = value;
214 	SetAppCursor();
215 }
216 
217 
218 //! Sets the cursor to the application cursor, if any.
219 void
220 ServerApp::SetAppCursor(void)
221 {
222 }
223 
224 
225 /*!
226 	\brief The thread function ServerApps use to monitor messages
227 	\param data Pointer to the thread's ServerApp object
228 	\return Throwaway value - always 0
229 */
230 int32
231 ServerApp::MonitorApp(void *data)
232 {
233 	// Message-dispatching loop for the ServerApp
234 
235 	ServerApp *app = (ServerApp *)data;
236 	BPrivate::LinkReceiver &reader = app->fLink.Receiver();
237 
238 	int32 code;
239 	status_t err = B_OK;
240 
241 	while (!app->fQuitting) {
242 		STRACE(("info: ServerApp::MonitorApp listening on port %ld.\n", app->fMessagePort));
243 		err = reader.GetNextMessage(code, B_INFINITE_TIMEOUT);
244 		if (err < B_OK) {
245 			STRACE(("ServerApp::MonitorApp(): GetNextMessage returned %s\n", strerror(err)));
246 
247 			// ToDo: this should kill the app, but it doesn't work
248 			BPrivate::LinkSender link(gAppServerPort);
249 			link.StartMessage(AS_DELETE_APP);
250 			link.Attach(&app->fMonitorThreadID, sizeof(thread_id));
251 			link.Flush();
252 			break;
253 		}
254 
255 		switch (code) {
256 			case AS_QUIT_APP:
257 			{
258 				// This message is received only when the app_server is asked to shut down in
259 				// test/debug mode. Of course, if we are testing while using AccelerantDriver, we do
260 				// NOT want to shut down client applications. The server can be quit o in this fashion
261 				// through the driver's interface, such as closing the ViewDriver's window.
262 
263 				STRACE(("ServerApp %s:Server shutdown notification received\n",
264 					app->fSignature.String()));
265 
266 				// If we are using the real, accelerated version of the
267 				// DisplayDriver, we do NOT want the user to be able shut down
268 				// the server. The results would NOT be pretty
269 				break;
270 			}
271 
272 			case B_QUIT_REQUESTED:
273 			{
274 				STRACE(("ServerApp %s: B_QUIT_REQUESTED\n",app->fSignature.String()));
275 				// Our BApplication sent us this message when it quit.
276 				// We need to ask the app_server to delete ourself.
277 				BPrivate::LinkSender sender(gAppServerPort);
278 				sender.StartMessage(AS_DELETE_APP);
279 				sender.Attach(&app->fMonitorThreadID, sizeof(thread_id));
280 				sender.Flush();
281 				break;
282 			}
283 
284 			default:
285 				STRACE(("ServerApp %s: Got a Message to dispatch\n", app->fSignature.String()));
286 				app->DispatchMessage(code, reader);
287 				break;
288 		}
289 	}
290 
291 	return 0;
292 }
293 
294 
295 /*!
296 	\brief Handler function for BApplication API messages
297 	\param code Identifier code for the message. Equivalent to BMessage::what
298 	\param buffer Any attachments
299 
300 	Note that the buffer's exact format is determined by the particular message.
301 	All attachments are placed in the buffer via a PortLink, so it will be a
302 	matter of casting and incrementing an index variable to access them.
303 */
304 void
305 ServerApp::DispatchMessage(int32 code, BPrivate::LinkReceiver &link)
306 {
307 	switch (code) {
308 		case AS_ACQUIRE_SERVERMEM:
309 		{
310 			// This particular call is more than a bit of a pain in the neck. We are given a
311 			// size of a chunk of memory needed. We need to (1) allocate it, (2) get the area for
312 			// this particular chunk, (3) find the offset in the area for this chunk, and (4)
313 			// tell the client about it. Good thing this particular call isn't used much
314 
315 			// Received from a ServerMemIO object requesting operating memory
316 			// Attached Data:
317 			// 1) size_t requested size
318 			// 2) port_id reply_port
319 
320 			size_t memsize;
321 			link.Read<size_t>(&memsize);
322 
323 			// TODO: I wonder if ACQUIRE_SERVERMEM should have a minimum size requirement?
324 
325 			void *sharedmem = fSharedMem->GetBuffer(memsize);
326 
327 			if (memsize < 1 || sharedmem == NULL) {
328 				fLink.StartMessage(SERVER_FALSE);
329 				fLink.Flush();
330 				break;
331 			}
332 
333 			area_id owningArea = area_for(sharedmem);
334 			area_info info;
335 
336 			if (owningArea == B_ERROR || get_area_info(owningArea, &info) < B_OK) {
337 				fLink.StartMessage(SERVER_FALSE);
338 				fLink.Flush();
339 				break;
340 			}
341 
342 			int32 areaoffset = (addr_t)sharedmem - (addr_t)info.address;
343 			STRACE(("Successfully allocated shared memory of size %ld\n",memsize));
344 
345 			fLink.StartMessage(SERVER_TRUE);
346 			fLink.Attach<area_id>(owningArea);
347 			fLink.Attach<int32>(areaoffset);
348 			fLink.Flush();
349 			break;
350 		}
351 		case AS_RELEASE_SERVERMEM:
352 		{
353 			// Received when a ServerMemIO object on destruction
354 			// Attached Data:
355 			// 1) area_id owning area
356 			// 2) int32 area offset
357 
358 			area_id owningArea;
359 			int32 areaoffset;
360 
361 			link.Read<area_id>(&owningArea);
362 			link.Read<int32>(&areaoffset);
363 
364 			area_info areaInfo;
365 			if (owningArea < 0 || get_area_info(owningArea, &areaInfo) != B_OK)
366 				break;
367 
368 			STRACE(("Successfully freed shared memory\n"));
369 			void *sharedmem = ((int32*)areaInfo.address) + areaoffset;
370 
371 			fSharedMem->ReleaseBuffer(sharedmem);
372 
373 			break;
374 		}
375 
376 		case AS_CURRENT_WORKSPACE:
377 		{
378 			STRACE(("ServerApp %s: get current workspace\n", fSignature.String()));
379 
380 			// TODO: Locking this way is not nice
381 			fLink.StartMessage(SERVER_TRUE);
382 			fLink.Attach<int32>(0);
383 			fLink.Flush();
384 			break;
385 		}
386 
387 		case AS_ACTIVATE_WORKSPACE:
388 		{
389 			STRACE(("ServerApp %s: activate workspace\n", fSignature.String()));
390 
391 			// TODO: See above
392 			int32 index;
393 			link.Read<int32>(&index);
394 			// no reply
395 
396 			break;
397 		}
398 
399 		case AS_QUERY_CURSOR_HIDDEN:
400 		{
401 			STRACE(("ServerApp %s: Received IsCursorHidden request\n", fSignature.String()));
402 			// Attached data
403 			// 1) int32 port to reply to
404 			fLink.StartMessage(fCursorHidden ? SERVER_TRUE : SERVER_FALSE);
405 			fLink.Flush();
406 			break;
407 		}
408 
409 		default:
410 			printf("ServerApp %s received unhandled message code offset %lx\n",
411 				fSignature.String(), code);
412 
413 			if (link.NeedsReply()) {
414 				// the client is now blocking and waiting for a reply!
415 				fLink.StartMessage(SERVER_FALSE);
416 				fLink.Flush();
417 			} else
418 				puts("message doesn't need a reply!");
419 			break;
420 	}
421 }
422 
423 
424 int32
425 ServerApp::CountBitmaps() const
426 {
427 	return fBitmapList ? fBitmapList->CountItems() : 0;
428 }
429 
430 
431 /*!
432 	\brief Looks up a ServerApp's ServerBitmap in its list
433 	\param token ID token of the bitmap to find
434 	\return The bitmap having that ID or NULL if not found
435 */
436 ServerBitmap *
437 ServerApp::FindBitmap(int32 token) const
438 {
439 	ServerBitmap *bitmap;
440 	for (int32 i = 0; i < fBitmapList->CountItems(); i++) {
441 		bitmap = static_cast<ServerBitmap *>(fBitmapList->ItemAt(i));
442 		if (bitmap && bitmap->Token() == token)
443 			return bitmap;
444 	}
445 
446 	return NULL;
447 }
448 
449 
450 int32
451 ServerApp::CountPictures() const
452 {
453 	return fPictureList ? fPictureList->CountItems() : 0;
454 }
455 
456 
457 ServerPicture *
458 ServerApp::FindPicture(int32 token) const
459 {
460 	return NULL;
461 }
462 
463 
464 team_id
465 ServerApp::ClientTeamID() const
466 {
467 	return fClientTeamID;
468 }
469 
470 
471 thread_id
472 ServerApp::MonitorThreadID() const
473 {
474 	return fMonitorThreadID;
475 }
476 
477