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