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