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