xref: /haiku/src/servers/app/Desktop.cpp (revision f09ba8ea46ba2f4e482d7cd03e8eb77f37a60663)
1 /*
2  * Copyright 2001-2007, 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 "Desktop.h"
15 
16 #include "AppServer.h"
17 #include "DesktopSettingsPrivate.h"
18 #include "DrawingEngine.h"
19 #include "HWInterface.h"
20 #include "InputManager.h"
21 #include "ServerApp.h"
22 #include "ServerConfig.h"
23 #include "ServerCursor.h"
24 #include "ServerScreen.h"
25 #include "ServerWindow.h"
26 #include "WindowPrivate.h"
27 #include "WindowLayer.h"
28 #include "Workspace.h"
29 #include "WorkspacesLayer.h"
30 
31 #include <WindowInfo.h>
32 #include <ServerProtocol.h>
33 
34 #include <DirectWindow.h>
35 #include <Entry.h>
36 #include <Message.h>
37 #include <MessageFilter.h>
38 #include <Region.h>
39 #include <Roster.h>
40 
41 #include <stdio.h>
42 #include <string.h>
43 #include <syslog.h>
44 
45 #if TEST_MODE
46 #	include "EventStream.h"
47 #endif
48 
49 //#define DEBUG_DESKTOP
50 #ifdef DEBUG_DESKTOP
51 #	define STRACE(a) printf(a)
52 #else
53 #	define STRACE(a) ;
54 #endif
55 
56 #if !USE_MULTI_LOCKER
57 #	define AutoWriteLocker BAutolock
58 #endif
59 
60 class KeyboardFilter : public EventFilter {
61 	public:
62 		KeyboardFilter(Desktop* desktop);
63 
64 		virtual filter_result Filter(BMessage* message, EventTarget** _target,
65 			int32* _viewToken, BMessage* latestMouseMoved);
66 		virtual void RemoveTarget(EventTarget* target);
67 
68 	private:
69 		void _UpdateFocus(int32 key, EventTarget** _target);
70 
71 		Desktop*		fDesktop;
72 		EventTarget*	fLastFocus;
73 		bigtime_t		fTimestamp;
74 };
75 
76 class MouseFilter : public EventFilter {
77 	public:
78 		MouseFilter(Desktop* desktop);
79 
80 		virtual filter_result Filter(BMessage* message, EventTarget** _target,
81 			int32* _viewToken, BMessage* latestMouseMoved);
82 
83 	private:
84 		Desktop*	fDesktop;
85 };
86 
87 
88 //	#pragma mark -
89 
90 
91 KeyboardFilter::KeyboardFilter(Desktop* desktop)
92 	:
93 	fDesktop(desktop),
94 	fLastFocus(NULL),
95 	fTimestamp(0)
96 {
97 }
98 
99 
100 void
101 KeyboardFilter::_UpdateFocus(int32 key, EventTarget** _target)
102 {
103 	if (!fDesktop->LockSingleWindow())
104 		return;
105 
106 	EventTarget* focus = fDesktop->KeyboardEventTarget();
107 	bigtime_t now = system_time();
108 
109 	// TODO: this is a try to not steal focus from the current window
110 	//	in case you enter some text and a window pops up you haven't
111 	//	triggered yourself (like a pop-up window in your browser while
112 	//	you're typing a password in another window) - maybe this should
113 	//	be done differently, though (using something like B_LOCK_WINDOW_FOCUS)
114 	//	(at least B_WINDOW_ACTIVATED must be postponed)
115 
116 	if (fLastFocus == NULL || (focus != fLastFocus && now - fTimestamp > 100000)) {
117 		// if the time span between the key presses is very short
118 		// we keep our previous focus alive - this is save even
119 		// if the target doesn't exist anymore, as we don't reset
120 		// it, and the event focus passed in is always valid (or NULL)
121 		*_target = focus;
122 		fLastFocus = focus;
123 	}
124 
125 	fDesktop->UnlockSingleWindow();
126 
127 	// we always allow to switch focus after the enter key has pressed
128 	if (key == B_ENTER)
129 		fTimestamp = 0;
130 	else
131 		fTimestamp = now;
132 }
133 
134 
135 filter_result
136 KeyboardFilter::Filter(BMessage* message, EventTarget** _target,
137 	int32* /*_viewToken*/, BMessage* /*latestMouseMoved*/)
138 {
139 	int32 key = 0;
140 	int32 modifiers;
141 
142 	if (message->what == B_KEY_DOWN
143 		&& message->FindInt32("key", &key) == B_OK
144 		&& message->FindInt32("modifiers", &modifiers) == B_OK) {
145 		// TODO: for some reason, one of the above is failing when pressing
146 		//	a modifier key at least with the old BMessage implementation
147 		//	(a message dump shows all entries, though)
148 		//	Try again with BMessage4!
149 
150 		// Check for safe video mode (F12 + l-cmd + l-ctrl + l-shift)
151 		if (key == 0x0d
152 			&& (modifiers & (B_LEFT_COMMAND_KEY
153 					| B_LEFT_CONTROL_KEY | B_LEFT_SHIFT_KEY)) != 0) {
154 			// TODO: Set to Safe Mode in KeyboardEventHandler:B_KEY_DOWN.
155 			STRACE(("Safe Video Mode invoked - code unimplemented\n"));
156 			return B_SKIP_MESSAGE;
157 		}
158 
159 		if (key > 0x01 && key < 0x0e) {
160 			// workspace change, F1-F12
161 
162 #if !TEST_MODE
163 			if (modifiers & B_COMMAND_KEY)
164 #else
165 			if (modifiers & B_CONTROL_KEY)
166 #endif
167 			{
168 				STRACE(("Set Workspace %ld\n", key - 1));
169 
170 				fDesktop->SetWorkspace(key - 2);
171 				return B_SKIP_MESSAGE;
172 			}
173 		}
174 
175 		// TODO: this should be moved client side!
176 		// (that's how it is done in BeOS, clients could need this key for
177 		// different purposes - also, it's preferrable to let the client
178 		// write the dump within his own environment)
179 		if (key == 0xe) {
180 			// screen dump, PrintScreen
181 			char filename[128];
182 			BEntry entry;
183 
184 			int32 index = 1;
185 			do {
186 				sprintf(filename, "/boot/home/screen%ld.png", index++);
187 				entry.SetTo(filename);
188 			} while(entry.Exists());
189 
190 			fDesktop->GetDrawingEngine()->DumpToFile(filename);
191 			return B_SKIP_MESSAGE;
192 		}
193 	}
194 
195 	if (message->what == B_KEY_DOWN
196 		|| message->what == B_MODIFIERS_CHANGED
197 		|| message->what == B_UNMAPPED_KEY_DOWN
198 		|| message->what == B_INPUT_METHOD_EVENT)
199 		_UpdateFocus(key, _target);
200 
201 	return B_DISPATCH_MESSAGE;
202 }
203 
204 
205 void
206 KeyboardFilter::RemoveTarget(EventTarget* target)
207 {
208 	if (target == fLastFocus)
209 		fLastFocus = NULL;
210 }
211 
212 
213 //	#pragma mark -
214 
215 
216 MouseFilter::MouseFilter(Desktop* desktop)
217 	:
218 	fDesktop(desktop)
219 {
220 }
221 
222 
223 filter_result
224 MouseFilter::Filter(BMessage* message, EventTarget** _target, int32* _viewToken,
225 	BMessage* latestMouseMoved)
226 {
227 	BPoint where;
228 	if (message->FindPoint("where", &where) != B_OK)
229 		return B_DISPATCH_MESSAGE;
230 
231 	if (!fDesktop->LockAllWindows())
232 		return B_DISPATCH_MESSAGE;
233 
234 	int32 viewToken = B_NULL_TOKEN;
235 
236 	WindowLayer* window = fDesktop->MouseEventWindow();
237 	if (window == NULL)
238 		window = fDesktop->WindowAt(where);
239 
240 	if (window != NULL) {
241 		// dispatch event in the window layers
242 		switch (message->what) {
243 			case B_MOUSE_DOWN:
244 				window->MouseDown(message, where, &viewToken);
245 				break;
246 
247 			case B_MOUSE_UP:
248 				window->MouseUp(message, where, &viewToken);
249 				fDesktop->SetMouseEventWindow(NULL);
250 				break;
251 
252 			case B_MOUSE_MOVED:
253 				window->MouseMoved(message, where, &viewToken,
254 					latestMouseMoved == NULL || latestMouseMoved == message);
255 				break;
256 		}
257 
258 		if (viewToken != B_NULL_TOKEN) {
259 			fDesktop->SetViewUnderMouse(window, viewToken);
260 
261 			*_viewToken = viewToken;
262 			*_target = &window->EventTarget();
263 		}
264 	}
265 
266 	if (window == NULL || viewToken == B_NULL_TOKEN) {
267 		// mouse is not over a window or over a decorator
268 		fDesktop->SetViewUnderMouse(window, B_NULL_TOKEN);
269 		fDesktop->SetCursor(NULL);
270 
271 		*_target = NULL;
272 	}
273 
274 	fDesktop->UnlockAllWindows();
275 
276 	return B_DISPATCH_MESSAGE;
277 }
278 
279 
280 //	#pragma mark -
281 
282 
283 static inline uint32
284 workspace_to_workspaces(int32 index)
285 {
286 	return 1UL << index;
287 }
288 
289 
290 static inline bool
291 workspace_in_workspaces(int32 index, uint32 workspaces)
292 {
293 	return (workspaces & (1UL << index)) != 0;
294 }
295 
296 
297 //	#pragma mark -
298 
299 
300 Desktop::Desktop(uid_t userID)
301 	: MessageLooper("desktop"),
302 
303 	fUserID(userID),
304 	fSettings(NULL),
305 	fSharedReadOnlyArea(-1),
306 	fApplicationsLock("application list"),
307 	fShutdownSemaphore(-1),
308 	fCurrentWorkspace(0),
309 	fAllWindows(kAllWindowList),
310 	fSubsetWindows(kSubsetList),
311 	fWorkspacesLayer(NULL),
312 	fActiveScreen(NULL),
313 	fWindowLock("window lock"),
314 	fMouseEventWindow(NULL),
315 	fWindowUnderMouse(NULL),
316 	fViewUnderMouse(B_NULL_TOKEN),
317 	fFocus(NULL),
318 	fFront(NULL),
319 	fBack(NULL)
320 {
321 	char name[B_OS_NAME_LENGTH];
322 	Desktop::_GetLooperName(name, sizeof(name));
323 
324 	fMessagePort = create_port(DEFAULT_MONITOR_PORT_SIZE, name);
325 	if (fMessagePort < B_OK)
326 		return;
327 
328 	fLink.SetReceiverPort(fMessagePort);
329 }
330 
331 
332 Desktop::~Desktop()
333 {
334 	delete fSettings;
335 
336 	delete_area(fSharedReadOnlyArea);
337 	delete_port(fMessagePort);
338 	gFontManager->DetachUser(fUserID);
339 }
340 
341 
342 status_t
343 Desktop::Init()
344 {
345 	if (fMessagePort < B_OK)
346 		return fMessagePort;
347 
348 	char name[B_OS_NAME_LENGTH];
349 	snprintf(name, sizeof(name), "d:%d:shared read only", /*id*/0);
350 	fSharedReadOnlyArea = create_area(name, (void **)&fServerReadOnlyMemory,
351 		B_ANY_ADDRESS, B_PAGE_SIZE, B_NO_LOCK, B_READ_AREA | B_WRITE_AREA);
352 	if (fSharedReadOnlyArea < B_OK)
353 		return fSharedReadOnlyArea;
354 
355 	fSettings = new DesktopSettingsPrivate(fServerReadOnlyMemory);
356 
357 	for (int32 i = 0; i < kMaxWorkspaces; i++) {
358 		_Windows(i).SetIndex(i);
359 		fWorkspaces[i].RestoreConfiguration(*fSettings->WorkspacesMessage(i));
360 	}
361 
362 	fVirtualScreen.RestoreConfiguration(*this, fSettings->WorkspacesMessage(0));
363 
364 	// TODO: temporary workaround, fActiveScreen will be removed
365 	fActiveScreen = fVirtualScreen.ScreenAt(0);
366 
367 	fVirtualScreen.HWInterface()->MoveCursorTo(fVirtualScreen.Frame().Width() / 2,
368 		fVirtualScreen.Frame().Height() / 2);
369 
370 #if TEST_MODE
371 	gInputManager->AddStream(new InputServerStream);
372 #endif
373 
374 	fEventDispatcher.SetDesktop(this);
375 	fEventDispatcher.SetTo(gInputManager->GetStream());
376 	if (fEventDispatcher.InitCheck() != B_OK)
377 		_LaunchInputServer();
378 
379 	fEventDispatcher.SetHWInterface(fVirtualScreen.HWInterface());
380 
381 	fEventDispatcher.SetMouseFilter(new MouseFilter(this));
382 	fEventDispatcher.SetKeyboardFilter(new KeyboardFilter(this));
383 
384 	// draw the background
385 
386 	fScreenRegion = fVirtualScreen.Frame();
387 
388 	BRegion stillAvailableOnScreen;
389 	_RebuildClippingForAllWindows(stillAvailableOnScreen);
390 	_SetBackground(stillAvailableOnScreen);
391 
392 	gFontManager->AttachUser(fUserID);
393 
394 	SetCursor(NULL);
395 		// this will set the default cursor
396 
397 	fVirtualScreen.HWInterface()->SetCursorVisible(true);
398 
399 	return B_OK;
400 }
401 
402 
403 void
404 Desktop::_LaunchInputServer()
405 {
406 	BRoster roster;
407 	status_t status = roster.Launch("application/x-vnd.Be-input_server");
408 	if (status != B_OK && status != B_ALREADY_RUNNING)
409 		syslog(LOG_ERR, "Failed to launch the input server: %s!\n", strerror(status));
410 }
411 
412 
413 void
414 Desktop::_GetLooperName(char* name, size_t length)
415 {
416 	snprintf(name, length, "d:%d:%s", /*id*/0, /*name*/"baron");
417 }
418 
419 
420 void
421 Desktop::_PrepareQuit()
422 {
423 	// let's kill all remaining applications
424 
425 	fApplicationsLock.Lock();
426 
427 	int32 count = fApplications.CountItems();
428 	for (int32 i = 0; i < count; i++) {
429 		ServerApp *app = fApplications.ItemAt(i);
430 		team_id clientTeam = app->ClientTeam();
431 
432 		app->Quit();
433 		kill_team(clientTeam);
434 	}
435 
436 	// wait for the last app to die
437 	if (count > 0)
438 		acquire_sem_etc(fShutdownSemaphore, fShutdownCount, B_RELATIVE_TIMEOUT, 250000);
439 
440 	fApplicationsLock.Unlock();
441 }
442 
443 
444 void
445 Desktop::_DispatchMessage(int32 code, BPrivate::LinkReceiver &link)
446 {
447 	switch (code) {
448 		case AS_CREATE_APP:
449 		{
450 			// Create the ServerApp to node monitor a new BApplication
451 
452 			// Attached data:
453 			// 1) port_id - receiver port of a regular app
454 			// 2) port_id - client looper port - for sending messages to the client
455 			// 2) team_id - app's team ID
456 			// 3) int32 - handler token of the regular app
457 			// 4) char * - signature of the regular app
458 
459 			// Find the necessary data
460 			team_id	clientTeamID = -1;
461 			port_id	clientLooperPort = -1;
462 			port_id clientReplyPort = -1;
463 			int32 htoken = B_NULL_TOKEN;
464 			char *appSignature = NULL;
465 
466 			link.Read<port_id>(&clientReplyPort);
467 			link.Read<port_id>(&clientLooperPort);
468 			link.Read<team_id>(&clientTeamID);
469 			link.Read<int32>(&htoken);
470 			if (link.ReadString(&appSignature) != B_OK)
471 				break;
472 
473 			ServerApp *app = new ServerApp(this, clientReplyPort,
474 				clientLooperPort, clientTeamID, htoken, appSignature);
475 			if (app->InitCheck() == B_OK
476 				&& app->Run()) {
477 				// add the new ServerApp to the known list of ServerApps
478 				fApplicationsLock.Lock();
479 				fApplications.AddItem(app);
480 				fApplicationsLock.Unlock();
481 			} else {
482 				delete app;
483 
484 				// if everything went well, ServerApp::Run() will notify
485 				// the client - but since it didn't, we do it here
486 				BPrivate::LinkSender reply(clientReplyPort);
487 				reply.StartMessage(B_ERROR);
488 				reply.Flush();
489 			}
490 
491 			// This is necessary because BPortLink::ReadString allocates memory
492 			free(appSignature);
493 			break;
494 		}
495 
496 		case AS_DELETE_APP:
497 		{
498 			// Delete a ServerApp. Received only from the respective ServerApp when a
499 			// BApplication asks it to quit.
500 
501 			// Attached Data:
502 			// 1) thread_id - thread ID of the ServerApp to be deleted
503 
504 			thread_id thread = -1;
505 			if (link.Read<thread_id>(&thread) < B_OK)
506 				break;
507 
508 			fApplicationsLock.Lock();
509 
510 			// Run through the list of apps and nuke the proper one
511 
512 			int32 count = fApplications.CountItems();
513 			ServerApp *removeApp = NULL;
514 
515 			for (int32 i = 0; i < count; i++) {
516 				ServerApp *app = fApplications.ItemAt(i);
517 
518 				if (app->Thread() == thread) {
519 					fApplications.RemoveItemAt(i);
520 					removeApp = app;
521 					break;
522 				}
523 			}
524 
525 			fApplicationsLock.Unlock();
526 
527 			if (removeApp != NULL)
528 				removeApp->Quit(fShutdownSemaphore);
529 
530 			if (fQuitting && count <= 1) {
531 				// wait for the last app to die
532 				acquire_sem_etc(fShutdownSemaphore, fShutdownCount, B_RELATIVE_TIMEOUT, 500000);
533 				PostMessage(kMsgQuitLooper);
534 			}
535 			break;
536 		}
537 
538 		case AS_ACTIVATE_APP:
539 		{
540 			// Someone is requesting to activation of a certain app.
541 
542 			// Attached data:
543 			// 1) port_id reply port
544 			// 2) team_id team
545 
546 			status_t status;
547 
548 			// get the parameters
549 			port_id replyPort;
550 			team_id team;
551 			if (link.Read(&replyPort) == B_OK
552 				&& link.Read(&team) == B_OK)
553 				status = _ActivateApp(team);
554 			else
555 				status = B_ERROR;
556 
557 			// send the reply
558 			BPrivate::PortLink replyLink(replyPort);
559 			replyLink.StartMessage(status);
560 			replyLink.Flush();
561 			break;
562 		}
563 
564 		case AS_APP_CRASHED:
565 		{
566 			BAutolock locker(fApplicationsLock);
567 
568 			team_id team;
569 			if (link.Read(&team) != B_OK)
570 				break;
571 
572 			for (int32 i = 0; i < fApplications.CountItems(); i++) {
573 				ServerApp* app = fApplications.ItemAt(i);
574 
575 				if (app->ClientTeam() == team)
576 					app->PostMessage(AS_APP_CRASHED);
577 			}
578 			break;
579 		}
580 
581 		case AS_EVENT_STREAM_CLOSED:
582 			_LaunchInputServer();
583 			break;
584 
585 		case B_QUIT_REQUESTED:
586 			// We've been asked to quit, so (for now) broadcast to all
587 			// test apps to quit. This situation will occur only when the server
588 			// is compiled as a regular Be application.
589 
590 			fApplicationsLock.Lock();
591 			fShutdownSemaphore = create_sem(0, "desktop shutdown");
592 			fShutdownCount = fApplications.CountItems();
593 			fApplicationsLock.Unlock();
594 
595 			fQuitting = true;
596 			BroadcastToAllApps(AS_QUIT_APP);
597 
598 			// We now need to process the remaining AS_DELETE_APP messages and
599 			// wait for the kMsgShutdownServer message.
600 			// If an application does not quit as asked, the picasso thread
601 			// will send us this message in 2-3 seconds.
602 
603 			// if there are no apps to quit, shutdown directly
604 			if (fShutdownCount == 0)
605 				PostMessage(kMsgQuitLooper);
606 			break;
607 
608 		default:
609 			printf("Desktop %d:%s received unexpected code %ld\n", 0, "baron", code);
610 
611 			if (link.NeedsReply()) {
612 				// the client is now blocking and waiting for a reply!
613 				fLink.StartMessage(B_ERROR);
614 				fLink.Flush();
615 			}
616 			break;
617 	}
618 }
619 
620 
621 /*!
622 	\brief activate one of the app's windows.
623 */
624 status_t
625 Desktop::_ActivateApp(team_id team)
626 {
627 	status_t status = B_BAD_TEAM_ID;
628 
629 	// search for an unhidden window to give focus to
630 
631 	for (WindowLayer* window = fAllWindows.FirstWindow(); window != NULL;
632 			window = window->NextWindow(kAllWindowList)) {
633 		// if window is a normal window of the team, and not hidden,
634 		// we've found our target
635 		if (!window->IsHidden() && window->IsNormal()
636 			&& window->ServerWindow()->ClientTeam() == team) {
637 			ActivateWindow(window);
638 			return B_OK;
639 		}
640 	}
641 
642 	return status;
643 }
644 
645 
646 /*!
647 	\brief Send a quick (no attachments) message to all applications
648 
649 	Quite useful for notification for things like server shutdown, system
650 	color changes, etc.
651 */
652 void
653 Desktop::BroadcastToAllApps(int32 code)
654 {
655 	BAutolock locker(fApplicationsLock);
656 
657 	for (int32 i = fApplications.CountItems(); i-- > 0;) {
658 		fApplications.ItemAt(i)->PostMessage(code);
659 	}
660 }
661 
662 
663 ServerCursor*
664 Desktop::Cursor() const
665 {
666 	return HWInterface()->Cursor();
667 }
668 
669 
670 void
671 Desktop::SetCursor(ServerCursor* newCursor)
672 {
673 	if (newCursor == NULL)
674 		newCursor = fCursorManager.GetCursor(B_CURSOR_DEFAULT);
675 
676 	ServerCursor* oldCursor = Cursor();
677 	if (newCursor == oldCursor)
678 		return;
679 
680 	HWInterface()->SetCursor(newCursor);
681 }
682 
683 
684 /*!
685 	\brief Redraws the background (ie. the desktop window, if any).
686 */
687 void
688 Desktop::RedrawBackground()
689 {
690 	LockAllWindows();
691 
692 	BRegion redraw;
693 
694 	WindowLayer* window = _CurrentWindows().FirstWindow();
695 	if (window->Feel() == kDesktopWindowFeel) {
696 		redraw = window->VisibleContentRegion();
697 
698 		// look for desktop background view, and update its background color
699 		// TODO: is there a better way to do this?
700 		ViewLayer* view = window->TopLayer();
701 		if (view != NULL)
702 			view = view->FirstChild();
703 
704 		while (view) {
705 			if (view->IsDesktopBackground()) {
706 				view->SetViewColor(fWorkspaces[fCurrentWorkspace].Color());
707 				break;
708 			}
709 			view = view->NextSibling();
710 		}
711 
712 		window->ProcessDirtyRegion(redraw);
713 	} else {
714 		redraw = BackgroundRegion();
715 		fBackgroundRegion.MakeEmpty();
716 		_SetBackground(redraw);
717 	}
718 
719 	_WindowChanged(NULL);
720 		// update workspaces view as well
721 
722 	UnlockAllWindows();
723 }
724 
725 
726 /*!
727 	\brief Store the workspace configuration
728 */
729 void
730 Desktop::StoreWorkspaceConfiguration(int32 index)
731 {
732 	const BMessage *oldSettings = fSettings->WorkspacesMessage(index);
733 	// store settings
734 	BMessage settings;
735 	if (oldSettings)
736 		settings = *oldSettings;
737 	fWorkspaces[index].StoreConfiguration(settings);
738 	fSettings->SetWorkspacesMessage(index, settings);
739 	fSettings->Save(kWorkspacesSettings);
740 }
741 
742 
743 status_t
744 Desktop::SetWorkspacesCount(int32 newCount)
745 {
746 	if (newCount < 1 || newCount > kMaxWorkspaces)
747 		return B_BAD_VALUE;
748 
749 	if (!LockAllWindows())
750 		return B_ERROR;
751 
752 	fSettings->SetWorkspacesCount(newCount);
753 
754 	// either update the workspaces window, or switch to
755 	// the last available workspace - which will update
756 	// the workspaces window automatically
757 	bool workspaceChanged = CurrentWorkspace() >= newCount;
758 	if (workspaceChanged)
759 		_SetWorkspace(newCount - 1);
760 	else
761 		_WindowChanged(NULL);
762 
763 	UnlockAllWindows();
764 
765 	if (workspaceChanged)
766 		_SendFakeMouseMoved();
767 
768 	return B_OK;
769 }
770 
771 
772 /*!
773 	Changes the current workspace to the one specified by \a index.
774 	You must not hold any window lock when calling this method.
775 */
776 void
777 Desktop::SetWorkspace(int32 index)
778 {
779 	LockAllWindows();
780 	DesktopSettings settings(this);
781 
782 	if (index < 0 || index >= settings.WorkspacesCount() || index == fCurrentWorkspace) {
783 		UnlockAllWindows();
784 		return;
785 	}
786 
787 	_SetWorkspace(index);
788 	UnlockAllWindows();
789 
790 	_SendFakeMouseMoved();
791 }
792 
793 
794 /*!
795 	Changes the current workspace to the one specified by \a index.
796 	You must hold the all window lock when calling this method.
797 */
798 void
799 Desktop::_SetWorkspace(int32 index)
800 {
801 	int32 previousIndex = fCurrentWorkspace;
802 	RGBColor previousColor = fWorkspaces[fCurrentWorkspace].Color();
803 	bool movedMouseEventWindow = false;
804 
805 	if (fMouseEventWindow != NULL) {
806 		if (fMouseEventWindow->IsNormal()) {
807 			if (!fMouseEventWindow->InWorkspace(index)) {
808 				// The window currently being dragged will follow us to this
809 				// workspace if it's not already on it.
810 				// But only normal windows are following
811 				uint32 oldWorkspaces = fMouseEventWindow->Workspaces();
812 
813 				_Windows(index).AddWindow(fMouseEventWindow);
814 				_Windows(previousIndex).RemoveWindow(fMouseEventWindow);
815 
816 				_UpdateSubsetWorkspaces(fMouseEventWindow, previousIndex, index);
817 				movedMouseEventWindow = true;
818 
819 				// send B_WORKSPACES_CHANGED message
820 				fMouseEventWindow->WorkspacesChanged(oldWorkspaces,
821 					fMouseEventWindow->Workspaces());
822 			} else {
823 				// make sure it's frontmost
824 				_Windows(index).RemoveWindow(fMouseEventWindow);
825 				_Windows(index).AddWindow(fMouseEventWindow,
826 					fMouseEventWindow->Frontmost(_Windows(index).FirstWindow(), index));
827 			}
828 		}
829 
830 		fMouseEventWindow->Anchor(index).position = fMouseEventWindow->Frame().LeftTop();
831 	}
832 
833 	// build region of windows that are no longer visible in the new workspace
834 
835 	BRegion dirty;
836 
837 	for (WindowLayer* window = _CurrentWindows().FirstWindow();
838 			window != NULL; window = window->NextWindow(previousIndex)) {
839 		// store current position in Workspace anchor
840 		window->Anchor(previousIndex).position = window->Frame().LeftTop();
841 
842 		window->WorkspaceActivated(previousIndex, false);
843 
844 		if (window->InWorkspace(index))
845 			continue;
846 
847 		if (!window->IsHidden()) {
848 			// this window will no longer be visible
849 			dirty.Include(&window->VisibleRegion());
850 		}
851 
852 		window->SetCurrentWorkspace(-1);
853 	}
854 
855 	fCurrentWorkspace = index;
856 
857 	// show windows, and include them in the changed region - but only
858 	// those that were not visible before (or whose position changed)
859 
860 	for (WindowLayer* window = _Windows(index).FirstWindow();
861 			window != NULL; window = window->NextWindow(index)) {
862 		BPoint position = window->Anchor(index).position;
863 
864 		window->SetCurrentWorkspace(index);
865 
866 		if (window->IsHidden())
867 			continue;
868 
869 		if (position == kInvalidWindowPosition) {
870 			// if you enter a workspace for the first time, the position
871 			// of the window in the previous workspace is adopted
872 			position = window->Frame().LeftTop();
873 				// TODO: make sure the window is still on-screen if it
874 				//	was before!
875 		}
876 
877 		if (!window->InWorkspace(previousIndex)) {
878 			// This window was not visible before, make sure its frame
879 			// is up-to-date
880 			if (window->Frame().LeftTop() != position) {
881 				BPoint offset = position - window->Frame().LeftTop();
882 				window->MoveBy(offset.x, offset.y);
883 			}
884 			continue;
885 		}
886 
887 		if (window->Frame().LeftTop() != position) {
888 			// the window was visible before, but its on-screen location changed
889 			BPoint offset = position - window->Frame().LeftTop();
890 			MoveWindowBy(window, offset.x, offset.y);
891 				// TODO: be a bit smarter than this...
892 		}
893 	}
894 
895 	_UpdateFronts(false);
896 	_UpdateFloating(previousIndex, index,
897 		movedMouseEventWindow ? fMouseEventWindow : NULL);
898 
899 	BRegion stillAvailableOnScreen;
900 	_RebuildClippingForAllWindows(stillAvailableOnScreen);
901 	_SetBackground(stillAvailableOnScreen);
902 
903 	for (WindowLayer* window = _Windows(index).FirstWindow(); window != NULL;
904 			window = window->NextWindow(index)) {
905 		// send B_WORKSPACE_ACTIVATED message
906 		window->WorkspaceActivated(index, true);
907 
908 		if (window->InWorkspace(previousIndex)
909 			|| (window == fMouseEventWindow && fMouseEventWindow->IsNormal())
910 			|| (!window->IsNormal() && window->HasInSubset(fMouseEventWindow))) {
911 			// this window was visible before, and is already handled in the above loop
912 			continue;
913 		}
914 
915 		dirty.Include(&window->VisibleRegion());
916 	}
917 
918 	// Set new focus to the front window, but keep focus to a floating
919 	// window if still visible
920 	if (!_Windows(index).HasWindow(FocusWindow()) || !FocusWindow()->IsFloating())
921 		SetFocusWindow(FrontWindow());
922 
923 	_WindowChanged(NULL);
924 	MarkDirty(dirty);
925 
926 	if (previousColor != fWorkspaces[fCurrentWorkspace].Color())
927 		RedrawBackground();
928 }
929 
930 
931 void
932 Desktop::ScreenChanged(Screen* screen, bool makeDefault)
933 {
934 	// TODO: confirm that everywhere this is used,
935 	// the Window WriteLock is held
936 
937 	// the entire screen is dirty, because we're actually
938 	// operating on an all new buffer in memory
939 	BRegion dirty(screen->Frame());
940 	// update our cached screen region
941 	fScreenRegion.Set(screen->Frame());
942 
943 	BRegion background;
944 	_RebuildClippingForAllWindows(background);
945 
946 	fBackgroundRegion.MakeEmpty();
947 		// makes sure that the complete background is redrawn
948 	_SetBackground(background);
949 
950 	// figure out dirty region
951 	dirty.Exclude(&background);
952 	_TriggerWindowRedrawing(dirty);
953 
954 	// send B_SCREEN_CHANGED to windows on that screen
955 	BMessage update(B_SCREEN_CHANGED);
956 	update.AddInt64("when", real_time_clock_usecs());
957 	update.AddRect("frame", screen->Frame());
958 	update.AddInt32("mode", screen->ColorSpace());
959 
960 	// TODO: currently ignores the screen argument!
961 	for (WindowLayer* window = fAllWindows.FirstWindow(); window != NULL;
962 			window = window->NextWindow(kAllWindowList)) {
963 		window->ServerWindow()->SendMessageToClient(&update);
964 	}
965 
966 	fVirtualScreen.UpdateFrame();
967 
968 	if (makeDefault) {
969 		// store settings
970 		BMessage settings;
971 		fVirtualScreen.StoreConfiguration(settings);
972 		fWorkspaces[fCurrentWorkspace].StoreConfiguration(settings);
973 
974 		fSettings->SetWorkspacesMessage(fCurrentWorkspace, settings);
975 		fSettings->Save(kWorkspacesSettings);
976 	}
977 }
978 
979 
980 //	#pragma mark - Methods for WindowLayer manipulation
981 
982 
983 WindowList&
984 Desktop::_CurrentWindows()
985 {
986 	return fWorkspaces[fCurrentWorkspace].Windows();
987 }
988 
989 
990 WindowList&
991 Desktop::_Windows(int32 index)
992 {
993 	return fWorkspaces[index].Windows();
994 }
995 
996 
997 void
998 Desktop::_UpdateFloating(int32 previousWorkspace, int32 nextWorkspace,
999 	WindowLayer* mouseEventWindow)
1000 {
1001 	if (previousWorkspace == -1)
1002 		previousWorkspace = fCurrentWorkspace;
1003 	if (nextWorkspace == -1)
1004 		nextWorkspace = previousWorkspace;
1005 
1006 	for (WindowLayer* floating = fSubsetWindows.FirstWindow(); floating != NULL;
1007 			floating = floating->NextWindow(kSubsetList)) {
1008 		// we only care about app/subset floating windows
1009 		if (floating->Feel() != B_FLOATING_SUBSET_WINDOW_FEEL
1010 			&& floating->Feel() != B_FLOATING_APP_WINDOW_FEEL)
1011 			continue;
1012 
1013 		if (fFront != NULL && fFront->IsNormal() && floating->HasInSubset(fFront)) {
1014 			// is now visible
1015 			if (_Windows(previousWorkspace).HasWindow(floating)
1016 				&& previousWorkspace != nextWorkspace) {
1017 				// but no longer on the previous workspace
1018 				_Windows(previousWorkspace).RemoveWindow(floating);
1019 				floating->SetCurrentWorkspace(-1);
1020 			}
1021 			if (!_Windows(nextWorkspace).HasWindow(floating)) {
1022 				// but wasn't before
1023 				_Windows(nextWorkspace).AddWindow(floating,
1024 					floating->Frontmost(_Windows(nextWorkspace).FirstWindow(), nextWorkspace));
1025 				floating->SetCurrentWorkspace(nextWorkspace);
1026 				if (mouseEventWindow != fFront)
1027 					_ShowWindow(floating);
1028 
1029 				// TODO:
1030 				// put the floating last in the floating window list to preserve
1031 				// the on screen window order
1032 			}
1033 		} else if (_Windows(previousWorkspace).HasWindow(floating)) {
1034 			// was visible, but is no longer
1035 
1036 			_Windows(previousWorkspace).RemoveWindow(floating);
1037 			floating->SetCurrentWorkspace(-1);
1038 			_HideWindow(floating);
1039 
1040 			if (FocusWindow() == floating)
1041 				SetFocusWindow(_CurrentWindows().LastWindow());
1042 		}
1043 	}
1044 }
1045 
1046 
1047 /*!
1048 	Search the visible windows for a valid back window
1049 	(only desktop windows can't be back windows)
1050 */
1051 void
1052 Desktop::_UpdateBack()
1053 {
1054 	fBack = NULL;
1055 
1056 	for (WindowLayer* window = _CurrentWindows().FirstWindow();
1057 			window != NULL; window = window->NextWindow(fCurrentWorkspace)) {
1058 		if (window->IsHidden() || window->Feel() == kDesktopWindowFeel)
1059 			continue;
1060 
1061 		fBack = window;
1062 		break;
1063 	}
1064 }
1065 
1066 
1067 /*!
1068 	Search the visible windows for a valid front window
1069 	(only normal and modal windows can be front windows)
1070 
1071 	The only place where you don't want to update floating windows is
1072 	during a workspace change - because then you'll call _UpdateFloating()
1073 	yourself.
1074 */
1075 void
1076 Desktop::_UpdateFront(bool updateFloating)
1077 {
1078 	fFront = NULL;
1079 
1080 	for (WindowLayer* window = _CurrentWindows().LastWindow();
1081 			window != NULL; window = window->PreviousWindow(fCurrentWorkspace)) {
1082 		if (window->IsHidden() || window->IsFloating() || !window->SupportsFront())
1083 			continue;
1084 
1085 		fFront = window;
1086 		break;
1087 	}
1088 
1089 	if (updateFloating)
1090 		_UpdateFloating();
1091 }
1092 
1093 
1094 void
1095 Desktop::_UpdateFronts(bool updateFloating)
1096 {
1097 	_UpdateBack();
1098 	_UpdateFront(updateFloating);
1099 }
1100 
1101 
1102 /*!
1103 	Returns the current keyboard event target candidate - which is either the
1104 	top-most window (in case it's a menu), or the one having focus.
1105 	The window lock must be held when calling this function.
1106 */
1107 EventTarget*
1108 Desktop::KeyboardEventTarget()
1109 {
1110 	// TODO: Not yet useful, and prevents a crashing application
1111 	// which has opened menu windows to be debugged (ticket #1152)
1112 #if 0
1113 	WindowLayer* window = _CurrentWindows().LastWindow();
1114 	if (window != NULL && window->Feel() == kMenuWindowFeel)
1115 		return &window->EventTarget();
1116 #endif
1117 	if (FocusWindow() != NULL)
1118 		return &FocusWindow()->EventTarget();
1119 
1120 	return NULL;
1121 }
1122 
1123 
1124 bool
1125 Desktop::_WindowHasModal(WindowLayer* window)
1126 {
1127 	if (window == NULL)
1128 		return false;
1129 
1130 	for (WindowLayer* modal = fSubsetWindows.FirstWindow(); modal != NULL;
1131 			modal = modal->NextWindow(kSubsetList)) {
1132 		// only visible modal windows count
1133 		if (!modal->IsModal() || modal->IsHidden())
1134 			continue;
1135 
1136 		if (modal->HasInSubset(window))
1137 			return true;
1138 	}
1139 
1140 	return false;
1141 }
1142 
1143 
1144 /*!
1145 	You must at least hold a single window lock when calling this method.
1146 */
1147 void
1148 Desktop::_WindowChanged(WindowLayer* window)
1149 {
1150 	if (fWorkspacesLayer == NULL)
1151 		return;
1152 
1153 	fWorkspacesLayer->WindowChanged(window);
1154 }
1155 
1156 
1157 /*!
1158 	\brief Sends a fake B_MOUSE_MOVED event to the window under the mouse,
1159 		and also updates the current view under the mouse.
1160 
1161 	This has only to be done in case the view changed without user interaction,
1162 	ie. because of a workspace change or a closing window.
1163 
1164 	Windows must not be locked when calling this method.
1165 */
1166 void
1167 Desktop::_SendFakeMouseMoved(WindowLayer* window)
1168 {
1169 	BPoint where;
1170 	int32 buttons;
1171 	EventDispatcher().GetMouse(where, buttons);
1172 
1173 	int32 viewToken = B_NULL_TOKEN;
1174 	EventTarget* target = NULL;
1175 
1176 	LockAllWindows();
1177 
1178 	if (window == NULL)
1179 		window = MouseEventWindow();
1180 	if (window == NULL)
1181 		window = WindowAt(where);
1182 
1183 	if (window != NULL) {
1184 		BMessage message;
1185 		window->MouseMoved(&message, where, &viewToken, true);
1186 
1187 		if (viewToken != B_NULL_TOKEN)
1188 			target = &window->EventTarget();
1189 	}
1190 
1191 	if (viewToken != B_NULL_TOKEN)
1192 		SetViewUnderMouse(window, viewToken);
1193 	else {
1194 		SetViewUnderMouse(NULL, B_NULL_TOKEN);
1195 		SetCursor(NULL);
1196 	}
1197 
1198 	UnlockAllWindows();
1199 
1200 	if (target != NULL)
1201 		EventDispatcher().SendFakeMouseMoved(*target, viewToken);
1202 }
1203 
1204 
1205 void
1206 Desktop::SetFocusWindow(WindowLayer* focus)
1207 {
1208 	if (!LockAllWindows())
1209 		return;
1210 
1211 	bool hasModal = _WindowHasModal(focus);
1212 
1213 	// TODO: test for FFM and B_LOCK_WINDOW_FOCUS
1214 
1215 	if (focus == fFocus && focus != NULL && !focus->IsHidden()
1216 		&& (focus->Flags() & B_AVOID_FOCUS) == 0 && !hasModal) {
1217 		// the window that is supposed to get focus already has focus
1218 		UnlockAllWindows();
1219 		return;
1220 	}
1221 
1222 	if (focus == NULL || hasModal) {
1223 		focus = FrontWindow();
1224 		if (focus == NULL) {
1225 			// there might be no front window in case of only a single
1226 			// window with B_FLOATING_ALL_WINDOW_FEEL
1227 			focus = _CurrentWindows().LastWindow();
1228 		}
1229 	}
1230 
1231 	// make sure no window is chosen that doesn't want focus or cannot have it
1232 	while (focus != NULL
1233 		&& ((focus->Flags() & B_AVOID_FOCUS) != 0
1234 			|| _WindowHasModal(focus)
1235 			|| focus->IsHidden())) {
1236 		focus = focus->PreviousWindow(fCurrentWorkspace);
1237 	}
1238 
1239 	if (fFocus == focus) {
1240 		// turns out the window that is supposed to get focus now already has it
1241 		UnlockAllWindows();
1242 		return;
1243 	}
1244 
1245 	team_id oldActiveApp = -1;
1246 	team_id newActiveApp = -1;
1247 
1248 	if (fFocus != NULL) {
1249 		fFocus->SetFocus(false);
1250 		oldActiveApp = fFocus->ServerWindow()->App()->ClientTeam();
1251 	}
1252 
1253 	fFocus = focus;
1254 
1255 	if (fFocus != NULL) {
1256 		fFocus->SetFocus(true);
1257 		newActiveApp = fFocus->ServerWindow()->App()->ClientTeam();
1258 	}
1259 
1260 	if (newActiveApp == -1) {
1261 		// make sure the cursor is visible
1262 		HWInterface()->SetCursorVisible(true);
1263 	}
1264 
1265 	UnlockAllWindows();
1266 
1267 	// change the "active" app if appropriate
1268 	if (oldActiveApp == newActiveApp)
1269 		return;
1270 
1271 	BAutolock locker(fApplicationsLock);
1272 
1273 	for (int32 i = 0; i < fApplications.CountItems(); i++) {
1274 		ServerApp *app = fApplications.ItemAt(i);
1275 
1276 		if (oldActiveApp != -1 && app->ClientTeam() == oldActiveApp)
1277 			app->Activate(false);
1278 		else if (newActiveApp != -1 && app->ClientTeam() == newActiveApp)
1279 			app->Activate(true);
1280 	}
1281 }
1282 
1283 
1284 void
1285 Desktop::_BringWindowsToFront(WindowList& windows, int32 list,
1286 	bool wereVisible)
1287 {
1288 	// we don't need to redraw what is currently
1289 	// visible of the window
1290 	BRegion clean;
1291 
1292 	for (WindowLayer* window = windows.FirstWindow(); window != NULL;
1293 			window = window->NextWindow(list)) {
1294 		if (wereVisible)
1295 			clean.Include(&window->VisibleRegion());
1296 
1297 		_CurrentWindows().AddWindow(window,
1298 			window->Frontmost(_CurrentWindows().FirstWindow(),
1299 				fCurrentWorkspace));
1300 
1301 		_WindowChanged(window);
1302 	}
1303 
1304 	BRegion dummy;
1305 	_RebuildClippingForAllWindows(dummy);
1306 
1307 	// redraw what became visible of the window(s)
1308 
1309 	BRegion dirty;
1310 	for (WindowLayer* window = windows.FirstWindow(); window != NULL;
1311 			window = window->NextWindow(list)) {
1312 		dirty.Include(&window->VisibleRegion());
1313 	}
1314 
1315 	dirty.Exclude(&clean);
1316 	MarkDirty(dirty);
1317 
1318 	_UpdateFront();
1319 
1320 	if (windows.FirstWindow() == fBack || fBack == NULL)
1321 		_UpdateBack();
1322 }
1323 
1324 
1325 /*!
1326 	\brief Tries to move the specified window to the front of the screen,
1327 		and make it the focus window.
1328 
1329 	If there are any modal windows on this screen, it might not actually
1330 	become the frontmost window, though, as modal windows stay in front
1331 	of their subset.
1332 */
1333 void
1334 Desktop::ActivateWindow(WindowLayer* window)
1335 {
1336 	STRACE(("ActivateWindow(%p, %s)\n", window, window ? window->Title() : "<none>"));
1337 
1338 	if (window == NULL) {
1339 		fBack = NULL;
1340 		fFront = NULL;
1341 		return;
1342 	}
1343 
1344 	bool windowOnOtherWorkspace = !window->InWorkspace(fCurrentWorkspace);
1345 
1346 	if (windowOnOtherWorkspace
1347 		&& (window->Flags() & B_NO_WORKSPACE_ACTIVATION)
1348 		&& !(window->Flags() & B_NOT_ANCHORED_ON_ACTIVATE))
1349 		return;
1350 
1351 	// TODO: take care about floating windows
1352 
1353 	if (!LockAllWindows())
1354 		return;
1355 
1356 	if (windowOnOtherWorkspace) {
1357 		// if the window wants to come to the current workspace,
1358 		// do so here - else activate the workspace on which this
1359 		// window is
1360 		if (window->Flags() & B_NOT_ANCHORED_ON_ACTIVATE) {
1361 			// bring the window to the current workspace
1362 			// TODO: what if this window is on multiple workspaces?!?
1363 			uint32 workspaces = workspace_to_workspaces(fCurrentWorkspace);
1364 			SetWindowWorkspaces(window, workspaces);
1365 		} else {
1366 			// switch to the workspace on which this window is
1367 			// (we'll take the first one that the window is on)
1368 			uint32 workspaces = window->Workspaces();
1369 			for (int32 i = 0; i < 32; i++) {
1370 				uint32 workspace = workspace_to_workspaces(i);
1371 				if (workspaces & workspace) {
1372 					SetWorkspace(i);
1373 					break;
1374 				}
1375 			}
1376 		}
1377 	}
1378 
1379 	if (window == FrontWindow()) {
1380 		// see if there is a normal B_AVOID_FRONT window still in front of us
1381 		WindowLayer* avoidsFront = window->NextWindow(fCurrentWorkspace);
1382 		while (avoidsFront && avoidsFront->IsNormal()
1383 			&& (avoidsFront->Flags() & B_AVOID_FRONT) == 0) {
1384 			avoidsFront = avoidsFront->NextWindow(fCurrentWorkspace);
1385 		}
1386 
1387 		if (avoidsFront == NULL) {
1388 			// we're already the frontmost window, we might just not have focus yet
1389 			if ((window->Flags() & B_AVOID_FOCUS) == 0)
1390 				SetFocusWindow(window);
1391 
1392 			UnlockAllWindows();
1393 			return;
1394 		}
1395 	}
1396 
1397 	// we don't need to redraw what is currently
1398 	// visible of the window
1399 	BRegion clean(window->VisibleRegion());
1400 	WindowList windows(kWorkingList);
1401 
1402 	WindowLayer* frontmost = window->Frontmost();
1403 
1404 	_CurrentWindows().RemoveWindow(window);
1405 	windows.AddWindow(window);
1406 
1407 	if (frontmost != NULL && frontmost->IsModal()) {
1408 		// all modal windows follow their subsets to the front
1409 		// (ie. they are staying in front of them, but they are
1410 		// not supposed to change their order because of that)
1411 
1412 		WindowLayer* nextModal;
1413 		for (WindowLayer* modal = frontmost; modal != NULL; modal = nextModal) {
1414 			// get the next modal window
1415 			nextModal = modal->NextWindow(fCurrentWorkspace);
1416 			while (nextModal != NULL && !nextModal->IsModal()) {
1417 				nextModal = nextModal->NextWindow(fCurrentWorkspace);
1418 			}
1419 			if (nextModal != NULL && !nextModal->HasInSubset(window))
1420 				nextModal = NULL;
1421 
1422 			_CurrentWindows().RemoveWindow(modal);
1423 			windows.AddWindow(modal);
1424 		}
1425 	}
1426 
1427 	_BringWindowsToFront(windows, kWorkingList, true);
1428 	if ((window->Flags() & B_AVOID_FOCUS) == 0)
1429 		SetFocusWindow(window);
1430 
1431 	UnlockAllWindows();
1432 }
1433 
1434 
1435 void
1436 Desktop::SendWindowBehind(WindowLayer* window, WindowLayer* behindOf)
1437 {
1438 	// TODO: should the "not in current workspace" be handled anyway?
1439 	//	(the code below would have to be changed then, though)
1440 	if (window == BackWindow()
1441 		|| !window->InWorkspace(fCurrentWorkspace)
1442 		|| (behindOf != NULL && !behindOf->InWorkspace(fCurrentWorkspace))
1443 		|| !LockAllWindows())
1444 		return;
1445 
1446 	// Is this a valid behindOf window?
1447 	if (behindOf != NULL && window->HasInSubset(behindOf))
1448 		behindOf = NULL;
1449 
1450 	// what is currently visible of the window
1451 	// might be dirty after the window is send to back
1452 	BRegion dirty(window->VisibleRegion());
1453 
1454 	// detach window and re-attach at desired position
1455 	WindowLayer* backmost = window->Backmost(behindOf);
1456 
1457 	_CurrentWindows().RemoveWindow(window);
1458 	_CurrentWindows().AddWindow(window, backmost
1459 		? backmost->NextWindow(fCurrentWorkspace) : BackWindow());
1460 
1461 	BRegion dummy;
1462 	_RebuildClippingForAllWindows(dummy);
1463 
1464 	// mark everything dirty that is no longer visible
1465 	BRegion clean(window->VisibleRegion());
1466 	dirty.Exclude(&clean);
1467 	MarkDirty(dirty);
1468 
1469 	_UpdateFronts();
1470 	SetFocusWindow(_CurrentWindows().LastWindow());
1471 	_WindowChanged(window);
1472 
1473 	UnlockAllWindows();
1474 }
1475 
1476 
1477 void
1478 Desktop::ShowWindow(WindowLayer* window)
1479 {
1480 	if (!window->IsHidden())
1481 		return;
1482 
1483 	LockAllWindows();
1484 
1485 	window->SetHidden(false);
1486 
1487 	if (window->InWorkspace(fCurrentWorkspace)) {
1488 		_ShowWindow(window, true);
1489 		_UpdateSubsetWorkspaces(window);
1490 		ActivateWindow(window);
1491 	} else {
1492 		// then we don't need to send the fake mouse event either
1493 		UnlockAllWindows();
1494 		return;
1495 	}
1496 
1497 	if (WorkspacesLayer* layer = dynamic_cast<WorkspacesLayer*>(window->TopLayer()))
1498 		fWorkspacesLayer = layer;
1499 
1500 	UnlockAllWindows();
1501 
1502 	// If the mouse cursor is directly over the newly visible window,
1503 	// we'll send a fake mouse moved message to the window, so that
1504 	// it knows the mouse is over it.
1505 
1506 	_SendFakeMouseMoved(window);
1507 }
1508 
1509 
1510 void
1511 Desktop::HideWindow(WindowLayer* window)
1512 {
1513 	if (window->IsHidden())
1514 		return;
1515 
1516 	if (!LockAllWindows())
1517 		return;
1518 
1519 	window->SetHidden(true);
1520 	if (fMouseEventWindow == window)
1521 		fMouseEventWindow = NULL;
1522 
1523 	if (window->InWorkspace(fCurrentWorkspace)) {
1524 		_UpdateSubsetWorkspaces(window);
1525 		_HideWindow(window);
1526 		_UpdateFronts();
1527 
1528 		if (FocusWindow() == window)
1529 			SetFocusWindow(_CurrentWindows().LastWindow());
1530 	}
1531 
1532 	if (fWorkspacesLayer != NULL)
1533 		fWorkspacesLayer->WindowRemoved(window);
1534 
1535 	if (dynamic_cast<WorkspacesLayer*>(window->TopLayer()) != NULL)
1536 		fWorkspacesLayer = NULL;
1537 
1538 	UnlockAllWindows();
1539 
1540 	if (window == fWindowUnderMouse)
1541 		_SendFakeMouseMoved();
1542 }
1543 
1544 
1545 /*!
1546 	Shows the window on the screen - it does this independently of the
1547 	WindowLayer::IsHidden() state.
1548 */
1549 void
1550 Desktop::_ShowWindow(WindowLayer* window, bool affectsOtherWindows)
1551 {
1552 	BRegion background;
1553 	_RebuildClippingForAllWindows(background);
1554 	_SetBackground(background);
1555 	_WindowChanged(window);
1556 
1557 	BRegion dirty(window->VisibleRegion());
1558 
1559 	if (!affectsOtherWindows) {
1560 		// everything that is now visible in the
1561 		// window needs a redraw, but other windows
1562 		// are not affected, we can call ProcessDirtyRegion()
1563 		// of the window, and don't have to use MarkDirty()
1564 		window->ProcessDirtyRegion(dirty);
1565 	} else
1566 		MarkDirty(dirty);
1567 }
1568 
1569 
1570 /*!
1571 	Hides the window from the screen - it does this independently of the
1572 	WindowLayer::IsHidden() state.
1573 */
1574 void
1575 Desktop::_HideWindow(WindowLayer* window)
1576 {
1577 	// after rebuilding the clipping,
1578 	// this window will not have a visible
1579 	// region anymore, so we need to remember
1580 	// it now
1581 	// (actually that's not true, since
1582 	// hidden windows are excluded from the
1583 	// clipping calculation, but anyways)
1584 	BRegion dirty(window->VisibleRegion());
1585 
1586 	BRegion background;
1587 	_RebuildClippingForAllWindows(background);
1588 	_SetBackground(background);
1589 	_WindowChanged(window);
1590 
1591 	MarkDirty(dirty);
1592 }
1593 
1594 
1595 void
1596 Desktop::MoveWindowBy(WindowLayer* window, float x, float y, int32 workspace)
1597 {
1598 	if (!LockAllWindows())
1599 		return;
1600 
1601 	if (workspace == -1)
1602 		workspace = fCurrentWorkspace;
1603 
1604 	if (!window->IsVisible() || workspace != fCurrentWorkspace) {
1605 		if (workspace != fCurrentWorkspace) {
1606 			// move the window on another workspace - this doesn't change it's
1607 			// current position
1608 			if (window->Anchor(workspace).position == kInvalidWindowPosition)
1609 				window->Anchor(workspace).position = window->Frame().LeftTop();
1610 
1611 			window->Anchor(workspace).position += BPoint(x, y);
1612 			_WindowChanged(window);
1613 		} else
1614 			window->MoveBy(x, y);
1615 
1616 		UnlockAllWindows();
1617 		return;
1618 	}
1619 
1620 	// the dirty region starts with the visible area of the window being moved
1621 	BRegion newDirtyRegion(window->VisibleRegion());
1622 
1623 	// no more drawing for DirectWindows
1624 	window->ServerWindow()->HandleDirectConnection(B_DIRECT_STOP);
1625 
1626 	window->MoveBy(x, y);
1627 
1628 	BRegion background;
1629 	_RebuildClippingForAllWindows(background);
1630 
1631 	// construct the region that is possible to be blitted
1632 	// to move the contents of the window
1633 	BRegion copyRegion(window->VisibleRegion());
1634 	copyRegion.OffsetBy(-x, -y);
1635 	copyRegion.IntersectWith(&newDirtyRegion);
1636 		// newDirtyRegion == the windows old visible region
1637 
1638 	// include the the new visible region of the window being
1639 	// moved into the dirty region (for now)
1640 	newDirtyRegion.Include(&window->VisibleRegion());
1641 
1642 	GetDrawingEngine()->CopyRegion(&copyRegion, x, y);
1643 
1644 	// allow DirectWindows to draw again after the visual
1645 	// content is at the new location
1646 	window->ServerWindow()->HandleDirectConnection(B_DIRECT_START | B_BUFFER_MOVED);
1647 
1648 	// in the dirty region, exclude the parts that we
1649 	// could move by blitting
1650 	copyRegion.OffsetBy(x, y);
1651 	newDirtyRegion.Exclude(&copyRegion);
1652 
1653 	MarkDirty(newDirtyRegion);
1654 	_SetBackground(background);
1655 	_WindowChanged(window);
1656 
1657 	UnlockAllWindows();
1658 }
1659 
1660 
1661 void
1662 Desktop::ResizeWindowBy(WindowLayer* window, float x, float y)
1663 {
1664 	if (!LockAllWindows())
1665 		return;
1666 
1667 	if (!window->IsVisible()) {
1668 		window->ResizeBy(x, y, NULL);
1669 		UnlockAllWindows();
1670 		return;
1671 	}
1672 
1673 	// the dirty region for the inside of the window is
1674 	// constructed by the window itself in ResizeBy()
1675 	BRegion newDirtyRegion;
1676 	// track the dirty region outside the window in case
1677 	// it is shrunk in "previouslyOccupiedRegion"
1678 	BRegion previouslyOccupiedRegion(window->VisibleRegion());
1679 
1680 	window->ResizeBy(x, y, &newDirtyRegion);
1681 
1682 	BRegion background;
1683 	_RebuildClippingForAllWindows(background);
1684 
1685 	// we just care for the region outside the window
1686 	previouslyOccupiedRegion.Exclude(&window->VisibleRegion());
1687 
1688 	// make sure the window cannot mark stuff dirty outside
1689 	// its visible region...
1690 	newDirtyRegion.IntersectWith(&window->VisibleRegion());
1691 	// ...because we do this outself
1692 	newDirtyRegion.Include(&previouslyOccupiedRegion);
1693 
1694 	MarkDirty(newDirtyRegion);
1695 	_SetBackground(background);
1696 	_WindowChanged(window);
1697 
1698 	UnlockAllWindows();
1699 }
1700 
1701 
1702 bool
1703 Desktop::SetWindowTabLocation(WindowLayer* window, float location)
1704 {
1705 	if (!LockAllWindows())
1706 		return false;
1707 
1708 	BRegion dirty;
1709 	bool changed = window->SetTabLocation(location, dirty);
1710 
1711 	if (changed && window->IsVisible() && dirty.CountRects() > 0) {
1712 		BRegion stillAvailableOnScreen;
1713 		_RebuildClippingForAllWindows(stillAvailableOnScreen);
1714 		_SetBackground(stillAvailableOnScreen);
1715 
1716 		_WindowChanged(window);
1717 		_TriggerWindowRedrawing(dirty);
1718 	}
1719 
1720 	UnlockAllWindows();
1721 
1722 	return changed;
1723 }
1724 
1725 
1726 bool
1727 Desktop::SetWindowDecoratorSettings(WindowLayer* window,
1728 									const BMessage& settings)
1729 {
1730 	// TODO: almost exact code duplication to above function...
1731 
1732 	if (!LockAllWindows())
1733 		return false;
1734 
1735 	BRegion dirty;
1736 	bool changed = window->SetDecoratorSettings(settings, dirty);
1737 
1738 	if (changed && window->IsVisible() && dirty.CountRects() > 0) {
1739 		BRegion stillAvailableOnScreen;
1740 		_RebuildClippingForAllWindows(stillAvailableOnScreen);
1741 		_SetBackground(stillAvailableOnScreen);
1742 
1743 		_TriggerWindowRedrawing(dirty);
1744 	}
1745 
1746 	UnlockAllWindows();
1747 
1748 	return changed;
1749 }
1750 
1751 
1752 /*!
1753 	Updates the workspaces of all subset windows with regard to the
1754 	specifed window.
1755 	If newIndex is not -1, it will move all subset windows that belong to
1756 	the specifed window to the new workspace; this form is only called by
1757 	SetWorkspace().
1758 */
1759 void
1760 Desktop::_UpdateSubsetWorkspaces(WindowLayer* window, int32 previousIndex,
1761 	int32 newIndex)
1762 {
1763 	STRACE(("_UpdateSubsetWorkspaces(window %p, %s)\n", window, window->Title()));
1764 
1765 	// if the window is hidden, the subset windows are up-to-date already
1766 	if (!window->IsNormal() || window->IsHidden())
1767 		return;
1768 
1769 	for (WindowLayer* subset = fSubsetWindows.FirstWindow(); subset != NULL;
1770 			subset = subset->NextWindow(kSubsetList)) {
1771 		if (subset->Feel() == B_MODAL_ALL_WINDOW_FEEL
1772 			|| subset->Feel() == B_FLOATING_ALL_WINDOW_FEEL) {
1773 			// These windows are always visible on all workspaces,
1774 			// no need to update them.
1775 			continue;
1776 		}
1777 
1778 		if (subset->IsFloating()) {
1779 			// Floating windows are inserted and removed to the current
1780 			// workspace as the need arises - they are not handled here
1781 			// but in _UpdateFront()
1782 			continue;
1783 		}
1784 
1785 		if (subset->HasInSubset(window)) {
1786 			// adopt the workspace change
1787 			if (newIndex != -1) {
1788 				_Windows(newIndex).AddWindow(subset);
1789 				_Windows(previousIndex).RemoveWindow(subset);
1790 			} else
1791 				SetWindowWorkspaces(subset, subset->SubsetWorkspaces());
1792 		}
1793 	}
1794 }
1795 
1796 
1797 /*!
1798 	\brief Adds or removes the window to or from the workspaces it's on.
1799 */
1800 void
1801 Desktop::_ChangeWindowWorkspaces(WindowLayer* window, uint32 oldWorkspaces,
1802 	uint32 newWorkspaces)
1803 {
1804 	// apply changes to the workspaces' window lists
1805 
1806 	LockAllWindows();
1807 
1808 	for (int32 i = 0; i < kMaxWorkspaces; i++) {
1809 		if (workspace_in_workspaces(i, oldWorkspaces)) {
1810 			// window is on this workspace, is it anymore?
1811 			if (!workspace_in_workspaces(i, newWorkspaces)) {
1812 				_Windows(i).RemoveWindow(window);
1813 
1814 				if (i == CurrentWorkspace()) {
1815 					// remove its appearance from the current workspace
1816 					window->SetCurrentWorkspace(-1);
1817 
1818 					if (!window->IsHidden())
1819 						_HideWindow(window);
1820 				}
1821 			}
1822 		} else {
1823 			// window was not on this workspace, is it now?
1824 			if (workspace_in_workspaces(i, newWorkspaces)) {
1825 				_Windows(i).AddWindow(window,
1826 					window->Frontmost(_Windows(i).FirstWindow(), i));
1827 
1828 				if (i == CurrentWorkspace()) {
1829 					// make the window visible in current workspace
1830 					window->SetCurrentWorkspace(fCurrentWorkspace);
1831 
1832 					if (!window->IsHidden()) {
1833 						// this only affects other windows if this windows has floating or
1834 						// modal windows that need to be shown as well
1835 						// TODO: take care of this
1836 						_ShowWindow(window, FrontWindow() == window);
1837 					}
1838 				}
1839 			}
1840 		}
1841 	}
1842 
1843 	// take care about modals and floating windows
1844 	_UpdateSubsetWorkspaces(window);
1845 
1846 	UnlockAllWindows();
1847 }
1848 
1849 
1850 void
1851 Desktop::SetWindowWorkspaces(WindowLayer* window, uint32 workspaces)
1852 {
1853 	LockAllWindows();
1854 
1855 	if (window->IsNormal() && workspaces == B_CURRENT_WORKSPACE)
1856 		workspaces = workspace_to_workspaces(CurrentWorkspace());
1857 
1858 	uint32 oldWorkspaces = window->Workspaces();
1859 
1860 	window->WorkspacesChanged(oldWorkspaces, workspaces);
1861 	_ChangeWindowWorkspaces(window, oldWorkspaces, workspaces);
1862 
1863 	UnlockAllWindows();
1864 }
1865 
1866 
1867 /*!	\brief Adds the window to the desktop.
1868 	At this point, the window is still hidden and must be shown explicetly
1869 	via ShowWindow().
1870 */
1871 void
1872 Desktop::AddWindow(WindowLayer *window)
1873 {
1874 	LockAllWindows();
1875 
1876 	fAllWindows.AddWindow(window);
1877 	if (!window->IsNormal())
1878 		fSubsetWindows.AddWindow(window);
1879 
1880 	if (window->IsNormal()) {
1881 		if (window->Workspaces() == B_CURRENT_WORKSPACE)
1882 			window->SetWorkspaces(workspace_to_workspaces(CurrentWorkspace()));
1883 	} else {
1884 		// subset windows are visible on all workspaces their subset is on
1885 		window->SetWorkspaces(window->SubsetWorkspaces());
1886 	}
1887 
1888 	_ChangeWindowWorkspaces(window, 0, window->Workspaces());
1889 	UnlockAllWindows();
1890 }
1891 
1892 
1893 void
1894 Desktop::RemoveWindow(WindowLayer *window)
1895 {
1896 	LockAllWindows();
1897 
1898 	if (!window->IsHidden())
1899 		HideWindow(window);
1900 
1901 	fAllWindows.RemoveWindow(window);
1902 	if (!window->IsNormal())
1903 		fSubsetWindows.RemoveWindow(window);
1904 
1905 	_ChangeWindowWorkspaces(window, window->Workspaces(), 0);
1906 	UnlockAllWindows();
1907 
1908 	// make sure this window won't get any events anymore
1909 
1910 	EventDispatcher().RemoveTarget(window->EventTarget());
1911 }
1912 
1913 
1914 bool
1915 Desktop::AddWindowToSubset(WindowLayer* subset, WindowLayer* window)
1916 {
1917 	if (!subset->AddToSubset(window))
1918 		return false;
1919 
1920 	_ChangeWindowWorkspaces(subset, subset->Workspaces(), subset->SubsetWorkspaces());
1921 	return true;
1922 }
1923 
1924 
1925 void
1926 Desktop::RemoveWindowFromSubset(WindowLayer* subset, WindowLayer* window)
1927 {
1928 	subset->RemoveFromSubset(window);
1929 	_ChangeWindowWorkspaces(subset, subset->Workspaces(), subset->SubsetWorkspaces());
1930 }
1931 
1932 
1933 void
1934 Desktop::SetWindowLook(WindowLayer *window, window_look newLook)
1935 {
1936 	if (window->Look() == newLook)
1937 		return;
1938 
1939 	if (!LockAllWindows())
1940 		return;
1941 
1942 	BRegion dirty;
1943 	window->SetLook(newLook, &dirty);
1944 		// TODO: test what happens when the window
1945 		// finds out it needs to resize itself...
1946 
1947 	BRegion stillAvailableOnScreen;
1948 	_RebuildClippingForAllWindows(stillAvailableOnScreen);
1949 	_SetBackground(stillAvailableOnScreen);
1950 	_WindowChanged(window);
1951 
1952 	_TriggerWindowRedrawing(dirty);
1953 
1954 	UnlockAllWindows();
1955 }
1956 
1957 
1958 void
1959 Desktop::SetWindowFeel(WindowLayer *window, window_feel newFeel)
1960 {
1961 	if (window->Feel() == newFeel)
1962 		return;
1963 
1964 	LockAllWindows();
1965 
1966 	bool wasNormal = window->IsNormal();
1967 
1968 	window->SetFeel(newFeel);
1969 
1970 	// move the window out of or into the subset window list as needed
1971 	if (window->IsNormal() && !wasNormal)
1972 		fSubsetWindows.RemoveWindow(window);
1973 	else if (!window->IsNormal() && wasNormal)
1974 		fSubsetWindows.AddWindow(window);
1975 
1976 	// A normal window that was once a floating or modal window will
1977 	// adopt the window's current workspaces
1978 
1979 	if (!window->IsNormal())
1980 		_ChangeWindowWorkspaces(window, window->Workspaces(), window->SubsetWorkspaces());
1981 
1982 	// make sure the window has the correct position in the window lists
1983 	//	(ie. all floating windows have to be on the top, ...)
1984 
1985 	for (int32 i = 0; i < kMaxWorkspaces; i++) {
1986 		if (!workspace_in_workspaces(i, window->Workspaces()))
1987 			continue;
1988 
1989 		bool changed = false;
1990 		BRegion visibleBefore;
1991 		if (i == fCurrentWorkspace && window->IsVisible())
1992 			visibleBefore = window->VisibleRegion();
1993 
1994 		WindowLayer* backmost = window->Backmost(_Windows(i).LastWindow(), i);
1995 		if (backmost != NULL) {
1996 			// check if the backmost window is really behind it
1997 			WindowLayer* previous = window->PreviousWindow(i);
1998 			while (previous != NULL) {
1999 				if (previous == backmost)
2000 					break;
2001 
2002 				previous = previous->PreviousWindow(i);
2003 			}
2004 
2005 			if (previous == NULL) {
2006 				// need to reinsert window before its backmost window
2007 				_Windows(i).RemoveWindow(window);
2008 				_Windows(i).AddWindow(window, backmost->NextWindow(i));
2009 				changed = true;
2010 			}
2011 		}
2012 
2013 		WindowLayer* frontmost = window->Frontmost(_Windows(i).FirstWindow(), i);
2014 		if (frontmost != NULL) {
2015 			// check if the frontmost window is really in front of it
2016 			WindowLayer* next = window->NextWindow(i);
2017 			while (next != NULL) {
2018 				if (next == frontmost)
2019 					break;
2020 
2021 				next = next->NextWindow(i);
2022 			}
2023 
2024 			if (next == NULL) {
2025 				// need to reinsert window behind its frontmost window
2026 				_Windows(i).RemoveWindow(window);
2027 				_Windows(i).AddWindow(window, frontmost);
2028 				changed = true;
2029 			}
2030 		}
2031 
2032 		if (i == fCurrentWorkspace && changed) {
2033 			BRegion dummy;
2034 			_RebuildClippingForAllWindows(dummy);
2035 
2036 			// mark everything dirty that is no longer visible, or
2037 			// is now visible and wasn't before
2038 			BRegion visibleAfter(window->VisibleRegion());
2039 			BRegion dirty(visibleAfter);
2040 			dirty.Exclude(&visibleBefore);
2041 			visibleBefore.Exclude(&visibleAfter);
2042 			dirty.Include(&visibleBefore);
2043 
2044 			MarkDirty(dirty);
2045 		}
2046 	}
2047 
2048 	_UpdateFronts();
2049 
2050 	if (window == FocusWindow() && !window->IsVisible())
2051 		SetFocusWindow(_CurrentWindows().LastWindow());
2052 
2053 	UnlockAllWindows();
2054 }
2055 
2056 
2057 void
2058 Desktop::SetWindowFlags(WindowLayer *window, uint32 newFlags)
2059 {
2060 	if (window->Flags() == newFlags)
2061 		return;
2062 
2063 	if (!LockAllWindows())
2064 		return;
2065 
2066 	BRegion dirty;
2067 	window->SetFlags(newFlags, &dirty);
2068 		// TODO: test what happens when the window
2069 		// finds out it needs to resize itself...
2070 
2071 	BRegion stillAvailableOnScreen;
2072 	_RebuildClippingForAllWindows(stillAvailableOnScreen);
2073 	_SetBackground(stillAvailableOnScreen);
2074 	_WindowChanged(window);
2075 
2076 	_TriggerWindowRedrawing(dirty);
2077 
2078 	UnlockAllWindows();
2079 }
2080 
2081 
2082 void
2083 Desktop::SetWindowTitle(WindowLayer *window, const char* title)
2084 {
2085 	if (!LockAllWindows())
2086 		return;
2087 
2088 	BRegion dirty;
2089 	window->SetTitle(title, dirty);
2090 
2091 	if (window->IsVisible() && dirty.CountRects() > 0) {
2092 		BRegion stillAvailableOnScreen;
2093 		_RebuildClippingForAllWindows(stillAvailableOnScreen);
2094 		_SetBackground(stillAvailableOnScreen);
2095 
2096 		_TriggerWindowRedrawing(dirty);
2097 	}
2098 
2099 	UnlockAllWindows();
2100 }
2101 
2102 
2103 /*!
2104 	Returns the window under the mouse cursor.
2105 	You need to have acquired the All Windows lock when calling this method.
2106 */
2107 WindowLayer*
2108 Desktop::WindowAt(BPoint where)
2109 {
2110 	for (WindowLayer* window = _CurrentWindows().LastWindow(); window;
2111 			window = window->PreviousWindow(fCurrentWorkspace)) {
2112 		if (window->IsVisible() && window->VisibleRegion().Contains(where))
2113 			return window;
2114 	}
2115 
2116 	return NULL;
2117 }
2118 
2119 
2120 void
2121 Desktop::SetMouseEventWindow(WindowLayer* window)
2122 {
2123 	fMouseEventWindow = window;
2124 }
2125 
2126 
2127 void
2128 Desktop::SetViewUnderMouse(const WindowLayer* window, int32 viewToken)
2129 {
2130 	fWindowUnderMouse = window;
2131 	fViewUnderMouse = viewToken;
2132 }
2133 
2134 
2135 int32
2136 Desktop::ViewUnderMouse(const WindowLayer* window)
2137 {
2138 	if (window != NULL && fWindowUnderMouse == window)
2139 		return fViewUnderMouse;
2140 
2141 	return B_NULL_TOKEN;
2142 }
2143 
2144 
2145 WindowLayer *
2146 Desktop::FindWindowLayerByClientToken(int32 token, team_id teamID)
2147 {
2148 	for (WindowLayer *window = fAllWindows.FirstWindow(); window != NULL;
2149 			window = window->NextWindow(kAllWindowList)) {
2150 		if (window->ServerWindow()->ClientToken() == token
2151 			&& window->ServerWindow()->ClientTeam() == teamID) {
2152 			return window;
2153 		}
2154 	}
2155 
2156 	return NULL;
2157 }
2158 
2159 
2160 void
2161 Desktop::MinimizeApplication(team_id team)
2162 {
2163 	AutoWriteLocker locker(fWindowLock);
2164 
2165 	// Just minimize all windows of that application
2166 
2167 	for (WindowLayer *window = fAllWindows.FirstWindow(); window != NULL;
2168 			window = window->NextWindow(kAllWindowList)) {
2169 		if (window->ServerWindow()->ClientTeam() != team)
2170 			continue;
2171 
2172 		window->ServerWindow()->NotifyMinimize(true);
2173 	}
2174 }
2175 
2176 
2177 void
2178 Desktop::BringApplicationToFront(team_id team)
2179 {
2180 	AutoWriteLocker locker(fWindowLock);
2181 
2182 	// TODO: for now, just maximize all windows of that application
2183 
2184 	for (WindowLayer *window = fAllWindows.FirstWindow(); window != NULL;
2185 			window = window->NextWindow(kAllWindowList)) {
2186 		if (window->ServerWindow()->ClientTeam() != team)
2187 			continue;
2188 
2189 		window->ServerWindow()->NotifyMinimize(false);
2190 	}
2191 }
2192 
2193 
2194 void
2195 Desktop::WindowAction(int32 windowToken, int32 action)
2196 {
2197 	if (action != B_MINIMIZE_WINDOW && action != B_BRING_TO_FRONT)
2198 		return;
2199 
2200 	LockAllWindows();
2201 
2202 	::ServerWindow* serverWindow;
2203 	WindowLayer* window;
2204 	if (BPrivate::gDefaultTokens.GetToken(windowToken,
2205 			B_SERVER_TOKEN, (void**)&serverWindow) != B_OK
2206 		|| (window = serverWindow->Window()) == NULL) {
2207 		UnlockAllWindows();
2208 		return;
2209 	}
2210 
2211 	if (action == B_BRING_TO_FRONT
2212 		&& !window->IsMinimized()) {
2213 		// the window is visible, we just need to make it the front window
2214 		ActivateWindow(window);
2215 	} else
2216 		serverWindow->NotifyMinimize(action == B_MINIMIZE_WINDOW);
2217 
2218 	UnlockAllWindows();
2219 }
2220 
2221 
2222 void
2223 Desktop::WriteWindowList(team_id team, BPrivate::LinkSender& sender)
2224 {
2225 	AutoWriteLocker locker(fWindowLock);
2226 
2227 	// compute the number of windows
2228 
2229 	int32 count = 0;
2230 
2231 	for (WindowLayer *window = fAllWindows.FirstWindow(); window != NULL;
2232 			window = window->NextWindow(kAllWindowList)) {
2233 		if (team < B_OK || window->ServerWindow()->ClientTeam() == team)
2234 			count++;
2235 	}
2236 
2237 	// write list
2238 
2239 	sender.StartMessage(B_OK);
2240 	sender.Attach<int32>(count);
2241 
2242 	for (WindowLayer *window = fAllWindows.FirstWindow(); window != NULL;
2243 			window = window->NextWindow(kAllWindowList)) {
2244 		if (team >= B_OK && window->ServerWindow()->ClientTeam() != team)
2245 			continue;
2246 
2247 		sender.Attach<int32>(window->ServerWindow()->ServerToken());
2248 	}
2249 
2250 	sender.Flush();
2251 }
2252 
2253 
2254 void
2255 Desktop::WriteWindowInfo(int32 serverToken, BPrivate::LinkSender& sender)
2256 {
2257 	AutoWriteLocker locker(fWindowLock);
2258 	BAutolock tokenLocker(BPrivate::gDefaultTokens);
2259 
2260 	::ServerWindow* window;
2261 	if (BPrivate::gDefaultTokens.GetToken(serverToken,
2262 			B_SERVER_TOKEN, (void**)&window) != B_OK) {
2263 		sender.StartMessage(B_ENTRY_NOT_FOUND);
2264 		sender.Flush();
2265 		return;
2266 	}
2267 
2268 	window_info info;
2269 	window->GetInfo(info);
2270 
2271 	int32 length = window->Title() ? strlen(window->Title()) : 0;
2272 
2273 	sender.StartMessage(B_OK);
2274 	sender.Attach<int32>(sizeof(window_info) + length + 1);
2275 	sender.Attach(&info, sizeof(window_info));
2276 	if (length > 0)
2277 		sender.Attach(window->Title(), length + 1);
2278 	else
2279 		sender.Attach<char>('\0');
2280 	sender.Flush();
2281 }
2282 
2283 
2284 void
2285 Desktop::MarkDirty(BRegion& region)
2286 {
2287 	if (region.CountRects() == 0)
2288 		return;
2289 
2290 	if (LockAllWindows()) {
2291 		// send redraw messages to all windows intersecting the dirty region
2292 		_TriggerWindowRedrawing(region);
2293 
2294 		UnlockAllWindows();
2295 	}
2296 }
2297 
2298 
2299 void
2300 Desktop::_RebuildClippingForAllWindows(BRegion& stillAvailableOnScreen)
2301 {
2302 	// the available region on screen starts with the entire screen area
2303 	// each window on the screen will take a portion from that area
2304 
2305 	// figure out what the entire screen area is
2306 	stillAvailableOnScreen = fScreenRegion;
2307 
2308 	// set clipping of each window
2309 	for (WindowLayer* window = _CurrentWindows().LastWindow(); window != NULL;
2310 			window = window->PreviousWindow(fCurrentWorkspace)) {
2311 		if (!window->IsHidden()) {
2312 			window->SetClipping(&stillAvailableOnScreen);
2313 			// that windows region is not available on screen anymore
2314 			stillAvailableOnScreen.Exclude(&window->VisibleRegion());
2315 		}
2316 	}
2317 }
2318 
2319 
2320 void
2321 Desktop::_TriggerWindowRedrawing(BRegion& newDirtyRegion)
2322 {
2323 	// send redraw messages to all windows intersecting the dirty region
2324 	for (WindowLayer* window = _CurrentWindows().LastWindow(); window != NULL;
2325 			window = window->PreviousWindow(fCurrentWorkspace)) {
2326 		if (!window->IsHidden()
2327 			&& newDirtyRegion.Intersects(window->VisibleRegion().Frame()))
2328 			window->ProcessDirtyRegion(newDirtyRegion);
2329 	}
2330 }
2331 
2332 
2333 void
2334 Desktop::_SetBackground(BRegion& background)
2335 {
2336 	// NOTE: the drawing operation is caried out
2337 	// in the clipping region rebuild, but it is
2338 	// ok actually, because it also avoids trails on
2339 	// moving windows
2340 
2341 	// remember the region not covered by any windows
2342 	// and redraw the dirty background
2343 	BRegion dirtyBackground(background);
2344 	dirtyBackground.Exclude(&fBackgroundRegion);
2345 	dirtyBackground.IntersectWith(&background);
2346 	fBackgroundRegion = background;
2347 	if (dirtyBackground.Frame().IsValid()) {
2348 		if (GetDrawingEngine()->LockParallelAccess()) {
2349 			GetDrawingEngine()->FillRegion(dirtyBackground,
2350 				fWorkspaces[fCurrentWorkspace].Color());
2351 
2352 			GetDrawingEngine()->UnlockParallelAccess();
2353 		}
2354 	}
2355 }
2356