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