xref: /haiku/src/servers/app/Desktop.cpp (revision 079c69cbfd7cd3c97baae91332251c8388a8bb02)
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 	rgb_color 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 	WindowLayer* window = _CurrentWindows().LastWindow();
1111 	if (window != NULL && window->Feel() == kMenuWindowFeel)
1112 		return &window->EventTarget();
1113 
1114 	if (FocusWindow() != NULL)
1115 		return &FocusWindow()->EventTarget();
1116 
1117 	return NULL;
1118 }
1119 
1120 
1121 bool
1122 Desktop::_WindowHasModal(WindowLayer* window)
1123 {
1124 	if (window == NULL)
1125 		return false;
1126 
1127 	for (WindowLayer* modal = fSubsetWindows.FirstWindow(); modal != NULL;
1128 			modal = modal->NextWindow(kSubsetList)) {
1129 		// only visible modal windows count
1130 		if (!modal->IsModal() || modal->IsHidden())
1131 			continue;
1132 
1133 		if (modal->HasInSubset(window))
1134 			return true;
1135 	}
1136 
1137 	return false;
1138 }
1139 
1140 
1141 /*!
1142 	You must at least hold a single window lock when calling this method.
1143 */
1144 void
1145 Desktop::_WindowChanged(WindowLayer* window)
1146 {
1147 	if (fWorkspacesLayer == NULL)
1148 		return;
1149 
1150 	fWorkspacesLayer->WindowChanged(window);
1151 }
1152 
1153 
1154 /*!
1155 	\brief Sends a fake B_MOUSE_MOVED event to the window under the mouse,
1156 		and also updates the current view under the mouse.
1157 
1158 	This has only to be done in case the view changed without user interaction,
1159 	ie. because of a workspace change or a closing window.
1160 
1161 	Windows must not be locked when calling this method.
1162 */
1163 void
1164 Desktop::_SendFakeMouseMoved(WindowLayer* window)
1165 {
1166 	BPoint where;
1167 	int32 buttons;
1168 	EventDispatcher().GetMouse(where, buttons);
1169 
1170 	int32 viewToken = B_NULL_TOKEN;
1171 	EventTarget* target = NULL;
1172 
1173 	LockAllWindows();
1174 
1175 	if (window == NULL)
1176 		window = MouseEventWindow();
1177 	if (window == NULL)
1178 		window = WindowAt(where);
1179 
1180 	if (window != NULL) {
1181 		BMessage message;
1182 		window->MouseMoved(&message, where, &viewToken, true);
1183 
1184 		if (viewToken != B_NULL_TOKEN)
1185 			target = &window->EventTarget();
1186 	}
1187 
1188 	if (viewToken != B_NULL_TOKEN)
1189 		SetViewUnderMouse(window, viewToken);
1190 	else {
1191 		SetViewUnderMouse(NULL, B_NULL_TOKEN);
1192 		SetCursor(NULL);
1193 	}
1194 
1195 	UnlockAllWindows();
1196 
1197 	if (target != NULL)
1198 		EventDispatcher().SendFakeMouseMoved(*target, viewToken);
1199 }
1200 
1201 
1202 void
1203 Desktop::SetFocusWindow(WindowLayer* focus)
1204 {
1205 	if (!LockAllWindows())
1206 		return;
1207 
1208 	bool hasModal = _WindowHasModal(focus);
1209 
1210 	// TODO: test for FFM and B_LOCK_WINDOW_FOCUS
1211 
1212 	if (focus == fFocus && focus != NULL && !focus->IsHidden()
1213 		&& (focus->Flags() & B_AVOID_FOCUS) == 0 && !hasModal) {
1214 		// the window that is supposed to get focus already has focus
1215 		UnlockAllWindows();
1216 		return;
1217 	}
1218 
1219 	if (focus == NULL || hasModal) {
1220 		focus = FrontWindow();
1221 		if (focus == NULL) {
1222 			// there might be no front window in case of only a single
1223 			// window with B_FLOATING_ALL_WINDOW_FEEL
1224 			focus = _CurrentWindows().LastWindow();
1225 		}
1226 	}
1227 
1228 	// make sure no window is chosen that doesn't want focus or cannot have it
1229 	while (focus != NULL
1230 		&& ((focus->Flags() & B_AVOID_FOCUS) != 0
1231 			|| _WindowHasModal(focus)
1232 			|| focus->IsHidden())) {
1233 		focus = focus->PreviousWindow(fCurrentWorkspace);
1234 	}
1235 
1236 	if (fFocus == focus) {
1237 		// turns out the window that is supposed to get focus now already has it
1238 		UnlockAllWindows();
1239 		return;
1240 	}
1241 
1242 	team_id oldActiveApp = -1;
1243 	team_id newActiveApp = -1;
1244 
1245 	if (fFocus != NULL) {
1246 		fFocus->SetFocus(false);
1247 		oldActiveApp = fFocus->ServerWindow()->App()->ClientTeam();
1248 	}
1249 
1250 	fFocus = focus;
1251 
1252 	if (fFocus != NULL) {
1253 		fFocus->SetFocus(true);
1254 		newActiveApp = fFocus->ServerWindow()->App()->ClientTeam();
1255 	}
1256 
1257 	if (newActiveApp == -1) {
1258 		// make sure the cursor is visible
1259 		HWInterface()->SetCursorVisible(true);
1260 	}
1261 
1262 	UnlockAllWindows();
1263 
1264 	// change the "active" app if appropriate
1265 	if (oldActiveApp == newActiveApp)
1266 		return;
1267 
1268 	BAutolock locker(fApplicationsLock);
1269 
1270 	for (int32 i = 0; i < fApplications.CountItems(); i++) {
1271 		ServerApp *app = fApplications.ItemAt(i);
1272 
1273 		if (oldActiveApp != -1 && app->ClientTeam() == oldActiveApp)
1274 			app->Activate(false);
1275 		else if (newActiveApp != -1 && app->ClientTeam() == newActiveApp)
1276 			app->Activate(true);
1277 	}
1278 }
1279 
1280 
1281 void
1282 Desktop::_BringWindowsToFront(WindowList& windows, int32 list,
1283 	bool wereVisible)
1284 {
1285 	// we don't need to redraw what is currently
1286 	// visible of the window
1287 	BRegion clean;
1288 
1289 	for (WindowLayer* window = windows.FirstWindow(); window != NULL;
1290 			window = window->NextWindow(list)) {
1291 		if (wereVisible)
1292 			clean.Include(&window->VisibleRegion());
1293 
1294 		_CurrentWindows().AddWindow(window,
1295 			window->Frontmost(_CurrentWindows().FirstWindow(),
1296 				fCurrentWorkspace));
1297 
1298 		_WindowChanged(window);
1299 	}
1300 
1301 	BRegion dummy;
1302 	_RebuildClippingForAllWindows(dummy);
1303 
1304 	// redraw what became visible of the window(s)
1305 
1306 	BRegion dirty;
1307 	for (WindowLayer* window = windows.FirstWindow(); window != NULL;
1308 			window = window->NextWindow(list)) {
1309 		dirty.Include(&window->VisibleRegion());
1310 	}
1311 
1312 	dirty.Exclude(&clean);
1313 	MarkDirty(dirty);
1314 
1315 	_UpdateFront();
1316 
1317 	if (windows.FirstWindow() == fBack || fBack == NULL)
1318 		_UpdateBack();
1319 }
1320 
1321 
1322 /*!
1323 	\brief Tries to move the specified window to the front of the screen,
1324 		and make it the focus window.
1325 
1326 	If there are any modal windows on this screen, it might not actually
1327 	become the frontmost window, though, as modal windows stay in front
1328 	of their subset.
1329 */
1330 void
1331 Desktop::ActivateWindow(WindowLayer* window)
1332 {
1333 	STRACE(("ActivateWindow(%p, %s)\n", window, window ? window->Title() : "<none>"));
1334 
1335 	if (window == NULL) {
1336 		fBack = NULL;
1337 		fFront = NULL;
1338 		return;
1339 	}
1340 
1341 	bool windowOnOtherWorkspace = !window->InWorkspace(fCurrentWorkspace);
1342 
1343 	if (windowOnOtherWorkspace
1344 		&& (window->Flags() & B_NO_WORKSPACE_ACTIVATION)
1345 		&& !(window->Flags() & B_NOT_ANCHORED_ON_ACTIVATE))
1346 		return;
1347 
1348 	// TODO: take care about floating windows
1349 
1350 	if (!LockAllWindows())
1351 		return;
1352 
1353 	if (windowOnOtherWorkspace) {
1354 		// if the window wants to come to the current workspace,
1355 		// do so here - else activate the workspace on which this
1356 		// window is
1357 		if (window->Flags() & B_NOT_ANCHORED_ON_ACTIVATE) {
1358 			// bring the window to the current workspace
1359 			// TODO: what if this window is on multiple workspaces?!?
1360 			uint32 workspaces = workspace_to_workspaces(fCurrentWorkspace);
1361 			SetWindowWorkspaces(window, workspaces);
1362 		} else {
1363 			// switch to the workspace on which this window is
1364 			// (we'll take the first one that the window is on)
1365 			uint32 workspaces = window->Workspaces();
1366 			for (int32 i = 0; i < 32; i++) {
1367 				uint32 workspace = workspace_to_workspaces(i);
1368 				if (workspaces & workspace) {
1369 					SetWorkspace(i);
1370 					break;
1371 				}
1372 			}
1373 		}
1374 	}
1375 
1376 	if (window == FrontWindow()) {
1377 		// see if there is a normal B_AVOID_FRONT window still in front of us
1378 		WindowLayer* avoidsFront = window->NextWindow(fCurrentWorkspace);
1379 		while (avoidsFront && avoidsFront->IsNormal()
1380 			&& (avoidsFront->Flags() & B_AVOID_FRONT) == 0) {
1381 			avoidsFront = avoidsFront->NextWindow(fCurrentWorkspace);
1382 		}
1383 
1384 		if (avoidsFront == NULL) {
1385 			// we're already the frontmost window, we might just not have focus yet
1386 			if ((window->Flags() & B_AVOID_FOCUS) == 0)
1387 				SetFocusWindow(window);
1388 
1389 			UnlockAllWindows();
1390 			return;
1391 		}
1392 	}
1393 
1394 	// we don't need to redraw what is currently
1395 	// visible of the window
1396 	BRegion clean(window->VisibleRegion());
1397 	WindowList windows(kWorkingList);
1398 
1399 	WindowLayer* frontmost = window->Frontmost();
1400 
1401 	_CurrentWindows().RemoveWindow(window);
1402 	windows.AddWindow(window);
1403 
1404 	if (frontmost != NULL && frontmost->IsModal()) {
1405 		// all modal windows follow their subsets to the front
1406 		// (ie. they are staying in front of them, but they are
1407 		// not supposed to change their order because of that)
1408 
1409 		WindowLayer* nextModal;
1410 		for (WindowLayer* modal = frontmost; modal != NULL; modal = nextModal) {
1411 			// get the next modal window
1412 			nextModal = modal->NextWindow(fCurrentWorkspace);
1413 			while (nextModal != NULL && !nextModal->IsModal()) {
1414 				nextModal = nextModal->NextWindow(fCurrentWorkspace);
1415 			}
1416 			if (nextModal != NULL && !nextModal->HasInSubset(window))
1417 				nextModal = NULL;
1418 
1419 			_CurrentWindows().RemoveWindow(modal);
1420 			windows.AddWindow(modal);
1421 		}
1422 	}
1423 
1424 	_BringWindowsToFront(windows, kWorkingList, true);
1425 	if ((window->Flags() & B_AVOID_FOCUS) == 0)
1426 		SetFocusWindow(window);
1427 
1428 	UnlockAllWindows();
1429 }
1430 
1431 
1432 void
1433 Desktop::SendWindowBehind(WindowLayer* window, WindowLayer* behindOf)
1434 {
1435 	// TODO: should the "not in current workspace" be handled anyway?
1436 	//	(the code below would have to be changed then, though)
1437 	if (window == BackWindow()
1438 		|| !window->InWorkspace(fCurrentWorkspace)
1439 		|| (behindOf != NULL && !behindOf->InWorkspace(fCurrentWorkspace))
1440 		|| !LockAllWindows())
1441 		return;
1442 
1443 	// Is this a valid behindOf window?
1444 	if (behindOf != NULL && window->HasInSubset(behindOf))
1445 		behindOf = NULL;
1446 
1447 	// what is currently visible of the window
1448 	// might be dirty after the window is send to back
1449 	BRegion dirty(window->VisibleRegion());
1450 
1451 	// detach window and re-attach at desired position
1452 	WindowLayer* backmost = window->Backmost(behindOf);
1453 
1454 	_CurrentWindows().RemoveWindow(window);
1455 	_CurrentWindows().AddWindow(window, backmost
1456 		? backmost->NextWindow(fCurrentWorkspace) : BackWindow());
1457 
1458 	BRegion dummy;
1459 	_RebuildClippingForAllWindows(dummy);
1460 
1461 	// mark everything dirty that is no longer visible
1462 	BRegion clean(window->VisibleRegion());
1463 	dirty.Exclude(&clean);
1464 	MarkDirty(dirty);
1465 
1466 	_UpdateFronts();
1467 	SetFocusWindow(_CurrentWindows().LastWindow());
1468 	_WindowChanged(window);
1469 
1470 	UnlockAllWindows();
1471 }
1472 
1473 
1474 void
1475 Desktop::ShowWindow(WindowLayer* window)
1476 {
1477 	if (!window->IsHidden())
1478 		return;
1479 
1480 	LockAllWindows();
1481 
1482 	window->SetHidden(false);
1483 
1484 	if (window->InWorkspace(fCurrentWorkspace)) {
1485 		_ShowWindow(window, true);
1486 		_UpdateSubsetWorkspaces(window);
1487 		ActivateWindow(window);
1488 	} else {
1489 		// then we don't need to send the fake mouse event either
1490 		_WindowChanged(window);
1491 		UnlockAllWindows();
1492 		return;
1493 	}
1494 
1495 	if (WorkspacesLayer* layer = dynamic_cast<WorkspacesLayer*>(window->TopLayer()))
1496 		fWorkspacesLayer = layer;
1497 
1498 	UnlockAllWindows();
1499 
1500 	// If the mouse cursor is directly over the newly visible window,
1501 	// we'll send a fake mouse moved message to the window, so that
1502 	// it knows the mouse is over it.
1503 
1504 	_SendFakeMouseMoved(window);
1505 }
1506 
1507 
1508 void
1509 Desktop::HideWindow(WindowLayer* window)
1510 {
1511 	if (window->IsHidden())
1512 		return;
1513 
1514 	if (!LockAllWindows())
1515 		return;
1516 
1517 	window->SetHidden(true);
1518 	if (fMouseEventWindow == window)
1519 		fMouseEventWindow = NULL;
1520 
1521 	if (window->InWorkspace(fCurrentWorkspace)) {
1522 		_UpdateSubsetWorkspaces(window);
1523 		_HideWindow(window);
1524 		_UpdateFronts();
1525 
1526 		if (FocusWindow() == window)
1527 			SetFocusWindow(_CurrentWindows().LastWindow());
1528 	} else
1529 		_WindowChanged(window);
1530 
1531 	if (fWorkspacesLayer != NULL)
1532 		fWorkspacesLayer->WindowRemoved(window);
1533 
1534 	if (dynamic_cast<WorkspacesLayer*>(window->TopLayer()) != NULL)
1535 		fWorkspacesLayer = NULL;
1536 
1537 	UnlockAllWindows();
1538 
1539 	if (window == fWindowUnderMouse)
1540 		_SendFakeMouseMoved();
1541 }
1542 
1543 
1544 /*!
1545 	Shows the window on the screen - it does this independently of the
1546 	WindowLayer::IsHidden() state.
1547 */
1548 void
1549 Desktop::_ShowWindow(WindowLayer* window, bool affectsOtherWindows)
1550 {
1551 	BRegion background;
1552 	_RebuildClippingForAllWindows(background);
1553 	_SetBackground(background);
1554 	_WindowChanged(window);
1555 
1556 	BRegion dirty(window->VisibleRegion());
1557 
1558 	if (!affectsOtherWindows) {
1559 		// everything that is now visible in the
1560 		// window needs a redraw, but other windows
1561 		// are not affected, we can call ProcessDirtyRegion()
1562 		// of the window, and don't have to use MarkDirty()
1563 		window->ProcessDirtyRegion(dirty);
1564 	} else
1565 		MarkDirty(dirty);
1566 }
1567 
1568 
1569 /*!
1570 	Hides the window from the screen - it does this independently of the
1571 	WindowLayer::IsHidden() state.
1572 */
1573 void
1574 Desktop::_HideWindow(WindowLayer* window)
1575 {
1576 	// after rebuilding the clipping,
1577 	// this window will not have a visible
1578 	// region anymore, so we need to remember
1579 	// it now
1580 	// (actually that's not true, since
1581 	// hidden windows are excluded from the
1582 	// clipping calculation, but anyways)
1583 	BRegion dirty(window->VisibleRegion());
1584 
1585 	BRegion background;
1586 	_RebuildClippingForAllWindows(background);
1587 	_SetBackground(background);
1588 	_WindowChanged(window);
1589 
1590 	MarkDirty(dirty);
1591 }
1592 
1593 
1594 void
1595 Desktop::MoveWindowBy(WindowLayer* window, float x, float y, int32 workspace)
1596 {
1597 	if (!LockAllWindows())
1598 		return;
1599 
1600 	if (workspace == -1)
1601 		workspace = fCurrentWorkspace;
1602 
1603 	if (!window->IsVisible() || workspace != fCurrentWorkspace) {
1604 		if (workspace != fCurrentWorkspace) {
1605 			// move the window on another workspace - this doesn't change it's
1606 			// current position
1607 			if (window->Anchor(workspace).position == kInvalidWindowPosition)
1608 				window->Anchor(workspace).position = window->Frame().LeftTop();
1609 
1610 			window->Anchor(workspace).position += BPoint(x, y);
1611 			_WindowChanged(window);
1612 		} else
1613 			window->MoveBy(x, y);
1614 
1615 		UnlockAllWindows();
1616 		return;
1617 	}
1618 
1619 	// the dirty region starts with the visible area of the window being moved
1620 	BRegion newDirtyRegion(window->VisibleRegion());
1621 
1622 	// no more drawing for DirectWindows
1623 	window->ServerWindow()->HandleDirectConnection(B_DIRECT_STOP);
1624 
1625 	window->MoveBy(x, y);
1626 
1627 	BRegion background;
1628 	_RebuildClippingForAllWindows(background);
1629 
1630 	// construct the region that is possible to be blitted
1631 	// to move the contents of the window
1632 	BRegion copyRegion(window->VisibleRegion());
1633 	copyRegion.OffsetBy(-x, -y);
1634 	copyRegion.IntersectWith(&newDirtyRegion);
1635 		// newDirtyRegion == the windows old visible region
1636 
1637 	// include the the new visible region of the window being
1638 	// moved into the dirty region (for now)
1639 	newDirtyRegion.Include(&window->VisibleRegion());
1640 
1641 	GetDrawingEngine()->CopyRegion(&copyRegion, x, y);
1642 
1643 	// allow DirectWindows to draw again after the visual
1644 	// content is at the new location
1645 	window->ServerWindow()->HandleDirectConnection(B_DIRECT_START | B_BUFFER_MOVED);
1646 
1647 	// in the dirty region, exclude the parts that we
1648 	// could move by blitting
1649 	copyRegion.OffsetBy(x, y);
1650 	newDirtyRegion.Exclude(&copyRegion);
1651 
1652 	MarkDirty(newDirtyRegion);
1653 	_SetBackground(background);
1654 	_WindowChanged(window);
1655 
1656 	UnlockAllWindows();
1657 }
1658 
1659 
1660 void
1661 Desktop::ResizeWindowBy(WindowLayer* window, float x, float y)
1662 {
1663 	if (!LockAllWindows())
1664 		return;
1665 
1666 	if (!window->IsVisible()) {
1667 		window->ResizeBy(x, y, NULL);
1668 		UnlockAllWindows();
1669 		return;
1670 	}
1671 
1672 	// the dirty region for the inside of the window is
1673 	// constructed by the window itself in ResizeBy()
1674 	BRegion newDirtyRegion;
1675 	// track the dirty region outside the window in case
1676 	// it is shrunk in "previouslyOccupiedRegion"
1677 	BRegion previouslyOccupiedRegion(window->VisibleRegion());
1678 
1679 	window->ResizeBy(x, y, &newDirtyRegion);
1680 
1681 	BRegion background;
1682 	_RebuildClippingForAllWindows(background);
1683 
1684 	// we just care for the region outside the window
1685 	previouslyOccupiedRegion.Exclude(&window->VisibleRegion());
1686 
1687 	// make sure the window cannot mark stuff dirty outside
1688 	// its visible region...
1689 	newDirtyRegion.IntersectWith(&window->VisibleRegion());
1690 	// ...because we do this outself
1691 	newDirtyRegion.Include(&previouslyOccupiedRegion);
1692 
1693 	MarkDirty(newDirtyRegion);
1694 	_SetBackground(background);
1695 	_WindowChanged(window);
1696 
1697 	UnlockAllWindows();
1698 }
1699 
1700 
1701 bool
1702 Desktop::SetWindowTabLocation(WindowLayer* window, float location)
1703 {
1704 	if (!LockAllWindows())
1705 		return false;
1706 
1707 	BRegion dirty;
1708 	bool changed = window->SetTabLocation(location, dirty);
1709 
1710 	if (changed && window->IsVisible() && dirty.CountRects() > 0) {
1711 		BRegion stillAvailableOnScreen;
1712 		_RebuildClippingForAllWindows(stillAvailableOnScreen);
1713 		_SetBackground(stillAvailableOnScreen);
1714 
1715 		_WindowChanged(window);
1716 		_TriggerWindowRedrawing(dirty);
1717 	}
1718 
1719 	UnlockAllWindows();
1720 
1721 	return changed;
1722 }
1723 
1724 
1725 bool
1726 Desktop::SetWindowDecoratorSettings(WindowLayer* window,
1727 									const BMessage& settings)
1728 {
1729 	// TODO: almost exact code duplication to above function...
1730 
1731 	if (!LockAllWindows())
1732 		return false;
1733 
1734 	BRegion dirty;
1735 	bool changed = window->SetDecoratorSettings(settings, dirty);
1736 
1737 	if (changed && window->IsVisible() && dirty.CountRects() > 0) {
1738 		BRegion stillAvailableOnScreen;
1739 		_RebuildClippingForAllWindows(stillAvailableOnScreen);
1740 		_SetBackground(stillAvailableOnScreen);
1741 
1742 		_TriggerWindowRedrawing(dirty);
1743 	}
1744 
1745 	UnlockAllWindows();
1746 
1747 	return changed;
1748 }
1749 
1750 
1751 /*!
1752 	Updates the workspaces of all subset windows with regard to the
1753 	specifed window.
1754 	If newIndex is not -1, it will move all subset windows that belong to
1755 	the specifed window to the new workspace; this form is only called by
1756 	SetWorkspace().
1757 */
1758 void
1759 Desktop::_UpdateSubsetWorkspaces(WindowLayer* window, int32 previousIndex,
1760 	int32 newIndex)
1761 {
1762 	STRACE(("_UpdateSubsetWorkspaces(window %p, %s)\n", window, window->Title()));
1763 
1764 	// if the window is hidden, the subset windows are up-to-date already
1765 	if (!window->IsNormal() || window->IsHidden())
1766 		return;
1767 
1768 	for (WindowLayer* subset = fSubsetWindows.FirstWindow(); subset != NULL;
1769 			subset = subset->NextWindow(kSubsetList)) {
1770 		if (subset->Feel() == B_MODAL_ALL_WINDOW_FEEL
1771 			|| subset->Feel() == B_FLOATING_ALL_WINDOW_FEEL) {
1772 			// These windows are always visible on all workspaces,
1773 			// no need to update them.
1774 			continue;
1775 		}
1776 
1777 		if (subset->IsFloating()) {
1778 			// Floating windows are inserted and removed to the current
1779 			// workspace as the need arises - they are not handled here
1780 			// but in _UpdateFront()
1781 			continue;
1782 		}
1783 
1784 		if (subset->HasInSubset(window)) {
1785 			// adopt the workspace change
1786 			if (newIndex != -1) {
1787 				_Windows(newIndex).AddWindow(subset);
1788 				_Windows(previousIndex).RemoveWindow(subset);
1789 			} else
1790 				SetWindowWorkspaces(subset, subset->SubsetWorkspaces());
1791 		}
1792 	}
1793 }
1794 
1795 
1796 /*!
1797 	\brief Adds or removes the window to or from the workspaces it's on.
1798 */
1799 void
1800 Desktop::_ChangeWindowWorkspaces(WindowLayer* window, uint32 oldWorkspaces,
1801 	uint32 newWorkspaces)
1802 {
1803 	// apply changes to the workspaces' window lists
1804 
1805 	LockAllWindows();
1806 
1807 	for (int32 i = 0; i < kMaxWorkspaces; i++) {
1808 		if (workspace_in_workspaces(i, oldWorkspaces)) {
1809 			// window is on this workspace, is it anymore?
1810 			if (!workspace_in_workspaces(i, newWorkspaces)) {
1811 				_Windows(i).RemoveWindow(window);
1812 
1813 				if (i == CurrentWorkspace()) {
1814 					// remove its appearance from the current workspace
1815 					window->SetCurrentWorkspace(-1);
1816 
1817 					if (!window->IsHidden())
1818 						_HideWindow(window);
1819 				}
1820 			}
1821 		} else {
1822 			// window was not on this workspace, is it now?
1823 			if (workspace_in_workspaces(i, newWorkspaces)) {
1824 				_Windows(i).AddWindow(window,
1825 					window->Frontmost(_Windows(i).FirstWindow(), i));
1826 
1827 				if (i == CurrentWorkspace()) {
1828 					// make the window visible in current workspace
1829 					window->SetCurrentWorkspace(fCurrentWorkspace);
1830 
1831 					if (!window->IsHidden()) {
1832 						// this only affects other windows if this windows has floating or
1833 						// modal windows that need to be shown as well
1834 						// TODO: take care of this
1835 						_ShowWindow(window, FrontWindow() == window);
1836 					}
1837 				}
1838 			}
1839 		}
1840 	}
1841 
1842 	// take care about modals and floating windows
1843 	_UpdateSubsetWorkspaces(window);
1844 
1845 	UnlockAllWindows();
1846 }
1847 
1848 
1849 void
1850 Desktop::SetWindowWorkspaces(WindowLayer* window, uint32 workspaces)
1851 {
1852 	LockAllWindows();
1853 
1854 	if (window->IsNormal() && workspaces == B_CURRENT_WORKSPACE)
1855 		workspaces = workspace_to_workspaces(CurrentWorkspace());
1856 
1857 	uint32 oldWorkspaces = window->Workspaces();
1858 
1859 	window->WorkspacesChanged(oldWorkspaces, workspaces);
1860 	_ChangeWindowWorkspaces(window, oldWorkspaces, workspaces);
1861 
1862 	UnlockAllWindows();
1863 }
1864 
1865 
1866 /*!	\brief Adds the window to the desktop.
1867 	At this point, the window is still hidden and must be shown explicetly
1868 	via ShowWindow().
1869 */
1870 void
1871 Desktop::AddWindow(WindowLayer *window)
1872 {
1873 	LockAllWindows();
1874 
1875 	fAllWindows.AddWindow(window);
1876 	if (!window->IsNormal())
1877 		fSubsetWindows.AddWindow(window);
1878 
1879 	if (window->IsNormal()) {
1880 		if (window->Workspaces() == B_CURRENT_WORKSPACE)
1881 			window->SetWorkspaces(workspace_to_workspaces(CurrentWorkspace()));
1882 	} else {
1883 		// subset windows are visible on all workspaces their subset is on
1884 		window->SetWorkspaces(window->SubsetWorkspaces());
1885 	}
1886 
1887 	_ChangeWindowWorkspaces(window, 0, window->Workspaces());
1888 	UnlockAllWindows();
1889 }
1890 
1891 
1892 void
1893 Desktop::RemoveWindow(WindowLayer *window)
1894 {
1895 	LockAllWindows();
1896 
1897 	if (!window->IsHidden())
1898 		HideWindow(window);
1899 
1900 	fAllWindows.RemoveWindow(window);
1901 	if (!window->IsNormal())
1902 		fSubsetWindows.RemoveWindow(window);
1903 
1904 	_ChangeWindowWorkspaces(window, window->Workspaces(), 0);
1905 	UnlockAllWindows();
1906 
1907 	// make sure this window won't get any events anymore
1908 
1909 	EventDispatcher().RemoveTarget(window->EventTarget());
1910 }
1911 
1912 
1913 bool
1914 Desktop::AddWindowToSubset(WindowLayer* subset, WindowLayer* window)
1915 {
1916 	if (!subset->AddToSubset(window))
1917 		return false;
1918 
1919 	_ChangeWindowWorkspaces(subset, subset->Workspaces(), subset->SubsetWorkspaces());
1920 	return true;
1921 }
1922 
1923 
1924 void
1925 Desktop::RemoveWindowFromSubset(WindowLayer* subset, WindowLayer* window)
1926 {
1927 	subset->RemoveFromSubset(window);
1928 	_ChangeWindowWorkspaces(subset, subset->Workspaces(), subset->SubsetWorkspaces());
1929 }
1930 
1931 
1932 void
1933 Desktop::SetWindowLook(WindowLayer *window, window_look newLook)
1934 {
1935 	if (window->Look() == newLook)
1936 		return;
1937 
1938 	if (!LockAllWindows())
1939 		return;
1940 
1941 	BRegion dirty;
1942 	window->SetLook(newLook, &dirty);
1943 		// TODO: test what happens when the window
1944 		// finds out it needs to resize itself...
1945 
1946 	BRegion stillAvailableOnScreen;
1947 	_RebuildClippingForAllWindows(stillAvailableOnScreen);
1948 	_SetBackground(stillAvailableOnScreen);
1949 	_WindowChanged(window);
1950 
1951 	_TriggerWindowRedrawing(dirty);
1952 
1953 	UnlockAllWindows();
1954 }
1955 
1956 
1957 void
1958 Desktop::SetWindowFeel(WindowLayer *window, window_feel newFeel)
1959 {
1960 	if (window->Feel() == newFeel)
1961 		return;
1962 
1963 	LockAllWindows();
1964 
1965 	bool wasNormal = window->IsNormal();
1966 
1967 	window->SetFeel(newFeel);
1968 
1969 	// move the window out of or into the subset window list as needed
1970 	if (window->IsNormal() && !wasNormal)
1971 		fSubsetWindows.RemoveWindow(window);
1972 	else if (!window->IsNormal() && wasNormal)
1973 		fSubsetWindows.AddWindow(window);
1974 
1975 	// A normal window that was once a floating or modal window will
1976 	// adopt the window's current workspaces
1977 
1978 	if (!window->IsNormal())
1979 		_ChangeWindowWorkspaces(window, window->Workspaces(), window->SubsetWorkspaces());
1980 
1981 	// make sure the window has the correct position in the window lists
1982 	//	(ie. all floating windows have to be on the top, ...)
1983 
1984 	for (int32 i = 0; i < kMaxWorkspaces; i++) {
1985 		if (!workspace_in_workspaces(i, window->Workspaces()))
1986 			continue;
1987 
1988 		bool changed = false;
1989 		BRegion visibleBefore;
1990 		if (i == fCurrentWorkspace && window->IsVisible())
1991 			visibleBefore = window->VisibleRegion();
1992 
1993 		WindowLayer* backmost = window->Backmost(_Windows(i).LastWindow(), i);
1994 		if (backmost != NULL) {
1995 			// check if the backmost window is really behind it
1996 			WindowLayer* previous = window->PreviousWindow(i);
1997 			while (previous != NULL) {
1998 				if (previous == backmost)
1999 					break;
2000 
2001 				previous = previous->PreviousWindow(i);
2002 			}
2003 
2004 			if (previous == NULL) {
2005 				// need to reinsert window before its backmost window
2006 				_Windows(i).RemoveWindow(window);
2007 				_Windows(i).AddWindow(window, backmost->NextWindow(i));
2008 				changed = true;
2009 			}
2010 		}
2011 
2012 		WindowLayer* frontmost = window->Frontmost(_Windows(i).FirstWindow(), i);
2013 		if (frontmost != NULL) {
2014 			// check if the frontmost window is really in front of it
2015 			WindowLayer* next = window->NextWindow(i);
2016 			while (next != NULL) {
2017 				if (next == frontmost)
2018 					break;
2019 
2020 				next = next->NextWindow(i);
2021 			}
2022 
2023 			if (next == NULL) {
2024 				// need to reinsert window behind its frontmost window
2025 				_Windows(i).RemoveWindow(window);
2026 				_Windows(i).AddWindow(window, frontmost);
2027 				changed = true;
2028 			}
2029 		}
2030 
2031 		if (i == fCurrentWorkspace && changed) {
2032 			BRegion dummy;
2033 			_RebuildClippingForAllWindows(dummy);
2034 
2035 			// mark everything dirty that is no longer visible, or
2036 			// is now visible and wasn't before
2037 			BRegion visibleAfter(window->VisibleRegion());
2038 			BRegion dirty(visibleAfter);
2039 			dirty.Exclude(&visibleBefore);
2040 			visibleBefore.Exclude(&visibleAfter);
2041 			dirty.Include(&visibleBefore);
2042 
2043 			MarkDirty(dirty);
2044 		}
2045 	}
2046 
2047 	_UpdateFronts();
2048 
2049 	if (window == FocusWindow() && !window->IsVisible())
2050 		SetFocusWindow(_CurrentWindows().LastWindow());
2051 
2052 	UnlockAllWindows();
2053 }
2054 
2055 
2056 void
2057 Desktop::SetWindowFlags(WindowLayer *window, uint32 newFlags)
2058 {
2059 	if (window->Flags() == newFlags)
2060 		return;
2061 
2062 	if (!LockAllWindows())
2063 		return;
2064 
2065 	BRegion dirty;
2066 	window->SetFlags(newFlags, &dirty);
2067 		// TODO: test what happens when the window
2068 		// finds out it needs to resize itself...
2069 
2070 	BRegion stillAvailableOnScreen;
2071 	_RebuildClippingForAllWindows(stillAvailableOnScreen);
2072 	_SetBackground(stillAvailableOnScreen);
2073 	_WindowChanged(window);
2074 
2075 	_TriggerWindowRedrawing(dirty);
2076 
2077 	UnlockAllWindows();
2078 }
2079 
2080 
2081 void
2082 Desktop::SetWindowTitle(WindowLayer *window, const char* title)
2083 {
2084 	if (!LockAllWindows())
2085 		return;
2086 
2087 	BRegion dirty;
2088 	window->SetTitle(title, dirty);
2089 
2090 	if (window->IsVisible() && dirty.CountRects() > 0) {
2091 		BRegion stillAvailableOnScreen;
2092 		_RebuildClippingForAllWindows(stillAvailableOnScreen);
2093 		_SetBackground(stillAvailableOnScreen);
2094 
2095 		_TriggerWindowRedrawing(dirty);
2096 	}
2097 
2098 	UnlockAllWindows();
2099 }
2100 
2101 
2102 /*!
2103 	Returns the window under the mouse cursor.
2104 	You need to have acquired the All Windows lock when calling this method.
2105 */
2106 WindowLayer*
2107 Desktop::WindowAt(BPoint where)
2108 {
2109 	for (WindowLayer* window = _CurrentWindows().LastWindow(); window;
2110 			window = window->PreviousWindow(fCurrentWorkspace)) {
2111 		if (window->IsVisible() && window->VisibleRegion().Contains(where))
2112 			return window;
2113 	}
2114 
2115 	return NULL;
2116 }
2117 
2118 
2119 void
2120 Desktop::SetMouseEventWindow(WindowLayer* window)
2121 {
2122 	fMouseEventWindow = window;
2123 }
2124 
2125 
2126 void
2127 Desktop::SetViewUnderMouse(const WindowLayer* window, int32 viewToken)
2128 {
2129 	fWindowUnderMouse = window;
2130 	fViewUnderMouse = viewToken;
2131 }
2132 
2133 
2134 int32
2135 Desktop::ViewUnderMouse(const WindowLayer* window)
2136 {
2137 	if (window != NULL && fWindowUnderMouse == window)
2138 		return fViewUnderMouse;
2139 
2140 	return B_NULL_TOKEN;
2141 }
2142 
2143 
2144 WindowLayer *
2145 Desktop::FindWindowLayerByClientToken(int32 token, team_id teamID)
2146 {
2147 	for (WindowLayer *window = fAllWindows.FirstWindow(); window != NULL;
2148 			window = window->NextWindow(kAllWindowList)) {
2149 		if (window->ServerWindow()->ClientToken() == token
2150 			&& window->ServerWindow()->ClientTeam() == teamID) {
2151 			return window;
2152 		}
2153 	}
2154 
2155 	return NULL;
2156 }
2157 
2158 
2159 void
2160 Desktop::MinimizeApplication(team_id team)
2161 {
2162 	AutoWriteLocker locker(fWindowLock);
2163 
2164 	// Just minimize all windows of that application
2165 
2166 	for (WindowLayer *window = fAllWindows.FirstWindow(); window != NULL;
2167 			window = window->NextWindow(kAllWindowList)) {
2168 		if (window->ServerWindow()->ClientTeam() != team)
2169 			continue;
2170 
2171 		window->ServerWindow()->NotifyMinimize(true);
2172 	}
2173 }
2174 
2175 
2176 void
2177 Desktop::BringApplicationToFront(team_id team)
2178 {
2179 	AutoWriteLocker locker(fWindowLock);
2180 
2181 	// TODO: for now, just maximize all windows of that application
2182 
2183 	for (WindowLayer *window = fAllWindows.FirstWindow(); window != NULL;
2184 			window = window->NextWindow(kAllWindowList)) {
2185 		if (window->ServerWindow()->ClientTeam() != team)
2186 			continue;
2187 
2188 		window->ServerWindow()->NotifyMinimize(false);
2189 	}
2190 }
2191 
2192 
2193 void
2194 Desktop::WindowAction(int32 windowToken, int32 action)
2195 {
2196 	if (action != B_MINIMIZE_WINDOW && action != B_BRING_TO_FRONT)
2197 		return;
2198 
2199 	LockAllWindows();
2200 
2201 	::ServerWindow* serverWindow;
2202 	WindowLayer* window;
2203 	if (BPrivate::gDefaultTokens.GetToken(windowToken,
2204 			B_SERVER_TOKEN, (void**)&serverWindow) != B_OK
2205 		|| (window = serverWindow->Window()) == NULL) {
2206 		UnlockAllWindows();
2207 		return;
2208 	}
2209 
2210 	if (action == B_BRING_TO_FRONT
2211 		&& !window->IsMinimized()) {
2212 		// the window is visible, we just need to make it the front window
2213 		ActivateWindow(window);
2214 	} else
2215 		serverWindow->NotifyMinimize(action == B_MINIMIZE_WINDOW);
2216 
2217 	UnlockAllWindows();
2218 }
2219 
2220 
2221 void
2222 Desktop::WriteWindowList(team_id team, BPrivate::LinkSender& sender)
2223 {
2224 	AutoWriteLocker locker(fWindowLock);
2225 
2226 	// compute the number of windows
2227 
2228 	int32 count = 0;
2229 
2230 	for (WindowLayer *window = fAllWindows.FirstWindow(); window != NULL;
2231 			window = window->NextWindow(kAllWindowList)) {
2232 		if (team < B_OK || window->ServerWindow()->ClientTeam() == team)
2233 			count++;
2234 	}
2235 
2236 	// write list
2237 
2238 	sender.StartMessage(B_OK);
2239 	sender.Attach<int32>(count);
2240 
2241 	for (WindowLayer *window = fAllWindows.FirstWindow(); window != NULL;
2242 			window = window->NextWindow(kAllWindowList)) {
2243 		if (team >= B_OK && window->ServerWindow()->ClientTeam() != team)
2244 			continue;
2245 
2246 		sender.Attach<int32>(window->ServerWindow()->ServerToken());
2247 	}
2248 
2249 	sender.Flush();
2250 }
2251 
2252 
2253 void
2254 Desktop::WriteWindowInfo(int32 serverToken, BPrivate::LinkSender& sender)
2255 {
2256 	AutoWriteLocker locker(fWindowLock);
2257 	BAutolock tokenLocker(BPrivate::gDefaultTokens);
2258 
2259 	::ServerWindow* window;
2260 	if (BPrivate::gDefaultTokens.GetToken(serverToken,
2261 			B_SERVER_TOKEN, (void**)&window) != B_OK) {
2262 		sender.StartMessage(B_ENTRY_NOT_FOUND);
2263 		sender.Flush();
2264 		return;
2265 	}
2266 
2267 	window_info info;
2268 	window->GetInfo(info);
2269 
2270 	int32 length = window->Title() ? strlen(window->Title()) : 0;
2271 
2272 	sender.StartMessage(B_OK);
2273 	sender.Attach<int32>(sizeof(window_info) + length + 1);
2274 	sender.Attach(&info, sizeof(window_info));
2275 	if (length > 0)
2276 		sender.Attach(window->Title(), length + 1);
2277 	else
2278 		sender.Attach<char>('\0');
2279 	sender.Flush();
2280 }
2281 
2282 
2283 void
2284 Desktop::MarkDirty(BRegion& region)
2285 {
2286 	if (region.CountRects() == 0)
2287 		return;
2288 
2289 	if (LockAllWindows()) {
2290 		// send redraw messages to all windows intersecting the dirty region
2291 		_TriggerWindowRedrawing(region);
2292 
2293 		UnlockAllWindows();
2294 	}
2295 }
2296 
2297 
2298 void
2299 Desktop::_RebuildClippingForAllWindows(BRegion& stillAvailableOnScreen)
2300 {
2301 	// the available region on screen starts with the entire screen area
2302 	// each window on the screen will take a portion from that area
2303 
2304 	// figure out what the entire screen area is
2305 	stillAvailableOnScreen = fScreenRegion;
2306 
2307 	// set clipping of each window
2308 	for (WindowLayer* window = _CurrentWindows().LastWindow(); window != NULL;
2309 			window = window->PreviousWindow(fCurrentWorkspace)) {
2310 		if (!window->IsHidden()) {
2311 			window->SetClipping(&stillAvailableOnScreen);
2312 			// that windows region is not available on screen anymore
2313 			stillAvailableOnScreen.Exclude(&window->VisibleRegion());
2314 		}
2315 	}
2316 }
2317 
2318 
2319 void
2320 Desktop::_TriggerWindowRedrawing(BRegion& newDirtyRegion)
2321 {
2322 	// send redraw messages to all windows intersecting the dirty region
2323 	for (WindowLayer* window = _CurrentWindows().LastWindow(); window != NULL;
2324 			window = window->PreviousWindow(fCurrentWorkspace)) {
2325 		if (!window->IsHidden()
2326 			&& newDirtyRegion.Intersects(window->VisibleRegion().Frame()))
2327 			window->ProcessDirtyRegion(newDirtyRegion);
2328 	}
2329 }
2330 
2331 
2332 void
2333 Desktop::_SetBackground(BRegion& background)
2334 {
2335 	// NOTE: the drawing operation is caried out
2336 	// in the clipping region rebuild, but it is
2337 	// ok actually, because it also avoids trails on
2338 	// moving windows
2339 
2340 	// remember the region not covered by any windows
2341 	// and redraw the dirty background
2342 	BRegion dirtyBackground(background);
2343 	dirtyBackground.Exclude(&fBackgroundRegion);
2344 	dirtyBackground.IntersectWith(&background);
2345 	fBackgroundRegion = background;
2346 	if (dirtyBackground.Frame().IsValid()) {
2347 		if (GetDrawingEngine()->LockParallelAccess()) {
2348 			GetDrawingEngine()->FillRegion(dirtyBackground,
2349 				fWorkspaces[fCurrentWorkspace].Color());
2350 
2351 			GetDrawingEngine()->UnlockParallelAccess();
2352 		}
2353 	}
2354 }
2355