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