xref: /haiku/src/servers/app/Desktop.cpp (revision 04a0e9c7b68cbe3a43d38e2bca8e860fd80936fb)
1 /*
2  * Copyright 2001-2013, 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 %" B_PRId32 "\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->ClickToFocusMouse()) {
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->FocusFollowsMouse())
1205 		SetFocusWindow(WindowAt(fLastMousePosition));
1206 	else if (fSettings->NormalMouse())
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 != NULL)
1361 		window = topWindow;
1362 
1363 	if (workspace == -1)
1364 		workspace = fCurrentWorkspace;
1365 	if (!window->IsVisible() || workspace != fCurrentWorkspace) {
1366 		if (workspace != fCurrentWorkspace) {
1367 			WindowStack* stack = window->GetWindowStack();
1368 			if (stack != NULL) {
1369 				for (int32 s = 0; s < stack->CountWindows(); s++) {
1370 					Window* stackWindow = stack->WindowAt(s);
1371 					// move the window on another workspace - this doesn't
1372 					// change it's current position
1373 					if (stackWindow->Anchor(workspace).position
1374 						== kInvalidWindowPosition) {
1375 						stackWindow->Anchor(workspace).position
1376 							= stackWindow->Frame().LeftTop();
1377 					}
1378 
1379 					stackWindow->Anchor(workspace).position += BPoint(x, y);
1380 					stackWindow->SetCurrentWorkspace(workspace);
1381 					_WindowChanged(stackWindow);
1382 				}
1383 			}
1384 		} else
1385 			window->MoveBy((int32)x, (int32)y);
1386 
1387 		NotifyWindowMoved(window);
1388 		return;
1389 	}
1390 
1391 	// the dirty region starts with the visible area of the window being moved
1392 	BRegion newDirtyRegion(window->VisibleRegion());
1393 
1394 	// stop direct frame buffer access
1395 	bool direct = false;
1396 	if (window->ServerWindow()->IsDirectlyAccessing()) {
1397 		window->ServerWindow()->HandleDirectConnection(B_DIRECT_STOP);
1398 		direct = true;
1399 	}
1400 
1401 	window->MoveBy((int32)x, (int32)y);
1402 
1403 	BRegion background;
1404 	_RebuildClippingForAllWindows(background);
1405 
1406 	// construct the region that is possible to be blitted
1407 	// to move the contents of the window
1408 	BRegion copyRegion(window->VisibleRegion());
1409 	copyRegion.OffsetBy((int32)-x, (int32)-y);
1410 	copyRegion.IntersectWith(&newDirtyRegion);
1411 		// newDirtyRegion == the windows old visible region
1412 
1413 	// include the the new visible region of the window being
1414 	// moved into the dirty region (for now)
1415 	newDirtyRegion.Include(&window->VisibleRegion());
1416 
1417 	// NOTE: Having all windows locked should prevent any
1418 	// problems with locking the drawing engine here.
1419 	if (GetDrawingEngine()->LockParallelAccess()) {
1420 		GetDrawingEngine()->CopyRegion(&copyRegion, (int32)x, (int32)y);
1421 		GetDrawingEngine()->UnlockParallelAccess();
1422 	}
1423 
1424 	// in the dirty region, exclude the parts that we
1425 	// could move by blitting
1426 	copyRegion.OffsetBy((int32)x, (int32)y);
1427 	newDirtyRegion.Exclude(&copyRegion);
1428 
1429 	MarkDirty(newDirtyRegion);
1430 	_SetBackground(background);
1431 	_WindowChanged(window);
1432 
1433 	// resume direct frame buffer access
1434 	if (direct) {
1435 		// TODO: the clipping actually only changes when we move our window
1436 		// off screen, or behind some other window
1437 		window->ServerWindow()->HandleDirectConnection(
1438 			B_DIRECT_START | B_BUFFER_MOVED | B_CLIPPING_MODIFIED);
1439 	}
1440 
1441 	NotifyWindowMoved(window);
1442 }
1443 
1444 
1445 void
1446 Desktop::ResizeWindowBy(Window* window, float x, float y)
1447 {
1448 	if (x == 0 && y == 0)
1449 		return;
1450 
1451 	AutoWriteLocker _(fWindowLock);
1452 
1453 	Window* topWindow = window->TopLayerStackWindow();
1454 	if (topWindow)
1455 		window = topWindow;
1456 
1457 	if (!window->IsVisible()) {
1458 		window->ResizeBy((int32)x, (int32)y, NULL);
1459 		NotifyWindowResized(window);
1460 		return;
1461 	}
1462 
1463 	// the dirty region for the inside of the window is
1464 	// constructed by the window itself in ResizeBy()
1465 	BRegion newDirtyRegion;
1466 	// track the dirty region outside the window in case
1467 	// it is shrunk in "previouslyOccupiedRegion"
1468 	BRegion previouslyOccupiedRegion(window->VisibleRegion());
1469 
1470 	// stop direct frame buffer access
1471 	bool direct = false;
1472 	if (window->ServerWindow()->IsDirectlyAccessing()) {
1473 		window->ServerWindow()->HandleDirectConnection(B_DIRECT_STOP);
1474 		direct = true;
1475 	}
1476 
1477 	window->ResizeBy((int32)x, (int32)y, &newDirtyRegion);
1478 
1479 	BRegion background;
1480 	_RebuildClippingForAllWindows(background);
1481 
1482 	// we just care for the region outside the window
1483 	previouslyOccupiedRegion.Exclude(&window->VisibleRegion());
1484 
1485 	// make sure the window cannot mark stuff dirty outside
1486 	// its visible region...
1487 	newDirtyRegion.IntersectWith(&window->VisibleRegion());
1488 	// ...because we do this outself
1489 	newDirtyRegion.Include(&previouslyOccupiedRegion);
1490 
1491 	MarkDirty(newDirtyRegion);
1492 	_SetBackground(background);
1493 	_WindowChanged(window);
1494 
1495 	// resume direct frame buffer access
1496 	if (direct) {
1497 		window->ServerWindow()->HandleDirectConnection(
1498 			B_DIRECT_START | B_BUFFER_RESIZED | B_CLIPPING_MODIFIED);
1499 	}
1500 
1501 	NotifyWindowResized(window);
1502 }
1503 
1504 
1505 bool
1506 Desktop::SetWindowTabLocation(Window* window, float location, bool isShifting)
1507 {
1508 	AutoWriteLocker _(fWindowLock);
1509 
1510 	BRegion dirty;
1511 	bool changed = window->SetTabLocation(location, isShifting, dirty);
1512 	if (changed)
1513 		RebuildAndRedrawAfterWindowChange(window, dirty);
1514 
1515 	NotifyWindowTabLocationChanged(window, location, isShifting);
1516 
1517 	return changed;
1518 }
1519 
1520 
1521 bool
1522 Desktop::SetWindowDecoratorSettings(Window* window, const BMessage& settings)
1523 {
1524 	AutoWriteLocker _(fWindowLock);
1525 
1526 	BRegion dirty;
1527 	bool changed = window->SetDecoratorSettings(settings, dirty);
1528 	bool listenerChanged = SetDecoratorSettings(window, settings);
1529 	if (changed || listenerChanged)
1530 		RebuildAndRedrawAfterWindowChange(window, dirty);
1531 
1532 	return changed;
1533 }
1534 
1535 
1536 void
1537 Desktop::SetWindowWorkspaces(Window* window, uint32 workspaces)
1538 {
1539 	LockAllWindows();
1540 
1541 	if (window->IsNormal() && workspaces == B_CURRENT_WORKSPACE)
1542 		workspaces = workspace_to_workspaces(CurrentWorkspace());
1543 
1544 	WindowStack* stack = window->GetWindowStack();
1545 	if (stack != NULL) {
1546 		for (int32 s = 0; s < stack->CountWindows(); s++) {
1547 			window = stack->LayerOrder().ItemAt(s);
1548 
1549 			uint32 oldWorkspaces = window->Workspaces();
1550 			window->WorkspacesChanged(oldWorkspaces, workspaces);
1551 			_ChangeWindowWorkspaces(window, oldWorkspaces, workspaces);
1552 		}
1553 	}
1554 	UnlockAllWindows();
1555 }
1556 
1557 
1558 /*!	\brief Adds the window to the desktop.
1559 	At this point, the window is still hidden and must be shown explicetly
1560 	via ShowWindow().
1561 */
1562 void
1563 Desktop::AddWindow(Window *window)
1564 {
1565 	LockAllWindows();
1566 
1567 	fAllWindows.AddWindow(window);
1568 	if (!window->IsNormal())
1569 		fSubsetWindows.AddWindow(window);
1570 
1571 	if (window->IsNormal()) {
1572 		if (window->Workspaces() == B_CURRENT_WORKSPACE)
1573 			window->SetWorkspaces(workspace_to_workspaces(CurrentWorkspace()));
1574 	} else {
1575 		// subset windows are visible on all workspaces their subset is on
1576 		window->SetWorkspaces(window->SubsetWorkspaces());
1577 	}
1578 
1579 	_ChangeWindowWorkspaces(window, 0, window->Workspaces());
1580 
1581 	NotifyWindowAdded(window);
1582 
1583 	UnlockAllWindows();
1584 }
1585 
1586 
1587 void
1588 Desktop::RemoveWindow(Window *window)
1589 {
1590 	LockAllWindows();
1591 
1592 	if (!window->IsHidden())
1593 		HideWindow(window);
1594 
1595 	fAllWindows.RemoveWindow(window);
1596 	if (!window->IsNormal())
1597 		fSubsetWindows.RemoveWindow(window);
1598 
1599 	_ChangeWindowWorkspaces(window, window->Workspaces(), 0);
1600 
1601 	NotifyWindowRemoved(window);
1602 
1603 	UnlockAllWindows();
1604 
1605 	// make sure this window won't get any events anymore
1606 
1607 	EventDispatcher().RemoveTarget(window->EventTarget());
1608 }
1609 
1610 
1611 bool
1612 Desktop::AddWindowToSubset(Window* subset, Window* window)
1613 {
1614 	if (!subset->AddToSubset(window))
1615 		return false;
1616 
1617 	_ChangeWindowWorkspaces(subset, subset->Workspaces(),
1618 		subset->SubsetWorkspaces());
1619 	return true;
1620 }
1621 
1622 
1623 void
1624 Desktop::RemoveWindowFromSubset(Window* subset, Window* window)
1625 {
1626 	subset->RemoveFromSubset(window);
1627 	_ChangeWindowWorkspaces(subset, subset->Workspaces(),
1628 		subset->SubsetWorkspaces());
1629 }
1630 
1631 
1632 void
1633 Desktop::FontsChanged(Window* window)
1634 {
1635 	AutoWriteLocker _(fWindowLock);
1636 
1637 	BRegion dirty;
1638 	window->FontsChanged(&dirty);
1639 
1640 	RebuildAndRedrawAfterWindowChange(window, dirty);
1641 }
1642 
1643 
1644 void
1645 Desktop::SetWindowLook(Window* window, window_look newLook)
1646 {
1647 	if (window->Look() == newLook)
1648 		return;
1649 
1650 	AutoWriteLocker _(fWindowLock);
1651 
1652 	BRegion dirty;
1653 	window->SetLook(newLook, &dirty);
1654 		// TODO: test what happens when the window
1655 		// finds out it needs to resize itself...
1656 
1657 	RebuildAndRedrawAfterWindowChange(window, dirty);
1658 
1659 	NotifyWindowLookChanged(window, newLook);
1660 }
1661 
1662 
1663 void
1664 Desktop::SetWindowFeel(Window* window, window_feel newFeel)
1665 {
1666 	if (window->Feel() == newFeel)
1667 		return;
1668 
1669 	LockAllWindows();
1670 
1671 	bool wasNormal = window->IsNormal();
1672 
1673 	window->SetFeel(newFeel);
1674 
1675 	// move the window out of or into the subset window list as needed
1676 	if (window->IsNormal() && !wasNormal)
1677 		fSubsetWindows.RemoveWindow(window);
1678 	else if (!window->IsNormal() && wasNormal)
1679 		fSubsetWindows.AddWindow(window);
1680 
1681 	// A normal window that was once a floating or modal window will
1682 	// adopt the window's current workspaces
1683 
1684 	if (!window->IsNormal()) {
1685 		_ChangeWindowWorkspaces(window, window->Workspaces(),
1686 			window->SubsetWorkspaces());
1687 	}
1688 
1689 	// make sure the window has the correct position in the window lists
1690 	// (ie. all floating windows have to be on the top, ...)
1691 
1692 	for (int32 i = 0; i < kMaxWorkspaces; i++) {
1693 		if (!workspace_in_workspaces(i, window->Workspaces()))
1694 			continue;
1695 
1696 		bool changed = false;
1697 		BRegion visibleBefore;
1698 		if (i == fCurrentWorkspace && window->IsVisible())
1699 			visibleBefore = window->VisibleRegion();
1700 
1701 		Window* backmost = window->Backmost(_Windows(i).LastWindow(), i);
1702 		if (backmost != NULL) {
1703 			// check if the backmost window is really behind it
1704 			Window* previous = window->PreviousWindow(i);
1705 			while (previous != NULL) {
1706 				if (previous == backmost)
1707 					break;
1708 
1709 				previous = previous->PreviousWindow(i);
1710 			}
1711 
1712 			if (previous == NULL) {
1713 				// need to reinsert window before its backmost window
1714 				_Windows(i).RemoveWindow(window);
1715 				_Windows(i).AddWindow(window, backmost->NextWindow(i));
1716 				changed = true;
1717 			}
1718 		}
1719 
1720 		Window* frontmost = window->Frontmost(_Windows(i).FirstWindow(), i);
1721 		if (frontmost != NULL) {
1722 			// check if the frontmost window is really in front of it
1723 			Window* next = window->NextWindow(i);
1724 			while (next != NULL) {
1725 				if (next == frontmost)
1726 					break;
1727 
1728 				next = next->NextWindow(i);
1729 			}
1730 
1731 			if (next == NULL) {
1732 				// need to reinsert window behind its frontmost window
1733 				_Windows(i).RemoveWindow(window);
1734 				_Windows(i).AddWindow(window, frontmost);
1735 				changed = true;
1736 			}
1737 		}
1738 
1739 		if (i == fCurrentWorkspace && changed) {
1740 			BRegion dummy;
1741 			_RebuildClippingForAllWindows(dummy);
1742 
1743 			// mark everything dirty that is no longer visible, or
1744 			// is now visible and wasn't before
1745 			BRegion visibleAfter(window->VisibleRegion());
1746 			BRegion dirty(visibleAfter);
1747 			dirty.Exclude(&visibleBefore);
1748 			visibleBefore.Exclude(&visibleAfter);
1749 			dirty.Include(&visibleBefore);
1750 
1751 			MarkDirty(dirty);
1752 		}
1753 	}
1754 
1755 	_UpdateFronts();
1756 
1757 	if (window == FocusWindow() && !window->IsVisible())
1758 		SetFocusWindow();
1759 
1760 	NotifyWindowFeelChanged(window, newFeel);
1761 
1762 	UnlockAllWindows();
1763 }
1764 
1765 
1766 void
1767 Desktop::SetWindowFlags(Window *window, uint32 newFlags)
1768 {
1769 	if (window->Flags() == newFlags)
1770 		return;
1771 
1772 	AutoWriteLocker _(fWindowLock);
1773 
1774 	BRegion dirty;
1775 	window->SetFlags(newFlags, &dirty);
1776 		// TODO: test what happens when the window
1777 		// finds out it needs to resize itself...
1778 
1779 	RebuildAndRedrawAfterWindowChange(window, dirty);
1780 }
1781 
1782 
1783 void
1784 Desktop::SetWindowTitle(Window *window, const char* title)
1785 {
1786 	AutoWriteLocker _(fWindowLock);
1787 
1788 	BRegion dirty;
1789 	window->SetTitle(title, dirty);
1790 
1791 	RebuildAndRedrawAfterWindowChange(window, dirty);
1792 }
1793 
1794 
1795 /*!	Returns the window under the mouse cursor.
1796 	You need to have acquired the All Windows lock when calling this method.
1797 */
1798 Window*
1799 Desktop::WindowAt(BPoint where)
1800 {
1801 	for (Window* window = CurrentWindows().LastWindow(); window;
1802 			window = window->PreviousWindow(fCurrentWorkspace)) {
1803 		if (window->IsVisible() && window->VisibleRegion().Contains(where))
1804 			return window->StackedWindowAt(where);
1805 	}
1806 
1807 	return NULL;
1808 }
1809 
1810 
1811 void
1812 Desktop::SetMouseEventWindow(Window* window)
1813 {
1814 	fMouseEventWindow = window;
1815 }
1816 
1817 
1818 void
1819 Desktop::SetViewUnderMouse(const Window* window, int32 viewToken)
1820 {
1821 	fWindowUnderMouse = window;
1822 	fViewUnderMouse = viewToken;
1823 }
1824 
1825 
1826 int32
1827 Desktop::ViewUnderMouse(const Window* window)
1828 {
1829 	if (window != NULL && fWindowUnderMouse == window)
1830 		return fViewUnderMouse;
1831 
1832 	return B_NULL_TOKEN;
1833 }
1834 
1835 
1836 /*!	Returns the current keyboard event target candidate - which is either the
1837 	top-most window (in case it has the kAcceptKeyboardFocusFlag flag set), or
1838 	the one having focus.
1839 	The window lock must be held when calling this function.
1840 */
1841 EventTarget*
1842 Desktop::KeyboardEventTarget()
1843 {
1844 	// Get the top most non-hidden window
1845 	Window* window = CurrentWindows().LastWindow();
1846 	while (window != NULL && window->IsHidden()) {
1847 		window = window->PreviousWindow(fCurrentWorkspace);
1848 	}
1849 
1850 	if (window != NULL && (window->Flags() & kAcceptKeyboardFocusFlag) != 0)
1851 		return &window->EventTarget();
1852 
1853 	if (FocusWindow() != NULL)
1854 		return &FocusWindow()->EventTarget();
1855 
1856 	return NULL;
1857 }
1858 
1859 
1860 /*!	Tries to set the focus to the specified \a focus window. It will make sure,
1861 	however, that the window actually can have focus. You are allowed to pass
1862 	in a NULL pointer for \a focus.
1863 
1864 	Besides the B_AVOID_FOCUS flag, a modal window, or a BWindowScreen can both
1865 	prevent it from getting focus.
1866 
1867 	In any case, this method makes sure that there is a focus window, if there
1868 	is any window at all, that is.
1869 */
1870 void
1871 Desktop::SetFocusWindow(Window* nextFocus)
1872 {
1873 	if (!LockAllWindows())
1874 		return;
1875 
1876 	// test for B_LOCK_WINDOW_FOCUS
1877 	if (fLockedFocusWindow && nextFocus != fLockedFocusWindow) {
1878 		UnlockAllWindows();
1879 		return;
1880 	}
1881 
1882 	bool hasModal = _WindowHasModal(nextFocus);
1883 	bool hasWindowScreen = false;
1884 
1885 	if (!hasModal && nextFocus != NULL) {
1886 		// Check whether or not a window screen is in front of the window
1887 		// (if it has a modal, the right thing is done, anyway)
1888 		Window* window = nextFocus;
1889 		while (true) {
1890 			window = window->NextWindow(fCurrentWorkspace);
1891 			if (window == NULL || window->Feel() == kWindowScreenFeel)
1892 				break;
1893 		}
1894 		if (window != NULL)
1895 			hasWindowScreen = true;
1896 	}
1897 
1898 	if (nextFocus == fFocus && nextFocus != NULL && !nextFocus->IsHidden()
1899 		&& (nextFocus->Flags() & B_AVOID_FOCUS) == 0
1900 		&& !hasModal && !hasWindowScreen) {
1901 		// the window that is supposed to get focus already has focus
1902 		UnlockAllWindows();
1903 		return;
1904 	}
1905 
1906 	uint32 listIndex = fCurrentWorkspace;
1907 	WindowList* list = &_Windows(fCurrentWorkspace);
1908 	if (!fSettings->NormalMouse()) {
1909 		listIndex = kFocusList;
1910 		list = &fFocusList;
1911 	}
1912 
1913 	if (nextFocus == NULL || hasModal || hasWindowScreen) {
1914 		nextFocus = list->LastWindow();
1915 
1916 		if (fSettings->NormalMouse()) {
1917 			// If the last window having focus is a window that cannot make it
1918 			// to the front, we use that as the next focus
1919 			Window* lastFocus = fFocusList.LastWindow();
1920 			if (lastFocus != NULL && !lastFocus->SupportsFront()
1921 				&& _WindowCanHaveFocus(lastFocus)) {
1922 				nextFocus = lastFocus;
1923 			}
1924 		}
1925 	}
1926 
1927 	// make sure no window is chosen that doesn't want focus or cannot have it
1928 	while (nextFocus != NULL && !_WindowCanHaveFocus(nextFocus)) {
1929 		nextFocus = nextFocus->PreviousWindow(listIndex);
1930 	}
1931 
1932 	if (fFocus == nextFocus) {
1933 		// turns out the window that is supposed to get focus now already has it
1934 		UnlockAllWindows();
1935 		return;
1936 	}
1937 
1938 	team_id oldActiveApp = -1;
1939 	team_id newActiveApp = -1;
1940 
1941 	if (fFocus != NULL) {
1942 		fFocus->SetFocus(false);
1943 		oldActiveApp = fFocus->ServerWindow()->App()->ClientTeam();
1944 	}
1945 
1946 	fFocus = nextFocus;
1947 
1948 	if (fFocus != NULL) {
1949 		fFocus->SetFocus(true);
1950 		newActiveApp = fFocus->ServerWindow()->App()->ClientTeam();
1951 
1952 		// move current focus to the end of the focus list
1953 		fFocusList.RemoveWindow(fFocus);
1954 		fFocusList.AddWindow(fFocus);
1955 	}
1956 
1957 	if (newActiveApp == -1) {
1958 		// make sure the cursor is visible
1959 		HWInterface()->SetCursorVisible(true);
1960 	}
1961 
1962 	UnlockAllWindows();
1963 
1964 	// change the "active" app if appropriate
1965 	if (oldActiveApp == newActiveApp)
1966 		return;
1967 
1968 	BAutolock locker(fApplicationsLock);
1969 
1970 	for (int32 i = 0; i < fApplications.CountItems(); i++) {
1971 		ServerApp* app = fApplications.ItemAt(i);
1972 
1973 		if (oldActiveApp != -1 && app->ClientTeam() == oldActiveApp)
1974 			app->Activate(false);
1975 		else if (newActiveApp != -1 && app->ClientTeam() == newActiveApp)
1976 			app->Activate(true);
1977 	}
1978 }
1979 
1980 
1981 void
1982 Desktop::SetFocusLocked(const Window* window)
1983 {
1984 	AutoWriteLocker _(fWindowLock);
1985 
1986 	if (window != NULL) {
1987 		// Don't allow this to be set when no mouse buttons
1988 		// are pressed. (BView::SetMouseEventMask() should only be called
1989 		// from mouse hooks.)
1990 		if (fLastMouseButtons == 0)
1991 			return;
1992 	}
1993 
1994 	fLockedFocusWindow = window;
1995 }
1996 
1997 
1998 Window*
1999 Desktop::FindWindowByClientToken(int32 token, team_id teamID)
2000 {
2001 	for (Window *window = fAllWindows.FirstWindow(); window != NULL;
2002 			window = window->NextWindow(kAllWindowList)) {
2003 		if (window->ServerWindow()->ClientToken() == token
2004 			&& window->ServerWindow()->ClientTeam() == teamID) {
2005 			return window;
2006 		}
2007 	}
2008 
2009 	return NULL;
2010 }
2011 
2012 
2013 ::EventTarget*
2014 Desktop::FindTarget(BMessenger& messenger)
2015 {
2016 	for (Window *window = fAllWindows.FirstWindow(); window != NULL;
2017 			window = window->NextWindow(kAllWindowList)) {
2018 		if (window->EventTarget().Messenger() == messenger)
2019 			return &window->EventTarget();
2020 	}
2021 
2022 	return NULL;
2023 }
2024 
2025 
2026 void
2027 Desktop::MarkDirty(BRegion& region)
2028 {
2029 	if (region.CountRects() == 0)
2030 		return;
2031 
2032 	if (LockAllWindows()) {
2033 		// send redraw messages to all windows intersecting the dirty region
2034 		_TriggerWindowRedrawing(region);
2035 
2036 		UnlockAllWindows();
2037 	}
2038 }
2039 
2040 
2041 void
2042 Desktop::Redraw()
2043 {
2044 	BRegion dirty(fVirtualScreen.Frame());
2045 	MarkDirty(dirty);
2046 }
2047 
2048 
2049 /*!	\brief Redraws the background (ie. the desktop window, if any).
2050 */
2051 void
2052 Desktop::RedrawBackground()
2053 {
2054 	LockAllWindows();
2055 
2056 	BRegion redraw;
2057 
2058 	Window* window = CurrentWindows().FirstWindow();
2059 	if (window->Feel() == kDesktopWindowFeel) {
2060 		redraw = window->VisibleContentRegion();
2061 
2062 		// look for desktop background view, and update its background color
2063 		// TODO: is there a better way to do this?
2064 		View* view = window->TopView();
2065 		if (view != NULL)
2066 			view = view->FirstChild();
2067 
2068 		while (view) {
2069 			if (view->IsDesktopBackground()) {
2070 				view->SetViewColor(fWorkspaces[fCurrentWorkspace].Color());
2071 				break;
2072 			}
2073 			view = view->NextSibling();
2074 		}
2075 
2076 		window->ProcessDirtyRegion(redraw);
2077 	} else {
2078 		redraw = BackgroundRegion();
2079 		fBackgroundRegion.MakeEmpty();
2080 		_SetBackground(redraw);
2081 	}
2082 
2083 	_WindowChanged(NULL);
2084 		// update workspaces view as well
2085 
2086 	UnlockAllWindows();
2087 }
2088 
2089 
2090 bool
2091 Desktop::ReloadDecor(DecorAddOn* oldDecor)
2092 {
2093 	AutoWriteLocker _(fWindowLock);
2094 
2095 	bool returnValue = true;
2096 
2097 	if (oldDecor != NULL) {
2098 		const DesktopListenerList* oldListeners
2099 			= &oldDecor->GetDesktopListeners();
2100 		for (int i = 0; i < oldListeners->CountItems(); i++)
2101 			UnregisterListener(oldListeners->ItemAt(i));
2102 	}
2103 
2104 	for (Window* window = fAllWindows.FirstWindow(); window != NULL;
2105 			window = window->NextWindow(kAllWindowList)) {
2106 		BRegion oldBorder;
2107 		window->GetBorderRegion(&oldBorder);
2108 
2109 		if (!window->ReloadDecor()) {
2110 			// prevent unloading previous add-on
2111 			returnValue = false;
2112 		}
2113 
2114 		BRegion border;
2115 		window->GetBorderRegion(&border);
2116 
2117 		border.Include(&oldBorder);
2118 		RebuildAndRedrawAfterWindowChange(window, border);
2119 	}
2120 
2121 	// register new listeners
2122 	const DesktopListenerList& newListeners
2123 		= gDecorManager.GetDesktopListeners();
2124 	for (int i = 0; i < newListeners.CountItems(); i++)
2125  		RegisterListener(newListeners.ItemAt(i));
2126 
2127  	return returnValue;
2128 }
2129 
2130 
2131 void
2132 Desktop::MinimizeApplication(team_id team)
2133 {
2134 	AutoWriteLocker locker(fWindowLock);
2135 
2136 	// Just minimize all windows of that application
2137 
2138 	for (Window *window = fAllWindows.FirstWindow(); window != NULL;
2139 			window = window->NextWindow(kAllWindowList)) {
2140 		if (window->ServerWindow()->ClientTeam() != team)
2141 			continue;
2142 
2143 		window->ServerWindow()->NotifyMinimize(true);
2144 	}
2145 }
2146 
2147 
2148 void
2149 Desktop::BringApplicationToFront(team_id team)
2150 {
2151 	AutoWriteLocker locker(fWindowLock);
2152 
2153 	// TODO: for now, just maximize all windows of that application
2154 	// TODO: have the ability to lock the current workspace
2155 
2156 	for (Window *window = fAllWindows.FirstWindow(); window != NULL;
2157 			window = window->NextWindow(kAllWindowList)) {
2158 		if (window->ServerWindow()->ClientTeam() != team)
2159 			continue;
2160 
2161 		window->ServerWindow()->NotifyMinimize(false);
2162 	}
2163 }
2164 
2165 
2166 void
2167 Desktop::WindowAction(int32 windowToken, int32 action)
2168 {
2169 	if (action != B_MINIMIZE_WINDOW && action != B_BRING_TO_FRONT)
2170 		return;
2171 
2172 	LockAllWindows();
2173 
2174 	::ServerWindow* serverWindow;
2175 	Window* window;
2176 	if (BPrivate::gDefaultTokens.GetToken(windowToken,
2177 			B_SERVER_TOKEN, (void**)&serverWindow) != B_OK
2178 		|| (window = serverWindow->Window()) == NULL) {
2179 		UnlockAllWindows();
2180 		return;
2181 	}
2182 
2183 	if (action == B_BRING_TO_FRONT && !window->IsMinimized()) {
2184 		// the window is visible, we just need to make it the front window
2185 		ActivateWindow(window);
2186 	} else {
2187 		// if not, ask the window if it wants to be unminimized
2188 		serverWindow->NotifyMinimize(action == B_MINIMIZE_WINDOW);
2189 	}
2190 
2191 	UnlockAllWindows();
2192 }
2193 
2194 
2195 void
2196 Desktop::WriteWindowList(team_id team, BPrivate::LinkSender& sender)
2197 {
2198 	AutoWriteLocker locker(fWindowLock);
2199 
2200 	// compute the number of windows
2201 
2202 	int32 count = 0;
2203 
2204 	for (Window *window = fAllWindows.FirstWindow(); window != NULL;
2205 			window = window->NextWindow(kAllWindowList)) {
2206 		if (team < B_OK || window->ServerWindow()->ClientTeam() == team)
2207 			count++;
2208 	}
2209 
2210 	// write list
2211 
2212 	sender.StartMessage(B_OK);
2213 	sender.Attach<int32>(count);
2214 
2215 	// first write the windows of the current workspace correctly ordered
2216 	for (Window *window = CurrentWindows().LastWindow(); window != NULL;
2217 			window = window->PreviousWindow(fCurrentWorkspace)) {
2218 		if (team >= B_OK && window->ServerWindow()->ClientTeam() != team)
2219 			continue;
2220 
2221 		sender.Attach<int32>(window->ServerWindow()->ServerToken());
2222 	}
2223 
2224 	// then write all the other windows
2225 	for (Window *window = fAllWindows.FirstWindow(); window != NULL;
2226 			window = window->NextWindow(kAllWindowList)) {
2227 		if ((team >= B_OK && window->ServerWindow()->ClientTeam() != team)
2228 			|| window->InWorkspace(fCurrentWorkspace))
2229 			continue;
2230 
2231 		sender.Attach<int32>(window->ServerWindow()->ServerToken());
2232 	}
2233 
2234 	sender.Flush();
2235 }
2236 
2237 
2238 void
2239 Desktop::WriteWindowInfo(int32 serverToken, BPrivate::LinkSender& sender)
2240 {
2241 	AutoWriteLocker locker(fWindowLock);
2242 	BAutolock tokenLocker(BPrivate::gDefaultTokens);
2243 
2244 	::ServerWindow* window;
2245 	if (BPrivate::gDefaultTokens.GetToken(serverToken,
2246 			B_SERVER_TOKEN, (void**)&window) != B_OK) {
2247 		sender.StartMessage(B_ENTRY_NOT_FOUND);
2248 		sender.Flush();
2249 		return;
2250 	}
2251 
2252 	window_info info;
2253 	window->GetInfo(info);
2254 
2255 	float tabSize = 0.0;
2256 	float borderSize = 0.0;
2257 	::Window* tmp = window->Window();
2258 	if (tmp) {
2259 		BMessage message;
2260 		if (tmp->GetDecoratorSettings(&message)) {
2261 			BRect tabFrame;
2262 			message.FindRect("tab frame", &tabFrame);
2263 			tabSize = tabFrame.bottom - tabFrame.top;
2264 			message.FindFloat("border width", &borderSize);
2265 		}
2266 	}
2267 
2268 	int32 length = window->Title() ? strlen(window->Title()) : 0;
2269 
2270 	sender.StartMessage(B_OK);
2271 	sender.Attach<int32>(sizeof(client_window_info) + length);
2272 	sender.Attach(&info, sizeof(window_info));
2273 	sender.Attach<float>(tabSize);
2274 	sender.Attach<float>(borderSize);
2275 
2276 	if (length > 0)
2277 		sender.Attach(window->Title(), length + 1);
2278 	else
2279 		sender.Attach<char>('\0');
2280 
2281 	sender.Flush();
2282 }
2283 
2284 
2285 void
2286 Desktop::WriteWindowOrder(int32 workspace, BPrivate::LinkSender& sender)
2287 {
2288 	LockSingleWindow();
2289 
2290 	if (workspace < 0)
2291 		workspace = fCurrentWorkspace;
2292 	else if (workspace >= kMaxWorkspaces) {
2293 		sender.StartMessage(B_BAD_VALUE);
2294 		sender.Flush();
2295 		UnlockSingleWindow();
2296 		return;
2297 	}
2298 
2299 	int32 count = _Windows(workspace).Count();
2300 
2301 	// write list
2302 
2303 	sender.StartMessage(B_OK);
2304 	sender.Attach<int32>(count);
2305 
2306 	for (Window *window = _Windows(workspace).LastWindow(); window != NULL;
2307 			window = window->PreviousWindow(workspace)) {
2308 		sender.Attach<int32>(window->ServerWindow()->ServerToken());
2309 	}
2310 
2311 	sender.Flush();
2312 
2313 	UnlockSingleWindow();
2314 }
2315 
2316 
2317 void
2318 Desktop::WriteApplicationOrder(int32 workspace, BPrivate::LinkSender& sender)
2319 {
2320 	fApplicationsLock.Lock();
2321 	LockSingleWindow();
2322 
2323 	int32 maxCount = fApplications.CountItems();
2324 
2325 	fApplicationsLock.Unlock();
2326 		// as long as we hold the window lock, no new window can appear
2327 
2328 	if (workspace < 0)
2329 		workspace = fCurrentWorkspace;
2330 	else if (workspace >= kMaxWorkspaces) {
2331 		sender.StartMessage(B_BAD_VALUE);
2332 		sender.Flush();
2333 		UnlockSingleWindow();
2334 		return;
2335 	}
2336 
2337 	// compute the list of applications on this workspace
2338 
2339 	team_id* teams = (team_id*)malloc(maxCount * sizeof(team_id));
2340 	if (teams == NULL) {
2341 		sender.StartMessage(B_NO_MEMORY);
2342 		sender.Flush();
2343 		UnlockSingleWindow();
2344 		return;
2345 	}
2346 
2347 	int32 count = 0;
2348 
2349 	for (Window *window = _Windows(workspace).LastWindow(); window != NULL;
2350 			window = window->PreviousWindow(workspace)) {
2351 		team_id team = window->ServerWindow()->ClientTeam();
2352 		if (count > 1) {
2353 			// see if we already have this team
2354 			bool found = false;
2355 			for (int32 i = 0; i < count; i++) {
2356 				if (teams[i] == team) {
2357 					found = true;
2358 					break;
2359 				}
2360 			}
2361 			if (found)
2362 				continue;
2363 		}
2364 
2365 		ASSERT(count < maxCount);
2366 		teams[count++] = team;
2367 	}
2368 
2369 	UnlockSingleWindow();
2370 
2371 	// write list
2372 
2373 	sender.StartMessage(B_OK);
2374 	sender.Attach<int32>(count);
2375 
2376 	for (int32 i = 0; i < count; i++) {
2377 		sender.Attach<int32>(teams[i]);
2378 	}
2379 
2380 	sender.Flush();
2381 	free(teams);
2382 }
2383 
2384 
2385 void
2386 Desktop::_LaunchInputServer()
2387 {
2388 	BRoster roster;
2389 	status_t status = roster.Launch("application/x-vnd.Be-input_server");
2390 	if (status == B_OK || status == B_ALREADY_RUNNING)
2391 		return;
2392 
2393 	// Could not load input_server by signature, try well-known location
2394 
2395 	BEntry entry;
2396 	BPath inputServerPath;
2397 	if (find_directory(B_SYSTEM_SERVERS_DIRECTORY, &inputServerPath) == B_OK
2398 		&& inputServerPath.Append("input_server") == B_OK) {
2399 		entry.SetTo(inputServerPath.Path());
2400 	} else
2401 		entry.SetTo("/system/servers/input_server");
2402 	entry_ref ref;
2403 	status_t entryStatus = entry.GetRef(&ref);
2404 	if (entryStatus == B_OK)
2405 		entryStatus = roster.Launch(&ref);
2406 	if (entryStatus == B_OK || entryStatus == B_ALREADY_RUNNING) {
2407 		syslog(LOG_ERR, "Failed to launch the input server by signature: %s!\n",
2408 			strerror(status));
2409 		return;
2410 	}
2411 
2412 	syslog(LOG_ERR, "Failed to launch the input server: %s!\n",
2413 		strerror(entryStatus));
2414 }
2415 
2416 
2417 void
2418 Desktop::_GetLooperName(char* name, size_t length)
2419 {
2420 	snprintf(name, length, "d:%d:%s", fUserID,
2421 		fTargetScreen == NULL ? "baron" : fTargetScreen);
2422 }
2423 
2424 
2425 void
2426 Desktop::_PrepareQuit()
2427 {
2428 	// let's kill all remaining applications
2429 
2430 	fApplicationsLock.Lock();
2431 
2432 	int32 count = fApplications.CountItems();
2433 	for (int32 i = 0; i < count; i++) {
2434 		ServerApp *app = fApplications.ItemAt(i);
2435 		team_id clientTeam = app->ClientTeam();
2436 
2437 		app->Quit();
2438 		kill_team(clientTeam);
2439 	}
2440 
2441 	// wait for the last app to die
2442 	if (count > 0) {
2443 		acquire_sem_etc(fShutdownSemaphore, fShutdownCount, B_RELATIVE_TIMEOUT,
2444 			250000);
2445 	}
2446 
2447 	fApplicationsLock.Unlock();
2448 }
2449 
2450 
2451 void
2452 Desktop::_DispatchMessage(int32 code, BPrivate::LinkReceiver& link)
2453 {
2454 	switch (code) {
2455 		case AS_CREATE_APP:
2456 		{
2457 			// Create the ServerApp to node monitor a new BApplication
2458 
2459 			// Attached data:
2460 			// 1) port_id - receiver port of a regular app
2461 			// 2) port_id - client looper port - for sending messages to the
2462 			//		client
2463 			// 2) team_id - app's team ID
2464 			// 3) int32 - handler token of the regular app
2465 			// 4) char * - signature of the regular app
2466 
2467 			// Find the necessary data
2468 			team_id	clientTeamID = -1;
2469 			port_id	clientLooperPort = -1;
2470 			port_id clientReplyPort = -1;
2471 			int32 htoken = B_NULL_TOKEN;
2472 			char* appSignature = NULL;
2473 
2474 			link.Read<port_id>(&clientReplyPort);
2475 			link.Read<port_id>(&clientLooperPort);
2476 			link.Read<team_id>(&clientTeamID);
2477 			link.Read<int32>(&htoken);
2478 			if (link.ReadString(&appSignature) != B_OK)
2479 				break;
2480 
2481 			ServerApp* app = new ServerApp(this, clientReplyPort,
2482 				clientLooperPort, clientTeamID, htoken, appSignature);
2483 			if (app->InitCheck() == B_OK
2484 				&& app->Run()) {
2485 				// add the new ServerApp to the known list of ServerApps
2486 				fApplicationsLock.Lock();
2487 				fApplications.AddItem(app);
2488 				fApplicationsLock.Unlock();
2489 			} else {
2490 				delete app;
2491 
2492 				// if everything went well, ServerApp::Run() will notify
2493 				// the client - but since it didn't, we do it here
2494 				BPrivate::LinkSender reply(clientReplyPort);
2495 				reply.StartMessage(B_ERROR);
2496 				reply.Flush();
2497 			}
2498 
2499 			// This is necessary because BPortLink::ReadString allocates memory
2500 			free(appSignature);
2501 			break;
2502 		}
2503 
2504 		case AS_DELETE_APP:
2505 		{
2506 			// Delete a ServerApp. Received only from the respective ServerApp
2507 			// when a BApplication asks it to quit.
2508 
2509 			// Attached Data:
2510 			// 1) thread_id - thread ID of the ServerApp to be deleted
2511 
2512 			thread_id thread = -1;
2513 			if (link.Read<thread_id>(&thread) < B_OK)
2514 				break;
2515 
2516 			fApplicationsLock.Lock();
2517 
2518 			// Run through the list of apps and nuke the proper one
2519 
2520 			int32 count = fApplications.CountItems();
2521 			ServerApp* removeApp = NULL;
2522 
2523 			for (int32 i = 0; i < count; i++) {
2524 				ServerApp* app = fApplications.ItemAt(i);
2525 
2526 				if (app->Thread() == thread) {
2527 					fApplications.RemoveItemAt(i);
2528 					removeApp = app;
2529 					break;
2530 				}
2531 			}
2532 
2533 			fApplicationsLock.Unlock();
2534 
2535 			if (removeApp != NULL)
2536 				removeApp->Quit(fShutdownSemaphore);
2537 
2538 			if (fQuitting && count <= 1) {
2539 				// wait for the last app to die
2540 				acquire_sem_etc(fShutdownSemaphore, fShutdownCount,
2541 					B_RELATIVE_TIMEOUT, 500000);
2542 				PostMessage(kMsgQuitLooper);
2543 			}
2544 			break;
2545 		}
2546 
2547 		case AS_ACTIVATE_APP:
2548 		{
2549 			// Someone is requesting to activation of a certain app.
2550 
2551 			// Attached data:
2552 			// 1) port_id reply port
2553 			// 2) team_id team
2554 
2555 			status_t status;
2556 
2557 			// get the parameters
2558 			port_id replyPort;
2559 			team_id team;
2560 			if (link.Read(&replyPort) == B_OK
2561 				&& link.Read(&team) == B_OK)
2562 				status = _ActivateApp(team);
2563 			else
2564 				status = B_ERROR;
2565 
2566 			// send the reply
2567 			BPrivate::PortLink replyLink(replyPort);
2568 			replyLink.StartMessage(status);
2569 			replyLink.Flush();
2570 			break;
2571 		}
2572 
2573 		case AS_APP_CRASHED:
2574 		case AS_DUMP_ALLOCATOR:
2575 		case AS_DUMP_BITMAPS:
2576 		{
2577 			BAutolock locker(fApplicationsLock);
2578 
2579 			team_id team;
2580 			if (link.Read(&team) != B_OK)
2581 				break;
2582 
2583 			for (int32 i = 0; i < fApplications.CountItems(); i++) {
2584 				ServerApp* app = fApplications.ItemAt(i);
2585 
2586 				if (app->ClientTeam() == team)
2587 					app->PostMessage(code);
2588 			}
2589 			break;
2590 		}
2591 
2592 		case AS_EVENT_STREAM_CLOSED:
2593 			_LaunchInputServer();
2594 			break;
2595 
2596 		case B_QUIT_REQUESTED:
2597 			// We've been asked to quit, so (for now) broadcast to all
2598 			// test apps to quit. This situation will occur only when the
2599 			// server is compiled as a regular Be application.
2600 
2601 			fApplicationsLock.Lock();
2602 			fShutdownSemaphore = create_sem(0, "desktop shutdown");
2603 			fShutdownCount = fApplications.CountItems();
2604 			fApplicationsLock.Unlock();
2605 
2606 			fQuitting = true;
2607 			BroadcastToAllApps(AS_QUIT_APP);
2608 
2609 			// We now need to process the remaining AS_DELETE_APP messages and
2610 			// wait for the kMsgShutdownServer message.
2611 			// If an application does not quit as asked, the picasso thread
2612 			// will send us this message in 2-3 seconds.
2613 
2614 			// if there are no apps to quit, shutdown directly
2615 			if (fShutdownCount == 0)
2616 				PostMessage(kMsgQuitLooper);
2617 			break;
2618 
2619 		case AS_ACTIVATE_WORKSPACE:
2620 		{
2621 			int32 index;
2622 			link.Read<int32>(&index);
2623 			if (index == -1)
2624 				index = fPreviousWorkspace;
2625 
2626 			bool moveFocusWindow;
2627 			link.Read<bool>(&moveFocusWindow);
2628 
2629 			SetWorkspace(index, moveFocusWindow);
2630 			break;
2631 		}
2632 
2633 		case AS_TALK_TO_DESKTOP_LISTENER:
2634 		{
2635 			port_id clientReplyPort;
2636 			if (link.Read<port_id>(&clientReplyPort) != B_OK)
2637 				break;
2638 
2639 			BPrivate::LinkSender reply(clientReplyPort);
2640 			AutoWriteLocker locker(fWindowLock);
2641 			if (MessageForListener(NULL, link, reply) != true) {
2642 				// unhandled message, at least send an error if needed
2643 				if (link.NeedsReply()) {
2644 					reply.StartMessage(B_ERROR);
2645 					reply.Flush();
2646 				}
2647 			}
2648 			break;
2649 		}
2650 
2651 		// ToDo: Remove this again. It is a message sent by the
2652 		// invalidate_on_exit kernel debugger add-on to trigger a redraw
2653 		// after exiting a kernel debugger session.
2654 		case 'KDLE':
2655 		{
2656 			BRegion dirty;
2657 			dirty.Include(fVirtualScreen.Frame());
2658 			MarkDirty(dirty);
2659 			break;
2660 		}
2661 
2662 		default:
2663 			printf("Desktop %d:%s received unexpected code %" B_PRId32 "\n", 0,
2664 				"baron", code);
2665 
2666 			if (link.NeedsReply()) {
2667 				// the client is now blocking and waiting for a reply!
2668 				fLink.StartMessage(B_ERROR);
2669 				fLink.Flush();
2670 			}
2671 			break;
2672 	}
2673 }
2674 
2675 
2676 WindowList&
2677 Desktop::CurrentWindows()
2678 {
2679 	return fWorkspaces[fCurrentWorkspace].Windows();
2680 }
2681 
2682 
2683 WindowList&
2684 Desktop::AllWindows()
2685 {
2686 	return fAllWindows;
2687 }
2688 
2689 
2690 Window*
2691 Desktop::WindowForClientLooperPort(port_id port)
2692 {
2693 	ASSERT_MULTI_LOCKED(fWindowLock);
2694 
2695 	for (Window* window = fAllWindows.FirstWindow(); window != NULL;
2696 			window = window->NextWindow(kAllWindowList)) {
2697 		if (window->ServerWindow()->ClientLooperPort() == port)
2698 			return window;
2699 	}
2700 	return NULL;
2701 }
2702 
2703 
2704 WindowList&
2705 Desktop::_Windows(int32 index)
2706 {
2707 	ASSERT(index >= 0 && index < kMaxWorkspaces);
2708 	return fWorkspaces[index].Windows();
2709 }
2710 
2711 
2712 void
2713 Desktop::_UpdateFloating(int32 previousWorkspace, int32 nextWorkspace,
2714 	Window* mouseEventWindow)
2715 {
2716 	if (previousWorkspace == -1)
2717 		previousWorkspace = fCurrentWorkspace;
2718 	if (nextWorkspace == -1)
2719 		nextWorkspace = previousWorkspace;
2720 
2721 	for (Window* floating = fSubsetWindows.FirstWindow(); floating != NULL;
2722 			floating = floating->NextWindow(kSubsetList)) {
2723 		// we only care about app/subset floating windows
2724 		if (floating->Feel() != B_FLOATING_SUBSET_WINDOW_FEEL
2725 			&& floating->Feel() != B_FLOATING_APP_WINDOW_FEEL)
2726 			continue;
2727 
2728 		if (fFront != NULL && fFront->IsNormal()
2729 			&& floating->HasInSubset(fFront)) {
2730 			// is now visible
2731 			if (_Windows(previousWorkspace).HasWindow(floating)
2732 				&& previousWorkspace != nextWorkspace
2733 				&& !floating->InSubsetWorkspace(previousWorkspace)) {
2734 				// but no longer on the previous workspace
2735 				_Windows(previousWorkspace).RemoveWindow(floating);
2736 				floating->SetCurrentWorkspace(-1);
2737 			}
2738 
2739 			if (!_Windows(nextWorkspace).HasWindow(floating)) {
2740 				// but wasn't before
2741 				_Windows(nextWorkspace).AddWindow(floating,
2742 					floating->Frontmost(_Windows(nextWorkspace).FirstWindow(),
2743 					nextWorkspace));
2744 				floating->SetCurrentWorkspace(nextWorkspace);
2745 				if (mouseEventWindow != fFront)
2746 					_ShowWindow(floating);
2747 
2748 				// TODO: put the floating last in the floating window list to
2749 				// preserve the on screen window order
2750 			}
2751 		} else if (_Windows(previousWorkspace).HasWindow(floating)
2752 			&& !floating->InSubsetWorkspace(previousWorkspace)) {
2753 			// was visible, but is no longer
2754 
2755 			_Windows(previousWorkspace).RemoveWindow(floating);
2756 			floating->SetCurrentWorkspace(-1);
2757 			_HideWindow(floating);
2758 
2759 			if (FocusWindow() == floating)
2760 				SetFocusWindow();
2761 		}
2762 	}
2763 }
2764 
2765 
2766 /*!	Search the visible windows for a valid back window
2767 	(only desktop windows can't be back windows)
2768 */
2769 void
2770 Desktop::_UpdateBack()
2771 {
2772 	fBack = NULL;
2773 
2774 	for (Window* window = CurrentWindows().FirstWindow(); window != NULL;
2775 			window = window->NextWindow(fCurrentWorkspace)) {
2776 		if (window->IsHidden() || window->Feel() == kDesktopWindowFeel)
2777 			continue;
2778 
2779 		fBack = window;
2780 		break;
2781 	}
2782 }
2783 
2784 
2785 /*!	Search the visible windows for a valid front window
2786 	(only normal and modal windows can be front windows)
2787 
2788 	The only place where you don't want to update floating windows is
2789 	during a workspace change - because then you'll call _UpdateFloating()
2790 	yourself.
2791 */
2792 void
2793 Desktop::_UpdateFront(bool updateFloating)
2794 {
2795 	fFront = NULL;
2796 
2797 	for (Window* window = CurrentWindows().LastWindow(); window != NULL;
2798 			window = window->PreviousWindow(fCurrentWorkspace)) {
2799 		if (window->IsHidden() || window->IsFloating()
2800 			|| !window->SupportsFront())
2801 			continue;
2802 
2803 		fFront = window;
2804 		break;
2805 	}
2806 
2807 	if (updateFloating)
2808 		_UpdateFloating();
2809 }
2810 
2811 
2812 void
2813 Desktop::_UpdateFronts(bool updateFloating)
2814 {
2815 	_UpdateBack();
2816 	_UpdateFront(updateFloating);
2817 }
2818 
2819 
2820 bool
2821 Desktop::_WindowHasModal(Window* window) const
2822 {
2823 	if (window == NULL)
2824 		return false;
2825 
2826 	for (Window* modal = fSubsetWindows.FirstWindow(); modal != NULL;
2827 			modal = modal->NextWindow(kSubsetList)) {
2828 		// only visible modal windows count
2829 		if (!modal->IsModal() || modal->IsHidden())
2830 			continue;
2831 
2832 		if (modal->HasInSubset(window))
2833 			return true;
2834 	}
2835 
2836 	return false;
2837 }
2838 
2839 
2840 /*!	Determines whether or not the specified \a window can have focus at all.
2841 */
2842 bool
2843 Desktop::_WindowCanHaveFocus(Window* window) const
2844 {
2845 	return window != NULL
2846 		&& window->InWorkspace(fCurrentWorkspace)
2847 		&& (window->Flags() & B_AVOID_FOCUS) == 0
2848 		&& !_WindowHasModal(window)
2849 		&& !window->IsHidden();
2850 }
2851 
2852 
2853 /*!	You must at least hold a single window lock when calling this method.
2854 */
2855 void
2856 Desktop::_WindowChanged(Window* window)
2857 {
2858 	ASSERT_MULTI_LOCKED(fWindowLock);
2859 
2860 	BAutolock _(fWorkspacesLock);
2861 
2862 	for (uint32 i = fWorkspacesViews.CountItems(); i-- > 0;) {
2863 		WorkspacesView* view = fWorkspacesViews.ItemAt(i);
2864 		view->WindowChanged(window);
2865 	}
2866 }
2867 
2868 
2869 /*!	You must at least hold a single window lock when calling this method.
2870 */
2871 void
2872 Desktop::_WindowRemoved(Window* window)
2873 {
2874 	ASSERT_MULTI_LOCKED(fWindowLock);
2875 
2876 	BAutolock _(fWorkspacesLock);
2877 
2878 	for (uint32 i = fWorkspacesViews.CountItems(); i-- > 0;) {
2879 		WorkspacesView* view = fWorkspacesViews.ItemAt(i);
2880 		view->WindowRemoved(window);
2881 	}
2882 }
2883 
2884 
2885 /*!	Shows the window on the screen - it does this independently of the
2886 	Window::IsHidden() state.
2887 */
2888 void
2889 Desktop::_ShowWindow(Window* window, bool affectsOtherWindows)
2890 {
2891 	BRegion background;
2892 	_RebuildClippingForAllWindows(background);
2893 	_SetBackground(background);
2894 	_WindowChanged(window);
2895 
2896 	BRegion dirty(window->VisibleRegion());
2897 
2898 	if (!affectsOtherWindows) {
2899 		// everything that is now visible in the
2900 		// window needs a redraw, but other windows
2901 		// are not affected, we can call ProcessDirtyRegion()
2902 		// of the window, and don't have to use MarkDirty()
2903 		window->ProcessDirtyRegion(dirty);
2904 	} else
2905 		MarkDirty(dirty);
2906 
2907 	if (window->ServerWindow()->HasDirectFrameBufferAccess()) {
2908 		window->ServerWindow()->HandleDirectConnection(
2909 			B_DIRECT_START | B_BUFFER_RESET);
2910 	}
2911 }
2912 
2913 
2914 /*!	Hides the window from the screen - it does this independently of the
2915 	Window::IsHidden() state.
2916 */
2917 void
2918 Desktop::_HideWindow(Window* window)
2919 {
2920 	if (window->ServerWindow()->IsDirectlyAccessing())
2921 		window->ServerWindow()->HandleDirectConnection(B_DIRECT_STOP);
2922 
2923 	// after rebuilding the clipping,
2924 	// this window will not have a visible
2925 	// region anymore, so we need to remember
2926 	// it now
2927 	// (actually that's not true, since
2928 	// hidden windows are excluded from the
2929 	// clipping calculation, but anyways)
2930 	BRegion dirty(window->VisibleRegion());
2931 
2932 	BRegion background;
2933 	_RebuildClippingForAllWindows(background);
2934 	_SetBackground(background);
2935 	_WindowChanged(window);
2936 
2937 	MarkDirty(dirty);
2938 }
2939 
2940 
2941 /*!	Updates the workspaces of all subset windows with regard to the
2942 	specifed window.
2943 	If newIndex is not -1, it will move all subset windows that belong to
2944 	the specifed window to the new workspace; this form is only called by
2945 	SetWorkspace().
2946 */
2947 void
2948 Desktop::_UpdateSubsetWorkspaces(Window* window, int32 previousIndex,
2949 	int32 newIndex)
2950 {
2951 	STRACE(("_UpdateSubsetWorkspaces(window %p, %s)\n", window,
2952 		window->Title()));
2953 
2954 	// if the window is hidden, the subset windows are up-to-date already
2955 	if (!window->IsNormal() || window->IsHidden())
2956 		return;
2957 
2958 	for (Window* subset = fSubsetWindows.FirstWindow(); subset != NULL;
2959 			subset = subset->NextWindow(kSubsetList)) {
2960 		if (subset->Feel() == B_MODAL_ALL_WINDOW_FEEL
2961 			|| subset->Feel() == B_FLOATING_ALL_WINDOW_FEEL) {
2962 			// These windows are always visible on all workspaces,
2963 			// no need to update them.
2964 			continue;
2965 		}
2966 
2967 		if (subset->IsFloating()) {
2968 			// Floating windows are inserted and removed to the current
2969 			// workspace as the need arises - they are not handled here
2970 			// but in _UpdateFront()
2971 			continue;
2972 		}
2973 
2974 		if (subset->HasInSubset(window)) {
2975 			// adopt the workspace change
2976 			SetWindowWorkspaces(subset, subset->SubsetWorkspaces());
2977 		}
2978 	}
2979 }
2980 
2981 
2982 /*!	\brief Adds or removes the window to or from the workspaces it's on.
2983 */
2984 void
2985 Desktop::_ChangeWindowWorkspaces(Window* window, uint32 oldWorkspaces,
2986 	uint32 newWorkspaces)
2987 {
2988 	if (oldWorkspaces == newWorkspaces)
2989 		return;
2990 
2991 	// apply changes to the workspaces' window lists
2992 
2993 	LockAllWindows();
2994 
2995 	// NOTE: we bypass the anchor-mechanism by intention when switching
2996 	// the workspace programmatically.
2997 
2998 	for (int32 i = 0; i < kMaxWorkspaces; i++) {
2999 		if (workspace_in_workspaces(i, oldWorkspaces)) {
3000 			// window is on this workspace, is it anymore?
3001 			if (!workspace_in_workspaces(i, newWorkspaces)) {
3002 				_Windows(i).RemoveWindow(window);
3003 				if (fLastWorkspaceFocus[i] == window)
3004 					fLastWorkspaceFocus[i] = NULL;
3005 
3006 				if (i == CurrentWorkspace()) {
3007 					// remove its appearance from the current workspace
3008 					window->SetCurrentWorkspace(-1);
3009 
3010 					if (!window->IsHidden())
3011 						_HideWindow(window);
3012 				}
3013 			}
3014 		} else {
3015 			// window was not on this workspace, is it now?
3016 			if (workspace_in_workspaces(i, newWorkspaces)) {
3017 				_Windows(i).AddWindow(window,
3018 					window->Frontmost(_Windows(i).FirstWindow(), i));
3019 
3020 				if (i == CurrentWorkspace()) {
3021 					// make the window visible in current workspace
3022 					window->SetCurrentWorkspace(fCurrentWorkspace);
3023 
3024 					if (!window->IsHidden()) {
3025 						// This only affects other windows if this window has
3026 						// floating or modal windows that need to be shown as
3027 						// well
3028 						// TODO: take care of this
3029 						_ShowWindow(window, FrontWindow() == window);
3030 					}
3031 				}
3032 			}
3033 		}
3034 	}
3035 
3036 	// If the window is visible only on one workspace, we set it's current
3037 	// position in that workspace (so that WorkspacesView will find us).
3038 	int32 firstWorkspace = -1;
3039 	for (int32 i = 0; i < kMaxWorkspaces; i++) {
3040 		if ((newWorkspaces & (1L << i)) != 0) {
3041 			if (firstWorkspace != -1) {
3042 				firstWorkspace = -1;
3043 				break;
3044 			}
3045 			firstWorkspace = i;
3046 		}
3047 	}
3048 	if (firstWorkspace >= 0)
3049 		window->Anchor(firstWorkspace).position = window->Frame().LeftTop();
3050 
3051 	// take care about modals and floating windows
3052 	_UpdateSubsetWorkspaces(window);
3053 
3054 	NotifyWindowWorkspacesChanged(window, newWorkspaces);
3055 
3056 	UnlockAllWindows();
3057 }
3058 
3059 
3060 void
3061 Desktop::_BringWindowsToFront(WindowList& windows, int32 list,
3062 	bool wereVisible)
3063 {
3064 	// we don't need to redraw what is currently
3065 	// visible of the window
3066 	BRegion clean;
3067 
3068 	for (Window* window = windows.FirstWindow(); window != NULL;
3069 			window = window->NextWindow(list)) {
3070 		if (wereVisible)
3071 			clean.Include(&window->VisibleRegion());
3072 
3073 		CurrentWindows().AddWindow(window,
3074 			window->Frontmost(CurrentWindows().FirstWindow(),
3075 				fCurrentWorkspace));
3076 
3077 		_WindowChanged(window);
3078 	}
3079 
3080 	BRegion dummy;
3081 	_RebuildClippingForAllWindows(dummy);
3082 
3083 	// redraw what became visible of the window(s)
3084 
3085 	BRegion dirty;
3086 	for (Window* window = windows.FirstWindow(); window != NULL;
3087 			window = window->NextWindow(list)) {
3088 		dirty.Include(&window->VisibleRegion());
3089 	}
3090 
3091 	dirty.Exclude(&clean);
3092 	MarkDirty(dirty);
3093 
3094 	_UpdateFront();
3095 
3096 	if (windows.FirstWindow() == fBack || fBack == NULL)
3097 		_UpdateBack();
3098 }
3099 
3100 
3101 /*!	Returns the last focussed non-hidden subset window belonging to the
3102 	specified \a window.
3103 */
3104 Window*
3105 Desktop::_LastFocusSubsetWindow(Window* window)
3106 {
3107 	if (window == NULL)
3108 		return NULL;
3109 
3110 	for (Window* front = fFocusList.LastWindow(); front != NULL;
3111 			front = front->PreviousWindow(kFocusList)) {
3112 		if (front != window && !front->IsHidden()
3113 			&& window->HasInSubset(front))
3114 			return front;
3115 	}
3116 
3117 	return NULL;
3118 }
3119 
3120 
3121 /*!	\brief Sends a fake B_MOUSE_MOVED event to the window under the mouse,
3122 		and also updates the current view under the mouse.
3123 
3124 	This has only to be done in case the view changed without user interaction,
3125 	ie. because of a workspace change or a closing window.
3126 */
3127 void
3128 Desktop::_SendFakeMouseMoved(Window* window)
3129 {
3130 	int32 viewToken = B_NULL_TOKEN;
3131 	EventTarget* target = NULL;
3132 
3133 	LockAllWindows();
3134 
3135 	if (window == NULL)
3136 		window = MouseEventWindow();
3137 	if (window == NULL)
3138 		window = WindowAt(fLastMousePosition);
3139 
3140 	if (window != NULL) {
3141 		BMessage message;
3142 		window->MouseMoved(&message, fLastMousePosition, &viewToken, true,
3143 			true);
3144 
3145 		if (viewToken != B_NULL_TOKEN)
3146 			target = &window->EventTarget();
3147 	}
3148 
3149 	if (viewToken != B_NULL_TOKEN)
3150 		SetViewUnderMouse(window, viewToken);
3151 	else {
3152 		SetViewUnderMouse(NULL, B_NULL_TOKEN);
3153 		SetCursor(NULL);
3154 	}
3155 
3156 	UnlockAllWindows();
3157 
3158 	if (target != NULL)
3159 		EventDispatcher().SendFakeMouseMoved(*target, viewToken);
3160 }
3161 
3162 
3163 Screen*
3164 Desktop::_DetermineScreenFor(BRect frame)
3165 {
3166 	AutoReadLocker _(fScreenLock);
3167 
3168 	// TODO: choose the screen depending on where most of the area is
3169 	return fVirtualScreen.ScreenAt(0);
3170 }
3171 
3172 
3173 void
3174 Desktop::_RebuildClippingForAllWindows(BRegion& stillAvailableOnScreen)
3175 {
3176 	// the available region on screen starts with the entire screen area
3177 	// each window on the screen will take a portion from that area
3178 
3179 	// figure out what the entire screen area is
3180 	stillAvailableOnScreen = fScreenRegion;
3181 
3182 	// set clipping of each window
3183 	for (Window* window = CurrentWindows().LastWindow(); window != NULL;
3184 			window = window->PreviousWindow(fCurrentWorkspace)) {
3185 		if (!window->IsHidden()) {
3186 			window->SetClipping(&stillAvailableOnScreen);
3187 			window->SetScreen(_DetermineScreenFor(window->Frame()));
3188 
3189 			if (window->ServerWindow()->IsDirectlyAccessing()) {
3190 				window->ServerWindow()->HandleDirectConnection(
3191 					B_DIRECT_MODIFY | B_CLIPPING_MODIFIED);
3192 			}
3193 
3194 			// that windows region is not available on screen anymore
3195 			stillAvailableOnScreen.Exclude(&window->VisibleRegion());
3196 		}
3197 	}
3198 }
3199 
3200 
3201 void
3202 Desktop::_TriggerWindowRedrawing(BRegion& newDirtyRegion)
3203 {
3204 	// send redraw messages to all windows intersecting the dirty region
3205 	for (Window* window = CurrentWindows().LastWindow(); window != NULL;
3206 			window = window->PreviousWindow(fCurrentWorkspace)) {
3207 		if (!window->IsHidden()
3208 			&& newDirtyRegion.Intersects(window->VisibleRegion().Frame()))
3209 			window->ProcessDirtyRegion(newDirtyRegion);
3210 	}
3211 }
3212 
3213 
3214 void
3215 Desktop::_SetBackground(BRegion& background)
3216 {
3217 	// NOTE: the drawing operation is caried out
3218 	// in the clipping region rebuild, but it is
3219 	// ok actually, because it also avoids trails on
3220 	// moving windows
3221 
3222 	// remember the region not covered by any windows
3223 	// and redraw the dirty background
3224 	BRegion dirtyBackground(background);
3225 	dirtyBackground.Exclude(&fBackgroundRegion);
3226 	dirtyBackground.IntersectWith(&background);
3227 	fBackgroundRegion = background;
3228 	if (dirtyBackground.Frame().IsValid()) {
3229 		if (GetDrawingEngine()->LockParallelAccess()) {
3230 			GetDrawingEngine()->FillRegion(dirtyBackground,
3231 				fWorkspaces[fCurrentWorkspace].Color());
3232 
3233 			GetDrawingEngine()->UnlockParallelAccess();
3234 		}
3235 	}
3236 }
3237 
3238 
3239 //!	The all window lock must be held when calling this function.
3240 void
3241 Desktop::RebuildAndRedrawAfterWindowChange(Window* changedWindow,
3242 	BRegion& dirty)
3243 {
3244 	ASSERT_MULTI_WRITE_LOCKED(fWindowLock);
3245 	if (!changedWindow->IsVisible() || dirty.CountRects() == 0)
3246 		return;
3247 
3248 	// The following loop is pretty much a copy of
3249 	// _RebuildClippingForAllWindows(), but will also
3250 	// take care about restricting our dirty region.
3251 
3252 	// figure out what the entire screen area is
3253 	BRegion stillAvailableOnScreen(fScreenRegion);
3254 
3255 	// set clipping of each window
3256 	for (Window* window = CurrentWindows().LastWindow(); window != NULL;
3257 			window = window->PreviousWindow(fCurrentWorkspace)) {
3258 		if (!window->IsHidden()) {
3259 			if (window == changedWindow)
3260 				dirty.IntersectWith(&stillAvailableOnScreen);
3261 
3262 			window->SetClipping(&stillAvailableOnScreen);
3263 			window->SetScreen(_DetermineScreenFor(window->Frame()));
3264 
3265 			if (window->ServerWindow()->IsDirectlyAccessing()) {
3266 				window->ServerWindow()->HandleDirectConnection(
3267 					B_DIRECT_MODIFY | B_CLIPPING_MODIFIED);
3268 			}
3269 
3270 			// that windows region is not available on screen anymore
3271 			stillAvailableOnScreen.Exclude(&window->VisibleRegion());
3272 		}
3273 	}
3274 
3275 	_SetBackground(stillAvailableOnScreen);
3276 	_WindowChanged(changedWindow);
3277 
3278 	_TriggerWindowRedrawing(dirty);
3279 }
3280 
3281 
3282 //! Suspend all windows with direct access to the frame buffer
3283 void
3284 Desktop::_SuspendDirectFrameBufferAccess()
3285 {
3286 	ASSERT_MULTI_LOCKED(fWindowLock);
3287 
3288 	for (Window* window = fAllWindows.FirstWindow(); window != NULL;
3289 			window = window->NextWindow(kAllWindowList)) {
3290 		if (window->ServerWindow()->IsDirectlyAccessing())
3291 			window->ServerWindow()->HandleDirectConnection(B_DIRECT_STOP);
3292 	}
3293 }
3294 
3295 
3296 //! Resume all windows with direct access to the frame buffer
3297 void
3298 Desktop::_ResumeDirectFrameBufferAccess()
3299 {
3300 	ASSERT_MULTI_LOCKED(fWindowLock);
3301 
3302 	for (Window* window = fAllWindows.FirstWindow(); window != NULL;
3303 			window = window->NextWindow(kAllWindowList)) {
3304 		if (window->IsHidden() || !window->InWorkspace(fCurrentWorkspace))
3305 			continue;
3306 
3307 		if (window->ServerWindow()->HasDirectFrameBufferAccess()) {
3308 			window->ServerWindow()->HandleDirectConnection(
3309 				B_DIRECT_START | B_BUFFER_RESET, B_MODE_CHANGED);
3310 		}
3311 	}
3312 }
3313 
3314 
3315 void
3316 Desktop::_ScreenChanged(Screen* screen)
3317 {
3318 	ASSERT_MULTI_WRITE_LOCKED(fWindowLock);
3319 
3320 	// the entire screen is dirty, because we're actually
3321 	// operating on an all new buffer in memory
3322 	BRegion dirty(screen->Frame());
3323 
3324 	// update our cached screen region
3325 	fScreenRegion.Set(screen->Frame());
3326 	gInputManager->UpdateScreenBounds(screen->Frame());
3327 
3328 	BRegion background;
3329 	_RebuildClippingForAllWindows(background);
3330 
3331 	fBackgroundRegion.MakeEmpty();
3332 		// makes sure that the complete background is redrawn
3333 	_SetBackground(background);
3334 
3335 	// figure out dirty region
3336 	dirty.Exclude(&background);
3337 	_TriggerWindowRedrawing(dirty);
3338 
3339 	// send B_SCREEN_CHANGED to windows on that screen
3340 	BMessage update(B_SCREEN_CHANGED);
3341 	update.AddInt64("when", real_time_clock_usecs());
3342 	update.AddRect("frame", screen->Frame());
3343 	update.AddInt32("mode", screen->ColorSpace());
3344 
3345 	fVirtualScreen.UpdateFrame();
3346 
3347 	for (Window* window = fAllWindows.FirstWindow(); window != NULL;
3348 			window = window->NextWindow(kAllWindowList)) {
3349 		if (window->Screen() == screen)
3350 			window->ServerWindow()->ScreenChanged(&update);
3351 	}
3352 }
3353 
3354 
3355 /*!	\brief activate one of the app's windows.
3356 */
3357 status_t
3358 Desktop::_ActivateApp(team_id team)
3359 {
3360 	// search for an unhidden window in the current workspace
3361 
3362 	AutoWriteLocker locker(fWindowLock);
3363 
3364 	for (Window* window = CurrentWindows().LastWindow(); window != NULL;
3365 			window = window->PreviousWindow(fCurrentWorkspace)) {
3366 		if (!window->IsHidden() && window->IsNormal()
3367 			&& window->ServerWindow()->ClientTeam() == team) {
3368 			ActivateWindow(window);
3369 			return B_OK;
3370 		}
3371 	}
3372 
3373 	// search for an unhidden window to give focus to
3374 
3375 	for (Window* window = fAllWindows.FirstWindow(); window != NULL;
3376 			window = window->NextWindow(kAllWindowList)) {
3377 		// if window is a normal window of the team, and not hidden,
3378 		// we've found our target
3379 		if (!window->IsHidden() && window->IsNormal()
3380 			&& window->ServerWindow()->ClientTeam() == team) {
3381 			ActivateWindow(window);
3382 			return B_OK;
3383 		}
3384 	}
3385 
3386 	// TODO: we cannot maximize minimized windows here (with the window lock
3387 	// write locked). To work-around this, we could forward the request to
3388 	// the ServerApp of this team - it maintains its own window list, and can
3389 	// therefore call ActivateWindow() without holding the window lock.
3390 	return B_BAD_VALUE;
3391 }
3392 
3393 
3394 void
3395 Desktop::_SetCurrentWorkspaceConfiguration()
3396 {
3397 	ASSERT_MULTI_WRITE_LOCKED(fWindowLock);
3398 
3399 	status_t status = fDirectScreenLock.LockWithTimeout(1000000L);
3400 	if (status != B_OK) {
3401 		// The application having the direct screen lock didn't give it up in
3402 		// time, make it crash
3403 		syslog(LOG_ERR, "Team %" B_PRId32 " did not give up its direct screen "
3404 			"lock.\n", fDirectScreenTeam);
3405 
3406 		debug_thread(fDirectScreenTeam);
3407 		fDirectScreenTeam = -1;
3408 	} else
3409 		fDirectScreenLock.Unlock();
3410 
3411 	AutoWriteLocker _(fScreenLock);
3412 
3413 	uint32 changedScreens;
3414 	fVirtualScreen.SetConfiguration(*this,
3415 		fWorkspaces[fCurrentWorkspace].CurrentScreenConfiguration(),
3416 		&changedScreens);
3417 
3418 	for (int32 i = 0; changedScreens != 0; i++, changedScreens /= 2) {
3419 		if ((changedScreens & (1 << i)) != 0)
3420 			_ScreenChanged(fVirtualScreen.ScreenAt(i));
3421 	}
3422 }
3423 
3424 
3425 /*!	Changes the current workspace to the one specified by \a index.
3426 	You must hold the all window lock when calling this method.
3427 */
3428 void
3429 Desktop::_SetWorkspace(int32 index, bool moveFocusWindow)
3430 {
3431 	ASSERT_MULTI_WRITE_LOCKED(fWindowLock);
3432 
3433 	int32 previousIndex = fCurrentWorkspace;
3434 	rgb_color previousColor = fWorkspaces[fCurrentWorkspace].Color();
3435 	bool movedMouseEventWindow = false;
3436 	Window* movedWindow = NULL;
3437 	if (moveFocusWindow) {
3438 		if (fMouseEventWindow != NULL)
3439 			movedWindow = fMouseEventWindow;
3440 		else
3441 			movedWindow = FocusWindow();
3442 	}
3443 
3444 	if (movedWindow != NULL) {
3445 		if (movedWindow->IsNormal()) {
3446 			if (!movedWindow->InWorkspace(index)) {
3447 				// The window currently being dragged will follow us to this
3448 				// workspace if it's not already on it.
3449 				// But only normal windows are following
3450 				uint32 oldWorkspaces = movedWindow->Workspaces();
3451 
3452 				WindowStack* stack = movedWindow->GetWindowStack();
3453 				if (stack != NULL) {
3454 					for (int32 s = 0; s < stack->CountWindows(); s++) {
3455 						Window* stackWindow = stack->LayerOrder().ItemAt(s);
3456 
3457 						_Windows(previousIndex).RemoveWindow(stackWindow);
3458 						_Windows(index).AddWindow(stackWindow,
3459 							stackWindow->Frontmost(
3460 								_Windows(index).FirstWindow(), index));
3461 
3462 						// send B_WORKSPACES_CHANGED message
3463 						stackWindow->WorkspacesChanged(oldWorkspaces,
3464 							stackWindow->Workspaces());
3465 					}
3466 				}
3467 				// TODO: subset windows will always flicker this way
3468 
3469 				movedMouseEventWindow = true;
3470 
3471 				NotifyWindowWorkspacesChanged(movedWindow,
3472 					movedWindow->Workspaces());
3473 			} else {
3474 				// make sure it's frontmost
3475 				_Windows(index).RemoveWindow(movedWindow);
3476 				_Windows(index).AddWindow(movedWindow,
3477 					movedWindow->Frontmost(_Windows(index).FirstWindow(),
3478 					index));
3479 			}
3480 		}
3481 
3482 		movedWindow->Anchor(index).position = movedWindow->Frame().LeftTop();
3483 	}
3484 
3485 	if (movedWindow == NULL || movedWindow->InWorkspace(previousIndex))
3486 		fLastWorkspaceFocus[previousIndex] = FocusWindow();
3487 	else
3488 		fLastWorkspaceFocus[previousIndex] = NULL;
3489 
3490 	// build region of windows that are no longer visible in the new workspace
3491 
3492 	BRegion dirty;
3493 
3494 	for (Window* window = CurrentWindows().FirstWindow();
3495 			window != NULL; window = window->NextWindow(previousIndex)) {
3496 		// store current position in Workspace anchor
3497 		window->Anchor(previousIndex).position = window->Frame().LeftTop();
3498 
3499 		if (!window->IsHidden()
3500 			&& window->ServerWindow()->IsDirectlyAccessing())
3501 			window->ServerWindow()->HandleDirectConnection(B_DIRECT_STOP);
3502 
3503 		window->WorkspaceActivated(previousIndex, false);
3504 
3505 		if (window->InWorkspace(index))
3506 			continue;
3507 
3508 		if (!window->IsHidden()) {
3509 			// this window will no longer be visible
3510 			dirty.Include(&window->VisibleRegion());
3511 		}
3512 
3513 		window->SetCurrentWorkspace(-1);
3514 	}
3515 
3516 	fPreviousWorkspace = fCurrentWorkspace;
3517 	fCurrentWorkspace = index;
3518 
3519 	// Change the display modes, if needed
3520 	_SetCurrentWorkspaceConfiguration();
3521 
3522 	// Show windows, and include them in the changed region - but only
3523 	// those that were not visible before (or whose position changed)
3524 
3525 	WindowList windows(kWorkingList);
3526 	BList previousRegions;
3527 
3528 	for (Window* window = _Windows(index).FirstWindow();
3529 			window != NULL; window = window->NextWindow(index)) {
3530 		BPoint position = window->Anchor(index).position;
3531 
3532 		window->SetCurrentWorkspace(index);
3533 
3534 		if (window->IsHidden())
3535 			continue;
3536 
3537 		if (position == kInvalidWindowPosition) {
3538 			// if you enter a workspace for the first time, the position
3539 			// of the window in the previous workspace is adopted
3540 			position = window->Frame().LeftTop();
3541 				// TODO: make sure the window is still on-screen if it
3542 				//	was before!
3543 		}
3544 
3545 		if (!window->InWorkspace(previousIndex)) {
3546 			// This window was not visible before, make sure its frame
3547 			// is up-to-date
3548 			if (window->Frame().LeftTop() != position) {
3549 				BPoint offset = position - window->Frame().LeftTop();
3550 				window->MoveBy((int32)offset.x, (int32)offset.y);
3551 			}
3552 			continue;
3553 		}
3554 
3555 		if (window->Frame().LeftTop() != position) {
3556 			// the window was visible before, but its on-screen location changed
3557 			BPoint offset = position - window->Frame().LeftTop();
3558 			MoveWindowBy(window, offset.x, offset.y);
3559 				// TODO: be a bit smarter than this...
3560 		} else {
3561 			// We need to remember the previous visible region of the
3562 			// window if they changed their order
3563 			BRegion* region = new (std::nothrow)
3564 				BRegion(window->VisibleRegion());
3565 			if (region != NULL) {
3566 				if (previousRegions.AddItem(region))
3567 					windows.AddWindow(window);
3568 				else
3569 					delete region;
3570 			}
3571 		}
3572 	}
3573 
3574 	_UpdateFronts(false);
3575 	_UpdateFloating(previousIndex, index,
3576 		movedMouseEventWindow ? movedWindow : NULL);
3577 
3578 	BRegion stillAvailableOnScreen;
3579 	_RebuildClippingForAllWindows(stillAvailableOnScreen);
3580 	_SetBackground(stillAvailableOnScreen);
3581 
3582 	for (Window* window = _Windows(index).FirstWindow(); window != NULL;
3583 			window = window->NextWindow(index)) {
3584 		// send B_WORKSPACE_ACTIVATED message
3585 		window->WorkspaceActivated(index, true);
3586 
3587 		if (!window->IsHidden()
3588 			&& window->ServerWindow()->HasDirectFrameBufferAccess()) {
3589 			window->ServerWindow()->HandleDirectConnection(
3590 				B_DIRECT_START | B_BUFFER_RESET, B_MODE_CHANGED);
3591 		}
3592 
3593 		if (window->InWorkspace(previousIndex) || window->IsHidden()
3594 			|| (window == movedWindow && movedWindow->IsNormal())
3595 			|| (!window->IsNormal()
3596 				&& window->HasInSubset(movedWindow))) {
3597 			// This window was visible before, and is already handled in the
3598 			// above loop
3599 			continue;
3600 		}
3601 
3602 		dirty.Include(&window->VisibleRegion());
3603 	}
3604 
3605 	// Catch order changes in the new workspaces window list
3606 	int32 i = 0;
3607 	for (Window* window = windows.FirstWindow(); window != NULL;
3608 			window = window->NextWindow(kWorkingList), i++) {
3609 		BRegion* region = (BRegion*)previousRegions.ItemAt(i);
3610 		region->ExclusiveInclude(&window->VisibleRegion());
3611 		dirty.Include(region);
3612 		delete region;
3613 	}
3614 
3615 	// Set new focus, but keep focus to a floating window if still visible
3616 	if (movedWindow != NULL)
3617 		SetFocusWindow(movedWindow);
3618 	else if (!_Windows(index).HasWindow(FocusWindow())
3619 		|| (FocusWindow() != NULL && !FocusWindow()->IsFloating()))
3620 		SetFocusWindow(fLastWorkspaceFocus[index]);
3621 
3622 	_WindowChanged(NULL);
3623 	MarkDirty(dirty);
3624 
3625 #if 0
3626 	// Show the dirty regions of this workspace switch
3627 	if (GetDrawingEngine()->LockParallelAccess()) {
3628 		GetDrawingEngine()->FillRegion(dirty, (rgb_color){255, 0, 0});
3629 		GetDrawingEngine()->UnlockParallelAccess();
3630 		snooze(100000);
3631 	}
3632 #endif
3633 
3634 	if (previousColor != fWorkspaces[fCurrentWorkspace].Color())
3635 		RedrawBackground();
3636 }
3637