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