xref: /haiku/src/servers/app/Desktop.cpp (revision c4b463c5cd2ae950989531a3caf7c1fa7c378573)
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 systemServersDir;
2377 	if (find_directory(B_SYSTEM_SERVERS_DIRECTORY, &systemServersDir) == B_OK)
2378 		entry.SetTo(systemServersDir.Path());
2379 	else
2380 		entry.SetTo("/system/servers/input_server");
2381 	entry_ref ref;
2382 	status_t entryStatus = entry.GetRef(&ref);
2383 	if (entryStatus == B_OK)
2384 		entryStatus = roster.Launch(&ref);
2385 	if (entryStatus == B_OK || entryStatus == B_ALREADY_RUNNING) {
2386 		syslog(LOG_ERR, "Failed to launch the input server by signature: %s!\n",
2387 			strerror(status));
2388 		return;
2389 	}
2390 
2391 	syslog(LOG_ERR, "Failed to launch the input server: %s!\n",
2392 		strerror(entryStatus));
2393 }
2394 
2395 
2396 void
2397 Desktop::_GetLooperName(char* name, size_t length)
2398 {
2399 	snprintf(name, length, "d:%d:%s", fUserID,
2400 		fTargetScreen == NULL ? "baron" : fTargetScreen);
2401 }
2402 
2403 
2404 void
2405 Desktop::_PrepareQuit()
2406 {
2407 	// let's kill all remaining applications
2408 
2409 	fApplicationsLock.Lock();
2410 
2411 	int32 count = fApplications.CountItems();
2412 	for (int32 i = 0; i < count; i++) {
2413 		ServerApp *app = fApplications.ItemAt(i);
2414 		team_id clientTeam = app->ClientTeam();
2415 
2416 		app->Quit();
2417 		kill_team(clientTeam);
2418 	}
2419 
2420 	// wait for the last app to die
2421 	if (count > 0) {
2422 		acquire_sem_etc(fShutdownSemaphore, fShutdownCount, B_RELATIVE_TIMEOUT,
2423 			250000);
2424 	}
2425 
2426 	fApplicationsLock.Unlock();
2427 }
2428 
2429 
2430 void
2431 Desktop::_DispatchMessage(int32 code, BPrivate::LinkReceiver& link)
2432 {
2433 	switch (code) {
2434 		case AS_CREATE_APP:
2435 		{
2436 			// Create the ServerApp to node monitor a new BApplication
2437 
2438 			// Attached data:
2439 			// 1) port_id - receiver port of a regular app
2440 			// 2) port_id - client looper port - for sending messages to the
2441 			//		client
2442 			// 2) team_id - app's team ID
2443 			// 3) int32 - handler token of the regular app
2444 			// 4) char * - signature of the regular app
2445 
2446 			// Find the necessary data
2447 			team_id	clientTeamID = -1;
2448 			port_id	clientLooperPort = -1;
2449 			port_id clientReplyPort = -1;
2450 			int32 htoken = B_NULL_TOKEN;
2451 			char* appSignature = NULL;
2452 
2453 			link.Read<port_id>(&clientReplyPort);
2454 			link.Read<port_id>(&clientLooperPort);
2455 			link.Read<team_id>(&clientTeamID);
2456 			link.Read<int32>(&htoken);
2457 			if (link.ReadString(&appSignature) != B_OK)
2458 				break;
2459 
2460 			ServerApp* app = new ServerApp(this, clientReplyPort,
2461 				clientLooperPort, clientTeamID, htoken, appSignature);
2462 			if (app->InitCheck() == B_OK
2463 				&& app->Run()) {
2464 				// add the new ServerApp to the known list of ServerApps
2465 				fApplicationsLock.Lock();
2466 				fApplications.AddItem(app);
2467 				fApplicationsLock.Unlock();
2468 			} else {
2469 				delete app;
2470 
2471 				// if everything went well, ServerApp::Run() will notify
2472 				// the client - but since it didn't, we do it here
2473 				BPrivate::LinkSender reply(clientReplyPort);
2474 				reply.StartMessage(B_ERROR);
2475 				reply.Flush();
2476 			}
2477 
2478 			// This is necessary because BPortLink::ReadString allocates memory
2479 			free(appSignature);
2480 			break;
2481 		}
2482 
2483 		case AS_DELETE_APP:
2484 		{
2485 			// Delete a ServerApp. Received only from the respective ServerApp
2486 			// when a BApplication asks it to quit.
2487 
2488 			// Attached Data:
2489 			// 1) thread_id - thread ID of the ServerApp to be deleted
2490 
2491 			thread_id thread = -1;
2492 			if (link.Read<thread_id>(&thread) < B_OK)
2493 				break;
2494 
2495 			fApplicationsLock.Lock();
2496 
2497 			// Run through the list of apps and nuke the proper one
2498 
2499 			int32 count = fApplications.CountItems();
2500 			ServerApp* removeApp = NULL;
2501 
2502 			for (int32 i = 0; i < count; i++) {
2503 				ServerApp* app = fApplications.ItemAt(i);
2504 
2505 				if (app->Thread() == thread) {
2506 					fApplications.RemoveItemAt(i);
2507 					removeApp = app;
2508 					break;
2509 				}
2510 			}
2511 
2512 			fApplicationsLock.Unlock();
2513 
2514 			if (removeApp != NULL)
2515 				removeApp->Quit(fShutdownSemaphore);
2516 
2517 			if (fQuitting && count <= 1) {
2518 				// wait for the last app to die
2519 				acquire_sem_etc(fShutdownSemaphore, fShutdownCount,
2520 					B_RELATIVE_TIMEOUT, 500000);
2521 				PostMessage(kMsgQuitLooper);
2522 			}
2523 			break;
2524 		}
2525 
2526 		case AS_ACTIVATE_APP:
2527 		{
2528 			// Someone is requesting to activation of a certain app.
2529 
2530 			// Attached data:
2531 			// 1) port_id reply port
2532 			// 2) team_id team
2533 
2534 			status_t status;
2535 
2536 			// get the parameters
2537 			port_id replyPort;
2538 			team_id team;
2539 			if (link.Read(&replyPort) == B_OK
2540 				&& link.Read(&team) == B_OK)
2541 				status = _ActivateApp(team);
2542 			else
2543 				status = B_ERROR;
2544 
2545 			// send the reply
2546 			BPrivate::PortLink replyLink(replyPort);
2547 			replyLink.StartMessage(status);
2548 			replyLink.Flush();
2549 			break;
2550 		}
2551 
2552 		case AS_APP_CRASHED:
2553 		case AS_DUMP_ALLOCATOR:
2554 		case AS_DUMP_BITMAPS:
2555 		{
2556 			BAutolock locker(fApplicationsLock);
2557 
2558 			team_id team;
2559 			if (link.Read(&team) != B_OK)
2560 				break;
2561 
2562 			for (int32 i = 0; i < fApplications.CountItems(); i++) {
2563 				ServerApp* app = fApplications.ItemAt(i);
2564 
2565 				if (app->ClientTeam() == team)
2566 					app->PostMessage(code);
2567 			}
2568 			break;
2569 		}
2570 
2571 		case AS_EVENT_STREAM_CLOSED:
2572 			_LaunchInputServer();
2573 			break;
2574 
2575 		case B_QUIT_REQUESTED:
2576 			// We've been asked to quit, so (for now) broadcast to all
2577 			// test apps to quit. This situation will occur only when the
2578 			// server is compiled as a regular Be application.
2579 
2580 			fApplicationsLock.Lock();
2581 			fShutdownSemaphore = create_sem(0, "desktop shutdown");
2582 			fShutdownCount = fApplications.CountItems();
2583 			fApplicationsLock.Unlock();
2584 
2585 			fQuitting = true;
2586 			BroadcastToAllApps(AS_QUIT_APP);
2587 
2588 			// We now need to process the remaining AS_DELETE_APP messages and
2589 			// wait for the kMsgShutdownServer message.
2590 			// If an application does not quit as asked, the picasso thread
2591 			// will send us this message in 2-3 seconds.
2592 
2593 			// if there are no apps to quit, shutdown directly
2594 			if (fShutdownCount == 0)
2595 				PostMessage(kMsgQuitLooper);
2596 			break;
2597 
2598 		case AS_ACTIVATE_WORKSPACE:
2599 		{
2600 			int32 index;
2601 			link.Read<int32>(&index);
2602 			if (index == -1)
2603 				index = fPreviousWorkspace;
2604 
2605 			bool moveFocusWindow;
2606 			link.Read<bool>(&moveFocusWindow);
2607 
2608 			SetWorkspace(index, moveFocusWindow);
2609 			break;
2610 		}
2611 
2612 		case AS_TALK_TO_DESKTOP_LISTENER:
2613 		{
2614 			port_id clientReplyPort;
2615 			if (link.Read<port_id>(&clientReplyPort) != B_OK)
2616 				break;
2617 
2618 			BPrivate::LinkSender reply(clientReplyPort);
2619 			AutoWriteLocker locker(fWindowLock);
2620 			if (MessageForListener(NULL, link, reply) != true) {
2621 				// unhandled message, at least send an error if needed
2622 				if (link.NeedsReply()) {
2623 					reply.StartMessage(B_ERROR);
2624 					reply.Flush();
2625 				}
2626 			}
2627 			break;
2628 		}
2629 
2630 		// ToDo: Remove this again. It is a message sent by the
2631 		// invalidate_on_exit kernel debugger add-on to trigger a redraw
2632 		// after exiting a kernel debugger session.
2633 		case 'KDLE':
2634 		{
2635 			BRegion dirty;
2636 			dirty.Include(fVirtualScreen.Frame());
2637 			MarkDirty(dirty);
2638 			break;
2639 		}
2640 
2641 		default:
2642 			printf("Desktop %d:%s received unexpected code %ld\n", 0, "baron",
2643 				code);
2644 
2645 			if (link.NeedsReply()) {
2646 				// the client is now blocking and waiting for a reply!
2647 				fLink.StartMessage(B_ERROR);
2648 				fLink.Flush();
2649 			}
2650 			break;
2651 	}
2652 }
2653 
2654 
2655 WindowList&
2656 Desktop::CurrentWindows()
2657 {
2658 	return fWorkspaces[fCurrentWorkspace].Windows();
2659 }
2660 
2661 
2662 WindowList&
2663 Desktop::AllWindows()
2664 {
2665 	return fAllWindows;
2666 }
2667 
2668 
2669 Window*
2670 Desktop::WindowForClientLooperPort(port_id port)
2671 {
2672 	ASSERT_MULTI_LOCKED(fWindowLock);
2673 
2674 	for (Window* window = fAllWindows.FirstWindow(); window != NULL;
2675 			window = window->NextWindow(kAllWindowList)) {
2676 		if (window->ServerWindow()->ClientLooperPort() == port)
2677 			return window;
2678 	}
2679 	return NULL;
2680 }
2681 
2682 
2683 WindowList&
2684 Desktop::_Windows(int32 index)
2685 {
2686 	return fWorkspaces[index].Windows();
2687 }
2688 
2689 
2690 void
2691 Desktop::_UpdateFloating(int32 previousWorkspace, int32 nextWorkspace,
2692 	Window* mouseEventWindow)
2693 {
2694 	if (previousWorkspace == -1)
2695 		previousWorkspace = fCurrentWorkspace;
2696 	if (nextWorkspace == -1)
2697 		nextWorkspace = previousWorkspace;
2698 
2699 	for (Window* floating = fSubsetWindows.FirstWindow(); floating != NULL;
2700 			floating = floating->NextWindow(kSubsetList)) {
2701 		// we only care about app/subset floating windows
2702 		if (floating->Feel() != B_FLOATING_SUBSET_WINDOW_FEEL
2703 			&& floating->Feel() != B_FLOATING_APP_WINDOW_FEEL)
2704 			continue;
2705 
2706 		if (fFront != NULL && fFront->IsNormal()
2707 			&& floating->HasInSubset(fFront)) {
2708 			// is now visible
2709 			if (_Windows(previousWorkspace).HasWindow(floating)
2710 				&& previousWorkspace != nextWorkspace
2711 				&& !floating->InSubsetWorkspace(previousWorkspace)) {
2712 				// but no longer on the previous workspace
2713 				_Windows(previousWorkspace).RemoveWindow(floating);
2714 				floating->SetCurrentWorkspace(-1);
2715 			}
2716 
2717 			if (!_Windows(nextWorkspace).HasWindow(floating)) {
2718 				// but wasn't before
2719 				_Windows(nextWorkspace).AddWindow(floating,
2720 					floating->Frontmost(_Windows(nextWorkspace).FirstWindow(),
2721 					nextWorkspace));
2722 				floating->SetCurrentWorkspace(nextWorkspace);
2723 				if (mouseEventWindow != fFront)
2724 					_ShowWindow(floating);
2725 
2726 				// TODO: put the floating last in the floating window list to
2727 				// preserve the on screen window order
2728 			}
2729 		} else if (_Windows(previousWorkspace).HasWindow(floating)
2730 			&& !floating->InSubsetWorkspace(previousWorkspace)) {
2731 			// was visible, but is no longer
2732 
2733 			_Windows(previousWorkspace).RemoveWindow(floating);
2734 			floating->SetCurrentWorkspace(-1);
2735 			_HideWindow(floating);
2736 
2737 			if (FocusWindow() == floating)
2738 				SetFocusWindow();
2739 		}
2740 	}
2741 }
2742 
2743 
2744 /*!	Search the visible windows for a valid back window
2745 	(only desktop windows can't be back windows)
2746 */
2747 void
2748 Desktop::_UpdateBack()
2749 {
2750 	fBack = NULL;
2751 
2752 	for (Window* window = CurrentWindows().FirstWindow(); window != NULL;
2753 			window = window->NextWindow(fCurrentWorkspace)) {
2754 		if (window->IsHidden() || window->Feel() == kDesktopWindowFeel)
2755 			continue;
2756 
2757 		fBack = window;
2758 		break;
2759 	}
2760 }
2761 
2762 
2763 /*!	Search the visible windows for a valid front window
2764 	(only normal and modal windows can be front windows)
2765 
2766 	The only place where you don't want to update floating windows is
2767 	during a workspace change - because then you'll call _UpdateFloating()
2768 	yourself.
2769 */
2770 void
2771 Desktop::_UpdateFront(bool updateFloating)
2772 {
2773 	fFront = NULL;
2774 
2775 	for (Window* window = CurrentWindows().LastWindow(); window != NULL;
2776 			window = window->PreviousWindow(fCurrentWorkspace)) {
2777 		if (window->IsHidden() || window->IsFloating()
2778 			|| !window->SupportsFront())
2779 			continue;
2780 
2781 		fFront = window;
2782 		break;
2783 	}
2784 
2785 	if (updateFloating)
2786 		_UpdateFloating();
2787 }
2788 
2789 
2790 void
2791 Desktop::_UpdateFronts(bool updateFloating)
2792 {
2793 	_UpdateBack();
2794 	_UpdateFront(updateFloating);
2795 }
2796 
2797 
2798 bool
2799 Desktop::_WindowHasModal(Window* window)
2800 {
2801 	if (window == NULL)
2802 		return false;
2803 
2804 	for (Window* modal = fSubsetWindows.FirstWindow(); modal != NULL;
2805 			modal = modal->NextWindow(kSubsetList)) {
2806 		// only visible modal windows count
2807 		if (!modal->IsModal() || modal->IsHidden())
2808 			continue;
2809 
2810 		if (modal->HasInSubset(window))
2811 			return true;
2812 	}
2813 
2814 	return false;
2815 }
2816 
2817 
2818 /*!	You must at least hold a single window lock when calling this method.
2819 */
2820 void
2821 Desktop::_WindowChanged(Window* window)
2822 {
2823 	ASSERT_MULTI_LOCKED(fWindowLock);
2824 
2825 	BAutolock _(fWorkspacesLock);
2826 
2827 	for (uint32 i = fWorkspacesViews.CountItems(); i-- > 0;) {
2828 		WorkspacesView* view = fWorkspacesViews.ItemAt(i);
2829 		view->WindowChanged(window);
2830 	}
2831 }
2832 
2833 
2834 /*!	You must at least hold a single window lock when calling this method.
2835 */
2836 void
2837 Desktop::_WindowRemoved(Window* window)
2838 {
2839 	ASSERT_MULTI_LOCKED(fWindowLock);
2840 
2841 	BAutolock _(fWorkspacesLock);
2842 
2843 	for (uint32 i = fWorkspacesViews.CountItems(); i-- > 0;) {
2844 		WorkspacesView* view = fWorkspacesViews.ItemAt(i);
2845 		view->WindowRemoved(window);
2846 	}
2847 }
2848 
2849 
2850 /*!	Shows the window on the screen - it does this independently of the
2851 	Window::IsHidden() state.
2852 */
2853 void
2854 Desktop::_ShowWindow(Window* window, bool affectsOtherWindows)
2855 {
2856 	BRegion background;
2857 	_RebuildClippingForAllWindows(background);
2858 	_SetBackground(background);
2859 	_WindowChanged(window);
2860 
2861 	BRegion dirty(window->VisibleRegion());
2862 
2863 	if (!affectsOtherWindows) {
2864 		// everything that is now visible in the
2865 		// window needs a redraw, but other windows
2866 		// are not affected, we can call ProcessDirtyRegion()
2867 		// of the window, and don't have to use MarkDirty()
2868 		window->ProcessDirtyRegion(dirty);
2869 	} else
2870 		MarkDirty(dirty);
2871 
2872 	if (window->ServerWindow()->HasDirectFrameBufferAccess()) {
2873 		window->ServerWindow()->HandleDirectConnection(
2874 			B_DIRECT_START | B_BUFFER_RESET);
2875 	}
2876 }
2877 
2878 
2879 /*!	Hides the window from the screen - it does this independently of the
2880 	Window::IsHidden() state.
2881 */
2882 void
2883 Desktop::_HideWindow(Window* window)
2884 {
2885 	if (window->ServerWindow()->IsDirectlyAccessing())
2886 		window->ServerWindow()->HandleDirectConnection(B_DIRECT_STOP);
2887 
2888 	// after rebuilding the clipping,
2889 	// this window will not have a visible
2890 	// region anymore, so we need to remember
2891 	// it now
2892 	// (actually that's not true, since
2893 	// hidden windows are excluded from the
2894 	// clipping calculation, but anyways)
2895 	BRegion dirty(window->VisibleRegion());
2896 
2897 	BRegion background;
2898 	_RebuildClippingForAllWindows(background);
2899 	_SetBackground(background);
2900 	_WindowChanged(window);
2901 
2902 	MarkDirty(dirty);
2903 }
2904 
2905 
2906 /*!	Updates the workspaces of all subset windows with regard to the
2907 	specifed window.
2908 	If newIndex is not -1, it will move all subset windows that belong to
2909 	the specifed window to the new workspace; this form is only called by
2910 	SetWorkspace().
2911 */
2912 void
2913 Desktop::_UpdateSubsetWorkspaces(Window* window, int32 previousIndex,
2914 	int32 newIndex)
2915 {
2916 	STRACE(("_UpdateSubsetWorkspaces(window %p, %s)\n", window,
2917 		window->Title()));
2918 
2919 	// if the window is hidden, the subset windows are up-to-date already
2920 	if (!window->IsNormal() || window->IsHidden())
2921 		return;
2922 
2923 	for (Window* subset = fSubsetWindows.FirstWindow(); subset != NULL;
2924 			subset = subset->NextWindow(kSubsetList)) {
2925 		if (subset->Feel() == B_MODAL_ALL_WINDOW_FEEL
2926 			|| subset->Feel() == B_FLOATING_ALL_WINDOW_FEEL) {
2927 			// These windows are always visible on all workspaces,
2928 			// no need to update them.
2929 			continue;
2930 		}
2931 
2932 		if (subset->IsFloating()) {
2933 			// Floating windows are inserted and removed to the current
2934 			// workspace as the need arises - they are not handled here
2935 			// but in _UpdateFront()
2936 			continue;
2937 		}
2938 
2939 		if (subset->HasInSubset(window)) {
2940 			// adopt the workspace change
2941 			SetWindowWorkspaces(subset, subset->SubsetWorkspaces());
2942 		}
2943 	}
2944 }
2945 
2946 
2947 /*!	\brief Adds or removes the window to or from the workspaces it's on.
2948 */
2949 void
2950 Desktop::_ChangeWindowWorkspaces(Window* window, uint32 oldWorkspaces,
2951 	uint32 newWorkspaces)
2952 {
2953 	if (oldWorkspaces == newWorkspaces)
2954 		return;
2955 
2956 	// apply changes to the workspaces' window lists
2957 
2958 	LockAllWindows();
2959 
2960 	// NOTE: we bypass the anchor-mechanism by intention when switching
2961 	// the workspace programmatically.
2962 
2963 	for (int32 i = 0; i < kMaxWorkspaces; i++) {
2964 		if (workspace_in_workspaces(i, oldWorkspaces)) {
2965 			// window is on this workspace, is it anymore?
2966 			if (!workspace_in_workspaces(i, newWorkspaces)) {
2967 				_Windows(i).RemoveWindow(window);
2968 				if (fLastWorkspaceFocus[i] == window)
2969 					fLastWorkspaceFocus[i] = NULL;
2970 
2971 				if (i == CurrentWorkspace()) {
2972 					// remove its appearance from the current workspace
2973 					window->SetCurrentWorkspace(-1);
2974 
2975 					if (!window->IsHidden())
2976 						_HideWindow(window);
2977 				}
2978 			}
2979 		} else {
2980 			// window was not on this workspace, is it now?
2981 			if (workspace_in_workspaces(i, newWorkspaces)) {
2982 				_Windows(i).AddWindow(window,
2983 					window->Frontmost(_Windows(i).FirstWindow(), i));
2984 
2985 				if (i == CurrentWorkspace()) {
2986 					// make the window visible in current workspace
2987 					window->SetCurrentWorkspace(fCurrentWorkspace);
2988 
2989 					if (!window->IsHidden()) {
2990 						// This only affects other windows if this window has
2991 						// floating or modal windows that need to be shown as
2992 						// well
2993 						// TODO: take care of this
2994 						_ShowWindow(window, FrontWindow() == window);
2995 					}
2996 				}
2997 			}
2998 		}
2999 	}
3000 
3001 	// If the window is visible only on one workspace, we set it's current
3002 	// position in that workspace (so that WorkspacesView will find us).
3003 	int32 firstWorkspace = -1;
3004 	for (int32 i = 0; i < kMaxWorkspaces; i++) {
3005 		if ((newWorkspaces & (1L << i)) != 0) {
3006 			if (firstWorkspace != -1) {
3007 				firstWorkspace = -1;
3008 				break;
3009 			}
3010 			firstWorkspace = i;
3011 		}
3012 	}
3013 	if (firstWorkspace >= 0)
3014 		window->Anchor(firstWorkspace).position = window->Frame().LeftTop();
3015 
3016 	// take care about modals and floating windows
3017 	_UpdateSubsetWorkspaces(window);
3018 
3019 	NotifyWindowWorkspacesChanged(window, newWorkspaces);
3020 
3021 	UnlockAllWindows();
3022 }
3023 
3024 
3025 void
3026 Desktop::_BringWindowsToFront(WindowList& windows, int32 list,
3027 	bool wereVisible)
3028 {
3029 	// we don't need to redraw what is currently
3030 	// visible of the window
3031 	BRegion clean;
3032 
3033 	for (Window* window = windows.FirstWindow(); window != NULL;
3034 			window = window->NextWindow(list)) {
3035 		if (wereVisible)
3036 			clean.Include(&window->VisibleRegion());
3037 
3038 		CurrentWindows().AddWindow(window,
3039 			window->Frontmost(CurrentWindows().FirstWindow(),
3040 				fCurrentWorkspace));
3041 
3042 		_WindowChanged(window);
3043 	}
3044 
3045 	BRegion dummy;
3046 	_RebuildClippingForAllWindows(dummy);
3047 
3048 	// redraw what became visible of the window(s)
3049 
3050 	BRegion dirty;
3051 	for (Window* window = windows.FirstWindow(); window != NULL;
3052 			window = window->NextWindow(list)) {
3053 		dirty.Include(&window->VisibleRegion());
3054 	}
3055 
3056 	dirty.Exclude(&clean);
3057 	MarkDirty(dirty);
3058 
3059 	_UpdateFront();
3060 
3061 	if (windows.FirstWindow() == fBack || fBack == NULL)
3062 		_UpdateBack();
3063 }
3064 
3065 
3066 /*!	Returns the last focussed non-hidden subset window belonging to the
3067 	specified \a window.
3068 */
3069 Window*
3070 Desktop::_LastFocusSubsetWindow(Window* window)
3071 {
3072 	if (window == NULL)
3073 		return NULL;
3074 
3075 	for (Window* front = fFocusList.LastWindow(); front != NULL;
3076 			front = front->PreviousWindow(kFocusList)) {
3077 		if (front != window && !front->IsHidden()
3078 			&& window->HasInSubset(front))
3079 			return front;
3080 	}
3081 
3082 	return NULL;
3083 }
3084 
3085 
3086 /*!	\brief Sends a fake B_MOUSE_MOVED event to the window under the mouse,
3087 		and also updates the current view under the mouse.
3088 
3089 	This has only to be done in case the view changed without user interaction,
3090 	ie. because of a workspace change or a closing window.
3091 */
3092 void
3093 Desktop::_SendFakeMouseMoved(Window* window)
3094 {
3095 	int32 viewToken = B_NULL_TOKEN;
3096 	EventTarget* target = NULL;
3097 
3098 	LockAllWindows();
3099 
3100 	if (window == NULL)
3101 		window = MouseEventWindow();
3102 	if (window == NULL)
3103 		window = WindowAt(fLastMousePosition);
3104 
3105 	if (window != NULL) {
3106 		BMessage message;
3107 		window->MouseMoved(&message, fLastMousePosition, &viewToken, true,
3108 			true);
3109 
3110 		if (viewToken != B_NULL_TOKEN)
3111 			target = &window->EventTarget();
3112 	}
3113 
3114 	if (viewToken != B_NULL_TOKEN)
3115 		SetViewUnderMouse(window, viewToken);
3116 	else {
3117 		SetViewUnderMouse(NULL, B_NULL_TOKEN);
3118 		SetCursor(NULL);
3119 	}
3120 
3121 	UnlockAllWindows();
3122 
3123 	if (target != NULL)
3124 		EventDispatcher().SendFakeMouseMoved(*target, viewToken);
3125 }
3126 
3127 
3128 Screen*
3129 Desktop::_DetermineScreenFor(BRect frame)
3130 {
3131 	AutoReadLocker _(fScreenLock);
3132 
3133 	// TODO: choose the screen depending on where most of the area is
3134 	return fVirtualScreen.ScreenAt(0);
3135 }
3136 
3137 
3138 void
3139 Desktop::_RebuildClippingForAllWindows(BRegion& stillAvailableOnScreen)
3140 {
3141 	// the available region on screen starts with the entire screen area
3142 	// each window on the screen will take a portion from that area
3143 
3144 	// figure out what the entire screen area is
3145 	stillAvailableOnScreen = fScreenRegion;
3146 
3147 	// set clipping of each window
3148 	for (Window* window = CurrentWindows().LastWindow(); window != NULL;
3149 			window = window->PreviousWindow(fCurrentWorkspace)) {
3150 		if (!window->IsHidden()) {
3151 			window->SetClipping(&stillAvailableOnScreen);
3152 			window->SetScreen(_DetermineScreenFor(window->Frame()));
3153 
3154 			if (window->ServerWindow()->IsDirectlyAccessing()) {
3155 				window->ServerWindow()->HandleDirectConnection(
3156 					B_DIRECT_MODIFY | B_CLIPPING_MODIFIED);
3157 			}
3158 
3159 			// that windows region is not available on screen anymore
3160 			stillAvailableOnScreen.Exclude(&window->VisibleRegion());
3161 		}
3162 	}
3163 }
3164 
3165 
3166 void
3167 Desktop::_TriggerWindowRedrawing(BRegion& newDirtyRegion)
3168 {
3169 	// send redraw messages to all windows intersecting the dirty region
3170 	for (Window* window = CurrentWindows().LastWindow(); window != NULL;
3171 			window = window->PreviousWindow(fCurrentWorkspace)) {
3172 		if (!window->IsHidden()
3173 			&& newDirtyRegion.Intersects(window->VisibleRegion().Frame()))
3174 			window->ProcessDirtyRegion(newDirtyRegion);
3175 	}
3176 }
3177 
3178 
3179 void
3180 Desktop::_SetBackground(BRegion& background)
3181 {
3182 	// NOTE: the drawing operation is caried out
3183 	// in the clipping region rebuild, but it is
3184 	// ok actually, because it also avoids trails on
3185 	// moving windows
3186 
3187 	// remember the region not covered by any windows
3188 	// and redraw the dirty background
3189 	BRegion dirtyBackground(background);
3190 	dirtyBackground.Exclude(&fBackgroundRegion);
3191 	dirtyBackground.IntersectWith(&background);
3192 	fBackgroundRegion = background;
3193 	if (dirtyBackground.Frame().IsValid()) {
3194 		if (GetDrawingEngine()->LockParallelAccess()) {
3195 			GetDrawingEngine()->FillRegion(dirtyBackground,
3196 				fWorkspaces[fCurrentWorkspace].Color());
3197 
3198 			GetDrawingEngine()->UnlockParallelAccess();
3199 		}
3200 	}
3201 }
3202 
3203 
3204 //!	The all window lock must be held when calling this function.
3205 void
3206 Desktop::RebuildAndRedrawAfterWindowChange(Window* changedWindow,
3207 	BRegion& dirty)
3208 {
3209 	ASSERT_MULTI_WRITE_LOCKED(fWindowLock);
3210 	if (!changedWindow->IsVisible() || dirty.CountRects() == 0)
3211 		return;
3212 
3213 	// The following loop is pretty much a copy of
3214 	// _RebuildClippingForAllWindows(), but will also
3215 	// take care about restricting our dirty region.
3216 
3217 	// figure out what the entire screen area is
3218 	BRegion stillAvailableOnScreen(fScreenRegion);
3219 
3220 	// set clipping of each window
3221 	for (Window* window = CurrentWindows().LastWindow(); window != NULL;
3222 			window = window->PreviousWindow(fCurrentWorkspace)) {
3223 		if (!window->IsHidden()) {
3224 			if (window == changedWindow)
3225 				dirty.IntersectWith(&stillAvailableOnScreen);
3226 
3227 			window->SetClipping(&stillAvailableOnScreen);
3228 			window->SetScreen(_DetermineScreenFor(window->Frame()));
3229 
3230 			if (window->ServerWindow()->IsDirectlyAccessing()) {
3231 				window->ServerWindow()->HandleDirectConnection(
3232 					B_DIRECT_MODIFY | B_CLIPPING_MODIFIED);
3233 			}
3234 
3235 			// that windows region is not available on screen anymore
3236 			stillAvailableOnScreen.Exclude(&window->VisibleRegion());
3237 		}
3238 	}
3239 
3240 	_SetBackground(stillAvailableOnScreen);
3241 	_WindowChanged(changedWindow);
3242 
3243 	_TriggerWindowRedrawing(dirty);
3244 }
3245 
3246 
3247 //! Suspend all windows with direct access to the frame buffer
3248 void
3249 Desktop::_SuspendDirectFrameBufferAccess()
3250 {
3251 	ASSERT_MULTI_LOCKED(fWindowLock);
3252 
3253 	for (Window* window = fAllWindows.FirstWindow(); window != NULL;
3254 			window = window->NextWindow(kAllWindowList)) {
3255 		if (window->ServerWindow()->IsDirectlyAccessing())
3256 			window->ServerWindow()->HandleDirectConnection(B_DIRECT_STOP);
3257 	}
3258 }
3259 
3260 
3261 //! Resume all windows with direct access to the frame buffer
3262 void
3263 Desktop::_ResumeDirectFrameBufferAccess()
3264 {
3265 	ASSERT_MULTI_LOCKED(fWindowLock);
3266 
3267 	for (Window* window = fAllWindows.FirstWindow(); window != NULL;
3268 			window = window->NextWindow(kAllWindowList)) {
3269 		if (window->IsHidden() || !window->InWorkspace(fCurrentWorkspace))
3270 			continue;
3271 
3272 		if (window->ServerWindow()->HasDirectFrameBufferAccess()) {
3273 			window->ServerWindow()->HandleDirectConnection(
3274 				B_DIRECT_START | B_BUFFER_RESET, B_MODE_CHANGED);
3275 		}
3276 	}
3277 }
3278 
3279 
3280 void
3281 Desktop::_ScreenChanged(Screen* screen)
3282 {
3283 	ASSERT_MULTI_WRITE_LOCKED(fWindowLock);
3284 
3285 	// the entire screen is dirty, because we're actually
3286 	// operating on an all new buffer in memory
3287 	BRegion dirty(screen->Frame());
3288 
3289 	// update our cached screen region
3290 	fScreenRegion.Set(screen->Frame());
3291 	gInputManager->UpdateScreenBounds(screen->Frame());
3292 
3293 	BRegion background;
3294 	_RebuildClippingForAllWindows(background);
3295 
3296 	fBackgroundRegion.MakeEmpty();
3297 		// makes sure that the complete background is redrawn
3298 	_SetBackground(background);
3299 
3300 	// figure out dirty region
3301 	dirty.Exclude(&background);
3302 	_TriggerWindowRedrawing(dirty);
3303 
3304 	// send B_SCREEN_CHANGED to windows on that screen
3305 	BMessage update(B_SCREEN_CHANGED);
3306 	update.AddInt64("when", real_time_clock_usecs());
3307 	update.AddRect("frame", screen->Frame());
3308 	update.AddInt32("mode", screen->ColorSpace());
3309 
3310 	fVirtualScreen.UpdateFrame();
3311 
3312 	for (Window* window = fAllWindows.FirstWindow(); window != NULL;
3313 			window = window->NextWindow(kAllWindowList)) {
3314 		if (window->Screen() == screen)
3315 			window->ServerWindow()->ScreenChanged(&update);
3316 	}
3317 }
3318 
3319 
3320 /*!	\brief activate one of the app's windows.
3321 */
3322 status_t
3323 Desktop::_ActivateApp(team_id team)
3324 {
3325 	// search for an unhidden window in the current workspace
3326 
3327 	AutoWriteLocker locker(fWindowLock);
3328 
3329 	for (Window* window = CurrentWindows().LastWindow(); window != NULL;
3330 			window = window->PreviousWindow(fCurrentWorkspace)) {
3331 		if (!window->IsHidden() && window->IsNormal()
3332 			&& window->ServerWindow()->ClientTeam() == team) {
3333 			ActivateWindow(window);
3334 			return B_OK;
3335 		}
3336 	}
3337 
3338 	// search for an unhidden window to give focus to
3339 
3340 	for (Window* window = fAllWindows.FirstWindow(); window != NULL;
3341 			window = window->NextWindow(kAllWindowList)) {
3342 		// if window is a normal window of the team, and not hidden,
3343 		// we've found our target
3344 		if (!window->IsHidden() && window->IsNormal()
3345 			&& window->ServerWindow()->ClientTeam() == team) {
3346 			ActivateWindow(window);
3347 			return B_OK;
3348 		}
3349 	}
3350 
3351 	// TODO: we cannot maximize minimized windows here (with the window lock
3352 	// write locked). To work-around this, we could forward the request to
3353 	// the ServerApp of this team - it maintains its own window list, and can
3354 	// therefore call ActivateWindow() without holding the window lock.
3355 	return B_BAD_VALUE;
3356 }
3357 
3358 
3359 void
3360 Desktop::_SetCurrentWorkspaceConfiguration()
3361 {
3362 	ASSERT_MULTI_WRITE_LOCKED(fWindowLock);
3363 
3364 	status_t status = fDirectScreenLock.LockWithTimeout(1000000L);
3365 	if (status != B_OK) {
3366 		// The application having the direct screen lock didn't give it up in
3367 		// time, make it crash
3368 		syslog(LOG_ERR, "Team %ld did not give up its direct screen lock.\n",
3369 			fDirectScreenTeam);
3370 
3371 		debug_thread(fDirectScreenTeam);
3372 		fDirectScreenTeam = -1;
3373 	} else
3374 		fDirectScreenLock.Unlock();
3375 
3376 	AutoWriteLocker _(fScreenLock);
3377 
3378 	uint32 changedScreens;
3379 	fVirtualScreen.SetConfiguration(*this,
3380 		fWorkspaces[fCurrentWorkspace].CurrentScreenConfiguration(),
3381 		&changedScreens);
3382 
3383 	for (int32 i = 0; changedScreens != 0; i++, changedScreens /= 2) {
3384 		if ((changedScreens & (1 << i)) != 0)
3385 			_ScreenChanged(fVirtualScreen.ScreenAt(i));
3386 	}
3387 }
3388 
3389 
3390 /*!	Changes the current workspace to the one specified by \a index.
3391 	You must hold the all window lock when calling this method.
3392 */
3393 void
3394 Desktop::_SetWorkspace(int32 index, bool moveFocusWindow)
3395 {
3396 	ASSERT_MULTI_WRITE_LOCKED(fWindowLock);
3397 
3398 	int32 previousIndex = fCurrentWorkspace;
3399 	rgb_color previousColor = fWorkspaces[fCurrentWorkspace].Color();
3400 	bool movedMouseEventWindow = false;
3401 	Window* movedWindow = NULL;
3402 	if (moveFocusWindow) {
3403 		if (fMouseEventWindow != NULL)
3404 			movedWindow = fMouseEventWindow;
3405 		else
3406 			movedWindow = FocusWindow();
3407 	}
3408 
3409 	if (movedWindow != NULL) {
3410 		if (movedWindow->IsNormal()) {
3411 			if (!movedWindow->InWorkspace(index)) {
3412 				// The window currently being dragged will follow us to this
3413 				// workspace if it's not already on it.
3414 				// But only normal windows are following
3415 				uint32 oldWorkspaces = movedWindow->Workspaces();
3416 
3417 				_Windows(previousIndex).RemoveWindow(movedWindow);
3418 				_Windows(index).AddWindow(movedWindow,
3419 					movedWindow->Frontmost(_Windows(index).FirstWindow(),
3420 					index));
3421 
3422 				// TODO: subset windows will always flicker this way
3423 
3424 				movedMouseEventWindow = true;
3425 
3426 				// send B_WORKSPACES_CHANGED message
3427 				movedWindow->WorkspacesChanged(oldWorkspaces,
3428 					movedWindow->Workspaces());
3429 				NotifyWindowWorkspacesChanged(movedWindow,
3430 					movedWindow->Workspaces());
3431 			} else {
3432 				// make sure it's frontmost
3433 				_Windows(index).RemoveWindow(movedWindow);
3434 				_Windows(index).AddWindow(movedWindow,
3435 					movedWindow->Frontmost(_Windows(index).FirstWindow(),
3436 					index));
3437 			}
3438 		}
3439 
3440 		movedWindow->Anchor(index).position = movedWindow->Frame().LeftTop();
3441 	}
3442 
3443 	if (movedWindow == NULL || movedWindow->InWorkspace(previousIndex))
3444 		fLastWorkspaceFocus[previousIndex] = FocusWindow();
3445 	else
3446 		fLastWorkspaceFocus[previousIndex] = NULL;
3447 
3448 	// build region of windows that are no longer visible in the new workspace
3449 
3450 	BRegion dirty;
3451 
3452 	for (Window* window = CurrentWindows().FirstWindow();
3453 			window != NULL; window = window->NextWindow(previousIndex)) {
3454 		// store current position in Workspace anchor
3455 		window->Anchor(previousIndex).position = window->Frame().LeftTop();
3456 
3457 		if (!window->IsHidden()
3458 			&& window->ServerWindow()->IsDirectlyAccessing())
3459 			window->ServerWindow()->HandleDirectConnection(B_DIRECT_STOP);
3460 
3461 		window->WorkspaceActivated(previousIndex, false);
3462 
3463 		if (window->InWorkspace(index))
3464 			continue;
3465 
3466 		if (!window->IsHidden()) {
3467 			// this window will no longer be visible
3468 			dirty.Include(&window->VisibleRegion());
3469 		}
3470 
3471 		window->SetCurrentWorkspace(-1);
3472 	}
3473 
3474 	fPreviousWorkspace = fCurrentWorkspace;
3475 	fCurrentWorkspace = index;
3476 
3477 	// Change the display modes, if needed
3478 	_SetCurrentWorkspaceConfiguration();
3479 
3480 	// Show windows, and include them in the changed region - but only
3481 	// those that were not visible before (or whose position changed)
3482 
3483 	WindowList windows(kWorkingList);
3484 	BList previousRegions;
3485 
3486 	for (Window* window = _Windows(index).FirstWindow();
3487 			window != NULL; window = window->NextWindow(index)) {
3488 		BPoint position = window->Anchor(index).position;
3489 
3490 		window->SetCurrentWorkspace(index);
3491 
3492 		if (window->IsHidden())
3493 			continue;
3494 
3495 		if (position == kInvalidWindowPosition) {
3496 			// if you enter a workspace for the first time, the position
3497 			// of the window in the previous workspace is adopted
3498 			position = window->Frame().LeftTop();
3499 				// TODO: make sure the window is still on-screen if it
3500 				//	was before!
3501 		}
3502 
3503 		if (!window->InWorkspace(previousIndex)) {
3504 			// This window was not visible before, make sure its frame
3505 			// is up-to-date
3506 			if (window->Frame().LeftTop() != position) {
3507 				BPoint offset = position - window->Frame().LeftTop();
3508 				window->MoveBy((int32)offset.x, (int32)offset.y);
3509 			}
3510 			continue;
3511 		}
3512 
3513 		if (window->Frame().LeftTop() != position) {
3514 			// the window was visible before, but its on-screen location changed
3515 			BPoint offset = position - window->Frame().LeftTop();
3516 			MoveWindowBy(window, offset.x, offset.y);
3517 				// TODO: be a bit smarter than this...
3518 		} else {
3519 			// We need to remember the previous visible region of the
3520 			// window if they changed their order
3521 			BRegion* region = new (std::nothrow)
3522 				BRegion(window->VisibleRegion());
3523 			if (region != NULL) {
3524 				if (previousRegions.AddItem(region))
3525 					windows.AddWindow(window);
3526 				else
3527 					delete region;
3528 			}
3529 		}
3530 	}
3531 
3532 	_UpdateFronts(false);
3533 	_UpdateFloating(previousIndex, index,
3534 		movedMouseEventWindow ? movedWindow : NULL);
3535 
3536 	BRegion stillAvailableOnScreen;
3537 	_RebuildClippingForAllWindows(stillAvailableOnScreen);
3538 	_SetBackground(stillAvailableOnScreen);
3539 
3540 	for (Window* window = _Windows(index).FirstWindow(); window != NULL;
3541 			window = window->NextWindow(index)) {
3542 		// send B_WORKSPACE_ACTIVATED message
3543 		window->WorkspaceActivated(index, true);
3544 
3545 		if (!window->IsHidden()
3546 			&& window->ServerWindow()->HasDirectFrameBufferAccess()) {
3547 			window->ServerWindow()->HandleDirectConnection(
3548 				B_DIRECT_START | B_BUFFER_RESET, B_MODE_CHANGED);
3549 		}
3550 
3551 		if (window->InWorkspace(previousIndex) || window->IsHidden()
3552 			|| (window == movedWindow && movedWindow->IsNormal())
3553 			|| (!window->IsNormal()
3554 				&& window->HasInSubset(movedWindow))) {
3555 			// This window was visible before, and is already handled in the
3556 			// above loop
3557 			continue;
3558 		}
3559 
3560 		dirty.Include(&window->VisibleRegion());
3561 	}
3562 
3563 	// Catch order changes in the new workspaces window list
3564 	int32 i = 0;
3565 	for (Window* window = windows.FirstWindow(); window != NULL;
3566 			window = window->NextWindow(kWorkingList), i++) {
3567 		BRegion* region = (BRegion*)previousRegions.ItemAt(i);
3568 		region->ExclusiveInclude(&window->VisibleRegion());
3569 		dirty.Include(region);
3570 		delete region;
3571 	}
3572 
3573 	// Set new focus, but keep focus to a floating window if still visible
3574 	if (movedWindow != NULL)
3575 		SetFocusWindow(movedWindow);
3576 	else if (!_Windows(index).HasWindow(FocusWindow())
3577 		|| (FocusWindow() != NULL && !FocusWindow()->IsFloating()))
3578 		SetFocusWindow(fLastWorkspaceFocus[index]);
3579 
3580 	_WindowChanged(NULL);
3581 	MarkDirty(dirty);
3582 
3583 #if 0
3584 	// Show the dirty regions of this workspace switch
3585 	if (GetDrawingEngine()->LockParallelAccess()) {
3586 		GetDrawingEngine()->FillRegion(dirty, (rgb_color){255, 0, 0});
3587 		GetDrawingEngine()->UnlockParallelAccess();
3588 		snooze(100000);
3589 	}
3590 #endif
3591 
3592 	if (previousColor != fWorkspaces[fCurrentWorkspace].Color())
3593 		RedrawBackground();
3594 }
3595