xref: /haiku/src/servers/app/Desktop.cpp (revision fef6144999c2fa611f59ee6ffe6dd7999501385c)
1 /*
2  * Copyright 2001-2005, Haiku.
3  * Distributed under the terms of the MIT License.
4  *
5  * Authors:
6  *		Adrian Oanca <adioanca@cotty.iren.ro>
7  *		Stephan Aßmus <superstippi@gmx.de>
8  *		Axel Dörfler, axeld@pinc-software.de
9  */
10 
11 /**	Class used to encapsulate desktop management */
12 
13 
14 #include <stdio.h>
15 
16 #include <Message.h>
17 #include <Region.h>
18 
19 #include <WindowInfo.h>
20 #include <ServerProtocol.h>
21 
22 #include "AppServer.h"
23 #include "DisplayDriverPainter.h"
24 #include "Globals.h"
25 #include "Layer.h"
26 #include "RootLayer.h"
27 #include "ServerConfig.h"
28 #include "ServerScreen.h"
29 #include "ServerApp.h"
30 #include "ServerWindow.h"
31 #include "WinBorder.h"
32 #include "Workspace.h"
33 #include "DesktopSettingsPrivate.h"
34 
35 
36 #ifdef __HAIKU__
37 #	define USE_ACCELERANT 1
38 #else
39 #	define USE_ACCELERANT 0
40 #endif
41 
42 #if USE_ACCELERANT
43 #	include "AccelerantHWInterface.h"
44 #else
45 #	include "ViewHWInterface.h"
46 #endif
47 
48 #include "Desktop.h"
49 
50 //#define DEBUG_DESKTOP
51 
52 #ifdef DEBUG_DESKTOP
53 #	define STRACE(a) printf(a)
54 #else
55 #	define STRACE(a) /* nothing */
56 #endif
57 
58 
59 Desktop::Desktop()
60 	: MessageLooper("desktop"),
61 	fSettings(new DesktopSettings::Private()),
62 	fAppListLock("application list"),
63 	fShutdownSemaphore(-1),
64 	fWinBorderList(64),
65 	fActiveScreen(NULL),
66 	fCursorManager()
67 {
68 	// TODO: use user name
69 	char name[B_OS_NAME_LENGTH];
70 	snprintf(name, sizeof(name), "d:%s", "baron");
71 
72 	fMessagePort = create_port(DEFAULT_MONITOR_PORT_SIZE, name);
73 	if (fMessagePort < B_OK)
74 		return;
75 
76 	fLink.SetReceiverPort(fMessagePort);
77 }
78 
79 
80 Desktop::~Desktop()
81 {
82 	for (int32 i = 0; WinBorder *border = (WinBorder *)fWinBorderList.ItemAt(i); i++)
83 		delete border;
84 
85 	delete fRootLayer;
86 	delete fSettings;
87 
88 	delete_port(fMessagePort);
89 }
90 
91 
92 void
93 Desktop::Init()
94 {
95 	fVirtualScreen.RestoreConfiguration(*this, fSettings->WorkspacesMessage(0));
96 
97 	// TODO: temporary workaround, fActiveScreen will be removed
98 	fActiveScreen = fVirtualScreen.ScreenAt(0);
99 
100 	// TODO: add user identity to the name
101 	char name[32];
102 	sprintf(name, "RootLayer %d", 1);
103 
104 	fRootLayer = new RootLayer(name, 4, this, GetDisplayDriver());
105 	fRootLayer->RunThread();
106 
107 	// take care of setting the default cursor
108 	ServerCursor *cursor = fCursorManager.GetCursor(B_CURSOR_DEFAULT);
109 	if (cursor)
110 		fVirtualScreen.HWInterface()->SetCursor(cursor);
111 }
112 
113 
114 void
115 Desktop::_GetLooperName(char* name, size_t length)
116 {
117 	snprintf(name, length, "d:%d:%s", /*id*/0, /*name*/"baron");
118 }
119 
120 
121 void
122 Desktop::_PrepareQuit()
123 {
124 	// let's kill all remaining applications
125 
126 	fAppListLock.Lock();
127 
128 	int32 count = fAppList.CountItems();
129 	for (int32 i = 0; i < count; i++) {
130 		ServerApp *app = (ServerApp*)fAppList.ItemAt(i);
131 		team_id clientTeam = app->ClientTeam();
132 
133 		app->Quit();
134 		kill_team(clientTeam);
135 	}
136 
137 	// wait for the last app to die
138 	if (count > 0)
139 		acquire_sem_etc(fShutdownSemaphore, fShutdownCount, B_RELATIVE_TIMEOUT, 250000);
140 
141 	fAppListLock.Unlock();
142 }
143 
144 
145 void
146 Desktop::_DispatchMessage(int32 code, BPrivate::LinkReceiver &link)
147 {
148 	switch (code) {
149 		case AS_CREATE_APP:
150 		{
151 			// Create the ServerApp to node monitor a new BApplication
152 
153 			// Attached data:
154 			// 1) port_id - receiver port of a regular app
155 			// 2) port_id - client looper port - for sending messages to the client
156 			// 2) team_id - app's team ID
157 			// 3) int32 - handler token of the regular app
158 			// 4) char * - signature of the regular app
159 
160 			// Find the necessary data
161 			team_id	clientTeamID = -1;
162 			port_id	clientLooperPort = -1;
163 			port_id clientReplyPort = -1;
164 			int32 htoken = B_NULL_TOKEN;
165 			char *appSignature = NULL;
166 
167 			link.Read<port_id>(&clientReplyPort);
168 			link.Read<port_id>(&clientLooperPort);
169 			link.Read<team_id>(&clientTeamID);
170 			link.Read<int32>(&htoken);
171 			if (link.ReadString(&appSignature) != B_OK)
172 				break;
173 
174 			ServerApp *app = new ServerApp(this, clientReplyPort,
175 				clientLooperPort, clientTeamID, htoken, appSignature);
176 			if (app->InitCheck() == B_OK
177 				&& app->Run()) {
178 				// add the new ServerApp to the known list of ServerApps
179 				fAppListLock.Lock();
180 				fAppList.AddItem(app);
181 				fAppListLock.Unlock();
182 			} else {
183 				delete app;
184 
185 				// if everything went well, ServerApp::Run() will notify
186 				// the client - but since it didn't, we do it here
187 				BPrivate::LinkSender reply(clientReplyPort);
188 				reply.StartMessage(SERVER_FALSE);
189 				reply.Flush();
190 			}
191 
192 			// This is necessary because BPortLink::ReadString allocates memory
193 			free(appSignature);
194 			break;
195 		}
196 
197 		case AS_DELETE_APP:
198 		{
199 			// Delete a ServerApp. Received only from the respective ServerApp when a
200 			// BApplication asks it to quit.
201 
202 			// Attached Data:
203 			// 1) thread_id - thread ID of the ServerApp to be deleted
204 
205 			thread_id thread = -1;
206 			if (link.Read<thread_id>(&thread) < B_OK)
207 				break;
208 
209 			fAppListLock.Lock();
210 
211 			// Run through the list of apps and nuke the proper one
212 
213 			int32 count = fAppList.CountItems();
214 			ServerApp *removeApp = NULL;
215 
216 			for (int32 i = 0; i < count; i++) {
217 				ServerApp *app = (ServerApp *)fAppList.ItemAt(i);
218 
219 				if (app != NULL && app->Thread() == thread) {
220 					fAppList.RemoveItem(i);
221 					removeApp = app;
222 					break;
223 				}
224 			}
225 
226 			fAppListLock.Unlock();
227 
228 			if (removeApp != NULL)
229 				removeApp->Quit(fShutdownSemaphore);
230 
231 			if (fQuitting && count <= 1) {
232 				// wait for the last app to die
233 				acquire_sem_etc(fShutdownSemaphore, fShutdownCount, B_RELATIVE_TIMEOUT, 500000);
234 				PostMessage(kMsgQuitLooper);
235 			}
236 			break;
237 		}
238 
239 		case B_QUIT_REQUESTED:
240 			// We've been asked to quit, so (for now) broadcast to all
241 			// test apps to quit. This situation will occur only when the server
242 			// is compiled as a regular Be application.
243 
244 			fAppListLock.Lock();
245 			fShutdownSemaphore = create_sem(0, "desktop shutdown");
246 			fShutdownCount = fAppList.CountItems();
247 			fAppListLock.Unlock();
248 
249 			fQuitting = true;
250 			BroadcastToAllApps(AS_QUIT_APP);
251 
252 			// We now need to process the remaining AS_DELETE_APP messages and
253 			// wait for the kMsgShutdownServer message.
254 			// If an application does not quit as asked, the picasso thread
255 			// will send us this message in 2-3 seconds.
256 
257 			// if there are no apps to quit, shutdown directly
258 			if (fShutdownCount == 0)
259 				PostMessage(kMsgQuitLooper);
260 			break;
261 
262 		default:
263 			printf("Desktop %d:%s received unexpected code %ld\n", 0, "baron", code);
264 
265 			if (link.NeedsReply()) {
266 				// the client is now blocking and waiting for a reply!
267 				fLink.StartMessage(B_ERROR);
268 				fLink.Flush();
269 			}
270 			break;
271 	}
272 }
273 
274 
275 /*!
276 	\brief Send a quick (no attachments) message to all applications
277 
278 	Quite useful for notification for things like server shutdown, system
279 	color changes, etc.
280 */
281 void
282 Desktop::BroadcastToAllApps(int32 code)
283 {
284 	BAutolock locker(fAppListLock);
285 
286 	for (int32 i = 0; i < fAppList.CountItems(); i++) {
287 		ServerApp *app = (ServerApp *)fAppList.ItemAt(i);
288 
289 		if (!app) {
290 			printf("PANIC in Broadcast()\n");
291 			continue;
292 		}
293 		app->PostMessage(code);
294 	}
295 }
296 
297 
298 //---------------------------------------------------------------------------
299 //				Methods for layer(WinBorder) manipulation.
300 //---------------------------------------------------------------------------
301 
302 
303 void
304 Desktop::AddWinBorder(WinBorder *winBorder)
305 {
306 	if (!winBorder)
307 		return;
308 
309 	// R2: how to determine the RootLayer to which this window should be added???
310 	// for now, use ActiveRootLayer() because we only have one instance.
311 
312 	int32 feel = winBorder->Feel();
313 
314 	// we are ServerApp thread, we need to lock RootLayer here.
315 	ActiveRootLayer()->Lock();
316 
317 	// we're playing with window list. lock first.
318 	Lock();
319 
320 	if (fWinBorderList.HasItem(winBorder)) {
321 		Unlock();
322 		ActiveRootLayer()->Unlock();
323 		debugger("AddWinBorder: WinBorder already in Desktop list\n");
324 		return;
325 	}
326 
327 	// we have a new window. store a record of it.
328 	fWinBorderList.AddItem(winBorder);
329 
330 	// add FLOATING_APP windows to the local list of all normal windows.
331 	// This is to keep the order all floating windows (app or subset) when we go from
332 	// one normal window to another.
333 	if (feel == B_FLOATING_APP_WINDOW_FEEL || feel == B_NORMAL_WINDOW_FEEL) {
334 		WinBorder *wb = NULL;
335 		int32 count = fWinBorderList.CountItems();
336 		int32 feelToLookFor = (feel == B_NORMAL_WINDOW_FEEL ?
337 			B_FLOATING_APP_WINDOW_FEEL : B_NORMAL_WINDOW_FEEL);
338 
339 		for (int32 i = 0; i < count; i++) {
340 			wb = (WinBorder *)fWinBorderList.ItemAt(i);
341 
342 			if (wb->App()->ClientTeam() == winBorder->App()->ClientTeam()
343 				&& wb->Feel() == feelToLookFor) {
344 				// R2: RootLayer comparison is needed.
345 				feel == B_NORMAL_WINDOW_FEEL ?
346 					winBorder->fSubWindowList.AddWinBorder(wb) :
347 					wb->fSubWindowList.AddWinBorder(winBorder);
348 			}
349 		}
350 	}
351 
352 	// add application's list of modal windows.
353 	if (feel == B_MODAL_APP_WINDOW_FEEL) {
354 		winBorder->App()->fAppSubWindowList.AddWinBorder(winBorder);
355 	}
356 
357 	// send WinBorder to be added to workspaces
358 	ActiveRootLayer()->AddWinBorder(winBorder);
359 
360 	// hey, unlock!
361 	Unlock();
362 
363 	ActiveRootLayer()->Unlock();
364 }
365 
366 
367 void
368 Desktop::RemoveWinBorder(WinBorder *winBorder)
369 {
370 	if (!winBorder)
371 		return;
372 
373 	// we are ServerApp thread, we need to lock RootLayer here.
374 	ActiveRootLayer()->Lock();
375 
376 	// we're playing with window list. lock first.
377 	Lock();
378 
379 	// remove from main WinBorder list.
380 	if (fWinBorderList.RemoveItem(winBorder)) {
381 		int32 feel = winBorder->Feel();
382 
383 		// floating app/subset and modal_subset windows require special atention because
384 		// they are/may_be added to the list of a lot normal windows.
385 		if (feel == B_FLOATING_SUBSET_WINDOW_FEEL
386 			|| feel == B_MODAL_SUBSET_WINDOW_FEEL
387 			|| feel == B_FLOATING_APP_WINDOW_FEEL)
388 		{
389 			WinBorder *wb = NULL;
390 			int32 count = fWinBorderList.CountItems();
391 
392 			for (int32 i = 0; i < count; i++) {
393 				wb = (WinBorder*)fWinBorderList.ItemAt(i);
394 
395 				if (wb->Feel() == B_NORMAL_WINDOW_FEEL
396 					&& wb->App()->ClientTeam() == winBorder->App()->ClientTeam()) {
397 					// R2: RootLayer comparison is needed. We'll see.
398 					wb->fSubWindowList.RemoveItem(winBorder);
399 				}
400 			}
401 		}
402 
403 		// remove from application's list
404 		if (feel == B_MODAL_APP_WINDOW_FEEL) {
405 			winBorder->App()->fAppSubWindowList.RemoveItem(winBorder);
406 		}
407 	} else {
408 		Unlock();
409 		ActiveRootLayer()->Unlock();
410 		debugger("RemoveWinBorder: WinBorder not found in Desktop list\n");
411 		return;
412 	}
413 
414 	// Tell to winBorder's RootLayer about this.
415 	ActiveRootLayer()->RemoveWinBorder(winBorder);
416 
417 	Unlock();
418 	ActiveRootLayer()->Unlock();
419 }
420 
421 
422 void
423 Desktop::AddWinBorderToSubset(WinBorder *winBorder, WinBorder *toWinBorder)
424 {
425 	// NOTE: we can safely lock the entire method body, because this method is called from
426 	//		 RootLayer's thread only.
427 
428 	// we're playing with window list. lock first.
429 	Lock();
430 
431 	if (!winBorder || !toWinBorder
432 		|| !fWinBorderList.HasItem(winBorder)
433 		|| !fWinBorderList.HasItem(toWinBorder)) {
434 		Unlock();
435 		debugger("AddWinBorderToSubset: NULL WinBorder or not found in Desktop list\n");
436 		return;
437 	}
438 
439 	if ((winBorder->Feel() == B_FLOATING_SUBSET_WINDOW_FEEL
440 			|| winBorder->Feel() == B_MODAL_SUBSET_WINDOW_FEEL)
441 		&& toWinBorder->Feel() == B_NORMAL_WINDOW_FEEL
442 		&& toWinBorder->App()->ClientTeam() == winBorder->App()->ClientTeam()
443 		&& !toWinBorder->fSubWindowList.HasItem(winBorder)) {
444 		// add to normal_window's list
445 		toWinBorder->fSubWindowList.AddWinBorder(winBorder);
446 	} else {
447 		Unlock();
448 		debugger("AddWinBorderToSubset: you must add a subset_window to a normal_window's subset with the same team_id\n");
449 		return;
450 	}
451 
452 	// send WinBorder to be added to workspaces, if not already in there.
453 	ActiveRootLayer()->AddSubsetWinBorder(winBorder, toWinBorder);
454 
455 	Unlock();
456 }
457 
458 
459 void
460 Desktop::RemoveWinBorderFromSubset(WinBorder *winBorder, WinBorder *fromWinBorder)
461 {
462 	// NOTE: we can safely lock the entire method body, because this method is called from
463 	//		 RootLayer's thread only.
464 
465 	// we're playing with window list. lock first.
466 	Lock();
467 
468 	if (!winBorder || !fromWinBorder
469 		|| !fWinBorderList.HasItem(winBorder)
470 		|| !fWinBorderList.HasItem(fromWinBorder)) {
471 		Unlock();
472 		debugger("RemoveWinBorderFromSubset: NULL WinBorder or not found in Desktop list\n");
473 		return;
474 	}
475 
476 	// remove WinBorder from workspace, if needed - some other windows may still have it in their subset
477 	ActiveRootLayer()->RemoveSubsetWinBorder(winBorder, fromWinBorder);
478 
479 	if (fromWinBorder->Feel() == B_NORMAL_WINDOW_FEEL) {
480 		//remove from this normal_window's subset.
481 		fromWinBorder->fSubWindowList.RemoveItem(winBorder);
482 	} else {
483 		Unlock();
484 		debugger("RemoveWinBorderFromSubset: you must remove a subset_window from a normal_window's subset\n");
485 		return;
486 	}
487 
488 	Unlock();
489 }
490 
491 
492 void
493 Desktop::SetWinBorderFeel(WinBorder *winBorder, uint32 feel)
494 {
495 	// NOTE: this method is called from RootLayer thread only
496 
497 	// we're playing with window list. lock first.
498 	Lock();
499 
500 	RemoveWinBorder(winBorder);
501 	winBorder->QuietlySetFeel(feel);
502 	AddWinBorder(winBorder);
503 
504 	Unlock();
505 }
506 
507 
508 WinBorder *
509 Desktop::FindWinBorderByClientToken(int32 token, team_id teamID)
510 {
511 	BAutolock locker(this);
512 
513 	WinBorder *wb;
514 	for (int32 i = 0; (wb = (WinBorder *)fWinBorderList.ItemAt(i)); i++) {
515 		if (wb->Window()->ClientToken() == token
516 			&& wb->Window()->ClientTeam() == teamID)
517 			return wb;
518 	}
519 
520 	return NULL;
521 }
522 
523 
524 void
525 Desktop::WriteWindowList(team_id team, BPrivate::LinkSender& sender)
526 {
527 	BAutolock locker(this);
528 
529 	// compute the number of windows
530 
531 	int32 count = 0;
532 	if (team >= B_OK) {
533 		for (int32 i = 0; i < fWinBorderList.CountItems(); i++) {
534 			WinBorder* border = (WinBorder*)fWinBorderList.ItemAt(i);
535 
536 			if (border->Window()->ClientTeam() == team)
537 				count++;
538 		}
539 	} else
540 		count = fWinBorderList.CountItems();
541 
542 	// write list
543 
544 	sender.StartMessage(SERVER_TRUE);
545 	sender.Attach<int32>(count);
546 
547 	for (int32 i = 0; i < fWinBorderList.CountItems(); i++) {
548 		WinBorder* border = (WinBorder*)fWinBorderList.ItemAt(i);
549 
550 		if (team >= B_OK && border->Window()->ClientTeam() != team)
551 			continue;
552 
553 		sender.Attach<int32>(border->Window()->ServerToken());
554 	}
555 
556 	sender.Flush();
557 }
558 
559 
560 void
561 Desktop::WriteWindowInfo(int32 serverToken, BPrivate::LinkSender& sender)
562 {
563 	BAutolock locker(this);
564 	BAutolock tokenLocker(BPrivate::gDefaultTokens);
565 
566 	ServerWindow* window;
567 	if (BPrivate::gDefaultTokens.GetToken(serverToken,
568 			B_SERVER_TOKEN, (void**)&window) != B_OK) {
569 		sender.StartMessage(B_ENTRY_NOT_FOUND);
570 		sender.Flush();
571 		return;
572 	}
573 
574 	window_info info;
575 	window->GetInfo(info);
576 
577 	int32 length = window->Title() ? strlen(window->Title()) : 0;
578 
579 	sender.StartMessage(B_OK);
580 	sender.Attach<int32>(sizeof(window_info) + length + 1);
581 	sender.Attach(&info, sizeof(window_info));
582 	if (length > 0)
583 		sender.Attach(window->Title(), length + 1);
584 	else
585 		sender.Attach<char>('\0');
586 	sender.Flush();
587 }
588 
589