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