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