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