xref: /haiku/src/servers/app/Desktop.cpp (revision 9918c8295480d70f55fafb98008e23109b598871)
1 /*
2  * Copyright 2001-2020, 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  *		Andrej Spielmann, andrej.spielmann@seh.ox.ac.uk
10  *		Brecht Machiels, brecht@mos6581.org
11  *		Clemens Zeidler, haiku@clemens-zeidler.de
12  *		Ingo Weinhold, ingo_weinhold@gmx.de
13  *		Joseph Groover, looncraz@looncraz.net
14  *		Tri-Edge AI
15  *		Jacob Secunda, secundja@gmail.com
16  */
17 
18 
19 /*!	Class used to encapsulate desktop management */
20 
21 
22 #include "Desktop.h"
23 
24 #include <stdio.h>
25 #include <string.h>
26 #include <syslog.h>
27 
28 #include <Debug.h>
29 #include <debugger.h>
30 #include <DirectWindow.h>
31 #include <Entry.h>
32 #include <FindDirectory.h>
33 #include <Message.h>
34 #include <MessageFilter.h>
35 #include <Path.h>
36 #include <Region.h>
37 #include <Roster.h>
38 
39 #include <PrivateScreen.h>
40 #include <ServerProtocol.h>
41 #include <ViewPrivate.h>
42 #include <WindowInfo.h>
43 
44 #include "AppServer.h"
45 #include "ClickTarget.h"
46 #include "DecorManager.h"
47 #include "DesktopSettingsPrivate.h"
48 #include "DrawingEngine.h"
49 #include "FontManager.h"
50 #include "HWInterface.h"
51 #include "InputManager.h"
52 #include "Screen.h"
53 #include "ServerApp.h"
54 #include "ServerConfig.h"
55 #include "ServerCursor.h"
56 #include "ServerWindow.h"
57 #include "SystemPalette.h"
58 #include "WindowPrivate.h"
59 #include "Window.h"
60 #include "Workspace.h"
61 #include "WorkspacesView.h"
62 
63 #if TEST_MODE
64 #	include "EventStream.h"
65 #endif
66 
67 
68 //#define DEBUG_DESKTOP
69 #ifdef DEBUG_DESKTOP
70 #	define STRACE(a) printf a
71 #else
72 #	define STRACE(a) ;
73 #endif
74 
75 
76 static inline float
77 square_vector_length(float x, float y)
78 {
79 	return x * x + y * y;
80 }
81 
82 
83 static inline float
84 square_distance(const BPoint& a, const BPoint& b)
85 {
86 	return square_vector_length(a.x - b.x, a.y - b.y);
87 }
88 
89 
90 class KeyboardFilter : public EventFilter {
91 	public:
92 		KeyboardFilter(Desktop* desktop);
93 
94 		virtual filter_result Filter(BMessage* message, EventTarget** _target,
95 			int32* _viewToken, BMessage* latestMouseMoved);
96 		virtual void RemoveTarget(EventTarget* target);
97 
98 	private:
99 		void _UpdateFocus(int32 key, uint32 modifiers, EventTarget** _target);
100 
101 		Desktop*		fDesktop;
102 		EventTarget*	fLastFocus;
103 		bigtime_t		fTimestamp;
104 };
105 
106 
107 class MouseFilter : public EventFilter {
108 public:
109 	MouseFilter(Desktop* desktop);
110 
111 	virtual filter_result Filter(BMessage* message, EventTarget** _target,
112 		int32* _viewToken, BMessage* latestMouseMoved);
113 
114 private:
115 	Desktop*	fDesktop;
116 	int32		fLastClickButtons;
117 	int32		fLastClickModifiers;
118 	int32		fResetClickCount;
119 	BPoint		fLastClickPoint;
120 	ClickTarget	fLastClickTarget;
121 };
122 
123 
124 //	#pragma mark -
125 
126 
127 KeyboardFilter::KeyboardFilter(Desktop* desktop)
128 	:
129 	fDesktop(desktop),
130 	fLastFocus(NULL),
131 	fTimestamp(0)
132 {
133 }
134 
135 
136 void
137 KeyboardFilter::_UpdateFocus(int32 key, uint32 modifiers, EventTarget** _target)
138 {
139 	if (!fDesktop->LockSingleWindow())
140 		return;
141 
142 	EventTarget* focus = fDesktop->KeyboardEventTarget();
143 
144 #if 0
145 	bigtime_t now = system_time();
146 
147 	// TODO: this is a try to not steal focus from the current window
148 	//	in case you enter some text and a window pops up you haven't
149 	//	triggered yourself (like a pop-up window in your browser while
150 	//	you're typing a password in another window) - maybe this should
151 	//	be done differently, though (using something like B_LOCK_WINDOW_FOCUS)
152 	//	(at least B_WINDOW_ACTIVATED must be postponed)
153 
154 	if (fLastFocus == NULL
155 		|| (focus != fLastFocus && now - fTimestamp > 100000)) {
156 		// if the time span between the key presses is very short
157 		// we keep our previous focus alive - this is safe even
158 		// if the target doesn't exist anymore, as we don't reset
159 		// it, and the event focus passed in is always valid (or NULL)
160 		*_target = focus;
161 		fLastFocus = focus;
162 	}
163 #endif
164 	*_target = focus;
165 	fLastFocus = focus;
166 
167 	fDesktop->UnlockSingleWindow();
168 
169 #if 0
170 	// we always allow to switch focus after the enter key has pressed
171 	if (key == B_ENTER || modifiers == B_COMMAND_KEY
172 		|| modifiers == B_CONTROL_KEY || modifiers == B_OPTION_KEY)
173 		fTimestamp = 0;
174 	else
175 		fTimestamp = now;
176 #endif
177 }
178 
179 
180 filter_result
181 KeyboardFilter::Filter(BMessage* message, EventTarget** _target,
182 	int32* /*_viewToken*/, BMessage* /*latestMouseMoved*/)
183 {
184 	int32 key = 0;
185 	int32 modifiers = 0;
186 
187 	message->FindInt32("key", &key);
188 	message->FindInt32("modifiers", &modifiers);
189 
190 	if ((message->what == B_KEY_DOWN || message->what == B_UNMAPPED_KEY_DOWN)) {
191 		// Check for safe video mode (shift + cmd + ctrl + escape)
192 		if (key == 0x01 && (modifiers & B_COMMAND_KEY) != 0
193 			&& (modifiers & B_CONTROL_KEY) != 0
194 			&& (modifiers & B_SHIFT_KEY) != 0) {
195 			system("screenmode --fall-back &");
196 			return B_SKIP_MESSAGE;
197 		}
198 
199 		bool takeWindow = (modifiers & B_SHIFT_KEY) != 0
200 			|| fDesktop->MouseEventWindow() != NULL;
201 		if (key >= B_F1_KEY && key <= B_F12_KEY) {
202 			// workspace change
203 
204 #if !TEST_MODE
205 			if ((modifiers & (B_COMMAND_KEY | B_CONTROL_KEY | B_OPTION_KEY))
206 					== B_COMMAND_KEY)
207 #else
208 			if ((modifiers & B_CONTROL_KEY) != 0)
209 #endif
210 			{
211 				STRACE(("Set Workspace %" B_PRId32 "\n", key - 1));
212 
213 				fDesktop->SetWorkspaceAsync(key - B_F1_KEY, takeWindow);
214 				return B_SKIP_MESSAGE;
215 			}
216 		} else if (key == 0x11
217 			&& (modifiers & (B_COMMAND_KEY | B_CONTROL_KEY | B_OPTION_KEY))
218 					== B_COMMAND_KEY) {
219 			// switch to previous workspace (command + `)
220 			fDesktop->SetWorkspaceAsync(-1, takeWindow);
221 			return B_SKIP_MESSAGE;
222 		}
223 	}
224 
225 	if (message->what == B_KEY_DOWN
226 		|| message->what == B_MODIFIERS_CHANGED
227 		|| message->what == B_UNMAPPED_KEY_DOWN
228 		|| message->what == B_INPUT_METHOD_EVENT)
229 		_UpdateFocus(key, modifiers, _target);
230 
231 	return fDesktop->KeyEvent(message->what, key, modifiers);
232 }
233 
234 
235 void
236 KeyboardFilter::RemoveTarget(EventTarget* target)
237 {
238 	if (target == fLastFocus)
239 		fLastFocus = NULL;
240 }
241 
242 
243 //	#pragma mark -
244 
245 
246 MouseFilter::MouseFilter(Desktop* desktop)
247 	:
248 	fDesktop(desktop),
249 	fLastClickButtons(0),
250 	fLastClickModifiers(0),
251 	fResetClickCount(0),
252 	fLastClickPoint(),
253 	fLastClickTarget()
254 {
255 }
256 
257 
258 filter_result
259 MouseFilter::Filter(BMessage* message, EventTarget** _target, int32* _viewToken,
260 	BMessage* latestMouseMoved)
261 {
262 	BPoint where;
263 	if (message->FindPoint("where", &where) != B_OK)
264 		return B_DISPATCH_MESSAGE;
265 
266 	int32 buttons;
267 	if (message->FindInt32("buttons", &buttons) != B_OK)
268 		buttons = 0;
269 
270 	if (!fDesktop->LockAllWindows())
271 		return B_DISPATCH_MESSAGE;
272 
273 	int32 viewToken = B_NULL_TOKEN;
274 
275 	Window* window = fDesktop->MouseEventWindow();
276 	if (window == NULL)
277 		window = fDesktop->WindowAt(where);
278 
279 	if (window != NULL) {
280 		// dispatch event to the window
281 		switch (message->what) {
282 			case B_MOUSE_DOWN:
283 			{
284 				int32 windowToken = window->ServerWindow()->ServerToken();
285 
286 				// First approximation of click count validation. We reset the
287 				// click count when modifiers or pressed buttons have changed
288 				// or when we've got a different click target, or when the
289 				// previous click location is too far from the new one. We can
290 				// only check the window of the click target here; we'll recheck
291 				// after asking the window.
292 				int32 modifiers = message->FindInt32("modifiers");
293 
294 				int32 originalClickCount = message->FindInt32("clicks");
295 				if (originalClickCount <= 0)
296 					originalClickCount = 1;
297 
298 				int32 clickCount = originalClickCount;
299 				if (clickCount > 1) {
300 					if (modifiers != fLastClickModifiers
301 						|| buttons != fLastClickButtons
302 						|| !fLastClickTarget.IsValid()
303 						|| fLastClickTarget.WindowToken() != windowToken
304 						|| square_distance(where, fLastClickPoint) >= 16
305 						|| clickCount - fResetClickCount < 1) {
306 						clickCount = 1;
307 					} else
308 						clickCount -= fResetClickCount;
309 				}
310 
311 				// notify the window
312 				ClickTarget clickTarget;
313 				window->MouseDown(message, where, fLastClickTarget, clickCount,
314 					clickTarget);
315 
316 				// If the click target changed, always reset the click count.
317 				if (clickCount != 1 && clickTarget != fLastClickTarget)
318 					clickCount = 1;
319 
320 				// update our click count management attributes
321 				fResetClickCount = originalClickCount - clickCount;
322 				fLastClickTarget = clickTarget;
323 				fLastClickButtons = buttons;
324 				fLastClickModifiers = modifiers;
325 				fLastClickPoint = where;
326 
327 				// get the view token from the click target
328 				if (clickTarget.GetType() == ClickTarget::TYPE_WINDOW_CONTENTS)
329 					viewToken = clickTarget.WindowElement();
330 
331 				// update the message's "clicks" field, if necessary
332 				if (clickCount != originalClickCount) {
333 					if (message->HasInt32("clicks"))
334 						message->ReplaceInt32("clicks", clickCount);
335 					else
336 						message->AddInt32("clicks", clickCount);
337 				}
338 
339 				// notify desktop listeners
340 				fDesktop->NotifyMouseDown(window, message, where);
341 				break;
342 			}
343 
344 			case B_MOUSE_UP:
345 				window->MouseUp(message, where, &viewToken);
346 				if (buttons == 0)
347 					fDesktop->SetMouseEventWindow(NULL);
348 				fDesktop->NotifyMouseUp(window, message, where);
349 				break;
350 
351 			case B_MOUSE_MOVED:
352 				window->MouseMoved(message, where, &viewToken,
353 					latestMouseMoved == NULL || latestMouseMoved == message,
354 					false);
355 				fDesktop->NotifyMouseMoved(window, message, where);
356 				break;
357 		}
358 
359 		if (viewToken != B_NULL_TOKEN) {
360 			fDesktop->SetViewUnderMouse(window, viewToken);
361 
362 			*_viewToken = viewToken;
363 			*_target = &window->EventTarget();
364 		}
365 	} else if (message->what == B_MOUSE_DOWN) {
366 		// the mouse-down didn't hit a window -- reset the click target
367 		fResetClickCount = 0;
368 		fLastClickTarget = ClickTarget();
369 		fLastClickButtons = message->FindInt32("buttons");
370 		fLastClickModifiers = message->FindInt32("modifiers");
371 		fLastClickPoint = where;
372 	}
373 
374 	if (window == NULL || viewToken == B_NULL_TOKEN) {
375 		// mouse is not over a window or over a decorator
376 		fDesktop->SetViewUnderMouse(window, B_NULL_TOKEN);
377 		fDesktop->SetCursor(NULL);
378 
379 		*_target = NULL;
380 	}
381 
382 	fDesktop->SetLastMouseState(where, buttons, window);
383 
384 	fDesktop->NotifyMouseEvent(message);
385 
386 	fDesktop->UnlockAllWindows();
387 
388 	return B_DISPATCH_MESSAGE;
389 }
390 
391 
392 //	#pragma mark -
393 
394 
395 static inline uint32
396 workspace_to_workspaces(int32 index)
397 {
398 	return 1UL << index;
399 }
400 
401 
402 static inline bool
403 workspace_in_workspaces(int32 index, uint32 workspaces)
404 {
405 	return (workspaces & (1UL << index)) != 0;
406 }
407 
408 
409 //	#pragma mark -
410 
411 
412 Desktop::Desktop(uid_t userID, const char* targetScreen)
413 	:
414 	MessageLooper("desktop"),
415 
416 	fUserID(userID),
417 	fTargetScreen(strdup(targetScreen)),
418 	fSettings(NULL),
419 	fSharedReadOnlyArea(-1),
420 	fApplicationsLock("application list"),
421 	fShutdownSemaphore(-1),
422 	fShutdownCount(0),
423 	fScreenLock("screen lock"),
424 	fDirectScreenLock("direct screen lock"),
425 	fDirectScreenTeam(-1),
426 	fCurrentWorkspace(0),
427 	fPreviousWorkspace(0),
428 	fAllWindows(kAllWindowList),
429 	fSubsetWindows(kSubsetList),
430 	fFocusList(kFocusList),
431 	fWorkspacesViews(false),
432 
433 	fWorkspacesLock("workspaces list"),
434 	fWindowLock("window lock"),
435 
436 	fMouseEventWindow(NULL),
437 	fWindowUnderMouse(NULL),
438 	fLockedFocusWindow(NULL),
439 	fViewUnderMouse(B_NULL_TOKEN),
440 	fLastMousePosition(B_ORIGIN),
441 	fLastMouseButtons(0),
442 
443 	fFocus(NULL),
444 	fFront(NULL),
445 	fBack(NULL)
446 {
447 	memset(fLastWorkspaceFocus, 0, sizeof(fLastWorkspaceFocus));
448 
449 	char name[B_OS_NAME_LENGTH];
450 	Desktop::_GetLooperName(name, sizeof(name));
451 
452 	fMessagePort = create_port(DEFAULT_MONITOR_PORT_SIZE, name);
453 	if (fMessagePort < B_OK)
454 		return;
455 
456 	fLink.SetReceiverPort(fMessagePort);
457 
458 	// register listeners
459 	RegisterListener(&fStackAndTile);
460 
461 	const DesktopListenerList& newListeners
462 		= gDecorManager.GetDesktopListeners();
463 	for (int i = 0; i < newListeners.CountItems(); i++)
464  		RegisterListener(newListeners.ItemAt(i));
465 }
466 
467 
468 Desktop::~Desktop()
469 {
470 	delete_area(fSharedReadOnlyArea);
471 	delete_port(fMessagePort);
472 	gFontManager->DetachUser(fUserID);
473 
474 	free(fTargetScreen);
475 }
476 
477 
478 void
479 Desktop::RegisterListener(DesktopListener* listener)
480 {
481 	DesktopObservable::RegisterListener(listener, this);
482 }
483 
484 
485 /*!	This method is allowed to throw exceptions.
486 */
487 status_t
488 Desktop::Init()
489 {
490 	if (fMessagePort < B_OK)
491 		return fMessagePort;
492 
493 	// the system palette needs to be initialized before the
494 	// desktop settings, since it is used there already
495 	InitializeColorMap();
496 
497 	const size_t areaSize = B_PAGE_SIZE;
498 	char name[B_OS_NAME_LENGTH];
499 	snprintf(name, sizeof(name), "d:%d:shared read only", fUserID);
500 	fSharedReadOnlyArea = create_area(name, (void **)&fServerReadOnlyMemory,
501 		B_ANY_ADDRESS, areaSize, B_NO_LOCK, B_READ_AREA | B_WRITE_AREA | B_CLONEABLE_AREA);
502 	if (fSharedReadOnlyArea < B_OK)
503 		return fSharedReadOnlyArea;
504 
505 	gFontManager->AttachUser(fUserID);
506 
507 	fSettings.SetTo(new DesktopSettingsPrivate(fServerReadOnlyMemory));
508 
509 	for (int32 i = 0; i < kMaxWorkspaces; i++) {
510 		_Windows(i).SetIndex(i);
511 		fWorkspaces[i].RestoreConfiguration(*fSettings->WorkspacesMessage(i));
512 	}
513 
514 	fVirtualScreen.SetConfiguration(*this,
515 		fWorkspaces[0].CurrentScreenConfiguration());
516 
517 	if (fVirtualScreen.HWInterface() == NULL) {
518 		debug_printf("Could not initialize graphics output. Exiting.\n");
519 		return B_ERROR;
520 	}
521 
522 	float brightness = fWorkspaces[0].StoredScreenConfiguration().Brightness(0);
523 	if (brightness > 0)
524 		HWInterface()->SetBrightness(brightness);
525 
526 	fVirtualScreen.HWInterface()->MoveCursorTo(
527 		fVirtualScreen.Frame().Width() / 2,
528 		fVirtualScreen.Frame().Height() / 2);
529 
530 #if TEST_MODE
531 	gInputManager->AddStream(new InputServerStream);
532 #endif
533 
534 	EventStream* stream = fVirtualScreen.HWInterface()->CreateEventStream();
535 	if (stream == NULL)
536 		stream = gInputManager->GetStream();
537 
538 	fEventDispatcher.SetDesktop(this);
539 	fEventDispatcher.SetTo(stream);
540 	if (fEventDispatcher.InitCheck() != B_OK)
541 		_LaunchInputServer();
542 
543 	fEventDispatcher.SetHWInterface(fVirtualScreen.HWInterface());
544 
545 	fEventDispatcher.SetMouseFilter(new MouseFilter(this));
546 	fEventDispatcher.SetKeyboardFilter(new KeyboardFilter(this));
547 
548 	// draw the background
549 
550 	fScreenRegion = fVirtualScreen.Frame();
551 
552 	BRegion stillAvailableOnScreen;
553 	_RebuildClippingForAllWindows(stillAvailableOnScreen);
554 	_SetBackground(stillAvailableOnScreen);
555 
556 	SetCursor(NULL);
557 		// this will set the default cursor
558 
559 	fVirtualScreen.HWInterface()->SetCursorVisible(true);
560 
561 	return B_OK;
562 }
563 
564 
565 /*!	\brief Send a quick (no attachments) message to all applications.
566 
567 	Quite useful for notification for things like server shutdown, system
568 	color changes, etc.
569 */
570 void
571 Desktop::BroadcastToAllApps(int32 code)
572 {
573 	BAutolock locker(fApplicationsLock);
574 
575 	for (int32 i = fApplications.CountItems(); i-- > 0;) {
576 		fApplications.ItemAt(i)->PostMessage(code);
577 	}
578 }
579 
580 
581 /*!	\brief Send a quick (no attachments) message to all windows.
582 */
583 void
584 Desktop::BroadcastToAllWindows(int32 code)
585 {
586 	AutoReadLocker _(fWindowLock);
587 
588 	for (Window* window = fAllWindows.FirstWindow(); window != NULL;
589 			window = window->NextWindow(kAllWindowList)) {
590 		window->ServerWindow()->PostMessage(code);
591 	}
592 }
593 
594 
595 int32
596 Desktop::GetAllWindowTargets(DelayedMessage& message)
597 {
598 	AutoReadLocker _(fWindowLock);
599 	int32 count = 0;
600 
601 	for (Window* window = fAllWindows.FirstWindow(); window != NULL;
602 			window = window->NextWindow(kAllWindowList)) {
603 		message.AddTarget(window->ServerWindow()->MessagePort());
604 		++count;
605 	}
606 
607 	return count;
608 }
609 
610 
611 int32
612 Desktop::GetAllAppTargets(DelayedMessage& message)
613 {
614 	BAutolock _(fApplicationsLock);
615 
616 	for (int32 index = 0; index < fApplications.CountItems(); ++index)
617 		message.AddTarget(fApplications.ItemAt(index)->MessagePort());
618 
619 	return fApplications.CountItems();
620 }
621 
622 
623 filter_result
624 Desktop::KeyEvent(uint32 what, int32 key, int32 modifiers)
625 {
626 	filter_result result = B_DISPATCH_MESSAGE;
627 	if (LockAllWindows()) {
628 		Window* window = MouseEventWindow();
629 		if (window == NULL)
630 			window = WindowAt(fLastMousePosition);
631 
632 		if (window != NULL) {
633 			if (what == B_MODIFIERS_CHANGED)
634 				window->ModifiersChanged(modifiers);
635 		}
636 
637 		if (NotifyKeyPressed(what, key, modifiers))
638 			result = B_SKIP_MESSAGE;
639 
640 		UnlockAllWindows();
641 	}
642 
643 	return result;
644 }
645 
646 
647 // #pragma mark - Mouse and cursor methods
648 
649 
650 void
651 Desktop::SetCursor(ServerCursor* newCursor)
652 {
653 	if (newCursor == NULL)
654 		newCursor = fCursorManager.GetCursor(B_CURSOR_ID_SYSTEM_DEFAULT);
655 
656 	if (newCursor == fCursor)
657 		return;
658 
659 	fCursor = newCursor;
660 
661 	if (!fManagementCursor.IsSet())
662 		HWInterface()->SetCursor(newCursor);
663 }
664 
665 
666 ServerCursorReference
667 Desktop::Cursor() const
668 {
669 	return fCursor;
670 }
671 
672 
673 void
674 Desktop::SetManagementCursor(ServerCursor* newCursor)
675 {
676 	if (newCursor == fManagementCursor)
677 		return;
678 
679 	fManagementCursor = newCursor;
680 
681 	HWInterface()->SetCursor(newCursor != NULL ? newCursor : fCursor.Get());
682 }
683 
684 
685 void
686 Desktop::SetLastMouseState(const BPoint& position, int32 buttons,
687 	Window* windowUnderMouse)
688 {
689 	// The all-window-lock is write-locked.
690 	fLastMousePosition = position;
691 	fLastMouseButtons = buttons;
692 
693 	if (fLastMouseButtons == 0 && fLockedFocusWindow) {
694 		fLockedFocusWindow = NULL;
695 		if (fSettings->FocusFollowsMouse())
696 			SetFocusWindow(windowUnderMouse);
697 	}
698 }
699 
700 
701 void
702 Desktop::GetLastMouseState(BPoint* position, int32* buttons) const
703 {
704 	*position = fLastMousePosition;
705 	*buttons = fLastMouseButtons;
706 }
707 
708 
709 //	#pragma mark - Screen methods
710 
711 
712 status_t
713 Desktop::SetScreenMode(int32 workspace, int32 id, const display_mode& mode,
714 	bool makeDefault)
715 {
716 	AutoWriteLocker _(fWindowLock);
717 
718 	if (workspace == B_CURRENT_WORKSPACE_INDEX)
719 		workspace = fCurrentWorkspace;
720 
721 	if (workspace < 0 || workspace >= kMaxWorkspaces)
722 		return B_BAD_VALUE;
723 
724 	Screen* screen = fVirtualScreen.ScreenByID(id);
725 	if (screen == NULL)
726 		return B_NAME_NOT_FOUND;
727 
728 	// Check if the mode has actually changed
729 
730 	if (workspace == fCurrentWorkspace) {
731 		// retrieve from current screen
732 		display_mode oldMode;
733 		screen->GetMode(oldMode);
734 
735 		if (!memcmp(&oldMode, &mode, sizeof(display_mode)))
736 			return B_OK;
737 
738 		// Set the new one
739 
740 		_SuspendDirectFrameBufferAccess();
741 
742 		AutoWriteLocker locker(fScreenLock);
743 
744 		status_t status = screen->SetMode(mode);
745 		if (status != B_OK) {
746 			locker.Unlock();
747 
748 			_ResumeDirectFrameBufferAccess();
749 			return status;
750 		}
751 	} else {
752 		// retrieve from settings
753 		screen_configuration* configuration
754 			= fWorkspaces[workspace].CurrentScreenConfiguration().CurrentByID(
755 				screen->ID());
756 		if (configuration != NULL
757 			&& !memcmp(&configuration->mode, &mode, sizeof(display_mode)))
758 			return B_OK;
759 	}
760 
761 	// Update our configurations
762 
763 	monitor_info info;
764 	bool hasInfo = screen->GetMonitorInfo(info) == B_OK;
765 
766 	fWorkspaces[workspace].CurrentScreenConfiguration().Set(id,
767 		hasInfo ? &info : NULL, screen->Frame(), mode);
768 	if (makeDefault) {
769 		fWorkspaces[workspace].StoredScreenConfiguration().Set(id,
770 			hasInfo ? &info : NULL, screen->Frame(), mode);
771 		StoreWorkspaceConfiguration(workspace);
772 	}
773 
774 	_ScreenChanged(screen);
775 	if (workspace == fCurrentWorkspace)
776 		_ResumeDirectFrameBufferAccess();
777 
778 	return B_OK;
779 }
780 
781 
782 status_t
783 Desktop::GetScreenMode(int32 workspace, int32 id, display_mode& mode)
784 {
785 	AutoReadLocker _(fScreenLock);
786 
787 	if (workspace == B_CURRENT_WORKSPACE_INDEX)
788 		workspace = fCurrentWorkspace;
789 
790 	if (workspace < 0 || workspace >= kMaxWorkspaces)
791 		return B_BAD_VALUE;
792 
793 	if (workspace == fCurrentWorkspace) {
794 		// retrieve from current screen
795 		Screen* screen = fVirtualScreen.ScreenByID(id);
796 		if (screen == NULL)
797 			return B_NAME_NOT_FOUND;
798 
799 		screen->GetMode(mode);
800 		return B_OK;
801 	}
802 
803 	// retrieve from settings
804 	screen_configuration* configuration
805 		= fWorkspaces[workspace].CurrentScreenConfiguration().CurrentByID(id);
806 	if (configuration == NULL)
807 		return B_NAME_NOT_FOUND;
808 
809 	mode = configuration->mode;
810 	return B_OK;
811 }
812 
813 
814 status_t
815 Desktop::GetScreenFrame(int32 workspace, int32 id, BRect& frame)
816 {
817 	AutoReadLocker _(fScreenLock);
818 
819 	if (workspace == B_CURRENT_WORKSPACE_INDEX)
820 		workspace = fCurrentWorkspace;
821 
822 	if (workspace < 0 || workspace >= kMaxWorkspaces)
823 		return B_BAD_VALUE;
824 
825 	if (workspace == fCurrentWorkspace) {
826 		// retrieve from current screen
827 		Screen* screen = fVirtualScreen.ScreenByID(id);
828 		if (screen == NULL)
829 			return B_NAME_NOT_FOUND;
830 
831 		frame = screen->Frame();
832 		return B_OK;
833 	}
834 
835 	// retrieve from settings
836 	screen_configuration* configuration
837 		= fWorkspaces[workspace].CurrentScreenConfiguration().CurrentByID(id);
838 	if (configuration == NULL)
839 		return B_NAME_NOT_FOUND;
840 
841 	frame = configuration->frame;
842 	return B_OK;
843 }
844 
845 
846 void
847 Desktop::RevertScreenModes(uint32 workspaces)
848 {
849 	if (workspaces == 0)
850 		return;
851 
852 	AutoWriteLocker _(fWindowLock);
853 
854 	for (int32 workspace = 0; workspace < kMaxWorkspaces; workspace++) {
855 		if ((workspaces & (1U << workspace)) == 0)
856 			continue;
857 
858 		// Revert all screens on this workspace
859 
860 		// TODO: ideally, we would know which screens to revert - this way, too
861 		// many of them could be reverted
862 
863 		for (int32 index = 0; index < fVirtualScreen.CountScreens(); index++) {
864 			Screen* screen = fVirtualScreen.ScreenAt(index);
865 
866 			// retrieve configurations
867 			screen_configuration* stored = fWorkspaces[workspace]
868 				.StoredScreenConfiguration().CurrentByID(screen->ID());
869 			screen_configuration* current = fWorkspaces[workspace]
870 				.CurrentScreenConfiguration().CurrentByID(screen->ID());
871 
872 			if ((stored != NULL && current != NULL
873 					&& !memcmp(&stored->mode, &current->mode,
874 							sizeof(display_mode)))
875 				|| (stored == NULL && current == NULL))
876 				continue;
877 
878 			if (stored == NULL) {
879 				fWorkspaces[workspace].CurrentScreenConfiguration()
880 					.Remove(current);
881 
882 				if (workspace == fCurrentWorkspace) {
883 					_SuspendDirectFrameBufferAccess();
884 					_SetCurrentWorkspaceConfiguration();
885 					_ResumeDirectFrameBufferAccess();
886 				}
887 			} else
888 				SetScreenMode(workspace, screen->ID(), stored->mode, false);
889 		}
890 	}
891 }
892 
893 
894 status_t
895 Desktop::SetBrightness(int32 id, float brightness)
896 {
897 	status_t result = HWInterface()->SetBrightness(brightness);
898 
899 	if (result == B_OK) {
900 		fWorkspaces[0].StoredScreenConfiguration().SetBrightness(id,
901 			brightness);
902 		// Save brightness for next boot
903 		StoreWorkspaceConfiguration(0);
904 	}
905 
906 	return result;
907 }
908 
909 
910 status_t
911 Desktop::LockDirectScreen(team_id team)
912 {
913 	// TODO: BWindowScreens should use the same mechanism as BDirectWindow,
914 	// which would make this method superfluous.
915 
916 	status_t status = fDirectScreenLock.LockWithTimeout(1000000L);
917 	if (status == B_OK)
918 		fDirectScreenTeam = team;
919 
920 	return status;
921 }
922 
923 
924 status_t
925 Desktop::UnlockDirectScreen(team_id team)
926 {
927 	if (fDirectScreenTeam == team) {
928 		fDirectScreenLock.Unlock();
929 		fDirectScreenTeam = -1;
930 		return B_OK;
931 	}
932 
933 	return B_PERMISSION_DENIED;
934 }
935 
936 
937 // #pragma mark - Workspaces methods
938 
939 
940 /*!	Changes the current workspace to the one specified by \a index.
941 */
942 void
943 Desktop::SetWorkspaceAsync(int32 index, bool moveFocusWindow)
944 {
945 	BPrivate::LinkSender link(MessagePort());
946 	link.StartMessage(AS_ACTIVATE_WORKSPACE);
947 	link.Attach<int32>(index);
948 	link.Attach<bool>(moveFocusWindow);
949 	link.Flush();
950 }
951 
952 
953 /*!	Changes the current workspace to the one specified by \a index.
954 	You must not hold any window lock when calling this method.
955 */
956 void
957 Desktop::SetWorkspace(int32 index, bool moveFocusWindow)
958 {
959 	LockAllWindows();
960 	DesktopSettings settings(this);
961 
962 	if (index < 0 || index >= settings.WorkspacesCount()
963 		|| index == fCurrentWorkspace) {
964 		UnlockAllWindows();
965 		return;
966 	}
967 
968 	_SetWorkspace(index, moveFocusWindow);
969 	UnlockAllWindows();
970 
971 	_SendFakeMouseMoved();
972 }
973 
974 
975 status_t
976 Desktop::SetWorkspacesLayout(int32 newColumns, int32 newRows)
977 {
978 	int32 newCount = newColumns * newRows;
979 	if (newCount < 1 || newCount > kMaxWorkspaces)
980 		return B_BAD_VALUE;
981 
982 	if (!LockAllWindows())
983 		return B_ERROR;
984 
985 	fSettings->SetWorkspacesLayout(newColumns, newRows);
986 
987 	// either update the workspaces window, or switch to
988 	// the last available workspace - which will update
989 	// the workspaces window automatically
990 	bool workspaceChanged = CurrentWorkspace() >= newCount;
991 	if (workspaceChanged)
992 		_SetWorkspace(newCount - 1);
993 	else
994 		_WindowChanged(NULL);
995 
996 	UnlockAllWindows();
997 
998 	if (workspaceChanged)
999 		_SendFakeMouseMoved();
1000 
1001 	return B_OK;
1002 }
1003 
1004 
1005 /*!	Returns the virtual screen frame of the workspace specified by \a index.
1006 */
1007 BRect
1008 Desktop::WorkspaceFrame(int32 index) const
1009 {
1010 	BRect frame;
1011 	if (index == fCurrentWorkspace)
1012 		frame = fVirtualScreen.Frame();
1013 	else if (index >= 0 && index < fSettings->WorkspacesCount()) {
1014 		BMessage screenData;
1015 		if (fSettings->WorkspacesMessage(index)->FindMessage("screen",
1016 				&screenData) != B_OK
1017 			|| screenData.FindRect("frame", &frame) != B_OK) {
1018 			frame = fVirtualScreen.Frame();
1019 		}
1020 	}
1021 
1022 	return frame;
1023 }
1024 
1025 
1026 /*!	\brief Stores the workspace configuration.
1027 	You must hold the window lock when calling this method.
1028 */
1029 void
1030 Desktop::StoreWorkspaceConfiguration(int32 index)
1031 {
1032 	// Retrieve settings
1033 
1034 	BMessage settings;
1035 	fWorkspaces[index].StoreConfiguration(settings);
1036 
1037 	// and store them
1038 
1039 	fSettings->SetWorkspacesMessage(index, settings);
1040 	fSettings->Save(kWorkspacesSettings);
1041 }
1042 
1043 
1044 void
1045 Desktop::AddWorkspacesView(WorkspacesView* view)
1046 {
1047 	if (view->Window() == NULL || view->Window()->IsHidden())
1048 		return;
1049 
1050 	BAutolock _(fWorkspacesLock);
1051 
1052 	if (!fWorkspacesViews.HasItem(view))
1053 		fWorkspacesViews.AddItem(view);
1054 }
1055 
1056 
1057 void
1058 Desktop::RemoveWorkspacesView(WorkspacesView* view)
1059 {
1060 	BAutolock _(fWorkspacesLock);
1061 	fWorkspacesViews.RemoveItem(view);
1062 }
1063 
1064 
1065 //	#pragma mark - Methods for Window manipulation
1066 
1067 
1068 /*!	\brief Activates or focusses the window based on the pointer position.
1069 */
1070 void
1071 Desktop::SelectWindow(Window* window)
1072 {
1073 	if (fSettings->ClickToFocusMouse()) {
1074 		// Only bring the window to front when it is not the window under the
1075 		// mouse pointer. This should result in sensible behaviour.
1076 		if (window != fWindowUnderMouse
1077 			|| (window == fWindowUnderMouse && window != FocusWindow()))
1078 			ActivateWindow(window);
1079 		else
1080 			SetFocusWindow(window);
1081 	} else
1082 		ActivateWindow(window);
1083 }
1084 
1085 
1086 /*!	\brief Tries to move the specified window to the front of the screen,
1087 		and make it the focus window.
1088 
1089 	If there are any modal windows on this screen, it might not actually
1090 	become the frontmost window, though, as modal windows stay in front
1091 	of their subset.
1092 */
1093 void
1094 Desktop::ActivateWindow(Window* window)
1095 {
1096 	STRACE(("ActivateWindow(%p, %s)\n", window, window
1097 		? window->Title() : "<none>"));
1098 
1099 	if (window == NULL) {
1100 		fBack = NULL;
1101 		fFront = NULL;
1102 		return;
1103 	}
1104 	if (window->Workspaces() == 0 && window->IsNormal())
1105 		return;
1106 
1107 	AutoWriteLocker allWindowLocker(fWindowLock);
1108 
1109 	NotifyWindowActivated(window);
1110 
1111 	bool windowOnOtherWorkspace = !window->InWorkspace(fCurrentWorkspace);
1112 	if (windowOnOtherWorkspace
1113 		&& (window->Flags() & B_NOT_ANCHORED_ON_ACTIVATE) == 0) {
1114 		if ((window->Flags() & B_NO_WORKSPACE_ACTIVATION) == 0) {
1115 			// Switch to the workspace on which this window is
1116 			// (we'll take the first one that the window is on)
1117 			uint32 workspaces = window->Workspaces();
1118 			for (int32 i = 0; i < fSettings->WorkspacesCount(); i++) {
1119 				uint32 workspace = workspace_to_workspaces(i);
1120 				if (workspaces & workspace) {
1121 					SetWorkspace(i);
1122 					windowOnOtherWorkspace = false;
1123 					break;
1124 				}
1125 			}
1126 		} else
1127 			return;
1128 	}
1129 
1130 	if (windowOnOtherWorkspace) {
1131 		if (!window->IsNormal()) {
1132 			// Bring a window to front that this floating window belongs to
1133 			Window* front = _LastFocusSubsetWindow(window);
1134 			if (front == NULL) {
1135 				// We can't do anything about those.
1136 				return;
1137 			}
1138 
1139 			ActivateWindow(front);
1140 
1141 			if (!window->InWorkspace(fCurrentWorkspace)) {
1142 				// This window can't be made active
1143 				return;
1144 			}
1145 		} else {
1146 			// Bring the window to the current workspace
1147 			// TODO: what if this window is on multiple workspaces?!?
1148 			uint32 workspaces = workspace_to_workspaces(fCurrentWorkspace);
1149 			SetWindowWorkspaces(window, workspaces);
1150 		}
1151 	}
1152 
1153 	if (window->IsMinimized()) {
1154 		// Unlike WindowAction(), this is called from the application itself,
1155 		// so we will just unminimize the window here.
1156 		window->SetMinimized(false);
1157 		ShowWindow(window);
1158 	}
1159 
1160 	if (window == FrontWindow()) {
1161 		// see if there is a normal B_AVOID_FRONT window still in front of us
1162 		Window* avoidsFront = window->NextWindow(fCurrentWorkspace);
1163 		while (avoidsFront && avoidsFront->IsNormal()
1164 			&& (avoidsFront->Flags() & B_AVOID_FRONT) == 0) {
1165 			avoidsFront = avoidsFront->NextWindow(fCurrentWorkspace);
1166 		}
1167 
1168 		if (avoidsFront == NULL) {
1169 			// we're already the frontmost window, we might just not have focus
1170 			// yet
1171 			if ((window->Flags() & B_AVOID_FOCUS) == 0)
1172 				SetFocusWindow(window);
1173 			return;
1174 		}
1175 	}
1176 
1177 	WindowList windows(kWorkingList);
1178 	Window* frontmost = window->Frontmost();
1179 	const Window* lastWindowUnderMouse = fWindowUnderMouse;
1180 
1181 	CurrentWindows().RemoveWindow(window);
1182 	windows.AddWindow(window);
1183 	window->MoveToTopStackLayer();
1184 
1185 	if (frontmost != NULL && frontmost->IsModal()) {
1186 		// all modal windows follow their subsets to the front
1187 		// (ie. they are staying in front of them, but they are
1188 		// not supposed to change their order because of that)
1189 
1190 		Window* nextModal;
1191 		for (Window* modal = frontmost; modal != NULL; modal = nextModal) {
1192 			// get the next modal window
1193 			nextModal = modal->NextWindow(fCurrentWorkspace);
1194 			while (nextModal != NULL && !nextModal->IsModal()) {
1195 				nextModal = nextModal->NextWindow(fCurrentWorkspace);
1196 			}
1197 			if (nextModal != NULL && !nextModal->HasInSubset(window))
1198 				nextModal = NULL;
1199 
1200 			CurrentWindows().RemoveWindow(modal);
1201 			windows.AddWindow(modal);
1202 		}
1203 	}
1204 
1205 	_BringWindowsToFront(windows, kWorkingList, true);
1206 
1207 	if ((window->Flags() & B_AVOID_FOCUS) == 0)
1208 		SetFocusWindow(window);
1209 
1210 	bool sendFakeMouseMoved = _CheckSendFakeMouseMoved(lastWindowUnderMouse);
1211 
1212 	allWindowLocker.Unlock();
1213 
1214 	if (sendFakeMouseMoved)
1215 		_SendFakeMouseMoved();
1216 }
1217 
1218 
1219 void
1220 Desktop::SendWindowBehind(Window* window, Window* behindOf, bool sendStack)
1221 {
1222 	if (!LockAllWindows())
1223 		return;
1224 
1225 	Window* orgWindow = window;
1226 	WindowStack* stack = window->GetWindowStack();
1227 	if (sendStack && stack != NULL)
1228 		window = stack->TopLayerWindow();
1229 
1230 	// TODO: should the "not in current workspace" be handled anyway?
1231 	//	(the code below would have to be changed then, though)
1232 	if (window == BackWindow()
1233 		|| !window->InWorkspace(fCurrentWorkspace)
1234 		|| (behindOf != NULL && !behindOf->InWorkspace(fCurrentWorkspace))) {
1235 		UnlockAllWindows();
1236 		return;
1237 	}
1238 
1239 	// Is this a valid behindOf window?
1240 	if (behindOf != NULL && window->HasInSubset(behindOf))
1241 		behindOf = NULL;
1242 
1243 	// what is currently visible of the window
1244 	// might be dirty after the window is send to back
1245 	BRegion dirty(window->VisibleRegion());
1246 
1247 	Window* backmost = window->Backmost(behindOf);
1248 	const Window* lastWindowUnderMouse = fWindowUnderMouse;
1249 
1250 	CurrentWindows().RemoveWindow(window);
1251 	CurrentWindows().AddWindow(window, backmost
1252 		? backmost->NextWindow(fCurrentWorkspace) : BackWindow());
1253 
1254 	BRegion dummy;
1255 	_RebuildClippingForAllWindows(dummy);
1256 
1257 	// only redraw the top layer window to avoid flicker
1258 	if (sendStack) {
1259 		// mark everything dirty that is no longer visible
1260 		BRegion clean(window->VisibleRegion());
1261 		dirty.Exclude(&clean);
1262 		MarkDirty(dirty);
1263 	}
1264 
1265 	_UpdateFronts();
1266 	if (fSettings->FocusFollowsMouse())
1267 		SetFocusWindow(WindowAt(fLastMousePosition));
1268 	else if (fSettings->NormalMouse())
1269 		SetFocusWindow(NULL);
1270 
1271 	_WindowChanged(window);
1272 
1273 	if (sendStack && stack != NULL) {
1274 		for (int32 i = 0; i < stack->CountWindows(); i++) {
1275 			Window* stackWindow = stack->LayerOrder().ItemAt(i);
1276 			if (stackWindow == window)
1277 				continue;
1278 			SendWindowBehind(stackWindow, behindOf, false);
1279 		}
1280 	}
1281 
1282 	bool sendFakeMouseMoved = _CheckSendFakeMouseMoved(lastWindowUnderMouse);
1283 	NotifyWindowSentBehind(orgWindow, behindOf);
1284 
1285 	UnlockAllWindows();
1286 
1287 	if (sendFakeMouseMoved)
1288 		_SendFakeMouseMoved();
1289 }
1290 
1291 
1292 void
1293 Desktop::ShowWindow(Window* window)
1294 {
1295 	if (!window->IsHidden())
1296 		return;
1297 
1298 	AutoWriteLocker locker(fWindowLock);
1299 
1300 	window->SetHidden(false);
1301 	fFocusList.AddWindow(window);
1302 
1303 	// If the window is on the current workspace, we'll show it. Special
1304 	// handling for floating windows, as they can only be shown if their
1305 	// subset is.
1306 	if (window->InWorkspace(fCurrentWorkspace)
1307 		|| (window->IsFloating() && _LastFocusSubsetWindow(window) != NULL)) {
1308 		_ShowWindow(window, true);
1309 		_UpdateSubsetWorkspaces(window);
1310 		ActivateWindow(window);
1311 	} else {
1312 		// then we don't need to send the fake mouse event either
1313 		_WindowChanged(window);
1314 		return;
1315 	}
1316 
1317 	if (window->HasWorkspacesViews()) {
1318 		// find workspaces views in view hierarchy
1319 		BAutolock _(fWorkspacesLock);
1320 		window->FindWorkspacesViews(fWorkspacesViews);
1321 	}
1322 
1323 	// If the mouse cursor is directly over the newly visible window,
1324 	// we'll send a fake mouse moved message to the window, so that
1325 	// it knows the mouse is over it.
1326 
1327 	_SendFakeMouseMoved(window);
1328 }
1329 
1330 
1331 void
1332 Desktop::HideWindow(Window* window, bool fromMinimize)
1333 {
1334 	if (window->IsHidden())
1335 		return;
1336 
1337 	if (!LockAllWindows())
1338 		return;
1339 
1340 	window->SetHidden(true);
1341 	fFocusList.RemoveWindow(window);
1342 
1343 	if (fMouseEventWindow == window) {
1344 		// Make its decorator lose the current mouse action
1345 		BMessage message;
1346 		int32 viewToken;
1347 		window->MouseUp(&message, fLastMousePosition, &viewToken);
1348 
1349 		fMouseEventWindow = NULL;
1350 	}
1351 
1352 	if (fLockedFocusWindow == window) {
1353 		// Remove the focus lock so the focus can be changed below
1354 		fLockedFocusWindow = NULL;
1355 	}
1356 
1357 	if (window->InWorkspace(fCurrentWorkspace)) {
1358 		_UpdateSubsetWorkspaces(window);
1359 		_HideWindow(window);
1360 		_UpdateFronts();
1361 	} else
1362 		_WindowChanged(window);
1363 
1364 	if (FocusWindow() == window)
1365 		SetFocusWindow();
1366 
1367 	_WindowRemoved(window);
1368 
1369 	if (window->HasWorkspacesViews()) {
1370 		// remove workspaces views from this window
1371 		BObjectList<WorkspacesView> list(false);
1372 		window->FindWorkspacesViews(list);
1373 
1374 		BAutolock _(fWorkspacesLock);
1375 
1376 		while (WorkspacesView* view = list.RemoveItemAt(0)) {
1377 			fWorkspacesViews.RemoveItem(view);
1378 		}
1379 	}
1380 
1381 	NotifyWindowHidden(window, fromMinimize);
1382 
1383 	UnlockAllWindows();
1384 
1385 	if (window == fWindowUnderMouse)
1386 		_SendFakeMouseMoved();
1387 }
1388 
1389 
1390 void
1391 Desktop::MinimizeWindow(Window* window, bool minimize)
1392 {
1393 	if (!LockAllWindows())
1394 		return;
1395 
1396 	if (minimize && !window->IsHidden()) {
1397 		HideWindow(window, true);
1398 		window->SetMinimized(minimize);
1399 		NotifyWindowMinimized(window, minimize);
1400 	} else if (!minimize && window->IsHidden()) {
1401 		ActivateWindow(window);
1402 			// this will unminimize the window for us
1403 		NotifyWindowMinimized(window, minimize);
1404 	}
1405 
1406 	UnlockAllWindows();
1407 }
1408 
1409 
1410 void
1411 Desktop::MoveWindowBy(Window* window, float x, float y, int32 workspace)
1412 {
1413 	if (x == 0 && y == 0)
1414 		return;
1415 
1416 	AutoWriteLocker _(fWindowLock);
1417 
1418 	Window* topWindow = window->TopLayerStackWindow();
1419 	if (topWindow != NULL)
1420 		window = topWindow;
1421 
1422 	if (workspace == -1)
1423 		workspace = fCurrentWorkspace;
1424 	if (!window->IsVisible() || workspace != fCurrentWorkspace) {
1425 		if (workspace != fCurrentWorkspace) {
1426 			WindowStack* stack = window->GetWindowStack();
1427 			if (stack != NULL) {
1428 				for (int32 s = 0; s < stack->CountWindows(); s++) {
1429 					Window* stackWindow = stack->WindowAt(s);
1430 					// move the window on another workspace - this doesn't
1431 					// change it's current position
1432 					if (stackWindow->Anchor(workspace).position
1433 						== kInvalidWindowPosition) {
1434 						stackWindow->Anchor(workspace).position
1435 							= stackWindow->Frame().LeftTop();
1436 					}
1437 
1438 					stackWindow->Anchor(workspace).position += BPoint(x, y);
1439 					stackWindow->SetCurrentWorkspace(workspace);
1440 					_WindowChanged(stackWindow);
1441 				}
1442 			}
1443 		} else
1444 			window->MoveBy((int32)x, (int32)y);
1445 
1446 		NotifyWindowMoved(window);
1447 		return;
1448 	}
1449 
1450 	// the dirty region starts with the visible area of the window being moved
1451 	BRegion newDirtyRegion(window->VisibleRegion());
1452 
1453 	// stop direct frame buffer access
1454 	bool direct = false;
1455 	if (window->ServerWindow()->IsDirectlyAccessing()) {
1456 		window->ServerWindow()->HandleDirectConnection(B_DIRECT_STOP);
1457 		direct = true;
1458 	}
1459 
1460 	window->MoveBy((int32)x, (int32)y);
1461 
1462 	BRegion background;
1463 	_RebuildClippingForAllWindows(background);
1464 
1465 	// construct the region that is possible to be blitted
1466 	// to move the contents of the window
1467 	BRegion copyRegion(window->VisibleRegion());
1468 	copyRegion.OffsetBy((int32)-x, (int32)-y);
1469 	copyRegion.IntersectWith(&newDirtyRegion);
1470 		// newDirtyRegion == the windows old visible region
1471 
1472 	// include the the new visible region of the window being
1473 	// moved into the dirty region (for now)
1474 	newDirtyRegion.Include(&window->VisibleRegion());
1475 
1476 	// NOTE: Having all windows locked should prevent any
1477 	// problems with locking the drawing engine here.
1478 	if (GetDrawingEngine()->LockParallelAccess()) {
1479 		GetDrawingEngine()->CopyRegion(&copyRegion, (int32)x, (int32)y);
1480 		GetDrawingEngine()->UnlockParallelAccess();
1481 	}
1482 
1483 	// in the dirty region, exclude the parts that we
1484 	// could move by blitting
1485 	copyRegion.OffsetBy((int32)x, (int32)y);
1486 	newDirtyRegion.Exclude(&copyRegion);
1487 
1488 	MarkDirty(newDirtyRegion);
1489 	_SetBackground(background);
1490 	_WindowChanged(window);
1491 
1492 	// resume direct frame buffer access
1493 	if (direct) {
1494 		// TODO: the clipping actually only changes when we move our window
1495 		// off screen, or behind some other window
1496 		window->ServerWindow()->HandleDirectConnection(
1497 			B_DIRECT_START | B_BUFFER_MOVED | B_CLIPPING_MODIFIED);
1498 	}
1499 
1500 	NotifyWindowMoved(window);
1501 }
1502 
1503 
1504 void
1505 Desktop::ResizeWindowBy(Window* window, float x, float y)
1506 {
1507 	if (x == 0 && y == 0)
1508 		return;
1509 
1510 	AutoWriteLocker _(fWindowLock);
1511 
1512 	Window* topWindow = window->TopLayerStackWindow();
1513 	if (topWindow)
1514 		window = topWindow;
1515 
1516 	if (!window->IsVisible()) {
1517 		window->ResizeBy((int32)x, (int32)y, NULL);
1518 		NotifyWindowResized(window);
1519 		return;
1520 	}
1521 
1522 	// the dirty region for the inside of the window is
1523 	// constructed by the window itself in ResizeBy()
1524 	BRegion newDirtyRegion;
1525 	// track the dirty region outside the window in case
1526 	// it is shrunk in "previouslyOccupiedRegion"
1527 	BRegion previouslyOccupiedRegion(window->VisibleRegion());
1528 
1529 	// stop direct frame buffer access
1530 	bool direct = false;
1531 	if (window->ServerWindow()->IsDirectlyAccessing()) {
1532 		window->ServerWindow()->HandleDirectConnection(B_DIRECT_STOP);
1533 		direct = true;
1534 	}
1535 
1536 	window->ResizeBy((int32)x, (int32)y, &newDirtyRegion);
1537 
1538 	BRegion background;
1539 	_RebuildClippingForAllWindows(background);
1540 
1541 	// we just care for the region outside the window
1542 	previouslyOccupiedRegion.Exclude(&window->VisibleRegion());
1543 
1544 	// make sure the window cannot mark stuff dirty outside
1545 	// its visible region...
1546 	newDirtyRegion.IntersectWith(&window->VisibleRegion());
1547 	// ...because we do this ourselves
1548 	newDirtyRegion.Include(&previouslyOccupiedRegion);
1549 
1550 	MarkDirty(newDirtyRegion);
1551 	_SetBackground(background);
1552 	_WindowChanged(window);
1553 
1554 	// resume direct frame buffer access
1555 	if (direct) {
1556 		window->ServerWindow()->HandleDirectConnection(
1557 			B_DIRECT_START | B_BUFFER_RESIZED | B_CLIPPING_MODIFIED);
1558 	}
1559 
1560 	NotifyWindowResized(window);
1561 }
1562 
1563 
1564 void
1565 Desktop::SetWindowOutlinesDelta(Window* window, BPoint delta)
1566 {
1567 	AutoWriteLocker _(fWindowLock);
1568 
1569 	if (!window->IsVisible())
1570 		return;
1571 
1572 	BRegion newDirtyRegion;
1573 	window->SetOutlinesDelta(delta, &newDirtyRegion);
1574 
1575 	BRegion background;
1576 	_RebuildClippingForAllWindows(background);
1577 
1578 	MarkDirty(newDirtyRegion);
1579 	_SetBackground(background);
1580 }
1581 
1582 
1583 bool
1584 Desktop::SetWindowTabLocation(Window* window, float location, bool isShifting)
1585 {
1586 	AutoWriteLocker _(fWindowLock);
1587 
1588 	BRegion dirty;
1589 	bool changed = window->SetTabLocation(location, isShifting, dirty);
1590 	if (changed)
1591 		RebuildAndRedrawAfterWindowChange(window, dirty);
1592 
1593 	NotifyWindowTabLocationChanged(window, location, isShifting);
1594 
1595 	return changed;
1596 }
1597 
1598 
1599 bool
1600 Desktop::SetWindowDecoratorSettings(Window* window, const BMessage& settings)
1601 {
1602 	AutoWriteLocker _(fWindowLock);
1603 
1604 	BRegion dirty;
1605 	bool changed = window->SetDecoratorSettings(settings, dirty);
1606 	bool listenerChanged = SetDecoratorSettings(window, settings);
1607 	if (changed || listenerChanged)
1608 		RebuildAndRedrawAfterWindowChange(window, dirty);
1609 
1610 	return changed;
1611 }
1612 
1613 
1614 void
1615 Desktop::SetWindowWorkspaces(Window* window, uint32 workspaces)
1616 {
1617 	LockAllWindows();
1618 
1619 	if (window->IsNormal() && workspaces == B_CURRENT_WORKSPACE)
1620 		workspaces = workspace_to_workspaces(CurrentWorkspace());
1621 
1622 	WindowStack* stack = window->GetWindowStack();
1623 	if (stack != NULL) {
1624 		for (int32 s = 0; s < stack->CountWindows(); s++) {
1625 			window = stack->LayerOrder().ItemAt(s);
1626 
1627 			uint32 oldWorkspaces = window->Workspaces();
1628 			window->WorkspacesChanged(oldWorkspaces, workspaces);
1629 			_ChangeWindowWorkspaces(window, oldWorkspaces, workspaces);
1630 		}
1631 	}
1632 	UnlockAllWindows();
1633 }
1634 
1635 
1636 /*!	\brief Adds the window to the desktop.
1637 	At this point, the window is still hidden and must be shown explicitly
1638 	via ShowWindow().
1639 */
1640 void
1641 Desktop::AddWindow(Window *window)
1642 {
1643 	LockAllWindows();
1644 
1645 	fAllWindows.AddWindow(window);
1646 	if (!window->IsNormal())
1647 		fSubsetWindows.AddWindow(window);
1648 
1649 	if (window->IsNormal()) {
1650 		if (window->Workspaces() == B_CURRENT_WORKSPACE)
1651 			window->SetWorkspaces(workspace_to_workspaces(CurrentWorkspace()));
1652 	} else {
1653 		// subset windows are visible on all workspaces their subset is on
1654 		window->SetWorkspaces(window->SubsetWorkspaces());
1655 	}
1656 
1657 	_ChangeWindowWorkspaces(window, 0, window->Workspaces());
1658 
1659 	NotifyWindowAdded(window);
1660 
1661 	UnlockAllWindows();
1662 }
1663 
1664 
1665 void
1666 Desktop::RemoveWindow(Window *window)
1667 {
1668 	LockAllWindows();
1669 
1670 	if (!window->IsHidden())
1671 		HideWindow(window);
1672 
1673 	fAllWindows.RemoveWindow(window);
1674 	if (!window->IsNormal())
1675 		fSubsetWindows.RemoveWindow(window);
1676 
1677 	_ChangeWindowWorkspaces(window, window->Workspaces(), 0);
1678 
1679 	NotifyWindowRemoved(window);
1680 
1681 	UnlockAllWindows();
1682 
1683 	// make sure this window won't get any events anymore
1684 
1685 	EventDispatcher().RemoveTarget(window->EventTarget());
1686 }
1687 
1688 
1689 bool
1690 Desktop::AddWindowToSubset(Window* subset, Window* window)
1691 {
1692 	if (!subset->AddToSubset(window))
1693 		return false;
1694 
1695 	_ChangeWindowWorkspaces(subset, subset->Workspaces(),
1696 		subset->SubsetWorkspaces());
1697 	return true;
1698 }
1699 
1700 
1701 void
1702 Desktop::RemoveWindowFromSubset(Window* subset, Window* window)
1703 {
1704 	subset->RemoveFromSubset(window);
1705 	_ChangeWindowWorkspaces(subset, subset->Workspaces(),
1706 		subset->SubsetWorkspaces());
1707 }
1708 
1709 
1710 void
1711 Desktop::FontsChanged(Window* window)
1712 {
1713 	AutoWriteLocker _(fWindowLock);
1714 
1715 	BRegion dirty;
1716 	window->FontsChanged(&dirty);
1717 
1718 	RebuildAndRedrawAfterWindowChange(window, dirty);
1719 }
1720 
1721 
1722 void
1723 Desktop::ColorUpdated(Window* window, color_which which, rgb_color color)
1724 {
1725 	AutoWriteLocker _(fWindowLock);
1726 
1727 	window->TopView()->ColorUpdated(which, color);
1728 
1729 	switch (which) {
1730 		case B_WINDOW_TAB_COLOR:
1731 		case B_WINDOW_TEXT_COLOR:
1732 		case B_WINDOW_INACTIVE_TAB_COLOR:
1733 		case B_WINDOW_INACTIVE_TEXT_COLOR:
1734 		case B_WINDOW_BORDER_COLOR:
1735 		case B_WINDOW_INACTIVE_BORDER_COLOR:
1736 			break;
1737 		default:
1738 			return;
1739 	}
1740 
1741 	BRegion dirty;
1742 	window->ColorsChanged(&dirty);
1743 	RebuildAndRedrawAfterWindowChange(window, dirty);
1744 }
1745 
1746 
1747 void
1748 Desktop::SetWindowLook(Window* window, window_look newLook)
1749 {
1750 	if (window->Look() == newLook)
1751 		return;
1752 
1753 	AutoWriteLocker _(fWindowLock);
1754 
1755 	BRegion dirty;
1756 	window->SetLook(newLook, &dirty);
1757 		// TODO: test what happens when the window
1758 		// finds out it needs to resize itself...
1759 
1760 	RebuildAndRedrawAfterWindowChange(window, dirty);
1761 
1762 	NotifyWindowLookChanged(window, newLook);
1763 }
1764 
1765 
1766 void
1767 Desktop::SetWindowFeel(Window* window, window_feel newFeel)
1768 {
1769 	if (window->Feel() == newFeel)
1770 		return;
1771 
1772 	LockAllWindows();
1773 
1774 	bool wasNormal = window->IsNormal();
1775 
1776 	window->SetFeel(newFeel);
1777 
1778 	// move the window out of or into the subset window list as needed
1779 	if (window->IsNormal() && !wasNormal)
1780 		fSubsetWindows.RemoveWindow(window);
1781 	else if (!window->IsNormal() && wasNormal)
1782 		fSubsetWindows.AddWindow(window);
1783 
1784 	// A normal window that was once a floating or modal window will
1785 	// adopt the window's current workspaces
1786 
1787 	if (!window->IsNormal()) {
1788 		_ChangeWindowWorkspaces(window, window->Workspaces(),
1789 			window->SubsetWorkspaces());
1790 	}
1791 
1792 	// make sure the window has the correct position in the window lists
1793 	// (ie. all floating windows have to be on the top, ...)
1794 
1795 	for (int32 i = 0; i < kMaxWorkspaces; i++) {
1796 		if (!workspace_in_workspaces(i, window->Workspaces()))
1797 			continue;
1798 
1799 		bool changed = false;
1800 		BRegion visibleBefore;
1801 		if (i == fCurrentWorkspace && window->IsVisible())
1802 			visibleBefore = window->VisibleRegion();
1803 
1804 		Window* backmost = window->Backmost(_Windows(i).LastWindow(), i);
1805 		if (backmost != NULL) {
1806 			// check if the backmost window is really behind it
1807 			Window* previous = window->PreviousWindow(i);
1808 			while (previous != NULL) {
1809 				if (previous == backmost)
1810 					break;
1811 
1812 				previous = previous->PreviousWindow(i);
1813 			}
1814 
1815 			if (previous == NULL) {
1816 				// need to reinsert window before its backmost window
1817 				_Windows(i).RemoveWindow(window);
1818 				_Windows(i).AddWindow(window, backmost->NextWindow(i));
1819 				changed = true;
1820 			}
1821 		}
1822 
1823 		Window* frontmost = window->Frontmost(_Windows(i).FirstWindow(), i);
1824 		if (frontmost != NULL) {
1825 			// check if the frontmost window is really in front of it
1826 			Window* next = window->NextWindow(i);
1827 			while (next != NULL) {
1828 				if (next == frontmost)
1829 					break;
1830 
1831 				next = next->NextWindow(i);
1832 			}
1833 
1834 			if (next == NULL) {
1835 				// need to reinsert window behind its frontmost window
1836 				_Windows(i).RemoveWindow(window);
1837 				_Windows(i).AddWindow(window, frontmost);
1838 				changed = true;
1839 			}
1840 		}
1841 
1842 		if (i == fCurrentWorkspace && changed) {
1843 			BRegion dummy;
1844 			_RebuildClippingForAllWindows(dummy);
1845 
1846 			// mark everything dirty that is no longer visible, or
1847 			// is now visible and wasn't before
1848 			BRegion visibleAfter(window->VisibleRegion());
1849 			BRegion dirty(visibleAfter);
1850 			dirty.Exclude(&visibleBefore);
1851 			visibleBefore.Exclude(&visibleAfter);
1852 			dirty.Include(&visibleBefore);
1853 
1854 			MarkDirty(dirty);
1855 		}
1856 	}
1857 
1858 	_UpdateFronts();
1859 
1860 	if (window == FocusWindow() && !window->IsVisible())
1861 		SetFocusWindow();
1862 
1863 	NotifyWindowFeelChanged(window, newFeel);
1864 
1865 	UnlockAllWindows();
1866 }
1867 
1868 
1869 void
1870 Desktop::SetWindowFlags(Window *window, uint32 newFlags)
1871 {
1872 	if (window->Flags() == newFlags)
1873 		return;
1874 
1875 	AutoWriteLocker _(fWindowLock);
1876 
1877 	BRegion dirty;
1878 	window->SetFlags(newFlags, &dirty);
1879 		// TODO: test what happens when the window
1880 		// finds out it needs to resize itself...
1881 
1882 	RebuildAndRedrawAfterWindowChange(window, dirty);
1883 }
1884 
1885 
1886 void
1887 Desktop::SetWindowTitle(Window *window, const char* title)
1888 {
1889 	AutoWriteLocker _(fWindowLock);
1890 
1891 	BRegion dirty;
1892 	window->SetTitle(title, dirty);
1893 
1894 	RebuildAndRedrawAfterWindowChange(window, dirty);
1895 }
1896 
1897 
1898 /*!	Returns the window under the mouse cursor.
1899 	You need to have acquired the All Windows lock when calling this method.
1900 */
1901 Window*
1902 Desktop::WindowAt(BPoint where)
1903 {
1904 	for (Window* window = CurrentWindows().LastWindow(); window;
1905 			window = window->PreviousWindow(fCurrentWorkspace)) {
1906 		if (window->IsVisible() && window->VisibleRegion().Contains(where))
1907 			return window->StackedWindowAt(where);
1908 	}
1909 
1910 	return NULL;
1911 }
1912 
1913 
1914 void
1915 Desktop::SetMouseEventWindow(Window* window)
1916 {
1917 	fMouseEventWindow = window;
1918 }
1919 
1920 
1921 void
1922 Desktop::SetViewUnderMouse(const Window* window, int32 viewToken)
1923 {
1924 	fWindowUnderMouse = window;
1925 	fViewUnderMouse = viewToken;
1926 }
1927 
1928 
1929 int32
1930 Desktop::ViewUnderMouse(const Window* window)
1931 {
1932 	if (window != NULL && fWindowUnderMouse == window)
1933 		return fViewUnderMouse;
1934 
1935 	return B_NULL_TOKEN;
1936 }
1937 
1938 
1939 /*!	Returns the current keyboard event target candidate - which is either the
1940 	top-most window (in case it has the kAcceptKeyboardFocusFlag flag set), or
1941 	the one having focus.
1942 	The window lock must be held when calling this function.
1943 */
1944 EventTarget*
1945 Desktop::KeyboardEventTarget()
1946 {
1947 	// Get the top most non-hidden window
1948 	Window* window = CurrentWindows().LastWindow();
1949 	while (window != NULL && window->IsHidden()) {
1950 		window = window->PreviousWindow(fCurrentWorkspace);
1951 	}
1952 
1953 	if (window != NULL && (window->Flags() & kAcceptKeyboardFocusFlag) != 0)
1954 		return &window->EventTarget();
1955 
1956 	if (FocusWindow() != NULL)
1957 		return &FocusWindow()->EventTarget();
1958 
1959 	return NULL;
1960 }
1961 
1962 
1963 /*!	Tries to set the focus to the specified \a focus window. It will make sure,
1964 	however, that the window actually can have focus. You are allowed to pass
1965 	in a NULL pointer for \a focus.
1966 
1967 	Besides the B_AVOID_FOCUS flag, a modal window, or a BWindowScreen can both
1968 	prevent it from getting focus.
1969 
1970 	In any case, this method makes sure that there is a focus window, if there
1971 	is any window at all, that is.
1972 */
1973 void
1974 Desktop::SetFocusWindow(Window* nextFocus)
1975 {
1976 	if (!LockAllWindows())
1977 		return;
1978 
1979 	// test for B_LOCK_WINDOW_FOCUS
1980 	if (fLockedFocusWindow && nextFocus != fLockedFocusWindow) {
1981 		UnlockAllWindows();
1982 		return;
1983 	}
1984 
1985 	bool hasModal = _WindowHasModal(nextFocus);
1986 	bool hasWindowScreen = false;
1987 
1988 	if (!hasModal && nextFocus != NULL) {
1989 		// Check whether or not a window screen is in front of the window
1990 		// (if it has a modal, the right thing is done, anyway)
1991 		Window* window = nextFocus;
1992 		while (true) {
1993 			window = window->NextWindow(fCurrentWorkspace);
1994 			if (window == NULL || window->Feel() == kWindowScreenFeel)
1995 				break;
1996 		}
1997 		if (window != NULL)
1998 			hasWindowScreen = true;
1999 	}
2000 
2001 	if (nextFocus == fFocus && nextFocus != NULL && !nextFocus->IsHidden()
2002 		&& (nextFocus->Flags() & B_AVOID_FOCUS) == 0
2003 		&& !hasModal && !hasWindowScreen) {
2004 		// the window that is supposed to get focus already has focus
2005 		UnlockAllWindows();
2006 		return;
2007 	}
2008 
2009 	uint32 listIndex = fCurrentWorkspace;
2010 	WindowList* list = &_Windows(fCurrentWorkspace);
2011 	if (!fSettings->NormalMouse()) {
2012 		listIndex = kFocusList;
2013 		list = &fFocusList;
2014 	}
2015 
2016 	if (nextFocus == NULL || hasModal || hasWindowScreen) {
2017 		nextFocus = list->LastWindow();
2018 
2019 		if (fSettings->NormalMouse()) {
2020 			// If the last window having focus is a window that cannot make it
2021 			// to the front, we use that as the next focus
2022 			Window* lastFocus = fFocusList.LastWindow();
2023 			if (lastFocus != NULL && !lastFocus->SupportsFront()
2024 				&& _WindowCanHaveFocus(lastFocus)) {
2025 				nextFocus = lastFocus;
2026 			}
2027 		}
2028 	}
2029 
2030 	// make sure no window is chosen that doesn't want focus or cannot have it
2031 	while (nextFocus != NULL && !_WindowCanHaveFocus(nextFocus)) {
2032 		nextFocus = nextFocus->PreviousWindow(listIndex);
2033 	}
2034 
2035 	if (fFocus == nextFocus) {
2036 		// turns out the window that is supposed to get focus now already has it
2037 		UnlockAllWindows();
2038 		return;
2039 	}
2040 
2041 	team_id oldActiveApp = -1;
2042 	team_id newActiveApp = -1;
2043 
2044 	if (fFocus != NULL) {
2045 		fFocus->SetFocus(false);
2046 		oldActiveApp = fFocus->ServerWindow()->App()->ClientTeam();
2047 	}
2048 
2049 	fFocus = nextFocus;
2050 
2051 	if (fFocus != NULL) {
2052 		fFocus->SetFocus(true);
2053 		newActiveApp = fFocus->ServerWindow()->App()->ClientTeam();
2054 
2055 		// move current focus to the end of the focus list
2056 		fFocusList.RemoveWindow(fFocus);
2057 		fFocusList.AddWindow(fFocus);
2058 	}
2059 
2060 	if (newActiveApp == -1) {
2061 		// make sure the cursor is visible
2062 		HWInterface()->SetCursorVisible(true);
2063 	}
2064 
2065 	UnlockAllWindows();
2066 
2067 	// change the "active" app if appropriate
2068 	if (oldActiveApp == newActiveApp)
2069 		return;
2070 
2071 	BAutolock locker(fApplicationsLock);
2072 
2073 	for (int32 i = 0; i < fApplications.CountItems(); i++) {
2074 		ServerApp* app = fApplications.ItemAt(i);
2075 
2076 		if (oldActiveApp != -1 && app->ClientTeam() == oldActiveApp)
2077 			app->Activate(false);
2078 		else if (newActiveApp != -1 && app->ClientTeam() == newActiveApp)
2079 			app->Activate(true);
2080 	}
2081 }
2082 
2083 
2084 void
2085 Desktop::SetFocusLocked(const Window* window)
2086 {
2087 	AutoWriteLocker _(fWindowLock);
2088 
2089 	if (window != NULL) {
2090 		// Don't allow this to be set when no mouse buttons
2091 		// are pressed. (BView::SetMouseEventMask() should only be called
2092 		// from mouse hooks.)
2093 		if (fLastMouseButtons == 0)
2094 			return;
2095 	}
2096 
2097 	fLockedFocusWindow = window;
2098 }
2099 
2100 
2101 Window*
2102 Desktop::FindWindowByClientToken(int32 token, team_id teamID)
2103 {
2104 	for (Window *window = fAllWindows.FirstWindow(); window != NULL;
2105 			window = window->NextWindow(kAllWindowList)) {
2106 		if (window->ServerWindow()->ClientToken() == token
2107 			&& window->ServerWindow()->ClientTeam() == teamID) {
2108 			return window;
2109 		}
2110 	}
2111 
2112 	return NULL;
2113 }
2114 
2115 
2116 ::EventTarget*
2117 Desktop::FindTarget(BMessenger& messenger)
2118 {
2119 	for (Window *window = fAllWindows.FirstWindow(); window != NULL;
2120 			window = window->NextWindow(kAllWindowList)) {
2121 		if (window->EventTarget().Messenger() == messenger)
2122 			return &window->EventTarget();
2123 	}
2124 
2125 	return NULL;
2126 }
2127 
2128 
2129 void
2130 Desktop::MarkDirty(BRegion& region)
2131 {
2132 	if (region.CountRects() == 0)
2133 		return;
2134 
2135 	if (LockAllWindows()) {
2136 		// send redraw messages to all windows intersecting the dirty region
2137 		_TriggerWindowRedrawing(region);
2138 
2139 		UnlockAllWindows();
2140 	}
2141 }
2142 
2143 
2144 void
2145 Desktop::Redraw()
2146 {
2147 	BRegion dirty(fVirtualScreen.Frame());
2148 	MarkDirty(dirty);
2149 }
2150 
2151 
2152 /*!	\brief Redraws the background (ie. the desktop window, if any).
2153 */
2154 void
2155 Desktop::RedrawBackground()
2156 {
2157 	LockAllWindows();
2158 
2159 	BRegion redraw;
2160 
2161 	Window* window = CurrentWindows().FirstWindow();
2162 	if (window != NULL && window->Feel() == kDesktopWindowFeel) {
2163 		redraw = window->VisibleContentRegion();
2164 
2165 		// look for desktop background view, and update its background color
2166 		// TODO: is there a better way to do this?
2167 		View* view = window->TopView();
2168 		if (view != NULL)
2169 			view = view->FirstChild();
2170 
2171 		while (view != NULL) {
2172 			if (view->IsDesktopBackground()) {
2173 				view->SetViewColor(fWorkspaces[fCurrentWorkspace].Color());
2174 				break;
2175 			}
2176 			view = view->NextSibling();
2177 		}
2178 
2179 		window->ProcessDirtyRegion(redraw);
2180 	} else {
2181 		redraw = BackgroundRegion();
2182 		fBackgroundRegion.MakeEmpty();
2183 		_SetBackground(redraw);
2184 	}
2185 
2186 	_WindowChanged(NULL);
2187 		// update workspaces view as well
2188 
2189 	UnlockAllWindows();
2190 }
2191 
2192 
2193 bool
2194 Desktop::ReloadDecor(DecorAddOn* oldDecor)
2195 {
2196 	AutoWriteLocker _(fWindowLock);
2197 
2198 	bool returnValue = true;
2199 
2200 	if (oldDecor != NULL) {
2201 		const DesktopListenerList* oldListeners
2202 			= &oldDecor->GetDesktopListeners();
2203 		for (int i = 0; i < oldListeners->CountItems(); i++)
2204 			UnregisterListener(oldListeners->ItemAt(i));
2205 	}
2206 
2207 	for (Window* window = fAllWindows.FirstWindow(); window != NULL;
2208 			window = window->NextWindow(kAllWindowList)) {
2209 		BRegion oldBorder;
2210 		window->GetBorderRegion(&oldBorder);
2211 
2212 		if (!window->ReloadDecor()) {
2213 			// prevent unloading previous add-on
2214 			returnValue = false;
2215 		}
2216 
2217 		BRegion border;
2218 		window->GetBorderRegion(&border);
2219 
2220 		border.Include(&oldBorder);
2221 		RebuildAndRedrawAfterWindowChange(window, border);
2222 	}
2223 
2224 	// register new listeners
2225 	const DesktopListenerList& newListeners
2226 		= gDecorManager.GetDesktopListeners();
2227 	for (int i = 0; i < newListeners.CountItems(); i++)
2228  		RegisterListener(newListeners.ItemAt(i));
2229 
2230  	return returnValue;
2231 }
2232 
2233 
2234 void
2235 Desktop::MinimizeApplication(team_id team)
2236 {
2237 	AutoWriteLocker locker(fWindowLock);
2238 
2239 	// Just minimize all windows of that application
2240 
2241 	for (Window *window = fAllWindows.FirstWindow(); window != NULL;
2242 			window = window->NextWindow(kAllWindowList)) {
2243 		if (window->ServerWindow()->ClientTeam() != team)
2244 			continue;
2245 
2246 		window->ServerWindow()->NotifyMinimize(true);
2247 	}
2248 }
2249 
2250 
2251 void
2252 Desktop::BringApplicationToFront(team_id team)
2253 {
2254 	AutoWriteLocker locker(fWindowLock);
2255 
2256 	// TODO: for now, just maximize all windows of that application
2257 	// TODO: have the ability to lock the current workspace
2258 
2259 	for (Window *window = fAllWindows.FirstWindow(); window != NULL;
2260 			window = window->NextWindow(kAllWindowList)) {
2261 		if (window->ServerWindow()->ClientTeam() != team)
2262 			continue;
2263 
2264 		window->ServerWindow()->NotifyMinimize(false);
2265 	}
2266 }
2267 
2268 
2269 void
2270 Desktop::WindowAction(int32 windowToken, int32 action)
2271 {
2272 	if (action != B_MINIMIZE_WINDOW && action != B_BRING_TO_FRONT)
2273 		return;
2274 
2275 	LockAllWindows();
2276 
2277 	::ServerWindow* serverWindow;
2278 	Window* window;
2279 	if (BPrivate::gDefaultTokens.GetToken(windowToken,
2280 			B_SERVER_TOKEN, (void**)&serverWindow) != B_OK
2281 		|| (window = serverWindow->Window()) == NULL) {
2282 		UnlockAllWindows();
2283 		return;
2284 	}
2285 
2286 	if (action == B_BRING_TO_FRONT && !window->IsMinimized()) {
2287 		// the window is visible, we just need to make it the front window
2288 		ActivateWindow(window);
2289 	} else {
2290 		// if not, ask the window if it wants to be unminimized
2291 		serverWindow->NotifyMinimize(action == B_MINIMIZE_WINDOW);
2292 	}
2293 
2294 	UnlockAllWindows();
2295 }
2296 
2297 
2298 void
2299 Desktop::WriteWindowList(team_id team, BPrivate::LinkSender& sender)
2300 {
2301 	AutoWriteLocker locker(fWindowLock);
2302 
2303 	// compute the number of windows
2304 
2305 	int32 count = 0;
2306 
2307 	for (Window *window = fAllWindows.FirstWindow(); window != NULL;
2308 			window = window->NextWindow(kAllWindowList)) {
2309 		if (team < B_OK || window->ServerWindow()->ClientTeam() == team)
2310 			count++;
2311 	}
2312 
2313 	// write list
2314 
2315 	sender.StartMessage(B_OK);
2316 	sender.Attach<int32>(count);
2317 
2318 	// first write the windows of the current workspace correctly ordered
2319 	for (Window *window = CurrentWindows().LastWindow(); window != NULL;
2320 			window = window->PreviousWindow(fCurrentWorkspace)) {
2321 		if (team >= B_OK && window->ServerWindow()->ClientTeam() != team)
2322 			continue;
2323 
2324 		sender.Attach<int32>(window->ServerWindow()->ServerToken());
2325 	}
2326 
2327 	// then write all the other windows
2328 	for (Window *window = fAllWindows.FirstWindow(); window != NULL;
2329 			window = window->NextWindow(kAllWindowList)) {
2330 		if ((team >= B_OK && window->ServerWindow()->ClientTeam() != team)
2331 			|| window->InWorkspace(fCurrentWorkspace))
2332 			continue;
2333 
2334 		sender.Attach<int32>(window->ServerWindow()->ServerToken());
2335 	}
2336 
2337 	sender.Flush();
2338 }
2339 
2340 
2341 void
2342 Desktop::WriteWindowInfo(int32 serverToken, BPrivate::LinkSender& sender)
2343 {
2344 	AutoWriteLocker locker(fWindowLock);
2345 	BAutolock tokenLocker(BPrivate::gDefaultTokens);
2346 
2347 	::ServerWindow* window;
2348 	if (BPrivate::gDefaultTokens.GetToken(serverToken,
2349 			B_SERVER_TOKEN, (void**)&window) != B_OK) {
2350 		sender.StartMessage(B_ENTRY_NOT_FOUND);
2351 		sender.Flush();
2352 		return;
2353 	}
2354 
2355 	window_info info;
2356 	window->GetInfo(info);
2357 
2358 	float tabSize = 0.0;
2359 	float borderSize = 0.0;
2360 	::Window* tmp = window->Window();
2361 	if (tmp) {
2362 		BMessage message;
2363 		if (tmp->GetDecoratorSettings(&message)) {
2364 			BRect tabFrame;
2365 			message.FindRect("tab frame", &tabFrame);
2366 			tabSize = tabFrame.bottom - tabFrame.top;
2367 			message.FindFloat("border width", &borderSize);
2368 		}
2369 	}
2370 
2371 	int32 length = window->Title() ? strlen(window->Title()) : 0;
2372 
2373 	sender.StartMessage(B_OK);
2374 	sender.Attach<int32>(sizeof(client_window_info) + length);
2375 	sender.Attach(&info, sizeof(window_info));
2376 	sender.Attach<float>(tabSize);
2377 	sender.Attach<float>(borderSize);
2378 
2379 	if (length > 0)
2380 		sender.Attach(window->Title(), length + 1);
2381 	else
2382 		sender.Attach<char>('\0');
2383 
2384 	sender.Flush();
2385 }
2386 
2387 
2388 void
2389 Desktop::WriteWindowOrder(int32 workspace, BPrivate::LinkSender& sender)
2390 {
2391 	LockSingleWindow();
2392 
2393 	if (workspace < 0)
2394 		workspace = fCurrentWorkspace;
2395 	else if (workspace >= kMaxWorkspaces) {
2396 		sender.StartMessage(B_BAD_VALUE);
2397 		sender.Flush();
2398 		UnlockSingleWindow();
2399 		return;
2400 	}
2401 
2402 	int32 count = _Windows(workspace).Count();
2403 
2404 	// write list
2405 
2406 	sender.StartMessage(B_OK);
2407 	sender.Attach<int32>(count);
2408 
2409 	for (Window *window = _Windows(workspace).LastWindow(); window != NULL;
2410 			window = window->PreviousWindow(workspace)) {
2411 		sender.Attach<int32>(window->ServerWindow()->ServerToken());
2412 	}
2413 
2414 	sender.Flush();
2415 
2416 	UnlockSingleWindow();
2417 }
2418 
2419 
2420 void
2421 Desktop::WriteApplicationOrder(int32 workspace, BPrivate::LinkSender& sender)
2422 {
2423 	fApplicationsLock.Lock();
2424 	LockSingleWindow();
2425 
2426 	int32 maxCount = fApplications.CountItems();
2427 
2428 	fApplicationsLock.Unlock();
2429 		// as long as we hold the window lock, no new window can appear
2430 
2431 	if (workspace < 0)
2432 		workspace = fCurrentWorkspace;
2433 	else if (workspace >= kMaxWorkspaces) {
2434 		sender.StartMessage(B_BAD_VALUE);
2435 		sender.Flush();
2436 		UnlockSingleWindow();
2437 		return;
2438 	}
2439 
2440 	// compute the list of applications on this workspace
2441 
2442 	team_id* teams = (team_id*)malloc(maxCount * sizeof(team_id));
2443 	if (teams == NULL) {
2444 		sender.StartMessage(B_NO_MEMORY);
2445 		sender.Flush();
2446 		UnlockSingleWindow();
2447 		return;
2448 	}
2449 
2450 	int32 count = 0;
2451 
2452 	for (Window *window = _Windows(workspace).LastWindow(); window != NULL;
2453 			window = window->PreviousWindow(workspace)) {
2454 		team_id team = window->ServerWindow()->ClientTeam();
2455 		if (count > 1) {
2456 			// see if we already have this team
2457 			bool found = false;
2458 			for (int32 i = 0; i < count; i++) {
2459 				if (teams[i] == team) {
2460 					found = true;
2461 					break;
2462 				}
2463 			}
2464 			if (found)
2465 				continue;
2466 		}
2467 
2468 		ASSERT(count < maxCount);
2469 		teams[count++] = team;
2470 	}
2471 
2472 	UnlockSingleWindow();
2473 
2474 	// write list
2475 
2476 	sender.StartMessage(B_OK);
2477 	sender.Attach<int32>(count);
2478 
2479 	for (int32 i = 0; i < count; i++) {
2480 		sender.Attach<int32>(teams[i]);
2481 	}
2482 
2483 	sender.Flush();
2484 	free(teams);
2485 }
2486 
2487 
2488 void
2489 Desktop::_LaunchInputServer()
2490 {
2491 	BRoster roster;
2492 	status_t status = roster.Launch("application/x-vnd.Be-input_server");
2493 	if (status == B_OK || status == B_ALREADY_RUNNING)
2494 		return;
2495 
2496 	// Could not load input_server by signature, try well-known location
2497 
2498 	BEntry entry;
2499 	BPath inputServerPath;
2500 	if (find_directory(B_SYSTEM_SERVERS_DIRECTORY, &inputServerPath) == B_OK
2501 		&& inputServerPath.Append("input_server") == B_OK) {
2502 		entry.SetTo(inputServerPath.Path());
2503 	} else
2504 		entry.SetTo("/system/servers/input_server");
2505 	entry_ref ref;
2506 	status_t entryStatus = entry.GetRef(&ref);
2507 	if (entryStatus == B_OK)
2508 		entryStatus = roster.Launch(&ref);
2509 	if (entryStatus == B_OK || entryStatus == B_ALREADY_RUNNING) {
2510 		syslog(LOG_ERR, "Failed to launch the input server by signature: %s!\n",
2511 			strerror(status));
2512 		return;
2513 	}
2514 
2515 	syslog(LOG_ERR, "Failed to launch the input server: %s!\n",
2516 		strerror(entryStatus));
2517 }
2518 
2519 
2520 void
2521 Desktop::_GetLooperName(char* name, size_t length)
2522 {
2523 	snprintf(name, length, "d:%d:%s", fUserID,
2524 		fTargetScreen == NULL ? "baron" : fTargetScreen);
2525 }
2526 
2527 
2528 void
2529 Desktop::_PrepareQuit()
2530 {
2531 	// let's kill all remaining applications
2532 
2533 	fApplicationsLock.Lock();
2534 
2535 	int32 count = fApplications.CountItems();
2536 	for (int32 i = 0; i < count; i++) {
2537 		ServerApp *app = fApplications.ItemAt(i);
2538 		team_id clientTeam = app->ClientTeam();
2539 
2540 		app->Quit();
2541 		kill_team(clientTeam);
2542 	}
2543 
2544 	// wait for the last app to die
2545 	if (count > 0) {
2546 		acquire_sem_etc(fShutdownSemaphore, fShutdownCount, B_RELATIVE_TIMEOUT,
2547 			250000);
2548 	}
2549 
2550 	fApplicationsLock.Unlock();
2551 }
2552 
2553 
2554 void
2555 Desktop::_DispatchMessage(int32 code, BPrivate::LinkReceiver& link)
2556 {
2557 	switch (code) {
2558 		case AS_CREATE_APP:
2559 		{
2560 			// Create the ServerApp to node monitor a new BApplication
2561 
2562 			// Attached data:
2563 			// 1) port_id - receiver port of a regular app
2564 			// 2) port_id - client looper port - for sending messages to the
2565 			//		client
2566 			// 2) team_id - app's team ID
2567 			// 3) int32 - handler token of the regular app
2568 			// 4) char * - signature of the regular app
2569 
2570 			// Find the necessary data
2571 			team_id	clientTeamID = -1;
2572 			port_id	clientLooperPort = -1;
2573 			port_id clientReplyPort = -1;
2574 			int32 htoken = B_NULL_TOKEN;
2575 			char* appSignature = NULL;
2576 
2577 			link.Read<port_id>(&clientReplyPort);
2578 			link.Read<port_id>(&clientLooperPort);
2579 			link.Read<team_id>(&clientTeamID);
2580 			link.Read<int32>(&htoken);
2581 			if (link.ReadString(&appSignature) != B_OK)
2582 				break;
2583 
2584 			ObjectDeleter<ServerApp> app(new (std::nothrow) ServerApp(this, clientReplyPort,
2585 				clientLooperPort, clientTeamID, htoken, appSignature));
2586 			status_t status = B_OK;
2587 			if (!app.IsSet())
2588 				status = B_NO_MEMORY;
2589 			if (status == B_OK)
2590 				status = app->InitCheck();
2591 			if (status == B_OK)
2592 				status = app->Run();
2593 			if (status == B_OK) {
2594 				// add the new ServerApp to the known list of ServerApps
2595 				fApplicationsLock.Lock();
2596 				fApplications.AddItem(app.Detach());
2597 				fApplicationsLock.Unlock();
2598 			} else {
2599 				// if everything went well, ServerApp::Run() will notify
2600 				// the client - but since it didn't, we do it here
2601 				BPrivate::LinkSender reply(clientReplyPort);
2602 				reply.StartMessage(status);
2603 				reply.Flush();
2604 			}
2605 
2606 			// This is necessary because BPortLink::ReadString allocates memory
2607 			free(appSignature);
2608 			break;
2609 		}
2610 
2611 		case AS_DELETE_APP:
2612 		{
2613 			// Delete a ServerApp. Received only from the respective ServerApp
2614 			// when a BApplication asks it to quit.
2615 
2616 			// Attached Data:
2617 			// 1) thread_id - thread ID of the ServerApp to be deleted
2618 
2619 			thread_id thread = -1;
2620 			if (link.Read<thread_id>(&thread) < B_OK)
2621 				break;
2622 
2623 			fApplicationsLock.Lock();
2624 
2625 			// Run through the list of apps and nuke the proper one
2626 
2627 			int32 count = fApplications.CountItems();
2628 			ServerApp* removeApp = NULL;
2629 
2630 			for (int32 i = 0; i < count; i++) {
2631 				ServerApp* app = fApplications.ItemAt(i);
2632 
2633 				if (app->Thread() == thread) {
2634 					fApplications.RemoveItemAt(i);
2635 					removeApp = app;
2636 					break;
2637 				}
2638 			}
2639 
2640 			fApplicationsLock.Unlock();
2641 
2642 			if (removeApp != NULL)
2643 				removeApp->Quit(fShutdownSemaphore);
2644 
2645 			if (fQuitting && count <= 1) {
2646 				// wait for the last app to die
2647 				acquire_sem_etc(fShutdownSemaphore, fShutdownCount,
2648 					B_RELATIVE_TIMEOUT, 500000);
2649 				PostMessage(kMsgQuitLooper);
2650 			}
2651 			break;
2652 		}
2653 
2654 		case AS_ACTIVATE_APP:
2655 		{
2656 			// Someone is requesting to activation of a certain app.
2657 
2658 			// Attached data:
2659 			// 1) port_id reply port
2660 			// 2) team_id team
2661 
2662 			status_t status;
2663 
2664 			// get the parameters
2665 			port_id replyPort;
2666 			team_id team;
2667 			if (link.Read(&replyPort) == B_OK
2668 				&& link.Read(&team) == B_OK)
2669 				status = _ActivateApp(team);
2670 			else
2671 				status = B_ERROR;
2672 
2673 			// send the reply
2674 			BPrivate::PortLink replyLink(replyPort);
2675 			replyLink.StartMessage(status);
2676 			replyLink.Flush();
2677 			break;
2678 		}
2679 
2680 		case AS_APP_CRASHED:
2681 		case AS_DUMP_ALLOCATOR:
2682 		case AS_DUMP_BITMAPS:
2683 		{
2684 			BAutolock locker(fApplicationsLock);
2685 
2686 			team_id team;
2687 			if (link.Read(&team) != B_OK)
2688 				break;
2689 
2690 			for (int32 i = 0; i < fApplications.CountItems(); i++) {
2691 				ServerApp* app = fApplications.ItemAt(i);
2692 
2693 				if (app->ClientTeam() == team)
2694 					app->PostMessage(code);
2695 			}
2696 			break;
2697 		}
2698 
2699 		case AS_EVENT_STREAM_CLOSED:
2700 			_LaunchInputServer();
2701 			break;
2702 
2703 		case B_QUIT_REQUESTED:
2704 			// We've been asked to quit, so (for now) broadcast to all
2705 			// test apps to quit. This situation will occur only when the
2706 			// server is compiled as a regular Be application.
2707 
2708 			fApplicationsLock.Lock();
2709 			fShutdownSemaphore = create_sem(0, "desktop shutdown");
2710 			fShutdownCount = fApplications.CountItems();
2711 			fApplicationsLock.Unlock();
2712 
2713 			fQuitting = true;
2714 			BroadcastToAllApps(AS_QUIT_APP);
2715 
2716 			// We now need to process the remaining AS_DELETE_APP messages.
2717 			// We quit the looper when the last app is deleted.
2718 
2719 			// if there are no apps to quit, shutdown directly
2720 			if (fShutdownCount == 0)
2721 				PostMessage(kMsgQuitLooper);
2722 
2723 			break;
2724 
2725 		case AS_ACTIVATE_WORKSPACE:
2726 		{
2727 			int32 index;
2728 			link.Read<int32>(&index);
2729 			if (index == -1)
2730 				index = fPreviousWorkspace;
2731 
2732 			bool moveFocusWindow;
2733 			link.Read<bool>(&moveFocusWindow);
2734 
2735 			SetWorkspace(index, moveFocusWindow);
2736 			break;
2737 		}
2738 
2739 		case AS_TALK_TO_DESKTOP_LISTENER:
2740 		{
2741 			port_id clientReplyPort;
2742 			if (link.Read<port_id>(&clientReplyPort) != B_OK)
2743 				break;
2744 
2745 			BPrivate::LinkSender reply(clientReplyPort);
2746 			AutoWriteLocker locker(fWindowLock);
2747 			if (MessageForListener(NULL, link, reply) != true) {
2748 				// unhandled message, at least send an error if needed
2749 				if (link.NeedsReply()) {
2750 					reply.StartMessage(B_ERROR);
2751 					reply.Flush();
2752 				}
2753 			}
2754 			break;
2755 		}
2756 
2757 		case AS_SET_UI_COLOR:
2758 		{
2759 			color_which which;
2760 			rgb_color color;
2761 
2762 			if (link.Read<color_which>(&which) == B_OK
2763 					&& link.Read<rgb_color>(&color) == B_OK) {
2764 
2765 				const char* colorName = ui_color_name(which);
2766 				fPendingColors.SetColor(colorName, color);
2767 
2768 				DelayedMessage delayed(AS_SET_UI_COLORS, DM_60HZ_DELAY);
2769 				delayed.AddTarget(MessagePort());
2770 				delayed.SetMerge(DM_MERGE_CANCEL);
2771 
2772 				delayed.Attach<bool>(true);
2773 				delayed.Flush();
2774 			}
2775 
2776 			break;
2777 		}
2778 
2779 		case AS_SET_UI_COLORS:
2780 		{
2781 			bool flushPendingOnly = false;
2782 
2783 			if (link.Read<bool>(&flushPendingOnly) != B_OK
2784 				|| (flushPendingOnly &&
2785 						fPendingColors.CountNames(B_RGB_32_BIT_TYPE) == 0)) {
2786 				break;
2787 			}
2788 
2789 			if (!flushPendingOnly) {
2790 				// Client wants to set a color map
2791 				color_which which = B_NO_COLOR;
2792 				rgb_color color;
2793 
2794 				do {
2795 					if (link.Read<color_which>(&which) != B_OK
2796 						|| link.Read<rgb_color>(&color) != B_OK)
2797 						break;
2798 
2799 					fPendingColors.SetColor(ui_color_name(which), color);
2800 				} while (which != B_NO_COLOR);
2801 			}
2802 
2803 			_FlushPendingColors();
2804 			break;
2805 		}
2806 
2807 		// ToDo: Remove this again. It is a message sent by the
2808 		// invalidate_on_exit kernel debugger add-on to trigger a redraw
2809 		// after exiting a kernel debugger session.
2810 		case 'KDLE':
2811 		{
2812 			BRegion dirty;
2813 			dirty.Include(fVirtualScreen.Frame());
2814 			MarkDirty(dirty);
2815 			break;
2816 		}
2817 
2818 		default:
2819 			printf("Desktop %d:%s received unexpected code %" B_PRId32 "\n", 0,
2820 				"baron", code);
2821 
2822 			if (link.NeedsReply()) {
2823 				// the client is now blocking and waiting for a reply!
2824 				fLink.StartMessage(B_ERROR);
2825 				fLink.Flush();
2826 			}
2827 			break;
2828 	}
2829 }
2830 
2831 
2832 WindowList&
2833 Desktop::CurrentWindows()
2834 {
2835 	return fWorkspaces[fCurrentWorkspace].Windows();
2836 }
2837 
2838 
2839 WindowList&
2840 Desktop::AllWindows()
2841 {
2842 	return fAllWindows;
2843 }
2844 
2845 
2846 Window*
2847 Desktop::WindowForClientLooperPort(port_id port)
2848 {
2849 	ASSERT_MULTI_LOCKED(fWindowLock);
2850 
2851 	for (Window* window = fAllWindows.FirstWindow(); window != NULL;
2852 			window = window->NextWindow(kAllWindowList)) {
2853 		if (window->ServerWindow()->ClientLooperPort() == port)
2854 			return window;
2855 	}
2856 	return NULL;
2857 }
2858 
2859 
2860 WindowList&
2861 Desktop::_Windows(int32 index)
2862 {
2863 	ASSERT(index >= 0 && index < kMaxWorkspaces);
2864 	return fWorkspaces[index].Windows();
2865 }
2866 
2867 
2868 void
2869 Desktop::_FlushPendingColors()
2870 {
2871 	// Update all windows while we are holding the write lock.
2872 
2873 	int32 count = fPendingColors.CountNames(B_RGB_32_BIT_TYPE);
2874 	if (count == 0)
2875 		return;
2876 
2877 	bool changed[count];
2878 	LockedDesktopSettings settings(this);
2879 	settings.SetUIColors(fPendingColors, &changed[0]);
2880 
2881 	int32 index = 0;
2882 	char* name = NULL;
2883 	type_code type = B_RGB_32_BIT_TYPE;
2884 	rgb_color color;
2885 	color_which which = B_NO_COLOR;
2886 	BMessage clientMessage(B_COLORS_UPDATED);
2887 
2888 	while (fPendingColors.GetInfo(type, index, &name, &type) == B_OK) {
2889 		which = which_ui_color(name);
2890 		if (which == B_NO_COLOR || fPendingColors.FindColor(name,
2891 				&color) != B_OK || !changed[index]) {
2892 			++index;
2893 			continue;
2894 		}
2895 
2896 		for (Window* window = fAllWindows.FirstWindow(); window != NULL;
2897 				window = window->NextWindow(kAllWindowList)) {
2898 			ColorUpdated(window, which, color);
2899 		}
2900 
2901 		// Ensure client only gets list of changed colors
2902 		clientMessage.AddColor(name, color);
2903 		++index;
2904 	}
2905 
2906 	// Notify client applications
2907 	BAutolock appListLock(fApplicationsLock);
2908 	for (int32 index = 0; index < fApplications.CountItems(); ++index) {
2909 		fApplications.ItemAt(index)->SendMessageToClient(&clientMessage);
2910 	}
2911 
2912 	fPendingColors.MakeEmpty();
2913 }
2914 
2915 
2916 void
2917 Desktop::_UpdateFloating(int32 previousWorkspace, int32 nextWorkspace,
2918 	Window* mouseEventWindow)
2919 {
2920 	if (previousWorkspace == -1)
2921 		previousWorkspace = fCurrentWorkspace;
2922 	if (nextWorkspace == -1)
2923 		nextWorkspace = previousWorkspace;
2924 
2925 	for (Window* floating = fSubsetWindows.FirstWindow(); floating != NULL;
2926 			floating = floating->NextWindow(kSubsetList)) {
2927 		// we only care about app/subset floating windows
2928 		if (floating->Feel() != B_FLOATING_SUBSET_WINDOW_FEEL
2929 			&& floating->Feel() != B_FLOATING_APP_WINDOW_FEEL)
2930 			continue;
2931 
2932 		if (fFront != NULL && fFront->IsNormal()
2933 			&& floating->HasInSubset(fFront)) {
2934 			// is now visible
2935 			if (_Windows(previousWorkspace).HasWindow(floating)
2936 				&& previousWorkspace != nextWorkspace
2937 				&& !floating->InSubsetWorkspace(previousWorkspace)) {
2938 				// but no longer on the previous workspace
2939 				_Windows(previousWorkspace).RemoveWindow(floating);
2940 				floating->SetCurrentWorkspace(-1);
2941 			}
2942 
2943 			if (!_Windows(nextWorkspace).HasWindow(floating)) {
2944 				// but wasn't before
2945 				_Windows(nextWorkspace).AddWindow(floating,
2946 					floating->Frontmost(_Windows(nextWorkspace).FirstWindow(),
2947 					nextWorkspace));
2948 				floating->SetCurrentWorkspace(nextWorkspace);
2949 				if (mouseEventWindow != fFront)
2950 					_ShowWindow(floating);
2951 
2952 				// TODO: put the floating last in the floating window list to
2953 				// preserve the on screen window order
2954 			}
2955 		} else if (_Windows(previousWorkspace).HasWindow(floating)
2956 			&& !floating->InSubsetWorkspace(previousWorkspace)) {
2957 			// was visible, but is no longer
2958 
2959 			_Windows(previousWorkspace).RemoveWindow(floating);
2960 			floating->SetCurrentWorkspace(-1);
2961 			_HideWindow(floating);
2962 
2963 			if (FocusWindow() == floating)
2964 				SetFocusWindow();
2965 		}
2966 	}
2967 }
2968 
2969 
2970 /*!	Search the visible windows for a valid back window
2971 	(only desktop windows can't be back windows)
2972 */
2973 void
2974 Desktop::_UpdateBack()
2975 {
2976 	fBack = NULL;
2977 
2978 	for (Window* window = CurrentWindows().FirstWindow(); window != NULL;
2979 			window = window->NextWindow(fCurrentWorkspace)) {
2980 		if (window->IsHidden() || window->Feel() == kDesktopWindowFeel)
2981 			continue;
2982 
2983 		fBack = window;
2984 		break;
2985 	}
2986 }
2987 
2988 
2989 /*!	Search the visible windows for a valid front window
2990 	(only normal and modal windows can be front windows)
2991 
2992 	The only place where you don't want to update floating windows is
2993 	during a workspace change - because then you'll call _UpdateFloating()
2994 	yourself.
2995 */
2996 void
2997 Desktop::_UpdateFront(bool updateFloating)
2998 {
2999 	fFront = NULL;
3000 
3001 	for (Window* window = CurrentWindows().LastWindow(); window != NULL;
3002 			window = window->PreviousWindow(fCurrentWorkspace)) {
3003 		if (window->IsHidden() || window->IsFloating()
3004 			|| !window->SupportsFront())
3005 			continue;
3006 
3007 		fFront = window;
3008 		break;
3009 	}
3010 
3011 	if (updateFloating)
3012 		_UpdateFloating();
3013 }
3014 
3015 
3016 void
3017 Desktop::_UpdateFronts(bool updateFloating)
3018 {
3019 	_UpdateBack();
3020 	_UpdateFront(updateFloating);
3021 }
3022 
3023 
3024 bool
3025 Desktop::_WindowHasModal(Window* window) const
3026 {
3027 	if (window == NULL)
3028 		return false;
3029 
3030 	for (Window* modal = fSubsetWindows.FirstWindow(); modal != NULL;
3031 			modal = modal->NextWindow(kSubsetList)) {
3032 		// only visible modal windows count
3033 		if (!modal->IsModal() || modal->IsHidden())
3034 			continue;
3035 
3036 		if (modal->HasInSubset(window))
3037 			return true;
3038 	}
3039 
3040 	return false;
3041 }
3042 
3043 
3044 /*!	Determines whether or not the specified \a window can have focus at all.
3045 */
3046 bool
3047 Desktop::_WindowCanHaveFocus(Window* window) const
3048 {
3049 	return window != NULL
3050 		&& window->InWorkspace(fCurrentWorkspace)
3051 		&& (window->Flags() & B_AVOID_FOCUS) == 0
3052 		&& !_WindowHasModal(window)
3053 		&& !window->IsHidden();
3054 }
3055 
3056 
3057 /*!	You must at least hold a single window lock when calling this method.
3058 */
3059 void
3060 Desktop::_WindowChanged(Window* window)
3061 {
3062 	ASSERT_MULTI_LOCKED(fWindowLock);
3063 
3064 	BAutolock _(fWorkspacesLock);
3065 
3066 	for (uint32 i = fWorkspacesViews.CountItems(); i-- > 0;) {
3067 		WorkspacesView* view = fWorkspacesViews.ItemAt(i);
3068 		view->WindowChanged(window);
3069 	}
3070 }
3071 
3072 
3073 /*!	You must at least hold a single window lock when calling this method.
3074 */
3075 void
3076 Desktop::_WindowRemoved(Window* window)
3077 {
3078 	ASSERT_MULTI_LOCKED(fWindowLock);
3079 
3080 	BAutolock _(fWorkspacesLock);
3081 
3082 	for (uint32 i = fWorkspacesViews.CountItems(); i-- > 0;) {
3083 		WorkspacesView* view = fWorkspacesViews.ItemAt(i);
3084 		view->WindowRemoved(window);
3085 	}
3086 }
3087 
3088 
3089 /*!	Shows the window on the screen - it does this independently of the
3090 	Window::IsHidden() state.
3091 */
3092 void
3093 Desktop::_ShowWindow(Window* window, bool affectsOtherWindows)
3094 {
3095 	BRegion background;
3096 	_RebuildClippingForAllWindows(background);
3097 	_SetBackground(background);
3098 	_WindowChanged(window);
3099 
3100 	BRegion dirty(window->VisibleRegion());
3101 
3102 	if (!affectsOtherWindows) {
3103 		// everything that is now visible in the
3104 		// window needs a redraw, but other windows
3105 		// are not affected, we can call ProcessDirtyRegion()
3106 		// of the window, and don't have to use MarkDirty()
3107 		window->ProcessDirtyRegion(dirty);
3108 	} else
3109 		MarkDirty(dirty);
3110 
3111 	if (window->ServerWindow()->HasDirectFrameBufferAccess()) {
3112 		window->ServerWindow()->HandleDirectConnection(
3113 			B_DIRECT_START | B_BUFFER_RESET);
3114 	}
3115 }
3116 
3117 
3118 /*!	Hides the window from the screen - it does this independently of the
3119 	Window::IsHidden() state.
3120 */
3121 void
3122 Desktop::_HideWindow(Window* window)
3123 {
3124 	if (window->ServerWindow()->IsDirectlyAccessing())
3125 		window->ServerWindow()->HandleDirectConnection(B_DIRECT_STOP);
3126 
3127 	// after rebuilding the clipping,
3128 	// this window will not have a visible
3129 	// region anymore, so we need to remember
3130 	// it now
3131 	// (actually that's not true, since
3132 	// hidden windows are excluded from the
3133 	// clipping calculation, but anyways)
3134 	BRegion dirty(window->VisibleRegion());
3135 
3136 	BRegion background;
3137 	_RebuildClippingForAllWindows(background);
3138 	_SetBackground(background);
3139 	_WindowChanged(window);
3140 
3141 	MarkDirty(dirty);
3142 }
3143 
3144 
3145 /*!	Updates the workspaces of all subset windows with regard to the
3146 	specifed window.
3147 	If newIndex is not -1, it will move all subset windows that belong to
3148 	the specifed window to the new workspace; this form is only called by
3149 	SetWorkspace().
3150 */
3151 void
3152 Desktop::_UpdateSubsetWorkspaces(Window* window, int32 previousIndex,
3153 	int32 newIndex)
3154 {
3155 	STRACE(("_UpdateSubsetWorkspaces(window %p, %s)\n", window,
3156 		window->Title()));
3157 
3158 	// if the window is hidden, the subset windows are up-to-date already
3159 	if (!window->IsNormal() || window->IsHidden())
3160 		return;
3161 
3162 	for (Window* subset = fSubsetWindows.FirstWindow(); subset != NULL;
3163 			subset = subset->NextWindow(kSubsetList)) {
3164 		if (subset->Feel() == B_MODAL_ALL_WINDOW_FEEL
3165 			|| subset->Feel() == B_FLOATING_ALL_WINDOW_FEEL) {
3166 			// These windows are always visible on all workspaces,
3167 			// no need to update them.
3168 			continue;
3169 		}
3170 
3171 		if (subset->IsFloating()) {
3172 			// Floating windows are inserted and removed to the current
3173 			// workspace as the need arises - they are not handled here
3174 			// but in _UpdateFront()
3175 			continue;
3176 		}
3177 
3178 		if (subset->HasInSubset(window)) {
3179 			// adopt the workspace change
3180 			SetWindowWorkspaces(subset, subset->SubsetWorkspaces());
3181 		}
3182 	}
3183 }
3184 
3185 
3186 /*!	\brief Adds or removes the window to or from the workspaces it's on.
3187 */
3188 void
3189 Desktop::_ChangeWindowWorkspaces(Window* window, uint32 oldWorkspaces,
3190 	uint32 newWorkspaces)
3191 {
3192 	if (oldWorkspaces == newWorkspaces)
3193 		return;
3194 
3195 	// apply changes to the workspaces' window lists
3196 
3197 	LockAllWindows();
3198 
3199 	// NOTE: we bypass the anchor-mechanism by intention when switching
3200 	// the workspace programmatically.
3201 
3202 	for (int32 i = 0; i < kMaxWorkspaces; i++) {
3203 		if (workspace_in_workspaces(i, oldWorkspaces)) {
3204 			// window is on this workspace, is it anymore?
3205 			if (!workspace_in_workspaces(i, newWorkspaces)) {
3206 				_Windows(i).RemoveWindow(window);
3207 				if (fLastWorkspaceFocus[i] == window)
3208 					fLastWorkspaceFocus[i] = NULL;
3209 
3210 				if (i == CurrentWorkspace()) {
3211 					// remove its appearance from the current workspace
3212 					window->SetCurrentWorkspace(-1);
3213 
3214 					if (!window->IsHidden())
3215 						_HideWindow(window);
3216 				}
3217 			}
3218 		} else {
3219 			// window was not on this workspace, is it now?
3220 			if (workspace_in_workspaces(i, newWorkspaces)) {
3221 				_Windows(i).AddWindow(window,
3222 					window->Frontmost(_Windows(i).FirstWindow(), i));
3223 
3224 				if (i == CurrentWorkspace()) {
3225 					// make the window visible in current workspace
3226 					window->SetCurrentWorkspace(fCurrentWorkspace);
3227 
3228 					if (!window->IsHidden()) {
3229 						// This only affects other windows if this window has
3230 						// floating or modal windows that need to be shown as
3231 						// well
3232 						// TODO: take care of this
3233 						_ShowWindow(window, FrontWindow() == window);
3234 					}
3235 				}
3236 			}
3237 		}
3238 	}
3239 
3240 	// If the window is visible only on one workspace, we set it's current
3241 	// position in that workspace (so that WorkspacesView will find us).
3242 	int32 firstWorkspace = -1;
3243 	for (int32 i = 0; i < kMaxWorkspaces; i++) {
3244 		if ((newWorkspaces & (1L << i)) != 0) {
3245 			if (firstWorkspace != -1) {
3246 				firstWorkspace = -1;
3247 				break;
3248 			}
3249 			firstWorkspace = i;
3250 		}
3251 	}
3252 	if (firstWorkspace >= 0)
3253 		window->Anchor(firstWorkspace).position = window->Frame().LeftTop();
3254 
3255 	// take care about modals and floating windows
3256 	_UpdateSubsetWorkspaces(window);
3257 
3258 	NotifyWindowWorkspacesChanged(window, newWorkspaces);
3259 
3260 	UnlockAllWindows();
3261 }
3262 
3263 
3264 void
3265 Desktop::_BringWindowsToFront(WindowList& windows, int32 list, bool wereVisible)
3266 {
3267 	// we don't need to redraw what is currently
3268 	// visible of the window
3269 	BRegion clean;
3270 
3271 	for (Window* window = windows.FirstWindow(); window != NULL;
3272 			window = window->NextWindow(list)) {
3273 		if (wereVisible)
3274 			clean.Include(&window->VisibleRegion());
3275 
3276 		CurrentWindows().AddWindow(window,
3277 			window->Frontmost(CurrentWindows().FirstWindow(),
3278 				fCurrentWorkspace));
3279 
3280 		_WindowChanged(window);
3281 	}
3282 
3283 	BRegion dummy;
3284 	_RebuildClippingForAllWindows(dummy);
3285 
3286 	// redraw what became visible of the window(s)
3287 
3288 	BRegion dirty;
3289 	for (Window* window = windows.FirstWindow(); window != NULL;
3290 			window = window->NextWindow(list)) {
3291 		dirty.Include(&window->VisibleRegion());
3292 	}
3293 
3294 	dirty.Exclude(&clean);
3295 	MarkDirty(dirty);
3296 
3297 	_UpdateFront();
3298 
3299 	if (windows.FirstWindow() == fBack || fBack == NULL)
3300 		_UpdateBack();
3301 }
3302 
3303 
3304 /*!	Returns the last focussed non-hidden subset window belonging to the
3305 	specified \a window.
3306 */
3307 Window*
3308 Desktop::_LastFocusSubsetWindow(Window* window)
3309 {
3310 	if (window == NULL)
3311 		return NULL;
3312 
3313 	for (Window* front = fFocusList.LastWindow(); front != NULL;
3314 			front = front->PreviousWindow(kFocusList)) {
3315 		if (front != window && !front->IsHidden()
3316 			&& window->HasInSubset(front))
3317 			return front;
3318 	}
3319 
3320 	return NULL;
3321 }
3322 
3323 
3324 /*!	\brief Checks whether or not a fake mouse moved message needs to be sent
3325 	to the previous mouse window.
3326 
3327 	You need to have the all window lock held when calling this method.
3328 */
3329 bool
3330 Desktop::_CheckSendFakeMouseMoved(const Window* lastWindowUnderMouse)
3331 {
3332 	Window* window = WindowAt(fLastMousePosition);
3333 	return window != lastWindowUnderMouse;
3334 }
3335 
3336 
3337 /*!	\brief Sends a fake B_MOUSE_MOVED event to the window under the mouse,
3338 		and also updates the current view under the mouse.
3339 
3340 	This has only to be done in case the view changed without mouse movement,
3341 	ie. because of a workspace change, a closing window, or programmatic window
3342 	movement.
3343 
3344 	You must not have locked any windows when calling this method.
3345 */
3346 void
3347 Desktop::_SendFakeMouseMoved(Window* window)
3348 {
3349 	int32 viewToken = B_NULL_TOKEN;
3350 	EventTarget* target = NULL;
3351 
3352 	LockAllWindows();
3353 
3354 	if (window == NULL)
3355 		window = WindowAt(fLastMousePosition);
3356 
3357 	if (window != NULL) {
3358 		BMessage message;
3359 		window->MouseMoved(&message, fLastMousePosition, &viewToken, true,
3360 			true);
3361 
3362 		if (viewToken != B_NULL_TOKEN)
3363 			target = &window->EventTarget();
3364 	}
3365 
3366 	if (viewToken != B_NULL_TOKEN)
3367 		SetViewUnderMouse(window, viewToken);
3368 	else {
3369 		SetViewUnderMouse(NULL, B_NULL_TOKEN);
3370 		SetCursor(NULL);
3371 	}
3372 
3373 	UnlockAllWindows();
3374 
3375 	if (target != NULL)
3376 		EventDispatcher().SendFakeMouseMoved(*target, viewToken);
3377 }
3378 
3379 
3380 Screen*
3381 Desktop::_DetermineScreenFor(BRect frame)
3382 {
3383 	AutoReadLocker _(fScreenLock);
3384 
3385 	// TODO: choose the screen depending on where most of the area is
3386 	return fVirtualScreen.ScreenAt(0);
3387 }
3388 
3389 
3390 void
3391 Desktop::_RebuildClippingForAllWindows(BRegion& stillAvailableOnScreen)
3392 {
3393 	// the available region on screen starts with the entire screen area
3394 	// each window on the screen will take a portion from that area
3395 
3396 	// figure out what the entire screen area is
3397 	stillAvailableOnScreen = fScreenRegion;
3398 
3399 	// set clipping of each window
3400 	for (Window* window = CurrentWindows().LastWindow(); window != NULL;
3401 			window = window->PreviousWindow(fCurrentWorkspace)) {
3402 		if (!window->IsHidden()) {
3403 			window->SetClipping(&stillAvailableOnScreen);
3404 			window->SetScreen(_DetermineScreenFor(window->Frame()));
3405 
3406 			if (window->ServerWindow()->IsDirectlyAccessing()) {
3407 				window->ServerWindow()->HandleDirectConnection(
3408 					B_DIRECT_MODIFY | B_CLIPPING_MODIFIED);
3409 			}
3410 
3411 			// that windows region is not available on screen anymore
3412 			stillAvailableOnScreen.Exclude(&window->VisibleRegion());
3413 		}
3414 	}
3415 }
3416 
3417 
3418 void
3419 Desktop::_TriggerWindowRedrawing(BRegion& newDirtyRegion)
3420 {
3421 	// send redraw messages to all windows intersecting the dirty region
3422 	for (Window* window = CurrentWindows().LastWindow(); window != NULL;
3423 			window = window->PreviousWindow(fCurrentWorkspace)) {
3424 		if (!window->IsHidden()
3425 			&& newDirtyRegion.Intersects(window->VisibleRegion().Frame()))
3426 			window->ProcessDirtyRegion(newDirtyRegion);
3427 	}
3428 }
3429 
3430 
3431 void
3432 Desktop::_SetBackground(BRegion& background)
3433 {
3434 	// NOTE: the drawing operation is caried out
3435 	// in the clipping region rebuild, but it is
3436 	// ok actually, because it also avoids trails on
3437 	// moving windows
3438 
3439 	// remember the region not covered by any windows
3440 	// and redraw the dirty background
3441 	BRegion dirtyBackground(background);
3442 	dirtyBackground.Exclude(&fBackgroundRegion);
3443 	dirtyBackground.IntersectWith(&background);
3444 	fBackgroundRegion = background;
3445 	if (dirtyBackground.Frame().IsValid()) {
3446 		if (GetDrawingEngine()->LockParallelAccess()) {
3447 			GetDrawingEngine()->FillRegion(dirtyBackground,
3448 				fWorkspaces[fCurrentWorkspace].Color());
3449 
3450 			GetDrawingEngine()->UnlockParallelAccess();
3451 		}
3452 	}
3453 }
3454 
3455 
3456 //!	The all window lock must be held when calling this function.
3457 void
3458 Desktop::RebuildAndRedrawAfterWindowChange(Window* changedWindow,
3459 	BRegion& dirty)
3460 {
3461 	ASSERT_MULTI_WRITE_LOCKED(fWindowLock);
3462 	if (!changedWindow->IsVisible() || dirty.CountRects() == 0)
3463 		return;
3464 
3465 	// The following loop is pretty much a copy of
3466 	// _RebuildClippingForAllWindows(), but will also
3467 	// take care about restricting our dirty region.
3468 
3469 	// figure out what the entire screen area is
3470 	BRegion stillAvailableOnScreen(fScreenRegion);
3471 
3472 	// set clipping of each window
3473 	for (Window* window = CurrentWindows().LastWindow(); window != NULL;
3474 			window = window->PreviousWindow(fCurrentWorkspace)) {
3475 		if (!window->IsHidden()) {
3476 			if (window == changedWindow)
3477 				dirty.IntersectWith(&stillAvailableOnScreen);
3478 
3479 			window->SetClipping(&stillAvailableOnScreen);
3480 			window->SetScreen(_DetermineScreenFor(window->Frame()));
3481 
3482 			if (window->ServerWindow()->IsDirectlyAccessing()) {
3483 				window->ServerWindow()->HandleDirectConnection(
3484 					B_DIRECT_MODIFY | B_CLIPPING_MODIFIED);
3485 			}
3486 
3487 			// that windows region is not available on screen anymore
3488 			stillAvailableOnScreen.Exclude(&window->VisibleRegion());
3489 		}
3490 	}
3491 
3492 	_SetBackground(stillAvailableOnScreen);
3493 	_WindowChanged(changedWindow);
3494 
3495 	_TriggerWindowRedrawing(dirty);
3496 }
3497 
3498 
3499 //! Suspend all windows with direct access to the frame buffer
3500 void
3501 Desktop::_SuspendDirectFrameBufferAccess()
3502 {
3503 	ASSERT_MULTI_LOCKED(fWindowLock);
3504 
3505 	for (Window* window = fAllWindows.FirstWindow(); window != NULL;
3506 			window = window->NextWindow(kAllWindowList)) {
3507 		if (window->ServerWindow()->IsDirectlyAccessing())
3508 			window->ServerWindow()->HandleDirectConnection(B_DIRECT_STOP);
3509 	}
3510 }
3511 
3512 
3513 //! Resume all windows with direct access to the frame buffer
3514 void
3515 Desktop::_ResumeDirectFrameBufferAccess()
3516 {
3517 	ASSERT_MULTI_LOCKED(fWindowLock);
3518 
3519 	for (Window* window = fAllWindows.FirstWindow(); window != NULL;
3520 			window = window->NextWindow(kAllWindowList)) {
3521 		if (window->IsHidden() || !window->InWorkspace(fCurrentWorkspace))
3522 			continue;
3523 
3524 		if (window->ServerWindow()->HasDirectFrameBufferAccess()) {
3525 			window->ServerWindow()->HandleDirectConnection(
3526 				B_DIRECT_START | B_BUFFER_RESET, B_MODE_CHANGED);
3527 		}
3528 	}
3529 }
3530 
3531 
3532 void
3533 Desktop::ScreenChanged(Screen* screen)
3534 {
3535 	AutoWriteLocker windowLocker(fWindowLock);
3536 
3537 	AutoWriteLocker screenLocker(fScreenLock);
3538 	screen->SetPreferredMode();
3539 	screenLocker.Unlock();
3540 
3541 	_ScreenChanged(screen);
3542 }
3543 
3544 
3545 void
3546 Desktop::_ScreenChanged(Screen* screen)
3547 {
3548 	ASSERT_MULTI_WRITE_LOCKED(fWindowLock);
3549 
3550 	// the entire screen is dirty, because we're actually
3551 	// operating on an all new buffer in memory
3552 	BRegion dirty(screen->Frame());
3553 
3554 	// update our cached screen region
3555 	fScreenRegion.Set(screen->Frame());
3556 	gInputManager->UpdateScreenBounds(screen->Frame());
3557 
3558 	BRegion background;
3559 	_RebuildClippingForAllWindows(background);
3560 
3561 	fBackgroundRegion.MakeEmpty();
3562 		// makes sure that the complete background is redrawn
3563 	_SetBackground(background);
3564 
3565 	// figure out dirty region
3566 	dirty.Exclude(&background);
3567 	_TriggerWindowRedrawing(dirty);
3568 
3569 	// send B_SCREEN_CHANGED to windows on that screen
3570 	BMessage update(B_SCREEN_CHANGED);
3571 	update.AddInt64("when", real_time_clock_usecs());
3572 	update.AddRect("frame", screen->Frame());
3573 	update.AddInt32("mode", screen->ColorSpace());
3574 
3575 	fVirtualScreen.UpdateFrame();
3576 
3577 	for (Window* window = fAllWindows.FirstWindow(); window != NULL;
3578 			window = window->NextWindow(kAllWindowList)) {
3579 		if (window->Screen() == screen)
3580 			window->ServerWindow()->ScreenChanged(&update);
3581 	}
3582 }
3583 
3584 
3585 /*!	\brief activate one of the app's windows.
3586 */
3587 status_t
3588 Desktop::_ActivateApp(team_id team)
3589 {
3590 	// search for an unhidden window in the current workspace
3591 
3592 	AutoWriteLocker locker(fWindowLock);
3593 
3594 	for (Window* window = CurrentWindows().LastWindow(); window != NULL;
3595 			window = window->PreviousWindow(fCurrentWorkspace)) {
3596 		if (!window->IsHidden() && window->IsNormal()
3597 			&& window->ServerWindow()->ClientTeam() == team) {
3598 			ActivateWindow(window);
3599 			return B_OK;
3600 		}
3601 	}
3602 
3603 	// search for an unhidden window to give focus to
3604 
3605 	for (Window* window = fAllWindows.FirstWindow(); window != NULL;
3606 			window = window->NextWindow(kAllWindowList)) {
3607 		// if window is a normal window of the team, and not hidden,
3608 		// we've found our target
3609 		if (!window->IsHidden() && window->IsNormal()
3610 			&& window->ServerWindow()->ClientTeam() == team) {
3611 			ActivateWindow(window);
3612 			return B_OK;
3613 		}
3614 	}
3615 
3616 	// TODO: we cannot maximize minimized windows here (with the window lock
3617 	// write locked). To work-around this, we could forward the request to
3618 	// the ServerApp of this team - it maintains its own window list, and can
3619 	// therefore call ActivateWindow() without holding the window lock.
3620 	return B_BAD_VALUE;
3621 }
3622 
3623 
3624 void
3625 Desktop::_SetCurrentWorkspaceConfiguration()
3626 {
3627 	ASSERT_MULTI_WRITE_LOCKED(fWindowLock);
3628 
3629 	status_t status = fDirectScreenLock.LockWithTimeout(1000000L);
3630 	if (status != B_OK) {
3631 		// The application having the direct screen lock didn't give it up in
3632 		// time, make it crash
3633 		syslog(LOG_ERR, "Team %" B_PRId32 " did not give up its direct screen "
3634 			"lock.\n", fDirectScreenTeam);
3635 
3636 		debug_thread(fDirectScreenTeam);
3637 		fDirectScreenTeam = -1;
3638 	} else
3639 		fDirectScreenLock.Unlock();
3640 
3641 	AutoWriteLocker _(fScreenLock);
3642 
3643 	uint32 changedScreens;
3644 	fVirtualScreen.SetConfiguration(*this,
3645 		fWorkspaces[fCurrentWorkspace].CurrentScreenConfiguration(),
3646 		&changedScreens);
3647 
3648 	for (int32 i = 0; changedScreens != 0; i++, changedScreens /= 2) {
3649 		if ((changedScreens & (1 << i)) != 0)
3650 			_ScreenChanged(fVirtualScreen.ScreenAt(i));
3651 	}
3652 }
3653 
3654 
3655 /*!	Changes the current workspace to the one specified by \a index.
3656 	You must hold the all window lock when calling this method.
3657 */
3658 void
3659 Desktop::_SetWorkspace(int32 index, bool moveFocusWindow)
3660 {
3661 	ASSERT_MULTI_WRITE_LOCKED(fWindowLock);
3662 
3663 	int32 previousIndex = fCurrentWorkspace;
3664 	rgb_color previousColor = fWorkspaces[fCurrentWorkspace].Color();
3665 	bool movedMouseEventWindow = false;
3666 	Window* movedWindow = NULL;
3667 	if (moveFocusWindow) {
3668 		if (fMouseEventWindow != NULL)
3669 			movedWindow = fMouseEventWindow;
3670 		else
3671 			movedWindow = FocusWindow();
3672 	}
3673 
3674 	if (movedWindow != NULL) {
3675 		if (movedWindow->IsNormal()) {
3676 			if (!movedWindow->InWorkspace(index)) {
3677 				// The window currently being dragged will follow us to this
3678 				// workspace if it's not already on it.
3679 				// But only normal windows are following
3680 				uint32 oldWorkspaces = movedWindow->Workspaces();
3681 
3682 				WindowStack* stack = movedWindow->GetWindowStack();
3683 				if (stack != NULL) {
3684 					for (int32 s = 0; s < stack->CountWindows(); s++) {
3685 						Window* stackWindow = stack->LayerOrder().ItemAt(s);
3686 
3687 						_Windows(previousIndex).RemoveWindow(stackWindow);
3688 						_Windows(index).AddWindow(stackWindow,
3689 							stackWindow->Frontmost(
3690 								_Windows(index).FirstWindow(), index));
3691 
3692 						// send B_WORKSPACES_CHANGED message
3693 						stackWindow->WorkspacesChanged(oldWorkspaces,
3694 							stackWindow->Workspaces());
3695 					}
3696 				}
3697 				// TODO: subset windows will always flicker this way
3698 
3699 				movedMouseEventWindow = true;
3700 
3701 				NotifyWindowWorkspacesChanged(movedWindow,
3702 					movedWindow->Workspaces());
3703 			} else {
3704 				// make sure it's frontmost
3705 				_Windows(index).RemoveWindow(movedWindow);
3706 				_Windows(index).AddWindow(movedWindow,
3707 					movedWindow->Frontmost(_Windows(index).FirstWindow(),
3708 					index));
3709 			}
3710 		}
3711 
3712 		movedWindow->Anchor(index).position = movedWindow->Frame().LeftTop();
3713 	}
3714 
3715 	if (movedWindow == NULL || movedWindow->InWorkspace(previousIndex))
3716 		fLastWorkspaceFocus[previousIndex] = FocusWindow();
3717 	else
3718 		fLastWorkspaceFocus[previousIndex] = NULL;
3719 
3720 	// build region of windows that are no longer visible in the new workspace
3721 
3722 	BRegion dirty;
3723 
3724 	for (Window* window = CurrentWindows().FirstWindow();
3725 			window != NULL; window = window->NextWindow(previousIndex)) {
3726 		// store current position in Workspace anchor
3727 		window->Anchor(previousIndex).position = window->Frame().LeftTop();
3728 
3729 		if (!window->IsHidden()
3730 			&& window->ServerWindow()->IsDirectlyAccessing())
3731 			window->ServerWindow()->HandleDirectConnection(B_DIRECT_STOP);
3732 
3733 		window->WorkspaceActivated(previousIndex, false);
3734 
3735 		if (window->InWorkspace(index))
3736 			continue;
3737 
3738 		if (!window->IsHidden()) {
3739 			// this window will no longer be visible
3740 			dirty.Include(&window->VisibleRegion());
3741 		}
3742 
3743 		window->SetCurrentWorkspace(-1);
3744 	}
3745 
3746 	fPreviousWorkspace = fCurrentWorkspace;
3747 	fCurrentWorkspace = index;
3748 
3749 	// Change the display modes, if needed
3750 	_SetCurrentWorkspaceConfiguration();
3751 
3752 	// Show windows, and include them in the changed region - but only
3753 	// those that were not visible before (or whose position changed)
3754 
3755 	WindowList windows(kWorkingList);
3756 	BList previousRegions;
3757 
3758 	for (Window* window = _Windows(index).FirstWindow();
3759 			window != NULL; window = window->NextWindow(index)) {
3760 		BPoint position = window->Anchor(index).position;
3761 
3762 		window->SetCurrentWorkspace(index);
3763 
3764 		if (window->IsHidden())
3765 			continue;
3766 
3767 		if (position == kInvalidWindowPosition) {
3768 			// if you enter a workspace for the first time, the position
3769 			// of the window in the previous workspace is adopted
3770 			position = window->Frame().LeftTop();
3771 				// TODO: make sure the window is still on-screen if it
3772 				//	was before!
3773 		}
3774 
3775 		if (!window->InWorkspace(previousIndex)) {
3776 			// This window was not visible before, make sure its frame
3777 			// is up-to-date
3778 			if (window->Frame().LeftTop() != position) {
3779 				BPoint offset = position - window->Frame().LeftTop();
3780 				window->MoveBy((int32)offset.x, (int32)offset.y);
3781 			}
3782 			continue;
3783 		}
3784 
3785 		if (window->Frame().LeftTop() != position) {
3786 			// the window was visible before, but its on-screen location changed
3787 			BPoint offset = position - window->Frame().LeftTop();
3788 			MoveWindowBy(window, offset.x, offset.y);
3789 				// TODO: be a bit smarter than this...
3790 		} else {
3791 			// We need to remember the previous visible region of the
3792 			// window if they changed their order
3793 			ObjectDeleter<BRegion> region(new (std::nothrow)
3794 				BRegion(window->VisibleRegion()));
3795 			if (region.IsSet()) {
3796 				if (previousRegions.AddItem(region.Detach()))
3797 					windows.AddWindow(window);
3798 			}
3799 		}
3800 	}
3801 
3802 	_UpdateFronts(false);
3803 	_UpdateFloating(previousIndex, index,
3804 		movedMouseEventWindow ? movedWindow : NULL);
3805 
3806 	BRegion stillAvailableOnScreen;
3807 	_RebuildClippingForAllWindows(stillAvailableOnScreen);
3808 	_SetBackground(stillAvailableOnScreen);
3809 
3810 	for (Window* window = _Windows(index).FirstWindow(); window != NULL;
3811 			window = window->NextWindow(index)) {
3812 		// send B_WORKSPACE_ACTIVATED message
3813 		window->WorkspaceActivated(index, true);
3814 
3815 		if (!window->IsHidden()
3816 			&& window->ServerWindow()->HasDirectFrameBufferAccess()) {
3817 			window->ServerWindow()->HandleDirectConnection(
3818 				B_DIRECT_START | B_BUFFER_RESET, B_MODE_CHANGED);
3819 		}
3820 
3821 		if (window->InWorkspace(previousIndex) || window->IsHidden()
3822 			|| (window == movedWindow && movedWindow->IsNormal())
3823 			|| (!window->IsNormal()
3824 				&& window->HasInSubset(movedWindow))) {
3825 			// This window was visible before, and is already handled in the
3826 			// above loop
3827 			continue;
3828 		}
3829 
3830 		dirty.Include(&window->VisibleRegion());
3831 	}
3832 
3833 	// Catch order changes in the new workspaces window list
3834 	int32 i = 0;
3835 	for (Window* window = windows.FirstWindow(); window != NULL;
3836 			window = window->NextWindow(kWorkingList), i++) {
3837 		BRegion* region = (BRegion*)previousRegions.ItemAt(i);
3838 		region->ExclusiveInclude(&window->VisibleRegion());
3839 		dirty.Include(region);
3840 		delete region;
3841 	}
3842 
3843 	// Set new focus, but keep focus to a floating window if still visible
3844 	if (movedWindow != NULL)
3845 		SetFocusWindow(movedWindow);
3846 	else if (!_Windows(index).HasWindow(FocusWindow())
3847 		|| (FocusWindow() != NULL && !FocusWindow()->IsFloating()))
3848 		SetFocusWindow(fLastWorkspaceFocus[index]);
3849 
3850 	_WindowChanged(NULL);
3851 	MarkDirty(dirty);
3852 
3853 #if 0
3854 	// Show the dirty regions of this workspace switch
3855 	if (GetDrawingEngine()->LockParallelAccess()) {
3856 		GetDrawingEngine()->FillRegion(dirty, (rgb_color){255, 0, 0});
3857 		GetDrawingEngine()->UnlockParallelAccess();
3858 		snooze(100000);
3859 	}
3860 #endif
3861 
3862 	if (previousColor != fWorkspaces[fCurrentWorkspace].Color())
3863 		RedrawBackground();
3864 }
3865