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