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