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