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