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