xref: /haiku/src/servers/app/Desktop.cpp (revision b028e77473189065f2baefc6f5e10d451cf591e2)
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 		_WindowChanged(window);
1494 		UnlockAllWindows();
1495 		return;
1496 	}
1497 
1498 	if (WorkspacesLayer* layer = dynamic_cast<WorkspacesLayer*>(window->TopLayer()))
1499 		fWorkspacesLayer = layer;
1500 
1501 	UnlockAllWindows();
1502 
1503 	// If the mouse cursor is directly over the newly visible window,
1504 	// we'll send a fake mouse moved message to the window, so that
1505 	// it knows the mouse is over it.
1506 
1507 	_SendFakeMouseMoved(window);
1508 }
1509 
1510 
1511 void
1512 Desktop::HideWindow(WindowLayer* window)
1513 {
1514 	if (window->IsHidden())
1515 		return;
1516 
1517 	if (!LockAllWindows())
1518 		return;
1519 
1520 	window->SetHidden(true);
1521 	if (fMouseEventWindow == window)
1522 		fMouseEventWindow = NULL;
1523 
1524 	if (window->InWorkspace(fCurrentWorkspace)) {
1525 		_UpdateSubsetWorkspaces(window);
1526 		_HideWindow(window);
1527 		_UpdateFronts();
1528 
1529 		if (FocusWindow() == window)
1530 			SetFocusWindow(_CurrentWindows().LastWindow());
1531 	} else
1532 		_WindowChanged(window);
1533 
1534 	if (fWorkspacesLayer != NULL)
1535 		fWorkspacesLayer->WindowRemoved(window);
1536 
1537 	if (dynamic_cast<WorkspacesLayer*>(window->TopLayer()) != NULL)
1538 		fWorkspacesLayer = NULL;
1539 
1540 	UnlockAllWindows();
1541 
1542 	if (window == fWindowUnderMouse)
1543 		_SendFakeMouseMoved();
1544 }
1545 
1546 
1547 /*!
1548 	Shows the window on the screen - it does this independently of the
1549 	WindowLayer::IsHidden() state.
1550 */
1551 void
1552 Desktop::_ShowWindow(WindowLayer* window, bool affectsOtherWindows)
1553 {
1554 	BRegion background;
1555 	_RebuildClippingForAllWindows(background);
1556 	_SetBackground(background);
1557 	_WindowChanged(window);
1558 
1559 	BRegion dirty(window->VisibleRegion());
1560 
1561 	if (!affectsOtherWindows) {
1562 		// everything that is now visible in the
1563 		// window needs a redraw, but other windows
1564 		// are not affected, we can call ProcessDirtyRegion()
1565 		// of the window, and don't have to use MarkDirty()
1566 		window->ProcessDirtyRegion(dirty);
1567 	} else
1568 		MarkDirty(dirty);
1569 }
1570 
1571 
1572 /*!
1573 	Hides the window from the screen - it does this independently of the
1574 	WindowLayer::IsHidden() state.
1575 */
1576 void
1577 Desktop::_HideWindow(WindowLayer* window)
1578 {
1579 	// after rebuilding the clipping,
1580 	// this window will not have a visible
1581 	// region anymore, so we need to remember
1582 	// it now
1583 	// (actually that's not true, since
1584 	// hidden windows are excluded from the
1585 	// clipping calculation, but anyways)
1586 	BRegion dirty(window->VisibleRegion());
1587 
1588 	BRegion background;
1589 	_RebuildClippingForAllWindows(background);
1590 	_SetBackground(background);
1591 	_WindowChanged(window);
1592 
1593 	MarkDirty(dirty);
1594 }
1595 
1596 
1597 void
1598 Desktop::MoveWindowBy(WindowLayer* window, float x, float y, int32 workspace)
1599 {
1600 	if (!LockAllWindows())
1601 		return;
1602 
1603 	if (workspace == -1)
1604 		workspace = fCurrentWorkspace;
1605 
1606 	if (!window->IsVisible() || workspace != fCurrentWorkspace) {
1607 		if (workspace != fCurrentWorkspace) {
1608 			// move the window on another workspace - this doesn't change it's
1609 			// current position
1610 			if (window->Anchor(workspace).position == kInvalidWindowPosition)
1611 				window->Anchor(workspace).position = window->Frame().LeftTop();
1612 
1613 			window->Anchor(workspace).position += BPoint(x, y);
1614 			_WindowChanged(window);
1615 		} else
1616 			window->MoveBy(x, y);
1617 
1618 		UnlockAllWindows();
1619 		return;
1620 	}
1621 
1622 	// the dirty region starts with the visible area of the window being moved
1623 	BRegion newDirtyRegion(window->VisibleRegion());
1624 
1625 	// no more drawing for DirectWindows
1626 	window->ServerWindow()->HandleDirectConnection(B_DIRECT_STOP);
1627 
1628 	window->MoveBy(x, y);
1629 
1630 	BRegion background;
1631 	_RebuildClippingForAllWindows(background);
1632 
1633 	// construct the region that is possible to be blitted
1634 	// to move the contents of the window
1635 	BRegion copyRegion(window->VisibleRegion());
1636 	copyRegion.OffsetBy(-x, -y);
1637 	copyRegion.IntersectWith(&newDirtyRegion);
1638 		// newDirtyRegion == the windows old visible region
1639 
1640 	// include the the new visible region of the window being
1641 	// moved into the dirty region (for now)
1642 	newDirtyRegion.Include(&window->VisibleRegion());
1643 
1644 	GetDrawingEngine()->CopyRegion(&copyRegion, x, y);
1645 
1646 	// allow DirectWindows to draw again after the visual
1647 	// content is at the new location
1648 	window->ServerWindow()->HandleDirectConnection(B_DIRECT_START | B_BUFFER_MOVED);
1649 
1650 	// in the dirty region, exclude the parts that we
1651 	// could move by blitting
1652 	copyRegion.OffsetBy(x, y);
1653 	newDirtyRegion.Exclude(&copyRegion);
1654 
1655 	MarkDirty(newDirtyRegion);
1656 	_SetBackground(background);
1657 	_WindowChanged(window);
1658 
1659 	UnlockAllWindows();
1660 }
1661 
1662 
1663 void
1664 Desktop::ResizeWindowBy(WindowLayer* window, float x, float y)
1665 {
1666 	if (!LockAllWindows())
1667 		return;
1668 
1669 	if (!window->IsVisible()) {
1670 		window->ResizeBy(x, y, NULL);
1671 		UnlockAllWindows();
1672 		return;
1673 	}
1674 
1675 	// the dirty region for the inside of the window is
1676 	// constructed by the window itself in ResizeBy()
1677 	BRegion newDirtyRegion;
1678 	// track the dirty region outside the window in case
1679 	// it is shrunk in "previouslyOccupiedRegion"
1680 	BRegion previouslyOccupiedRegion(window->VisibleRegion());
1681 
1682 	window->ResizeBy(x, y, &newDirtyRegion);
1683 
1684 	BRegion background;
1685 	_RebuildClippingForAllWindows(background);
1686 
1687 	// we just care for the region outside the window
1688 	previouslyOccupiedRegion.Exclude(&window->VisibleRegion());
1689 
1690 	// make sure the window cannot mark stuff dirty outside
1691 	// its visible region...
1692 	newDirtyRegion.IntersectWith(&window->VisibleRegion());
1693 	// ...because we do this outself
1694 	newDirtyRegion.Include(&previouslyOccupiedRegion);
1695 
1696 	MarkDirty(newDirtyRegion);
1697 	_SetBackground(background);
1698 	_WindowChanged(window);
1699 
1700 	UnlockAllWindows();
1701 }
1702 
1703 
1704 bool
1705 Desktop::SetWindowTabLocation(WindowLayer* window, float location)
1706 {
1707 	if (!LockAllWindows())
1708 		return false;
1709 
1710 	BRegion dirty;
1711 	bool changed = window->SetTabLocation(location, dirty);
1712 
1713 	if (changed && window->IsVisible() && dirty.CountRects() > 0) {
1714 		BRegion stillAvailableOnScreen;
1715 		_RebuildClippingForAllWindows(stillAvailableOnScreen);
1716 		_SetBackground(stillAvailableOnScreen);
1717 
1718 		_WindowChanged(window);
1719 		_TriggerWindowRedrawing(dirty);
1720 	}
1721 
1722 	UnlockAllWindows();
1723 
1724 	return changed;
1725 }
1726 
1727 
1728 bool
1729 Desktop::SetWindowDecoratorSettings(WindowLayer* window,
1730 									const BMessage& settings)
1731 {
1732 	// TODO: almost exact code duplication to above function...
1733 
1734 	if (!LockAllWindows())
1735 		return false;
1736 
1737 	BRegion dirty;
1738 	bool changed = window->SetDecoratorSettings(settings, dirty);
1739 
1740 	if (changed && window->IsVisible() && dirty.CountRects() > 0) {
1741 		BRegion stillAvailableOnScreen;
1742 		_RebuildClippingForAllWindows(stillAvailableOnScreen);
1743 		_SetBackground(stillAvailableOnScreen);
1744 
1745 		_TriggerWindowRedrawing(dirty);
1746 	}
1747 
1748 	UnlockAllWindows();
1749 
1750 	return changed;
1751 }
1752 
1753 
1754 /*!
1755 	Updates the workspaces of all subset windows with regard to the
1756 	specifed window.
1757 	If newIndex is not -1, it will move all subset windows that belong to
1758 	the specifed window to the new workspace; this form is only called by
1759 	SetWorkspace().
1760 */
1761 void
1762 Desktop::_UpdateSubsetWorkspaces(WindowLayer* window, int32 previousIndex,
1763 	int32 newIndex)
1764 {
1765 	STRACE(("_UpdateSubsetWorkspaces(window %p, %s)\n", window, window->Title()));
1766 
1767 	// if the window is hidden, the subset windows are up-to-date already
1768 	if (!window->IsNormal() || window->IsHidden())
1769 		return;
1770 
1771 	for (WindowLayer* subset = fSubsetWindows.FirstWindow(); subset != NULL;
1772 			subset = subset->NextWindow(kSubsetList)) {
1773 		if (subset->Feel() == B_MODAL_ALL_WINDOW_FEEL
1774 			|| subset->Feel() == B_FLOATING_ALL_WINDOW_FEEL) {
1775 			// These windows are always visible on all workspaces,
1776 			// no need to update them.
1777 			continue;
1778 		}
1779 
1780 		if (subset->IsFloating()) {
1781 			// Floating windows are inserted and removed to the current
1782 			// workspace as the need arises - they are not handled here
1783 			// but in _UpdateFront()
1784 			continue;
1785 		}
1786 
1787 		if (subset->HasInSubset(window)) {
1788 			// adopt the workspace change
1789 			if (newIndex != -1) {
1790 				_Windows(newIndex).AddWindow(subset);
1791 				_Windows(previousIndex).RemoveWindow(subset);
1792 			} else
1793 				SetWindowWorkspaces(subset, subset->SubsetWorkspaces());
1794 		}
1795 	}
1796 }
1797 
1798 
1799 /*!
1800 	\brief Adds or removes the window to or from the workspaces it's on.
1801 */
1802 void
1803 Desktop::_ChangeWindowWorkspaces(WindowLayer* window, uint32 oldWorkspaces,
1804 	uint32 newWorkspaces)
1805 {
1806 	// apply changes to the workspaces' window lists
1807 
1808 	LockAllWindows();
1809 
1810 	for (int32 i = 0; i < kMaxWorkspaces; i++) {
1811 		if (workspace_in_workspaces(i, oldWorkspaces)) {
1812 			// window is on this workspace, is it anymore?
1813 			if (!workspace_in_workspaces(i, newWorkspaces)) {
1814 				_Windows(i).RemoveWindow(window);
1815 
1816 				if (i == CurrentWorkspace()) {
1817 					// remove its appearance from the current workspace
1818 					window->SetCurrentWorkspace(-1);
1819 
1820 					if (!window->IsHidden())
1821 						_HideWindow(window);
1822 				}
1823 			}
1824 		} else {
1825 			// window was not on this workspace, is it now?
1826 			if (workspace_in_workspaces(i, newWorkspaces)) {
1827 				_Windows(i).AddWindow(window,
1828 					window->Frontmost(_Windows(i).FirstWindow(), i));
1829 
1830 				if (i == CurrentWorkspace()) {
1831 					// make the window visible in current workspace
1832 					window->SetCurrentWorkspace(fCurrentWorkspace);
1833 
1834 					if (!window->IsHidden()) {
1835 						// this only affects other windows if this windows has floating or
1836 						// modal windows that need to be shown as well
1837 						// TODO: take care of this
1838 						_ShowWindow(window, FrontWindow() == window);
1839 					}
1840 				}
1841 			}
1842 		}
1843 	}
1844 
1845 	// take care about modals and floating windows
1846 	_UpdateSubsetWorkspaces(window);
1847 
1848 	UnlockAllWindows();
1849 }
1850 
1851 
1852 void
1853 Desktop::SetWindowWorkspaces(WindowLayer* window, uint32 workspaces)
1854 {
1855 	LockAllWindows();
1856 
1857 	if (window->IsNormal() && workspaces == B_CURRENT_WORKSPACE)
1858 		workspaces = workspace_to_workspaces(CurrentWorkspace());
1859 
1860 	uint32 oldWorkspaces = window->Workspaces();
1861 
1862 	window->WorkspacesChanged(oldWorkspaces, workspaces);
1863 	_ChangeWindowWorkspaces(window, oldWorkspaces, workspaces);
1864 
1865 	UnlockAllWindows();
1866 }
1867 
1868 
1869 /*!	\brief Adds the window to the desktop.
1870 	At this point, the window is still hidden and must be shown explicetly
1871 	via ShowWindow().
1872 */
1873 void
1874 Desktop::AddWindow(WindowLayer *window)
1875 {
1876 	LockAllWindows();
1877 
1878 	fAllWindows.AddWindow(window);
1879 	if (!window->IsNormal())
1880 		fSubsetWindows.AddWindow(window);
1881 
1882 	if (window->IsNormal()) {
1883 		if (window->Workspaces() == B_CURRENT_WORKSPACE)
1884 			window->SetWorkspaces(workspace_to_workspaces(CurrentWorkspace()));
1885 	} else {
1886 		// subset windows are visible on all workspaces their subset is on
1887 		window->SetWorkspaces(window->SubsetWorkspaces());
1888 	}
1889 
1890 	_ChangeWindowWorkspaces(window, 0, window->Workspaces());
1891 	UnlockAllWindows();
1892 }
1893 
1894 
1895 void
1896 Desktop::RemoveWindow(WindowLayer *window)
1897 {
1898 	LockAllWindows();
1899 
1900 	if (!window->IsHidden())
1901 		HideWindow(window);
1902 
1903 	fAllWindows.RemoveWindow(window);
1904 	if (!window->IsNormal())
1905 		fSubsetWindows.RemoveWindow(window);
1906 
1907 	_ChangeWindowWorkspaces(window, window->Workspaces(), 0);
1908 	UnlockAllWindows();
1909 
1910 	// make sure this window won't get any events anymore
1911 
1912 	EventDispatcher().RemoveTarget(window->EventTarget());
1913 }
1914 
1915 
1916 bool
1917 Desktop::AddWindowToSubset(WindowLayer* subset, WindowLayer* window)
1918 {
1919 	if (!subset->AddToSubset(window))
1920 		return false;
1921 
1922 	_ChangeWindowWorkspaces(subset, subset->Workspaces(), subset->SubsetWorkspaces());
1923 	return true;
1924 }
1925 
1926 
1927 void
1928 Desktop::RemoveWindowFromSubset(WindowLayer* subset, WindowLayer* window)
1929 {
1930 	subset->RemoveFromSubset(window);
1931 	_ChangeWindowWorkspaces(subset, subset->Workspaces(), subset->SubsetWorkspaces());
1932 }
1933 
1934 
1935 void
1936 Desktop::SetWindowLook(WindowLayer *window, window_look newLook)
1937 {
1938 	if (window->Look() == newLook)
1939 		return;
1940 
1941 	if (!LockAllWindows())
1942 		return;
1943 
1944 	BRegion dirty;
1945 	window->SetLook(newLook, &dirty);
1946 		// TODO: test what happens when the window
1947 		// finds out it needs to resize itself...
1948 
1949 	BRegion stillAvailableOnScreen;
1950 	_RebuildClippingForAllWindows(stillAvailableOnScreen);
1951 	_SetBackground(stillAvailableOnScreen);
1952 	_WindowChanged(window);
1953 
1954 	_TriggerWindowRedrawing(dirty);
1955 
1956 	UnlockAllWindows();
1957 }
1958 
1959 
1960 void
1961 Desktop::SetWindowFeel(WindowLayer *window, window_feel newFeel)
1962 {
1963 	if (window->Feel() == newFeel)
1964 		return;
1965 
1966 	LockAllWindows();
1967 
1968 	bool wasNormal = window->IsNormal();
1969 
1970 	window->SetFeel(newFeel);
1971 
1972 	// move the window out of or into the subset window list as needed
1973 	if (window->IsNormal() && !wasNormal)
1974 		fSubsetWindows.RemoveWindow(window);
1975 	else if (!window->IsNormal() && wasNormal)
1976 		fSubsetWindows.AddWindow(window);
1977 
1978 	// A normal window that was once a floating or modal window will
1979 	// adopt the window's current workspaces
1980 
1981 	if (!window->IsNormal())
1982 		_ChangeWindowWorkspaces(window, window->Workspaces(), window->SubsetWorkspaces());
1983 
1984 	// make sure the window has the correct position in the window lists
1985 	//	(ie. all floating windows have to be on the top, ...)
1986 
1987 	for (int32 i = 0; i < kMaxWorkspaces; i++) {
1988 		if (!workspace_in_workspaces(i, window->Workspaces()))
1989 			continue;
1990 
1991 		bool changed = false;
1992 		BRegion visibleBefore;
1993 		if (i == fCurrentWorkspace && window->IsVisible())
1994 			visibleBefore = window->VisibleRegion();
1995 
1996 		WindowLayer* backmost = window->Backmost(_Windows(i).LastWindow(), i);
1997 		if (backmost != NULL) {
1998 			// check if the backmost window is really behind it
1999 			WindowLayer* previous = window->PreviousWindow(i);
2000 			while (previous != NULL) {
2001 				if (previous == backmost)
2002 					break;
2003 
2004 				previous = previous->PreviousWindow(i);
2005 			}
2006 
2007 			if (previous == NULL) {
2008 				// need to reinsert window before its backmost window
2009 				_Windows(i).RemoveWindow(window);
2010 				_Windows(i).AddWindow(window, backmost->NextWindow(i));
2011 				changed = true;
2012 			}
2013 		}
2014 
2015 		WindowLayer* frontmost = window->Frontmost(_Windows(i).FirstWindow(), i);
2016 		if (frontmost != NULL) {
2017 			// check if the frontmost window is really in front of it
2018 			WindowLayer* next = window->NextWindow(i);
2019 			while (next != NULL) {
2020 				if (next == frontmost)
2021 					break;
2022 
2023 				next = next->NextWindow(i);
2024 			}
2025 
2026 			if (next == NULL) {
2027 				// need to reinsert window behind its frontmost window
2028 				_Windows(i).RemoveWindow(window);
2029 				_Windows(i).AddWindow(window, frontmost);
2030 				changed = true;
2031 			}
2032 		}
2033 
2034 		if (i == fCurrentWorkspace && changed) {
2035 			BRegion dummy;
2036 			_RebuildClippingForAllWindows(dummy);
2037 
2038 			// mark everything dirty that is no longer visible, or
2039 			// is now visible and wasn't before
2040 			BRegion visibleAfter(window->VisibleRegion());
2041 			BRegion dirty(visibleAfter);
2042 			dirty.Exclude(&visibleBefore);
2043 			visibleBefore.Exclude(&visibleAfter);
2044 			dirty.Include(&visibleBefore);
2045 
2046 			MarkDirty(dirty);
2047 		}
2048 	}
2049 
2050 	_UpdateFronts();
2051 
2052 	if (window == FocusWindow() && !window->IsVisible())
2053 		SetFocusWindow(_CurrentWindows().LastWindow());
2054 
2055 	UnlockAllWindows();
2056 }
2057 
2058 
2059 void
2060 Desktop::SetWindowFlags(WindowLayer *window, uint32 newFlags)
2061 {
2062 	if (window->Flags() == newFlags)
2063 		return;
2064 
2065 	if (!LockAllWindows())
2066 		return;
2067 
2068 	BRegion dirty;
2069 	window->SetFlags(newFlags, &dirty);
2070 		// TODO: test what happens when the window
2071 		// finds out it needs to resize itself...
2072 
2073 	BRegion stillAvailableOnScreen;
2074 	_RebuildClippingForAllWindows(stillAvailableOnScreen);
2075 	_SetBackground(stillAvailableOnScreen);
2076 	_WindowChanged(window);
2077 
2078 	_TriggerWindowRedrawing(dirty);
2079 
2080 	UnlockAllWindows();
2081 }
2082 
2083 
2084 void
2085 Desktop::SetWindowTitle(WindowLayer *window, const char* title)
2086 {
2087 	if (!LockAllWindows())
2088 		return;
2089 
2090 	BRegion dirty;
2091 	window->SetTitle(title, dirty);
2092 
2093 	if (window->IsVisible() && dirty.CountRects() > 0) {
2094 		BRegion stillAvailableOnScreen;
2095 		_RebuildClippingForAllWindows(stillAvailableOnScreen);
2096 		_SetBackground(stillAvailableOnScreen);
2097 
2098 		_TriggerWindowRedrawing(dirty);
2099 	}
2100 
2101 	UnlockAllWindows();
2102 }
2103 
2104 
2105 /*!
2106 	Returns the window under the mouse cursor.
2107 	You need to have acquired the All Windows lock when calling this method.
2108 */
2109 WindowLayer*
2110 Desktop::WindowAt(BPoint where)
2111 {
2112 	for (WindowLayer* window = _CurrentWindows().LastWindow(); window;
2113 			window = window->PreviousWindow(fCurrentWorkspace)) {
2114 		if (window->IsVisible() && window->VisibleRegion().Contains(where))
2115 			return window;
2116 	}
2117 
2118 	return NULL;
2119 }
2120 
2121 
2122 void
2123 Desktop::SetMouseEventWindow(WindowLayer* window)
2124 {
2125 	fMouseEventWindow = window;
2126 }
2127 
2128 
2129 void
2130 Desktop::SetViewUnderMouse(const WindowLayer* window, int32 viewToken)
2131 {
2132 	fWindowUnderMouse = window;
2133 	fViewUnderMouse = viewToken;
2134 }
2135 
2136 
2137 int32
2138 Desktop::ViewUnderMouse(const WindowLayer* window)
2139 {
2140 	if (window != NULL && fWindowUnderMouse == window)
2141 		return fViewUnderMouse;
2142 
2143 	return B_NULL_TOKEN;
2144 }
2145 
2146 
2147 WindowLayer *
2148 Desktop::FindWindowLayerByClientToken(int32 token, team_id teamID)
2149 {
2150 	for (WindowLayer *window = fAllWindows.FirstWindow(); window != NULL;
2151 			window = window->NextWindow(kAllWindowList)) {
2152 		if (window->ServerWindow()->ClientToken() == token
2153 			&& window->ServerWindow()->ClientTeam() == teamID) {
2154 			return window;
2155 		}
2156 	}
2157 
2158 	return NULL;
2159 }
2160 
2161 
2162 void
2163 Desktop::MinimizeApplication(team_id team)
2164 {
2165 	AutoWriteLocker locker(fWindowLock);
2166 
2167 	// Just minimize all windows of that application
2168 
2169 	for (WindowLayer *window = fAllWindows.FirstWindow(); window != NULL;
2170 			window = window->NextWindow(kAllWindowList)) {
2171 		if (window->ServerWindow()->ClientTeam() != team)
2172 			continue;
2173 
2174 		window->ServerWindow()->NotifyMinimize(true);
2175 	}
2176 }
2177 
2178 
2179 void
2180 Desktop::BringApplicationToFront(team_id team)
2181 {
2182 	AutoWriteLocker locker(fWindowLock);
2183 
2184 	// TODO: for now, just maximize all windows of that application
2185 
2186 	for (WindowLayer *window = fAllWindows.FirstWindow(); window != NULL;
2187 			window = window->NextWindow(kAllWindowList)) {
2188 		if (window->ServerWindow()->ClientTeam() != team)
2189 			continue;
2190 
2191 		window->ServerWindow()->NotifyMinimize(false);
2192 	}
2193 }
2194 
2195 
2196 void
2197 Desktop::WindowAction(int32 windowToken, int32 action)
2198 {
2199 	if (action != B_MINIMIZE_WINDOW && action != B_BRING_TO_FRONT)
2200 		return;
2201 
2202 	LockAllWindows();
2203 
2204 	::ServerWindow* serverWindow;
2205 	WindowLayer* window;
2206 	if (BPrivate::gDefaultTokens.GetToken(windowToken,
2207 			B_SERVER_TOKEN, (void**)&serverWindow) != B_OK
2208 		|| (window = serverWindow->Window()) == NULL) {
2209 		UnlockAllWindows();
2210 		return;
2211 	}
2212 
2213 	if (action == B_BRING_TO_FRONT
2214 		&& !window->IsMinimized()) {
2215 		// the window is visible, we just need to make it the front window
2216 		ActivateWindow(window);
2217 	} else
2218 		serverWindow->NotifyMinimize(action == B_MINIMIZE_WINDOW);
2219 
2220 	UnlockAllWindows();
2221 }
2222 
2223 
2224 void
2225 Desktop::WriteWindowList(team_id team, BPrivate::LinkSender& sender)
2226 {
2227 	AutoWriteLocker locker(fWindowLock);
2228 
2229 	// compute the number of windows
2230 
2231 	int32 count = 0;
2232 
2233 	for (WindowLayer *window = fAllWindows.FirstWindow(); window != NULL;
2234 			window = window->NextWindow(kAllWindowList)) {
2235 		if (team < B_OK || window->ServerWindow()->ClientTeam() == team)
2236 			count++;
2237 	}
2238 
2239 	// write list
2240 
2241 	sender.StartMessage(B_OK);
2242 	sender.Attach<int32>(count);
2243 
2244 	for (WindowLayer *window = fAllWindows.FirstWindow(); window != NULL;
2245 			window = window->NextWindow(kAllWindowList)) {
2246 		if (team >= B_OK && window->ServerWindow()->ClientTeam() != team)
2247 			continue;
2248 
2249 		sender.Attach<int32>(window->ServerWindow()->ServerToken());
2250 	}
2251 
2252 	sender.Flush();
2253 }
2254 
2255 
2256 void
2257 Desktop::WriteWindowInfo(int32 serverToken, BPrivate::LinkSender& sender)
2258 {
2259 	AutoWriteLocker locker(fWindowLock);
2260 	BAutolock tokenLocker(BPrivate::gDefaultTokens);
2261 
2262 	::ServerWindow* window;
2263 	if (BPrivate::gDefaultTokens.GetToken(serverToken,
2264 			B_SERVER_TOKEN, (void**)&window) != B_OK) {
2265 		sender.StartMessage(B_ENTRY_NOT_FOUND);
2266 		sender.Flush();
2267 		return;
2268 	}
2269 
2270 	window_info info;
2271 	window->GetInfo(info);
2272 
2273 	int32 length = window->Title() ? strlen(window->Title()) : 0;
2274 
2275 	sender.StartMessage(B_OK);
2276 	sender.Attach<int32>(sizeof(window_info) + length + 1);
2277 	sender.Attach(&info, sizeof(window_info));
2278 	if (length > 0)
2279 		sender.Attach(window->Title(), length + 1);
2280 	else
2281 		sender.Attach<char>('\0');
2282 	sender.Flush();
2283 }
2284 
2285 
2286 void
2287 Desktop::MarkDirty(BRegion& region)
2288 {
2289 	if (region.CountRects() == 0)
2290 		return;
2291 
2292 	if (LockAllWindows()) {
2293 		// send redraw messages to all windows intersecting the dirty region
2294 		_TriggerWindowRedrawing(region);
2295 
2296 		UnlockAllWindows();
2297 	}
2298 }
2299 
2300 
2301 void
2302 Desktop::_RebuildClippingForAllWindows(BRegion& stillAvailableOnScreen)
2303 {
2304 	// the available region on screen starts with the entire screen area
2305 	// each window on the screen will take a portion from that area
2306 
2307 	// figure out what the entire screen area is
2308 	stillAvailableOnScreen = fScreenRegion;
2309 
2310 	// set clipping of each window
2311 	for (WindowLayer* window = _CurrentWindows().LastWindow(); window != NULL;
2312 			window = window->PreviousWindow(fCurrentWorkspace)) {
2313 		if (!window->IsHidden()) {
2314 			window->SetClipping(&stillAvailableOnScreen);
2315 			// that windows region is not available on screen anymore
2316 			stillAvailableOnScreen.Exclude(&window->VisibleRegion());
2317 		}
2318 	}
2319 }
2320 
2321 
2322 void
2323 Desktop::_TriggerWindowRedrawing(BRegion& newDirtyRegion)
2324 {
2325 	// send redraw messages to all windows intersecting the dirty region
2326 	for (WindowLayer* window = _CurrentWindows().LastWindow(); window != NULL;
2327 			window = window->PreviousWindow(fCurrentWorkspace)) {
2328 		if (!window->IsHidden()
2329 			&& newDirtyRegion.Intersects(window->VisibleRegion().Frame()))
2330 			window->ProcessDirtyRegion(newDirtyRegion);
2331 	}
2332 }
2333 
2334 
2335 void
2336 Desktop::_SetBackground(BRegion& background)
2337 {
2338 	// NOTE: the drawing operation is caried out
2339 	// in the clipping region rebuild, but it is
2340 	// ok actually, because it also avoids trails on
2341 	// moving windows
2342 
2343 	// remember the region not covered by any windows
2344 	// and redraw the dirty background
2345 	BRegion dirtyBackground(background);
2346 	dirtyBackground.Exclude(&fBackgroundRegion);
2347 	dirtyBackground.IntersectWith(&background);
2348 	fBackgroundRegion = background;
2349 	if (dirtyBackground.Frame().IsValid()) {
2350 		if (GetDrawingEngine()->LockParallelAccess()) {
2351 			GetDrawingEngine()->FillRegion(dirtyBackground,
2352 				fWorkspaces[fCurrentWorkspace].Color());
2353 
2354 			GetDrawingEngine()->UnlockParallelAccess();
2355 		}
2356 	}
2357 }
2358