xref: /haiku/src/servers/app/Desktop.cpp (revision 4a55cc230cf7566cadcbb23b1928eefff8aea9a2)
1 /*
2  * Copyright 2001-2020, Haiku.
3  * Distributed under the terms of the MIT License.
4  *
5  * Authors:
6  *		Adrian Oanca, adioanca@cotty.iren.ro
7  *		Stephan Aßmus, superstippi@gmx.de
8  *		Axel Dörfler, axeld@pinc-software.de
9  *		Andrej Spielmann, andrej.spielmann@seh.ox.ac.uk
10  *		Brecht Machiels, brecht@mos6581.org
11  *		Clemens Zeidler, haiku@clemens-zeidler.de
12  *		Ingo Weinhold, ingo_weinhold@gmx.de
13  *		Joseph Groover, looncraz@looncraz.net
14  *		Tri-Edge AI
15  *		Jacob Secunda, secundja@gmail.com
16  */
17 
18 
19 /*!	Class used to encapsulate desktop management */
20 
21 
22 #include "Desktop.h"
23 
24 #include <stdio.h>
25 #include <string.h>
26 #include <syslog.h>
27 
28 #include <Debug.h>
29 #include <debugger.h>
30 #include <DirectWindow.h>
31 #include <Entry.h>
32 #include <FindDirectory.h>
33 #include <Message.h>
34 #include <MessageFilter.h>
35 #include <Path.h>
36 #include <Region.h>
37 #include <Roster.h>
38 
39 #include <PrivateScreen.h>
40 #include <ServerProtocol.h>
41 #include <ViewPrivate.h>
42 #include <WindowInfo.h>
43 
44 #include "AppServer.h"
45 #include "ClickTarget.h"
46 #include "DecorManager.h"
47 #include "DesktopSettingsPrivate.h"
48 #include "DrawingEngine.h"
49 #include "FontManager.h"
50 #include "HWInterface.h"
51 #include "InputManager.h"
52 #include "Screen.h"
53 #include "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
1562 	// constructed by the window itself in ResizeBy()
1563 	BRegion newDirtyRegion;
1564 	// track the dirty region outside the window in case
1565 	// it is shrunk in "previouslyOccupiedRegion"
1566 	BRegion previouslyOccupiedRegion(window->VisibleRegion());
1567 
1568 	// stop direct frame buffer access
1569 	bool direct = false;
1570 	if (window->ServerWindow()->IsDirectlyAccessing()) {
1571 		window->ServerWindow()->HandleDirectConnection(B_DIRECT_STOP);
1572 		direct = true;
1573 	}
1574 
1575 	window->ResizeBy((int32)x, (int32)y, &newDirtyRegion);
1576 
1577 	BRegion background;
1578 	_RebuildClippingForAllWindows(background);
1579 
1580 	// we just care for the region outside the window
1581 	previouslyOccupiedRegion.Exclude(&window->VisibleRegion());
1582 
1583 	// make sure the window cannot mark stuff dirty outside
1584 	// its visible region...
1585 	newDirtyRegion.IntersectWith(&window->VisibleRegion());
1586 	// ...because we do this ourselves
1587 	newDirtyRegion.Include(&previouslyOccupiedRegion);
1588 
1589 	MarkDirty(newDirtyRegion);
1590 	_SetBackground(background);
1591 	_WindowChanged(window);
1592 
1593 	// resume direct frame buffer access
1594 	if (direct) {
1595 		window->ServerWindow()->HandleDirectConnection(
1596 			B_DIRECT_START | B_BUFFER_RESIZED | B_CLIPPING_MODIFIED);
1597 	}
1598 
1599 	NotifyWindowResized(window);
1600 }
1601 
1602 
1603 void
1604 Desktop::SetWindowOutlinesDelta(Window* window, BPoint delta)
1605 {
1606 	AutoWriteLocker _(fWindowLock);
1607 
1608 	if (!window->IsVisible())
1609 		return;
1610 
1611 	BRegion newDirtyRegion;
1612 	window->SetOutlinesDelta(delta, &newDirtyRegion);
1613 
1614 	BRegion background;
1615 	_RebuildClippingForAllWindows(background);
1616 
1617 	MarkDirty(newDirtyRegion);
1618 	_SetBackground(background);
1619 }
1620 
1621 
1622 bool
1623 Desktop::SetWindowTabLocation(Window* window, float location, bool isShifting)
1624 {
1625 	AutoWriteLocker _(fWindowLock);
1626 
1627 	BRegion dirty;
1628 	bool changed = window->SetTabLocation(location, isShifting, dirty);
1629 	if (changed)
1630 		RebuildAndRedrawAfterWindowChange(window, dirty);
1631 
1632 	NotifyWindowTabLocationChanged(window, location, isShifting);
1633 
1634 	return changed;
1635 }
1636 
1637 
1638 bool
1639 Desktop::SetWindowDecoratorSettings(Window* window, const BMessage& settings)
1640 {
1641 	AutoWriteLocker _(fWindowLock);
1642 
1643 	BRegion dirty;
1644 	bool changed = window->SetDecoratorSettings(settings, dirty);
1645 	bool listenerChanged = SetDecoratorSettings(window, settings);
1646 	if (changed || listenerChanged)
1647 		RebuildAndRedrawAfterWindowChange(window, dirty);
1648 
1649 	return changed;
1650 }
1651 
1652 
1653 void
1654 Desktop::SetWindowWorkspaces(Window* window, uint32 workspaces)
1655 {
1656 	LockAllWindows();
1657 
1658 	if (window->IsNormal() && workspaces == B_CURRENT_WORKSPACE)
1659 		workspaces = workspace_to_workspaces(CurrentWorkspace());
1660 
1661 	WindowStack* stack = window->GetWindowStack();
1662 	if (stack != NULL) {
1663 		for (int32 s = 0; s < stack->CountWindows(); s++) {
1664 			window = stack->LayerOrder().ItemAt(s);
1665 
1666 			uint32 oldWorkspaces = window->Workspaces();
1667 			window->WorkspacesChanged(oldWorkspaces, workspaces);
1668 			_ChangeWindowWorkspaces(window, oldWorkspaces, workspaces);
1669 		}
1670 	}
1671 	UnlockAllWindows();
1672 }
1673 
1674 
1675 /*!	\brief Adds the window to the desktop.
1676 	At this point, the window is still hidden and must be shown explicitly
1677 	via ShowWindow().
1678 */
1679 void
1680 Desktop::AddWindow(Window *window)
1681 {
1682 	LockAllWindows();
1683 
1684 	fAllWindows.AddWindow(window);
1685 	if (!window->IsNormal())
1686 		fSubsetWindows.AddWindow(window);
1687 
1688 	if (window->IsNormal()) {
1689 		if (window->Workspaces() == B_CURRENT_WORKSPACE)
1690 			window->SetWorkspaces(workspace_to_workspaces(CurrentWorkspace()));
1691 	} else {
1692 		// subset windows are visible on all workspaces their subset is on
1693 		window->SetWorkspaces(window->SubsetWorkspaces());
1694 	}
1695 
1696 	_ChangeWindowWorkspaces(window, 0, window->Workspaces());
1697 
1698 	NotifyWindowAdded(window);
1699 
1700 	UnlockAllWindows();
1701 }
1702 
1703 
1704 void
1705 Desktop::RemoveWindow(Window *window)
1706 {
1707 	LockAllWindows();
1708 
1709 	if (!window->IsHidden())
1710 		HideWindow(window);
1711 
1712 	fAllWindows.RemoveWindow(window);
1713 	if (!window->IsNormal())
1714 		fSubsetWindows.RemoveWindow(window);
1715 
1716 	_ChangeWindowWorkspaces(window, window->Workspaces(), 0);
1717 
1718 	NotifyWindowRemoved(window);
1719 
1720 	UnlockAllWindows();
1721 
1722 	// make sure this window won't get any events anymore
1723 
1724 	EventDispatcher().RemoveTarget(window->EventTarget());
1725 }
1726 
1727 
1728 bool
1729 Desktop::AddWindowToSubset(Window* subset, Window* window)
1730 {
1731 	if (!subset->AddToSubset(window))
1732 		return false;
1733 
1734 	_ChangeWindowWorkspaces(subset, subset->Workspaces(),
1735 		subset->SubsetWorkspaces());
1736 	return true;
1737 }
1738 
1739 
1740 void
1741 Desktop::RemoveWindowFromSubset(Window* subset, Window* window)
1742 {
1743 	subset->RemoveFromSubset(window);
1744 	_ChangeWindowWorkspaces(subset, subset->Workspaces(),
1745 		subset->SubsetWorkspaces());
1746 }
1747 
1748 
1749 void
1750 Desktop::FontsChanged(Window* window)
1751 {
1752 	AutoWriteLocker _(fWindowLock);
1753 
1754 	BRegion dirty;
1755 	window->FontsChanged(&dirty);
1756 
1757 	RebuildAndRedrawAfterWindowChange(window, dirty);
1758 }
1759 
1760 
1761 void
1762 Desktop::ColorUpdated(Window* window, color_which which, rgb_color color)
1763 {
1764 	AutoWriteLocker _(fWindowLock);
1765 
1766 	window->TopView()->ColorUpdated(which, color);
1767 
1768 	switch (which) {
1769 		case B_WINDOW_TAB_COLOR:
1770 		case B_WINDOW_TEXT_COLOR:
1771 		case B_WINDOW_INACTIVE_TAB_COLOR:
1772 		case B_WINDOW_INACTIVE_TEXT_COLOR:
1773 		case B_WINDOW_BORDER_COLOR:
1774 		case B_WINDOW_INACTIVE_BORDER_COLOR:
1775 			break;
1776 		default:
1777 			return;
1778 	}
1779 
1780 	BRegion dirty;
1781 	window->ColorsChanged(&dirty);
1782 	RebuildAndRedrawAfterWindowChange(window, dirty);
1783 }
1784 
1785 
1786 void
1787 Desktop::SetWindowLook(Window* window, window_look newLook)
1788 {
1789 	if (window->Look() == newLook)
1790 		return;
1791 
1792 	AutoWriteLocker _(fWindowLock);
1793 
1794 	BRegion dirty;
1795 	window->SetLook(newLook, &dirty);
1796 		// TODO: test what happens when the window
1797 		// finds out it needs to resize itself...
1798 
1799 	RebuildAndRedrawAfterWindowChange(window, dirty);
1800 
1801 	NotifyWindowLookChanged(window, newLook);
1802 }
1803 
1804 
1805 void
1806 Desktop::SetWindowFeel(Window* window, window_feel newFeel)
1807 {
1808 	if (window->Feel() == newFeel)
1809 		return;
1810 
1811 	LockAllWindows();
1812 
1813 	bool wasNormal = window->IsNormal();
1814 
1815 	window->SetFeel(newFeel);
1816 
1817 	// move the window out of or into the subset window list as needed
1818 	if (window->IsNormal() && !wasNormal)
1819 		fSubsetWindows.RemoveWindow(window);
1820 	else if (!window->IsNormal() && wasNormal)
1821 		fSubsetWindows.AddWindow(window);
1822 
1823 	// A normal window that was once a floating or modal window will
1824 	// adopt the window's current workspaces
1825 
1826 	if (!window->IsNormal()) {
1827 		_ChangeWindowWorkspaces(window, window->Workspaces(),
1828 			window->SubsetWorkspaces());
1829 	}
1830 
1831 	// make sure the window has the correct position in the window lists
1832 	// (ie. all floating windows have to be on the top, ...)
1833 
1834 	for (int32 i = 0; i < kMaxWorkspaces; i++) {
1835 		if (!workspace_in_workspaces(i, window->Workspaces()))
1836 			continue;
1837 
1838 		bool changed = false;
1839 		BRegion visibleBefore;
1840 		if (i == fCurrentWorkspace && window->IsVisible())
1841 			visibleBefore = window->VisibleRegion();
1842 
1843 		Window* backmost = window->Backmost(_Windows(i).LastWindow(), i);
1844 		if (backmost != NULL) {
1845 			// check if the backmost window is really behind it
1846 			Window* previous = window->PreviousWindow(i);
1847 			while (previous != NULL) {
1848 				if (previous == backmost)
1849 					break;
1850 
1851 				previous = previous->PreviousWindow(i);
1852 			}
1853 
1854 			if (previous == NULL) {
1855 				// need to reinsert window before its backmost window
1856 				_Windows(i).RemoveWindow(window);
1857 				_Windows(i).AddWindow(window, backmost->NextWindow(i));
1858 				changed = true;
1859 			}
1860 		}
1861 
1862 		Window* frontmost = window->Frontmost(_Windows(i).FirstWindow(), i);
1863 		if (frontmost != NULL) {
1864 			// check if the frontmost window is really in front of it
1865 			Window* next = window->NextWindow(i);
1866 			while (next != NULL) {
1867 				if (next == frontmost)
1868 					break;
1869 
1870 				next = next->NextWindow(i);
1871 			}
1872 
1873 			if (next == NULL) {
1874 				// need to reinsert window behind its frontmost window
1875 				_Windows(i).RemoveWindow(window);
1876 				_Windows(i).AddWindow(window, frontmost);
1877 				changed = true;
1878 			}
1879 		}
1880 
1881 		if (i == fCurrentWorkspace && changed) {
1882 			BRegion dummy;
1883 			_RebuildClippingForAllWindows(dummy);
1884 
1885 			// mark everything dirty that is no longer visible, or
1886 			// is now visible and wasn't before
1887 			BRegion visibleAfter(window->VisibleRegion());
1888 			BRegion dirty(visibleAfter);
1889 			dirty.Exclude(&visibleBefore);
1890 			visibleBefore.Exclude(&visibleAfter);
1891 			dirty.Include(&visibleBefore);
1892 
1893 			MarkDirty(dirty);
1894 		}
1895 	}
1896 
1897 	_UpdateFronts();
1898 
1899 	if (window == FocusWindow() && !window->IsVisible())
1900 		SetFocusWindow();
1901 
1902 	NotifyWindowFeelChanged(window, newFeel);
1903 
1904 	UnlockAllWindows();
1905 }
1906 
1907 
1908 void
1909 Desktop::SetWindowFlags(Window *window, uint32 newFlags)
1910 {
1911 	if (window->Flags() == newFlags)
1912 		return;
1913 
1914 	AutoWriteLocker _(fWindowLock);
1915 
1916 	BRegion dirty;
1917 	window->SetFlags(newFlags, &dirty);
1918 		// TODO: test what happens when the window
1919 		// finds out it needs to resize itself...
1920 
1921 	RebuildAndRedrawAfterWindowChange(window, dirty);
1922 }
1923 
1924 
1925 void
1926 Desktop::SetWindowTitle(Window *window, const char* title)
1927 {
1928 	AutoWriteLocker _(fWindowLock);
1929 
1930 	BRegion dirty;
1931 	window->SetTitle(title, dirty);
1932 
1933 	RebuildAndRedrawAfterWindowChange(window, dirty);
1934 }
1935 
1936 
1937 /*!	Returns the window under the mouse cursor.
1938 	You need to have acquired the All Windows lock when calling this method.
1939 */
1940 Window*
1941 Desktop::WindowAt(BPoint where)
1942 {
1943 	for (Window* window = CurrentWindows().LastWindow(); window;
1944 			window = window->PreviousWindow(fCurrentWorkspace)) {
1945 		if (window->IsVisible() && window->VisibleRegion().Contains(where))
1946 			return window->StackedWindowAt(where);
1947 	}
1948 
1949 	return NULL;
1950 }
1951 
1952 
1953 void
1954 Desktop::SetMouseEventWindow(Window* window)
1955 {
1956 	fMouseEventWindow = window;
1957 }
1958 
1959 
1960 void
1961 Desktop::SetViewUnderMouse(const Window* window, int32 viewToken)
1962 {
1963 	fWindowUnderMouse = window;
1964 	fViewUnderMouse = viewToken;
1965 }
1966 
1967 
1968 int32
1969 Desktop::ViewUnderMouse(const Window* window)
1970 {
1971 	if (window != NULL && fWindowUnderMouse == window)
1972 		return fViewUnderMouse;
1973 
1974 	return B_NULL_TOKEN;
1975 }
1976 
1977 
1978 /*!	Returns the current keyboard event target candidate - which is either the
1979 	top-most window (in case it has the kAcceptKeyboardFocusFlag flag set), or
1980 	the one having focus.
1981 	The window lock must be held when calling this function.
1982 */
1983 EventTarget*
1984 Desktop::KeyboardEventTarget()
1985 {
1986 	// Get the top most non-hidden window
1987 	Window* window = CurrentWindows().LastWindow();
1988 	while (window != NULL && window->IsHidden()) {
1989 		window = window->PreviousWindow(fCurrentWorkspace);
1990 	}
1991 
1992 	if (window != NULL && (window->Flags() & kAcceptKeyboardFocusFlag) != 0)
1993 		return &window->EventTarget();
1994 
1995 	if (FocusWindow() != NULL)
1996 		return &FocusWindow()->EventTarget();
1997 
1998 	return NULL;
1999 }
2000 
2001 
2002 /*!	Tries to set the focus to the specified \a focus window. It will make sure,
2003 	however, that the window actually can have focus. You are allowed to pass
2004 	in a NULL pointer for \a focus.
2005 
2006 	Besides the B_AVOID_FOCUS flag, a modal window, or a BWindowScreen can both
2007 	prevent it from getting focus.
2008 
2009 	In any case, this method makes sure that there is a focus window, if there
2010 	is any window at all, that is.
2011 */
2012 void
2013 Desktop::SetFocusWindow(Window* nextFocus)
2014 {
2015 	if (!LockAllWindows())
2016 		return;
2017 
2018 	// test for B_LOCK_WINDOW_FOCUS
2019 	if (fLockedFocusWindow && nextFocus != fLockedFocusWindow) {
2020 		UnlockAllWindows();
2021 		return;
2022 	}
2023 
2024 	bool hasModal = _WindowHasModal(nextFocus);
2025 	bool hasWindowScreen = false;
2026 
2027 	if (!hasModal && nextFocus != NULL) {
2028 		// Check whether or not a window screen is in front of the window
2029 		// (if it has a modal, the right thing is done, anyway)
2030 		Window* window = nextFocus;
2031 		while (true) {
2032 			window = window->NextWindow(fCurrentWorkspace);
2033 			if (window == NULL || window->Feel() == kWindowScreenFeel)
2034 				break;
2035 		}
2036 		if (window != NULL)
2037 			hasWindowScreen = true;
2038 	}
2039 
2040 	if (nextFocus == fFocus && nextFocus != NULL && !nextFocus->IsHidden()
2041 		&& (nextFocus->Flags() & B_AVOID_FOCUS) == 0
2042 		&& !hasModal && !hasWindowScreen) {
2043 		// the window that is supposed to get focus already has focus
2044 		UnlockAllWindows();
2045 		return;
2046 	}
2047 
2048 	uint32 listIndex = fCurrentWorkspace;
2049 	WindowList* list = &_Windows(fCurrentWorkspace);
2050 	if (!fSettings->NormalMouse()) {
2051 		listIndex = kFocusList;
2052 		list = &fFocusList;
2053 	}
2054 
2055 	if (nextFocus == NULL || hasModal || hasWindowScreen) {
2056 		nextFocus = list->LastWindow();
2057 
2058 		if (fSettings->NormalMouse()) {
2059 			// If the last window having focus is a window that cannot make it
2060 			// to the front, we use that as the next focus
2061 			Window* lastFocus = fFocusList.LastWindow();
2062 			if (lastFocus != NULL && !lastFocus->SupportsFront()
2063 				&& _WindowCanHaveFocus(lastFocus)) {
2064 				nextFocus = lastFocus;
2065 			}
2066 		}
2067 	}
2068 
2069 	// make sure no window is chosen that doesn't want focus or cannot have it
2070 	while (nextFocus != NULL && !_WindowCanHaveFocus(nextFocus)) {
2071 		nextFocus = nextFocus->PreviousWindow(listIndex);
2072 	}
2073 
2074 	if (fFocus == nextFocus) {
2075 		// turns out the window that is supposed to get focus now already has it
2076 		UnlockAllWindows();
2077 		return;
2078 	}
2079 
2080 	team_id oldActiveApp = -1;
2081 	team_id newActiveApp = -1;
2082 
2083 	if (fFocus != NULL) {
2084 		fFocus->SetFocus(false);
2085 		oldActiveApp = fFocus->ServerWindow()->App()->ClientTeam();
2086 	}
2087 
2088 	fFocus = nextFocus;
2089 
2090 	if (fFocus != NULL) {
2091 		fFocus->SetFocus(true);
2092 		newActiveApp = fFocus->ServerWindow()->App()->ClientTeam();
2093 
2094 		// move current focus to the end of the focus list
2095 		fFocusList.RemoveWindow(fFocus);
2096 		fFocusList.AddWindow(fFocus);
2097 	}
2098 
2099 	if (newActiveApp == -1) {
2100 		// make sure the cursor is visible
2101 		HWInterface()->SetCursorVisible(true);
2102 	}
2103 
2104 	UnlockAllWindows();
2105 
2106 	// change the "active" app if appropriate
2107 	if (oldActiveApp == newActiveApp)
2108 		return;
2109 
2110 	BAutolock locker(fApplicationsLock);
2111 
2112 	for (int32 i = 0; i < fApplications.CountItems(); i++) {
2113 		ServerApp* app = fApplications.ItemAt(i);
2114 
2115 		if (oldActiveApp != -1 && app->ClientTeam() == oldActiveApp)
2116 			app->Activate(false);
2117 		else if (newActiveApp != -1 && app->ClientTeam() == newActiveApp)
2118 			app->Activate(true);
2119 	}
2120 }
2121 
2122 
2123 void
2124 Desktop::SetFocusLocked(const Window* window)
2125 {
2126 	AutoWriteLocker _(fWindowLock);
2127 
2128 	if (window != NULL) {
2129 		// Don't allow this to be set when no mouse buttons
2130 		// are pressed. (BView::SetMouseEventMask() should only be called
2131 		// from mouse hooks.)
2132 		if (fLastMouseButtons == 0)
2133 			return;
2134 	}
2135 
2136 	fLockedFocusWindow = window;
2137 }
2138 
2139 
2140 Window*
2141 Desktop::FindWindowByClientToken(int32 token, team_id teamID)
2142 {
2143 	for (Window *window = fAllWindows.FirstWindow(); window != NULL;
2144 			window = window->NextWindow(kAllWindowList)) {
2145 		if (window->ServerWindow()->ClientToken() == token
2146 			&& window->ServerWindow()->ClientTeam() == teamID) {
2147 			return window;
2148 		}
2149 	}
2150 
2151 	return NULL;
2152 }
2153 
2154 
2155 ::EventTarget*
2156 Desktop::FindTarget(BMessenger& messenger)
2157 {
2158 	for (Window *window = fAllWindows.FirstWindow(); window != NULL;
2159 			window = window->NextWindow(kAllWindowList)) {
2160 		if (window->EventTarget().Messenger() == messenger)
2161 			return &window->EventTarget();
2162 	}
2163 
2164 	return NULL;
2165 }
2166 
2167 
2168 void
2169 Desktop::MarkDirty(BRegion& region)
2170 {
2171 	if (region.CountRects() == 0)
2172 		return;
2173 
2174 	if (LockAllWindows()) {
2175 		// send redraw messages to all windows intersecting the dirty region
2176 		_TriggerWindowRedrawing(region);
2177 
2178 		UnlockAllWindows();
2179 	}
2180 }
2181 
2182 
2183 void
2184 Desktop::Redraw()
2185 {
2186 	BRegion dirty(fVirtualScreen.Frame());
2187 	MarkDirty(dirty);
2188 }
2189 
2190 
2191 /*!	\brief Redraws the background (ie. the desktop window, if any).
2192 */
2193 void
2194 Desktop::RedrawBackground()
2195 {
2196 	LockAllWindows();
2197 
2198 	BRegion redraw;
2199 
2200 	Window* window = CurrentWindows().FirstWindow();
2201 	if (window != NULL && window->Feel() == kDesktopWindowFeel) {
2202 		redraw = window->VisibleContentRegion();
2203 
2204 		// look for desktop background view, and update its background color
2205 		// TODO: is there a better way to do this?
2206 		View* view = window->TopView();
2207 		if (view != NULL)
2208 			view = view->FirstChild();
2209 
2210 		while (view != NULL) {
2211 			if (view->IsDesktopBackground()) {
2212 				view->SetViewColor(fWorkspaces[fCurrentWorkspace].Color());
2213 				break;
2214 			}
2215 			view = view->NextSibling();
2216 		}
2217 
2218 		window->ProcessDirtyRegion(redraw);
2219 	} else {
2220 		redraw = BackgroundRegion();
2221 		fBackgroundRegion.MakeEmpty();
2222 		_SetBackground(redraw);
2223 	}
2224 
2225 	_WindowChanged(NULL);
2226 		// update workspaces view as well
2227 
2228 	UnlockAllWindows();
2229 }
2230 
2231 
2232 bool
2233 Desktop::ReloadDecor(DecorAddOn* oldDecor)
2234 {
2235 	AutoWriteLocker _(fWindowLock);
2236 
2237 	bool returnValue = true;
2238 
2239 	if (oldDecor != NULL) {
2240 		const DesktopListenerList* oldListeners
2241 			= &oldDecor->GetDesktopListeners();
2242 		for (int i = 0; i < oldListeners->CountItems(); i++)
2243 			UnregisterListener(oldListeners->ItemAt(i));
2244 	}
2245 
2246 	for (Window* window = fAllWindows.FirstWindow(); window != NULL;
2247 			window = window->NextWindow(kAllWindowList)) {
2248 		BRegion oldBorder;
2249 		window->GetBorderRegion(&oldBorder);
2250 
2251 		if (!window->ReloadDecor()) {
2252 			// prevent unloading previous add-on
2253 			returnValue = false;
2254 		}
2255 
2256 		BRegion border;
2257 		window->GetBorderRegion(&border);
2258 
2259 		border.Include(&oldBorder);
2260 		RebuildAndRedrawAfterWindowChange(window, border);
2261 	}
2262 
2263 	// register new listeners
2264 	const DesktopListenerList& newListeners
2265 		= gDecorManager.GetDesktopListeners();
2266 	for (int i = 0; i < newListeners.CountItems(); i++)
2267  		RegisterListener(newListeners.ItemAt(i));
2268 
2269  	return returnValue;
2270 }
2271 
2272 
2273 void
2274 Desktop::MinimizeApplication(team_id team)
2275 {
2276 	AutoWriteLocker locker(fWindowLock);
2277 
2278 	// Just minimize all windows of that application
2279 
2280 	for (Window *window = fAllWindows.FirstWindow(); window != NULL;
2281 			window = window->NextWindow(kAllWindowList)) {
2282 		if (window->ServerWindow()->ClientTeam() != team)
2283 			continue;
2284 
2285 		window->ServerWindow()->NotifyMinimize(true);
2286 	}
2287 }
2288 
2289 
2290 void
2291 Desktop::BringApplicationToFront(team_id team)
2292 {
2293 	AutoWriteLocker locker(fWindowLock);
2294 
2295 	// TODO: for now, just maximize all windows of that application
2296 	// TODO: have the ability to lock the current workspace
2297 
2298 	for (Window *window = fAllWindows.FirstWindow(); window != NULL;
2299 			window = window->NextWindow(kAllWindowList)) {
2300 		if (window->ServerWindow()->ClientTeam() != team)
2301 			continue;
2302 
2303 		window->ServerWindow()->NotifyMinimize(false);
2304 	}
2305 }
2306 
2307 
2308 void
2309 Desktop::WindowAction(int32 windowToken, int32 action)
2310 {
2311 	if (action != B_MINIMIZE_WINDOW && action != B_BRING_TO_FRONT)
2312 		return;
2313 
2314 	LockAllWindows();
2315 
2316 	::ServerWindow* serverWindow;
2317 	Window* window;
2318 	if (BPrivate::gDefaultTokens.GetToken(windowToken,
2319 			B_SERVER_TOKEN, (void**)&serverWindow) != B_OK
2320 		|| (window = serverWindow->Window()) == NULL) {
2321 		UnlockAllWindows();
2322 		return;
2323 	}
2324 
2325 	if (action == B_BRING_TO_FRONT && !window->IsMinimized()) {
2326 		// the window is visible, we just need to make it the front window
2327 		ActivateWindow(window);
2328 	} else {
2329 		// if not, ask the window if it wants to be unminimized
2330 		serverWindow->NotifyMinimize(action == B_MINIMIZE_WINDOW);
2331 	}
2332 
2333 	UnlockAllWindows();
2334 }
2335 
2336 
2337 void
2338 Desktop::WriteWindowList(team_id team, BPrivate::LinkSender& sender)
2339 {
2340 	AutoWriteLocker locker(fWindowLock);
2341 
2342 	// compute the number of windows
2343 
2344 	int32 count = 0;
2345 
2346 	for (Window *window = fAllWindows.FirstWindow(); window != NULL;
2347 			window = window->NextWindow(kAllWindowList)) {
2348 		if (team < B_OK || window->ServerWindow()->ClientTeam() == team)
2349 			count++;
2350 	}
2351 
2352 	// write list
2353 
2354 	sender.StartMessage(B_OK);
2355 	sender.Attach<int32>(count);
2356 
2357 	// first write the windows of the current workspace correctly ordered
2358 	for (Window *window = CurrentWindows().LastWindow(); window != NULL;
2359 			window = window->PreviousWindow(fCurrentWorkspace)) {
2360 		if (team >= B_OK && window->ServerWindow()->ClientTeam() != team)
2361 			continue;
2362 
2363 		sender.Attach<int32>(window->ServerWindow()->ServerToken());
2364 	}
2365 
2366 	// then write all the other windows
2367 	for (Window *window = fAllWindows.FirstWindow(); window != NULL;
2368 			window = window->NextWindow(kAllWindowList)) {
2369 		if ((team >= B_OK && window->ServerWindow()->ClientTeam() != team)
2370 			|| window->InWorkspace(fCurrentWorkspace))
2371 			continue;
2372 
2373 		sender.Attach<int32>(window->ServerWindow()->ServerToken());
2374 	}
2375 
2376 	sender.Flush();
2377 }
2378 
2379 
2380 void
2381 Desktop::WriteWindowInfo(int32 serverToken, BPrivate::LinkSender& sender)
2382 {
2383 	AutoWriteLocker locker(fWindowLock);
2384 	BAutolock tokenLocker(BPrivate::gDefaultTokens);
2385 
2386 	::ServerWindow* window;
2387 	if (BPrivate::gDefaultTokens.GetToken(serverToken,
2388 			B_SERVER_TOKEN, (void**)&window) != B_OK) {
2389 		sender.StartMessage(B_ENTRY_NOT_FOUND);
2390 		sender.Flush();
2391 		return;
2392 	}
2393 
2394 	window_info info;
2395 	window->GetInfo(info);
2396 
2397 	float tabSize = 0.0;
2398 	float borderSize = 0.0;
2399 	::Window* tmp = window->Window();
2400 	if (tmp) {
2401 		BMessage message;
2402 		if (tmp->GetDecoratorSettings(&message)) {
2403 			BRect tabFrame;
2404 			message.FindRect("tab frame", &tabFrame);
2405 			tabSize = tabFrame.bottom - tabFrame.top;
2406 			message.FindFloat("border width", &borderSize);
2407 		}
2408 	}
2409 
2410 	int32 length = window->Title() ? strlen(window->Title()) : 0;
2411 
2412 	sender.StartMessage(B_OK);
2413 	sender.Attach<int32>(sizeof(client_window_info) + length + 1);
2414 	sender.Attach(&info, sizeof(window_info));
2415 	sender.Attach<float>(tabSize);
2416 	sender.Attach<float>(borderSize);
2417 
2418 	if (length > 0)
2419 		sender.Attach(window->Title(), length + 1);
2420 	else
2421 		sender.Attach<char>('\0');
2422 
2423 	sender.Flush();
2424 }
2425 
2426 
2427 void
2428 Desktop::WriteWindowOrder(int32 workspace, BPrivate::LinkSender& sender)
2429 {
2430 	LockSingleWindow();
2431 
2432 	if (workspace < 0)
2433 		workspace = fCurrentWorkspace;
2434 	else if (workspace >= kMaxWorkspaces) {
2435 		sender.StartMessage(B_BAD_VALUE);
2436 		sender.Flush();
2437 		UnlockSingleWindow();
2438 		return;
2439 	}
2440 
2441 	int32 count = _Windows(workspace).Count();
2442 
2443 	// write list
2444 
2445 	sender.StartMessage(B_OK);
2446 	sender.Attach<int32>(count);
2447 
2448 	for (Window *window = _Windows(workspace).LastWindow(); window != NULL;
2449 			window = window->PreviousWindow(workspace)) {
2450 		sender.Attach<int32>(window->ServerWindow()->ServerToken());
2451 	}
2452 
2453 	sender.Flush();
2454 
2455 	UnlockSingleWindow();
2456 }
2457 
2458 
2459 void
2460 Desktop::WriteApplicationOrder(int32 workspace, BPrivate::LinkSender& sender)
2461 {
2462 	fApplicationsLock.Lock();
2463 	LockSingleWindow();
2464 
2465 	int32 maxCount = fApplications.CountItems();
2466 
2467 	fApplicationsLock.Unlock();
2468 		// as long as we hold the window lock, no new window can appear
2469 
2470 	if (workspace < 0)
2471 		workspace = fCurrentWorkspace;
2472 	else if (workspace >= kMaxWorkspaces) {
2473 		sender.StartMessage(B_BAD_VALUE);
2474 		sender.Flush();
2475 		UnlockSingleWindow();
2476 		return;
2477 	}
2478 
2479 	// compute the list of applications on this workspace
2480 
2481 	team_id* teams = (team_id*)malloc(maxCount * sizeof(team_id));
2482 	if (teams == NULL) {
2483 		sender.StartMessage(B_NO_MEMORY);
2484 		sender.Flush();
2485 		UnlockSingleWindow();
2486 		return;
2487 	}
2488 
2489 	int32 count = 0;
2490 
2491 	for (Window *window = _Windows(workspace).LastWindow(); window != NULL;
2492 			window = window->PreviousWindow(workspace)) {
2493 		team_id team = window->ServerWindow()->ClientTeam();
2494 		if (count > 1) {
2495 			// see if we already have this team
2496 			bool found = false;
2497 			for (int32 i = 0; i < count; i++) {
2498 				if (teams[i] == team) {
2499 					found = true;
2500 					break;
2501 				}
2502 			}
2503 			if (found)
2504 				continue;
2505 		}
2506 
2507 		ASSERT(count < maxCount);
2508 		teams[count++] = team;
2509 	}
2510 
2511 	UnlockSingleWindow();
2512 
2513 	// write list
2514 
2515 	sender.StartMessage(B_OK);
2516 	sender.Attach<int32>(count);
2517 
2518 	for (int32 i = 0; i < count; i++) {
2519 		sender.Attach<int32>(teams[i]);
2520 	}
2521 
2522 	sender.Flush();
2523 	free(teams);
2524 }
2525 
2526 
2527 void
2528 Desktop::_LaunchInputServer()
2529 {
2530 	BRoster roster;
2531 	status_t status = roster.Launch("application/x-vnd.Be-input_server");
2532 	if (status == B_OK || status == B_ALREADY_RUNNING)
2533 		return;
2534 
2535 	// Could not load input_server by signature, try well-known location
2536 
2537 	BEntry entry;
2538 	BPath inputServerPath;
2539 	if (find_directory(B_SYSTEM_SERVERS_DIRECTORY, &inputServerPath) == B_OK
2540 		&& inputServerPath.Append("input_server") == B_OK) {
2541 		entry.SetTo(inputServerPath.Path());
2542 	} else
2543 		entry.SetTo("/system/servers/input_server");
2544 	entry_ref ref;
2545 	status_t entryStatus = entry.GetRef(&ref);
2546 	if (entryStatus == B_OK)
2547 		entryStatus = roster.Launch(&ref);
2548 	if (entryStatus == B_OK || entryStatus == B_ALREADY_RUNNING) {
2549 		syslog(LOG_ERR, "Failed to launch the input server by signature: %s!\n",
2550 			strerror(status));
2551 		return;
2552 	}
2553 
2554 	syslog(LOG_ERR, "Failed to launch the input server: %s!\n",
2555 		strerror(entryStatus));
2556 }
2557 
2558 
2559 void
2560 Desktop::_GetLooperName(char* name, size_t length)
2561 {
2562 	snprintf(name, length, "d:%d:%s", fUserID,
2563 		fTargetScreen == NULL ? "baron" : fTargetScreen);
2564 }
2565 
2566 
2567 void
2568 Desktop::_PrepareQuit()
2569 {
2570 	// let's kill all remaining applications
2571 
2572 	fApplicationsLock.Lock();
2573 
2574 	int32 count = fApplications.CountItems();
2575 	for (int32 i = 0; i < count; i++) {
2576 		ServerApp *app = fApplications.ItemAt(i);
2577 		team_id clientTeam = app->ClientTeam();
2578 
2579 		app->Quit();
2580 		kill_team(clientTeam);
2581 	}
2582 
2583 	// wait for the last app to die
2584 	if (count > 0) {
2585 		acquire_sem_etc(fShutdownSemaphore, fShutdownCount, B_RELATIVE_TIMEOUT,
2586 			250000);
2587 	}
2588 
2589 	fApplicationsLock.Unlock();
2590 }
2591 
2592 
2593 void
2594 Desktop::_DispatchMessage(int32 code, BPrivate::LinkReceiver& link)
2595 {
2596 	switch (code) {
2597 		case AS_CREATE_APP:
2598 		{
2599 			// Create the ServerApp to node monitor a new BApplication
2600 
2601 			// Attached data:
2602 			// 1) port_id - receiver port of a regular app
2603 			// 2) port_id - client looper port - for sending messages to the
2604 			//		client
2605 			// 2) team_id - app's team ID
2606 			// 3) int32 - handler token of the regular app
2607 			// 4) char * - signature of the regular app
2608 
2609 			// Find the necessary data
2610 			team_id	clientTeamID = -1;
2611 			port_id	clientLooperPort = -1;
2612 			port_id clientReplyPort = -1;
2613 			int32 htoken = B_NULL_TOKEN;
2614 			char* appSignature = NULL;
2615 
2616 			link.Read<port_id>(&clientReplyPort);
2617 			link.Read<port_id>(&clientLooperPort);
2618 			link.Read<team_id>(&clientTeamID);
2619 			link.Read<int32>(&htoken);
2620 			if (link.ReadString(&appSignature) != B_OK)
2621 				break;
2622 
2623 			ObjectDeleter<ServerApp> app(new (std::nothrow) ServerApp(this, clientReplyPort,
2624 				clientLooperPort, clientTeamID, htoken, appSignature));
2625 			status_t status = B_OK;
2626 			if (!app.IsSet())
2627 				status = B_NO_MEMORY;
2628 			if (status == B_OK)
2629 				status = app->InitCheck();
2630 			if (status == B_OK)
2631 				status = app->Run();
2632 			if (status == B_OK) {
2633 				// add the new ServerApp to the known list of ServerApps
2634 				fApplicationsLock.Lock();
2635 				fApplications.AddItem(app.Detach());
2636 				fApplicationsLock.Unlock();
2637 			} else {
2638 				// if everything went well, ServerApp::Run() will notify
2639 				// the client - but since it didn't, we do it here
2640 				BPrivate::LinkSender reply(clientReplyPort);
2641 				reply.StartMessage(status);
2642 				reply.Flush();
2643 			}
2644 
2645 			// This is necessary because BPortLink::ReadString allocates memory
2646 			free(appSignature);
2647 			break;
2648 		}
2649 
2650 		case AS_DELETE_APP:
2651 		{
2652 			// Delete a ServerApp. Received only from the respective ServerApp
2653 			// when a BApplication asks it to quit.
2654 
2655 			// Attached Data:
2656 			// 1) thread_id - thread ID of the ServerApp to be deleted
2657 
2658 			thread_id thread = -1;
2659 			if (link.Read<thread_id>(&thread) < B_OK)
2660 				break;
2661 
2662 			fApplicationsLock.Lock();
2663 
2664 			// Run through the list of apps and nuke the proper one
2665 
2666 			int32 count = fApplications.CountItems();
2667 			ServerApp* removeApp = NULL;
2668 
2669 			for (int32 i = 0; i < count; i++) {
2670 				ServerApp* app = fApplications.ItemAt(i);
2671 
2672 				if (app->Thread() == thread) {
2673 					fApplications.RemoveItemAt(i);
2674 					removeApp = app;
2675 					break;
2676 				}
2677 			}
2678 
2679 			fApplicationsLock.Unlock();
2680 
2681 			if (removeApp != NULL)
2682 				removeApp->Quit(fShutdownSemaphore);
2683 
2684 			if (fQuitting && count <= 1) {
2685 				// wait for the last app to die
2686 				acquire_sem_etc(fShutdownSemaphore, fShutdownCount,
2687 					B_RELATIVE_TIMEOUT, 500000);
2688 				PostMessage(kMsgQuitLooper);
2689 			}
2690 			break;
2691 		}
2692 
2693 		case AS_ACTIVATE_APP:
2694 		{
2695 			// Someone is requesting to activation of a certain app.
2696 
2697 			// Attached data:
2698 			// 1) port_id reply port
2699 			// 2) team_id team
2700 
2701 			status_t status;
2702 
2703 			// get the parameters
2704 			port_id replyPort;
2705 			team_id team;
2706 			if (link.Read(&replyPort) == B_OK
2707 				&& link.Read(&team) == B_OK)
2708 				status = _ActivateApp(team);
2709 			else
2710 				status = B_ERROR;
2711 
2712 			// send the reply
2713 			BPrivate::PortLink replyLink(replyPort);
2714 			replyLink.StartMessage(status);
2715 			replyLink.Flush();
2716 			break;
2717 		}
2718 
2719 		case AS_APP_CRASHED:
2720 		case AS_DUMP_ALLOCATOR:
2721 		case AS_DUMP_BITMAPS:
2722 		{
2723 			BAutolock locker(fApplicationsLock);
2724 
2725 			team_id team;
2726 			if (link.Read(&team) != B_OK)
2727 				break;
2728 
2729 			for (int32 i = 0; i < fApplications.CountItems(); i++) {
2730 				ServerApp* app = fApplications.ItemAt(i);
2731 
2732 				if (app->ClientTeam() == team)
2733 					app->PostMessage(code);
2734 			}
2735 			break;
2736 		}
2737 
2738 		case AS_EVENT_STREAM_CLOSED:
2739 			_LaunchInputServer();
2740 			break;
2741 
2742 		case B_QUIT_REQUESTED:
2743 			// We've been asked to quit, so (for now) broadcast to all
2744 			// test apps to quit. This situation will occur only when the
2745 			// server is compiled as a regular Be application.
2746 
2747 			fApplicationsLock.Lock();
2748 			fShutdownSemaphore = create_sem(0, "desktop shutdown");
2749 			fShutdownCount = fApplications.CountItems();
2750 			fApplicationsLock.Unlock();
2751 
2752 			fQuitting = true;
2753 			BroadcastToAllApps(AS_QUIT_APP);
2754 
2755 			// We now need to process the remaining AS_DELETE_APP messages.
2756 			// We quit the looper when the last app is deleted.
2757 
2758 			// if there are no apps to quit, shutdown directly
2759 			if (fShutdownCount == 0)
2760 				PostMessage(kMsgQuitLooper);
2761 
2762 			break;
2763 
2764 		case AS_ACTIVATE_WORKSPACE:
2765 		{
2766 			int32 index;
2767 			link.Read<int32>(&index);
2768 			if (index == -1)
2769 				index = fPreviousWorkspace;
2770 
2771 			bool moveFocusWindow;
2772 			link.Read<bool>(&moveFocusWindow);
2773 
2774 			SetWorkspace(index, moveFocusWindow);
2775 			break;
2776 		}
2777 
2778 		case AS_TALK_TO_DESKTOP_LISTENER:
2779 		{
2780 			port_id clientReplyPort;
2781 			if (link.Read<port_id>(&clientReplyPort) != B_OK)
2782 				break;
2783 
2784 			BPrivate::LinkSender reply(clientReplyPort);
2785 			AutoWriteLocker locker(fWindowLock);
2786 			if (MessageForListener(NULL, link, reply) != true) {
2787 				// unhandled message, at least send an error if needed
2788 				if (link.NeedsReply()) {
2789 					reply.StartMessage(B_ERROR);
2790 					reply.Flush();
2791 				}
2792 			}
2793 			break;
2794 		}
2795 
2796 		case AS_SET_UI_COLOR:
2797 		{
2798 			color_which which;
2799 			rgb_color color;
2800 
2801 			if (link.Read<color_which>(&which) == B_OK
2802 					&& link.Read<rgb_color>(&color) == B_OK) {
2803 
2804 				const char* colorName = ui_color_name(which);
2805 				fPendingColors.SetColor(colorName, color);
2806 
2807 				DelayedMessage delayed(AS_SET_UI_COLORS, DM_60HZ_DELAY);
2808 				delayed.AddTarget(MessagePort());
2809 				delayed.SetMerge(DM_MERGE_CANCEL);
2810 
2811 				delayed.Attach<bool>(true);
2812 				delayed.Flush();
2813 			}
2814 
2815 			break;
2816 		}
2817 
2818 		case AS_SET_UI_COLORS:
2819 		{
2820 			bool flushPendingOnly = false;
2821 
2822 			if (link.Read<bool>(&flushPendingOnly) != B_OK
2823 				|| (flushPendingOnly &&
2824 						fPendingColors.CountNames(B_RGB_32_BIT_TYPE) == 0)) {
2825 				break;
2826 			}
2827 
2828 			if (!flushPendingOnly) {
2829 				// Client wants to set a color map
2830 				color_which which = B_NO_COLOR;
2831 				rgb_color color;
2832 
2833 				do {
2834 					if (link.Read<color_which>(&which) != B_OK
2835 						|| link.Read<rgb_color>(&color) != B_OK)
2836 						break;
2837 
2838 					fPendingColors.SetColor(ui_color_name(which), color);
2839 				} while (which != B_NO_COLOR);
2840 			}
2841 
2842 			_FlushPendingColors();
2843 			break;
2844 		}
2845 
2846 		// ToDo: Remove this again. It is a message sent by the
2847 		// invalidate_on_exit kernel debugger add-on to trigger a redraw
2848 		// after exiting a kernel debugger session.
2849 		case 'KDLE':
2850 		{
2851 			BRegion dirty;
2852 			dirty.Include(fVirtualScreen.Frame());
2853 			MarkDirty(dirty);
2854 			break;
2855 		}
2856 
2857 		default:
2858 			printf("Desktop %d:%s received unexpected code %" B_PRId32 "\n", 0,
2859 				"baron", code);
2860 
2861 			if (link.NeedsReply()) {
2862 				// the client is now blocking and waiting for a reply!
2863 				fLink.StartMessage(B_ERROR);
2864 				fLink.Flush();
2865 			}
2866 			break;
2867 	}
2868 }
2869 
2870 
2871 WindowList&
2872 Desktop::CurrentWindows()
2873 {
2874 	return fWorkspaces[fCurrentWorkspace].Windows();
2875 }
2876 
2877 
2878 WindowList&
2879 Desktop::AllWindows()
2880 {
2881 	return fAllWindows;
2882 }
2883 
2884 
2885 Window*
2886 Desktop::WindowForClientLooperPort(port_id port)
2887 {
2888 	ASSERT_MULTI_LOCKED(fWindowLock);
2889 
2890 	for (Window* window = fAllWindows.FirstWindow(); window != NULL;
2891 			window = window->NextWindow(kAllWindowList)) {
2892 		if (window->ServerWindow()->ClientLooperPort() == port)
2893 			return window;
2894 	}
2895 	return NULL;
2896 }
2897 
2898 
2899 WindowList&
2900 Desktop::_Windows(int32 index)
2901 {
2902 	ASSERT(index >= 0 && index < kMaxWorkspaces);
2903 	return fWorkspaces[index].Windows();
2904 }
2905 
2906 
2907 void
2908 Desktop::_FlushPendingColors()
2909 {
2910 	// Update all windows while we are holding the write lock.
2911 
2912 	int32 count = fPendingColors.CountNames(B_RGB_32_BIT_TYPE);
2913 	if (count == 0)
2914 		return;
2915 
2916 	bool changed[count];
2917 	LockedDesktopSettings settings(this);
2918 	settings.SetUIColors(fPendingColors, &changed[0]);
2919 
2920 	int32 index = 0;
2921 	char* name = NULL;
2922 	type_code type = B_RGB_32_BIT_TYPE;
2923 	rgb_color color;
2924 	color_which which = B_NO_COLOR;
2925 	BMessage clientMessage(B_COLORS_UPDATED);
2926 
2927 	while (fPendingColors.GetInfo(type, index, &name, &type) == B_OK) {
2928 		which = which_ui_color(name);
2929 		if (which == B_NO_COLOR || fPendingColors.FindColor(name,
2930 				&color) != B_OK || !changed[index]) {
2931 			++index;
2932 			continue;
2933 		}
2934 
2935 		for (Window* window = fAllWindows.FirstWindow(); window != NULL;
2936 				window = window->NextWindow(kAllWindowList)) {
2937 			ColorUpdated(window, which, color);
2938 		}
2939 
2940 		// Ensure client only gets list of changed colors
2941 		clientMessage.AddColor(name, color);
2942 		++index;
2943 	}
2944 
2945 	// Notify client applications
2946 	BAutolock appListLock(fApplicationsLock);
2947 	for (int32 index = 0; index < fApplications.CountItems(); ++index) {
2948 		fApplications.ItemAt(index)->SendMessageToClient(&clientMessage);
2949 	}
2950 
2951 	fPendingColors.MakeEmpty();
2952 }
2953 
2954 
2955 void
2956 Desktop::_UpdateFloating(int32 previousWorkspace, int32 nextWorkspace,
2957 	Window* mouseEventWindow)
2958 {
2959 	if (previousWorkspace == -1)
2960 		previousWorkspace = fCurrentWorkspace;
2961 	if (nextWorkspace == -1)
2962 		nextWorkspace = previousWorkspace;
2963 
2964 	for (Window* floating = fSubsetWindows.FirstWindow(); floating != NULL;
2965 			floating = floating->NextWindow(kSubsetList)) {
2966 		// we only care about app/subset floating windows
2967 		if (floating->Feel() != B_FLOATING_SUBSET_WINDOW_FEEL
2968 			&& floating->Feel() != B_FLOATING_APP_WINDOW_FEEL)
2969 			continue;
2970 
2971 		if (fFront != NULL && fFront->IsNormal()
2972 			&& floating->HasInSubset(fFront)) {
2973 			// is now visible
2974 			if (_Windows(previousWorkspace).HasWindow(floating)
2975 				&& previousWorkspace != nextWorkspace
2976 				&& !floating->InSubsetWorkspace(previousWorkspace)) {
2977 				// but no longer on the previous workspace
2978 				_Windows(previousWorkspace).RemoveWindow(floating);
2979 				floating->SetCurrentWorkspace(-1);
2980 			}
2981 
2982 			if (!_Windows(nextWorkspace).HasWindow(floating)) {
2983 				// but wasn't before
2984 				_Windows(nextWorkspace).AddWindow(floating,
2985 					floating->Frontmost(_Windows(nextWorkspace).FirstWindow(),
2986 					nextWorkspace));
2987 				floating->SetCurrentWorkspace(nextWorkspace);
2988 				if (mouseEventWindow != fFront)
2989 					_ShowWindow(floating);
2990 
2991 				// TODO: put the floating last in the floating window list to
2992 				// preserve the on screen window order
2993 			}
2994 		} else if (_Windows(previousWorkspace).HasWindow(floating)
2995 			&& !floating->InSubsetWorkspace(previousWorkspace)) {
2996 			// was visible, but is no longer
2997 
2998 			_Windows(previousWorkspace).RemoveWindow(floating);
2999 			floating->SetCurrentWorkspace(-1);
3000 			_HideWindow(floating);
3001 
3002 			if (FocusWindow() == floating)
3003 				SetFocusWindow();
3004 		}
3005 	}
3006 }
3007 
3008 
3009 /*!	Search the visible windows for a valid back window
3010 	(only desktop windows can't be back windows)
3011 */
3012 void
3013 Desktop::_UpdateBack()
3014 {
3015 	fBack = NULL;
3016 
3017 	for (Window* window = CurrentWindows().FirstWindow(); window != NULL;
3018 			window = window->NextWindow(fCurrentWorkspace)) {
3019 		if (window->IsHidden() || window->Feel() == kDesktopWindowFeel)
3020 			continue;
3021 
3022 		fBack = window;
3023 		break;
3024 	}
3025 }
3026 
3027 
3028 /*!	Search the visible windows for a valid front window
3029 	(only normal and modal windows can be front windows)
3030 
3031 	The only place where you don't want to update floating windows is
3032 	during a workspace change - because then you'll call _UpdateFloating()
3033 	yourself.
3034 */
3035 void
3036 Desktop::_UpdateFront(bool updateFloating)
3037 {
3038 	fFront = NULL;
3039 
3040 	for (Window* window = CurrentWindows().LastWindow(); window != NULL;
3041 			window = window->PreviousWindow(fCurrentWorkspace)) {
3042 		if (window->IsHidden() || window->IsFloating()
3043 			|| !window->SupportsFront())
3044 			continue;
3045 
3046 		fFront = window;
3047 		break;
3048 	}
3049 
3050 	if (updateFloating)
3051 		_UpdateFloating();
3052 }
3053 
3054 
3055 void
3056 Desktop::_UpdateFronts(bool updateFloating)
3057 {
3058 	_UpdateBack();
3059 	_UpdateFront(updateFloating);
3060 }
3061 
3062 
3063 bool
3064 Desktop::_WindowHasModal(Window* window) const
3065 {
3066 	if (window == NULL)
3067 		return false;
3068 
3069 	for (Window* modal = fSubsetWindows.FirstWindow(); modal != NULL;
3070 			modal = modal->NextWindow(kSubsetList)) {
3071 		// only visible modal windows count
3072 		if (!modal->IsModal() || modal->IsHidden())
3073 			continue;
3074 
3075 		if (modal->HasInSubset(window))
3076 			return true;
3077 	}
3078 
3079 	return false;
3080 }
3081 
3082 
3083 /*!	Determines whether or not the specified \a window can have focus at all.
3084 */
3085 bool
3086 Desktop::_WindowCanHaveFocus(Window* window) const
3087 {
3088 	return window != NULL
3089 		&& window->InWorkspace(fCurrentWorkspace)
3090 		&& (window->Flags() & B_AVOID_FOCUS) == 0
3091 		&& !_WindowHasModal(window)
3092 		&& !window->IsHidden();
3093 }
3094 
3095 
3096 /*!	You must at least hold a single window lock when calling this method.
3097 */
3098 void
3099 Desktop::_WindowChanged(Window* window)
3100 {
3101 	ASSERT_MULTI_LOCKED(fWindowLock);
3102 
3103 	BAutolock _(fWorkspacesLock);
3104 
3105 	for (uint32 i = fWorkspacesViews.CountItems(); i-- > 0;) {
3106 		WorkspacesView* view = fWorkspacesViews.ItemAt(i);
3107 		view->WindowChanged(window);
3108 	}
3109 }
3110 
3111 
3112 /*!	You must at least hold a single window lock when calling this method.
3113 */
3114 void
3115 Desktop::_WindowRemoved(Window* window)
3116 {
3117 	ASSERT_MULTI_LOCKED(fWindowLock);
3118 
3119 	BAutolock _(fWorkspacesLock);
3120 
3121 	for (uint32 i = fWorkspacesViews.CountItems(); i-- > 0;) {
3122 		WorkspacesView* view = fWorkspacesViews.ItemAt(i);
3123 		view->WindowRemoved(window);
3124 	}
3125 }
3126 
3127 
3128 /*!	Shows the window on the screen - it does this independently of the
3129 	Window::IsHidden() state.
3130 */
3131 void
3132 Desktop::_ShowWindow(Window* window, bool affectsOtherWindows)
3133 {
3134 	BRegion background;
3135 	_RebuildClippingForAllWindows(background);
3136 	_SetBackground(background);
3137 	_WindowChanged(window);
3138 
3139 	BRegion dirty(window->VisibleRegion());
3140 
3141 	if (!affectsOtherWindows) {
3142 		// everything that is now visible in the
3143 		// window needs a redraw, but other windows
3144 		// are not affected, we can call ProcessDirtyRegion()
3145 		// of the window, and don't have to use MarkDirty()
3146 		window->ProcessDirtyRegion(dirty);
3147 	} else
3148 		MarkDirty(dirty);
3149 
3150 	if (window->ServerWindow()->HasDirectFrameBufferAccess()) {
3151 		window->ServerWindow()->HandleDirectConnection(
3152 			B_DIRECT_START | B_BUFFER_RESET);
3153 	}
3154 }
3155 
3156 
3157 /*!	Hides the window from the screen - it does this independently of the
3158 	Window::IsHidden() state.
3159 */
3160 void
3161 Desktop::_HideWindow(Window* window)
3162 {
3163 	if (window->ServerWindow()->IsDirectlyAccessing())
3164 		window->ServerWindow()->HandleDirectConnection(B_DIRECT_STOP);
3165 
3166 	// after rebuilding the clipping,
3167 	// this window will not have a visible
3168 	// region anymore, so we need to remember
3169 	// it now
3170 	// (actually that's not true, since
3171 	// hidden windows are excluded from the
3172 	// clipping calculation, but anyways)
3173 	BRegion dirty(window->VisibleRegion());
3174 
3175 	BRegion background;
3176 	_RebuildClippingForAllWindows(background);
3177 	_SetBackground(background);
3178 	_WindowChanged(window);
3179 
3180 	MarkDirty(dirty);
3181 }
3182 
3183 
3184 /*!	Updates the workspaces of all subset windows with regard to the
3185 	specifed window.
3186 	If newIndex is not -1, it will move all subset windows that belong to
3187 	the specifed window to the new workspace; this form is only called by
3188 	SetWorkspace().
3189 */
3190 void
3191 Desktop::_UpdateSubsetWorkspaces(Window* window, int32 previousIndex,
3192 	int32 newIndex)
3193 {
3194 	STRACE(("_UpdateSubsetWorkspaces(window %p, %s)\n", window,
3195 		window->Title()));
3196 
3197 	// if the window is hidden, the subset windows are up-to-date already
3198 	if (!window->IsNormal() || window->IsHidden())
3199 		return;
3200 
3201 	for (Window* subset = fSubsetWindows.FirstWindow(); subset != NULL;
3202 			subset = subset->NextWindow(kSubsetList)) {
3203 		if (subset->Feel() == B_MODAL_ALL_WINDOW_FEEL
3204 			|| subset->Feel() == B_FLOATING_ALL_WINDOW_FEEL) {
3205 			// These windows are always visible on all workspaces,
3206 			// no need to update them.
3207 			continue;
3208 		}
3209 
3210 		if (subset->IsFloating()) {
3211 			// Floating windows are inserted and removed to the current
3212 			// workspace as the need arises - they are not handled here
3213 			// but in _UpdateFront()
3214 			continue;
3215 		}
3216 
3217 		if (subset->HasInSubset(window)) {
3218 			// adopt the workspace change
3219 			SetWindowWorkspaces(subset, subset->SubsetWorkspaces());
3220 		}
3221 	}
3222 }
3223 
3224 
3225 /*!	\brief Adds or removes the window to or from the workspaces it's on.
3226 */
3227 void
3228 Desktop::_ChangeWindowWorkspaces(Window* window, uint32 oldWorkspaces,
3229 	uint32 newWorkspaces)
3230 {
3231 	if (oldWorkspaces == newWorkspaces)
3232 		return;
3233 
3234 	// apply changes to the workspaces' window lists
3235 
3236 	LockAllWindows();
3237 
3238 	// NOTE: we bypass the anchor-mechanism by intention when switching
3239 	// the workspace programmatically.
3240 
3241 	for (int32 i = 0; i < kMaxWorkspaces; i++) {
3242 		if (workspace_in_workspaces(i, oldWorkspaces)) {
3243 			// window is on this workspace, is it anymore?
3244 			if (!workspace_in_workspaces(i, newWorkspaces)) {
3245 				_Windows(i).RemoveWindow(window);
3246 				if (fLastWorkspaceFocus[i] == window)
3247 					fLastWorkspaceFocus[i] = NULL;
3248 
3249 				if (i == CurrentWorkspace()) {
3250 					// remove its appearance from the current workspace
3251 					window->SetCurrentWorkspace(-1);
3252 
3253 					if (!window->IsHidden())
3254 						_HideWindow(window);
3255 				}
3256 			}
3257 		} else {
3258 			// window was not on this workspace, is it now?
3259 			if (workspace_in_workspaces(i, newWorkspaces)) {
3260 				_Windows(i).AddWindow(window,
3261 					window->Frontmost(_Windows(i).FirstWindow(), i));
3262 
3263 				if (i == CurrentWorkspace()) {
3264 					// make the window visible in current workspace
3265 					window->SetCurrentWorkspace(fCurrentWorkspace);
3266 
3267 					if (!window->IsHidden()) {
3268 						// This only affects other windows if this window has
3269 						// floating or modal windows that need to be shown as
3270 						// well
3271 						// TODO: take care of this
3272 						_ShowWindow(window, FrontWindow() == window);
3273 					}
3274 				}
3275 			}
3276 		}
3277 	}
3278 
3279 	// If the window is visible only on one workspace, we set it's current
3280 	// position in that workspace (so that WorkspacesView will find us).
3281 	int32 firstWorkspace = -1;
3282 	for (int32 i = 0; i < kMaxWorkspaces; i++) {
3283 		if ((newWorkspaces & (1L << i)) != 0) {
3284 			if (firstWorkspace != -1) {
3285 				firstWorkspace = -1;
3286 				break;
3287 			}
3288 			firstWorkspace = i;
3289 		}
3290 	}
3291 	if (firstWorkspace >= 0)
3292 		window->Anchor(firstWorkspace).position = window->Frame().LeftTop();
3293 
3294 	// take care about modals and floating windows
3295 	_UpdateSubsetWorkspaces(window);
3296 
3297 	NotifyWindowWorkspacesChanged(window, newWorkspaces);
3298 
3299 	UnlockAllWindows();
3300 }
3301 
3302 
3303 void
3304 Desktop::_BringWindowsToFront(WindowList& windows, int32 list, bool wereVisible)
3305 {
3306 	// we don't need to redraw what is currently
3307 	// visible of the window
3308 	BRegion clean;
3309 
3310 	for (Window* window = windows.FirstWindow(); window != NULL;
3311 			window = window->NextWindow(list)) {
3312 		if (wereVisible)
3313 			clean.Include(&window->VisibleRegion());
3314 
3315 		CurrentWindows().AddWindow(window,
3316 			window->Frontmost(CurrentWindows().FirstWindow(),
3317 				fCurrentWorkspace));
3318 
3319 		_WindowChanged(window);
3320 	}
3321 
3322 	BRegion dummy;
3323 	_RebuildClippingForAllWindows(dummy);
3324 
3325 	// redraw what became visible of the window(s)
3326 
3327 	BRegion dirty;
3328 	for (Window* window = windows.FirstWindow(); window != NULL;
3329 			window = window->NextWindow(list)) {
3330 		dirty.Include(&window->VisibleRegion());
3331 	}
3332 
3333 	dirty.Exclude(&clean);
3334 	MarkDirty(dirty);
3335 
3336 	_UpdateFront();
3337 
3338 	if (windows.FirstWindow() == fBack || fBack == NULL)
3339 		_UpdateBack();
3340 }
3341 
3342 
3343 /*!	Returns the last focussed non-hidden subset window belonging to the
3344 	specified \a window.
3345 */
3346 Window*
3347 Desktop::_LastFocusSubsetWindow(Window* window)
3348 {
3349 	if (window == NULL)
3350 		return NULL;
3351 
3352 	for (Window* front = fFocusList.LastWindow(); front != NULL;
3353 			front = front->PreviousWindow(kFocusList)) {
3354 		if (front != window && !front->IsHidden()
3355 			&& window->HasInSubset(front))
3356 			return front;
3357 	}
3358 
3359 	return NULL;
3360 }
3361 
3362 
3363 /*!	\brief Checks whether or not a fake mouse moved message needs to be sent
3364 	to the previous mouse window.
3365 
3366 	You need to have the all window lock held when calling this method.
3367 */
3368 bool
3369 Desktop::_CheckSendFakeMouseMoved(const Window* lastWindowUnderMouse)
3370 {
3371 	Window* window = WindowAt(fLastMousePosition);
3372 	return window != lastWindowUnderMouse;
3373 }
3374 
3375 
3376 /*!	\brief Sends a fake B_MOUSE_MOVED event to the window under the mouse,
3377 		and also updates the current view under the mouse.
3378 
3379 	This has only to be done in case the view changed without mouse movement,
3380 	ie. because of a workspace change, a closing window, or programmatic window
3381 	movement.
3382 
3383 	You must not have locked any windows when calling this method.
3384 */
3385 void
3386 Desktop::_SendFakeMouseMoved(Window* window)
3387 {
3388 	int32 viewToken = B_NULL_TOKEN;
3389 	EventTarget* target = NULL;
3390 
3391 	LockAllWindows();
3392 
3393 	if (window == NULL)
3394 		window = WindowAt(fLastMousePosition);
3395 
3396 	if (window != NULL) {
3397 		BMessage message;
3398 		window->MouseMoved(&message, fLastMousePosition, &viewToken, true,
3399 			true);
3400 
3401 		if (viewToken != B_NULL_TOKEN)
3402 			target = &window->EventTarget();
3403 	}
3404 
3405 	if (viewToken != B_NULL_TOKEN)
3406 		SetViewUnderMouse(window, viewToken);
3407 	else {
3408 		SetViewUnderMouse(NULL, B_NULL_TOKEN);
3409 		SetCursor(NULL);
3410 	}
3411 
3412 	UnlockAllWindows();
3413 
3414 	if (target != NULL)
3415 		EventDispatcher().SendFakeMouseMoved(*target, viewToken);
3416 }
3417 
3418 
3419 Screen*
3420 Desktop::_DetermineScreenFor(BRect frame)
3421 {
3422 	AutoReadLocker _(fScreenLock);
3423 
3424 	// TODO: choose the screen depending on where most of the area is
3425 	return fVirtualScreen.ScreenAt(0);
3426 }
3427 
3428 
3429 void
3430 Desktop::_RebuildClippingForAllWindows(BRegion& stillAvailableOnScreen)
3431 {
3432 	// the available region on screen starts with the entire screen area
3433 	// each window on the screen will take a portion from that area
3434 
3435 	// figure out what the entire screen area is
3436 	stillAvailableOnScreen = fScreenRegion;
3437 
3438 	// set clipping of each window
3439 	for (Window* window = CurrentWindows().LastWindow(); window != NULL;
3440 			window = window->PreviousWindow(fCurrentWorkspace)) {
3441 		if (!window->IsHidden()) {
3442 			window->SetClipping(&stillAvailableOnScreen);
3443 			window->SetScreen(_DetermineScreenFor(window->Frame()));
3444 
3445 			if (window->ServerWindow()->IsDirectlyAccessing()) {
3446 				window->ServerWindow()->HandleDirectConnection(
3447 					B_DIRECT_MODIFY | B_CLIPPING_MODIFIED);
3448 			}
3449 
3450 			// that windows region is not available on screen anymore
3451 			stillAvailableOnScreen.Exclude(&window->VisibleRegion());
3452 		}
3453 	}
3454 }
3455 
3456 
3457 void
3458 Desktop::_TriggerWindowRedrawing(BRegion& newDirtyRegion)
3459 {
3460 	// send redraw messages to all windows intersecting the dirty region
3461 	for (Window* window = CurrentWindows().LastWindow(); window != NULL;
3462 			window = window->PreviousWindow(fCurrentWorkspace)) {
3463 		if (!window->IsHidden()
3464 			&& newDirtyRegion.Intersects(window->VisibleRegion().Frame()))
3465 			window->ProcessDirtyRegion(newDirtyRegion);
3466 	}
3467 }
3468 
3469 
3470 void
3471 Desktop::_SetBackground(BRegion& background)
3472 {
3473 	// NOTE: the drawing operation is caried out
3474 	// in the clipping region rebuild, but it is
3475 	// ok actually, because it also avoids trails on
3476 	// moving windows
3477 
3478 	// remember the region not covered by any windows
3479 	// and redraw the dirty background
3480 	BRegion dirtyBackground(background);
3481 	dirtyBackground.Exclude(&fBackgroundRegion);
3482 	dirtyBackground.IntersectWith(&background);
3483 	fBackgroundRegion = background;
3484 	if (dirtyBackground.Frame().IsValid()) {
3485 		if (GetDrawingEngine()->LockParallelAccess()) {
3486 			GetDrawingEngine()->FillRegion(dirtyBackground,
3487 				fWorkspaces[fCurrentWorkspace].Color());
3488 
3489 			GetDrawingEngine()->UnlockParallelAccess();
3490 		}
3491 	}
3492 }
3493 
3494 
3495 //!	The all window lock must be held when calling this function.
3496 void
3497 Desktop::RebuildAndRedrawAfterWindowChange(Window* changedWindow,
3498 	BRegion& dirty)
3499 {
3500 	ASSERT_MULTI_WRITE_LOCKED(fWindowLock);
3501 	if (!changedWindow->IsVisible() || dirty.CountRects() == 0)
3502 		return;
3503 
3504 	// The following loop is pretty much a copy of
3505 	// _RebuildClippingForAllWindows(), but will also
3506 	// take care about restricting our dirty region.
3507 
3508 	// figure out what the entire screen area is
3509 	BRegion stillAvailableOnScreen(fScreenRegion);
3510 
3511 	// set clipping of each window
3512 	for (Window* window = CurrentWindows().LastWindow(); window != NULL;
3513 			window = window->PreviousWindow(fCurrentWorkspace)) {
3514 		if (!window->IsHidden()) {
3515 			if (window == changedWindow)
3516 				dirty.IntersectWith(&stillAvailableOnScreen);
3517 
3518 			window->SetClipping(&stillAvailableOnScreen);
3519 			window->SetScreen(_DetermineScreenFor(window->Frame()));
3520 
3521 			if (window->ServerWindow()->IsDirectlyAccessing()) {
3522 				window->ServerWindow()->HandleDirectConnection(
3523 					B_DIRECT_MODIFY | B_CLIPPING_MODIFIED);
3524 			}
3525 
3526 			// that windows region is not available on screen anymore
3527 			stillAvailableOnScreen.Exclude(&window->VisibleRegion());
3528 		}
3529 	}
3530 
3531 	_SetBackground(stillAvailableOnScreen);
3532 	_WindowChanged(changedWindow);
3533 
3534 	_TriggerWindowRedrawing(dirty);
3535 }
3536 
3537 
3538 //! Suspend all windows with direct access to the frame buffer
3539 void
3540 Desktop::_SuspendDirectFrameBufferAccess()
3541 {
3542 	ASSERT_MULTI_LOCKED(fWindowLock);
3543 
3544 	for (Window* window = fAllWindows.FirstWindow(); window != NULL;
3545 			window = window->NextWindow(kAllWindowList)) {
3546 		if (window->ServerWindow()->IsDirectlyAccessing())
3547 			window->ServerWindow()->HandleDirectConnection(B_DIRECT_STOP);
3548 	}
3549 }
3550 
3551 
3552 //! Resume all windows with direct access to the frame buffer
3553 void
3554 Desktop::_ResumeDirectFrameBufferAccess()
3555 {
3556 	ASSERT_MULTI_LOCKED(fWindowLock);
3557 
3558 	for (Window* window = fAllWindows.FirstWindow(); window != NULL;
3559 			window = window->NextWindow(kAllWindowList)) {
3560 		if (window->IsHidden() || !window->InWorkspace(fCurrentWorkspace))
3561 			continue;
3562 
3563 		if (window->ServerWindow()->HasDirectFrameBufferAccess()) {
3564 			window->ServerWindow()->HandleDirectConnection(
3565 				B_DIRECT_START | B_BUFFER_RESET, B_MODE_CHANGED);
3566 		}
3567 	}
3568 }
3569 
3570 
3571 void
3572 Desktop::ScreenChanged(Screen* screen)
3573 {
3574 	AutoWriteLocker windowLocker(fWindowLock);
3575 
3576 	AutoWriteLocker screenLocker(fScreenLock);
3577 	screen->SetPreferredMode();
3578 	screenLocker.Unlock();
3579 
3580 	_ScreenChanged(screen);
3581 }
3582 
3583 
3584 void
3585 Desktop::_ScreenChanged(Screen* screen)
3586 {
3587 	ASSERT_MULTI_WRITE_LOCKED(fWindowLock);
3588 
3589 	// the entire screen is dirty, because we're actually
3590 	// operating on an all new buffer in memory
3591 	BRegion dirty(screen->Frame());
3592 
3593 	// update our cached screen region
3594 	fScreenRegion.Set(screen->Frame());
3595 	gInputManager->UpdateScreenBounds(screen->Frame());
3596 
3597 	BRegion background;
3598 	_RebuildClippingForAllWindows(background);
3599 
3600 	fBackgroundRegion.MakeEmpty();
3601 		// makes sure that the complete background is redrawn
3602 	_SetBackground(background);
3603 
3604 	// figure out dirty region
3605 	dirty.Exclude(&background);
3606 	_TriggerWindowRedrawing(dirty);
3607 
3608 	// send B_SCREEN_CHANGED to windows on that screen
3609 	BMessage update(B_SCREEN_CHANGED);
3610 	update.AddInt64("when", real_time_clock_usecs());
3611 	update.AddRect("frame", screen->Frame());
3612 	update.AddInt32("mode", screen->ColorSpace());
3613 
3614 	fVirtualScreen.UpdateFrame();
3615 
3616 	for (Window* window = fAllWindows.FirstWindow(); window != NULL;
3617 			window = window->NextWindow(kAllWindowList)) {
3618 		if (window->Screen() == screen)
3619 			window->ServerWindow()->ScreenChanged(&update);
3620 	}
3621 }
3622 
3623 
3624 /*!	\brief activate one of the app's windows.
3625 */
3626 status_t
3627 Desktop::_ActivateApp(team_id team)
3628 {
3629 	// search for an unhidden window in the current workspace
3630 
3631 	AutoWriteLocker locker(fWindowLock);
3632 
3633 	for (Window* window = CurrentWindows().LastWindow(); window != NULL;
3634 			window = window->PreviousWindow(fCurrentWorkspace)) {
3635 		if (!window->IsHidden() && window->IsNormal()
3636 			&& window->ServerWindow()->ClientTeam() == team) {
3637 			ActivateWindow(window);
3638 			return B_OK;
3639 		}
3640 	}
3641 
3642 	// search for an unhidden window to give focus to
3643 
3644 	for (Window* window = fAllWindows.FirstWindow(); window != NULL;
3645 			window = window->NextWindow(kAllWindowList)) {
3646 		// if window is a normal window of the team, and not hidden,
3647 		// we've found our target
3648 		if (!window->IsHidden() && window->IsNormal()
3649 			&& window->ServerWindow()->ClientTeam() == team) {
3650 			ActivateWindow(window);
3651 			return B_OK;
3652 		}
3653 	}
3654 
3655 	// TODO: we cannot maximize minimized windows here (with the window lock
3656 	// write locked). To work-around this, we could forward the request to
3657 	// the ServerApp of this team - it maintains its own window list, and can
3658 	// therefore call ActivateWindow() without holding the window lock.
3659 	return B_BAD_VALUE;
3660 }
3661 
3662 
3663 void
3664 Desktop::_SetCurrentWorkspaceConfiguration()
3665 {
3666 	ASSERT_MULTI_WRITE_LOCKED(fWindowLock);
3667 
3668 	status_t status = fDirectScreenLock.LockWithTimeout(1000000L);
3669 	if (status != B_OK) {
3670 		// The application having the direct screen lock didn't give it up in
3671 		// time, make it crash
3672 		syslog(LOG_ERR, "Team %" B_PRId32 " did not give up its direct screen "
3673 			"lock.\n", fDirectScreenTeam);
3674 
3675 		debug_thread(fDirectScreenTeam);
3676 		fDirectScreenTeam = -1;
3677 	} else
3678 		fDirectScreenLock.Unlock();
3679 
3680 	AutoWriteLocker _(fScreenLock);
3681 
3682 	uint32 changedScreens;
3683 	fVirtualScreen.SetConfiguration(*this,
3684 		fWorkspaces[fCurrentWorkspace].CurrentScreenConfiguration(),
3685 		&changedScreens);
3686 
3687 	for (int32 i = 0; changedScreens != 0; i++, changedScreens /= 2) {
3688 		if ((changedScreens & (1 << i)) != 0)
3689 			_ScreenChanged(fVirtualScreen.ScreenAt(i));
3690 	}
3691 }
3692 
3693 
3694 /*!	Changes the current workspace to the one specified by \a index.
3695 	You must hold the all window lock when calling this method.
3696 */
3697 void
3698 Desktop::_SetWorkspace(int32 index, bool moveFocusWindow)
3699 {
3700 	ASSERT_MULTI_WRITE_LOCKED(fWindowLock);
3701 
3702 	int32 previousIndex = fCurrentWorkspace;
3703 	rgb_color previousColor = fWorkspaces[fCurrentWorkspace].Color();
3704 	bool movedMouseEventWindow = false;
3705 	Window* movedWindow = NULL;
3706 	if (moveFocusWindow) {
3707 		if (fMouseEventWindow != NULL)
3708 			movedWindow = fMouseEventWindow;
3709 		else
3710 			movedWindow = FocusWindow();
3711 	}
3712 
3713 	if (movedWindow != NULL) {
3714 		if (movedWindow->IsNormal()) {
3715 			if (!movedWindow->InWorkspace(index)) {
3716 				// The window currently being dragged will follow us to this
3717 				// workspace if it's not already on it.
3718 				// But only normal windows are following
3719 				uint32 oldWorkspaces = movedWindow->Workspaces();
3720 
3721 				WindowStack* stack = movedWindow->GetWindowStack();
3722 				if (stack != NULL) {
3723 					for (int32 s = 0; s < stack->CountWindows(); s++) {
3724 						Window* stackWindow = stack->LayerOrder().ItemAt(s);
3725 
3726 						_Windows(previousIndex).RemoveWindow(stackWindow);
3727 						_Windows(index).AddWindow(stackWindow,
3728 							stackWindow->Frontmost(
3729 								_Windows(index).FirstWindow(), index));
3730 
3731 						// send B_WORKSPACES_CHANGED message
3732 						stackWindow->WorkspacesChanged(oldWorkspaces,
3733 							stackWindow->Workspaces());
3734 					}
3735 				}
3736 				// TODO: subset windows will always flicker this way
3737 
3738 				movedMouseEventWindow = true;
3739 
3740 				NotifyWindowWorkspacesChanged(movedWindow,
3741 					movedWindow->Workspaces());
3742 			} else {
3743 				// make sure it's frontmost
3744 				_Windows(index).RemoveWindow(movedWindow);
3745 				_Windows(index).AddWindow(movedWindow,
3746 					movedWindow->Frontmost(_Windows(index).FirstWindow(),
3747 					index));
3748 			}
3749 		}
3750 
3751 		movedWindow->Anchor(index).position = movedWindow->Frame().LeftTop();
3752 	}
3753 
3754 	if (movedWindow == NULL || movedWindow->InWorkspace(previousIndex))
3755 		fLastWorkspaceFocus[previousIndex] = FocusWindow();
3756 	else
3757 		fLastWorkspaceFocus[previousIndex] = NULL;
3758 
3759 	// build region of windows that are no longer visible in the new workspace
3760 
3761 	BRegion dirty;
3762 
3763 	for (Window* window = CurrentWindows().FirstWindow();
3764 			window != NULL; window = window->NextWindow(previousIndex)) {
3765 		// store current position in Workspace anchor
3766 		window->Anchor(previousIndex).position = window->Frame().LeftTop();
3767 
3768 		if (!window->IsHidden()
3769 			&& window->ServerWindow()->IsDirectlyAccessing())
3770 			window->ServerWindow()->HandleDirectConnection(B_DIRECT_STOP);
3771 
3772 		window->WorkspaceActivated(previousIndex, false);
3773 
3774 		if (window->InWorkspace(index))
3775 			continue;
3776 
3777 		if (!window->IsHidden()) {
3778 			// this window will no longer be visible
3779 			dirty.Include(&window->VisibleRegion());
3780 		}
3781 
3782 		window->SetCurrentWorkspace(-1);
3783 	}
3784 
3785 	fPreviousWorkspace = fCurrentWorkspace;
3786 	fCurrentWorkspace = index;
3787 
3788 	// Change the display modes, if needed
3789 	_SetCurrentWorkspaceConfiguration();
3790 
3791 	// Show windows, and include them in the changed region - but only
3792 	// those that were not visible before (or whose position changed)
3793 
3794 	WindowList windows(kWorkingList);
3795 	BList previousRegions;
3796 
3797 	for (Window* window = _Windows(index).FirstWindow();
3798 			window != NULL; window = window->NextWindow(index)) {
3799 		BPoint position = window->Anchor(index).position;
3800 
3801 		window->SetCurrentWorkspace(index);
3802 
3803 		if (window->IsHidden())
3804 			continue;
3805 
3806 		if (position == kInvalidWindowPosition) {
3807 			// if you enter a workspace for the first time, the position
3808 			// of the window in the previous workspace is adopted
3809 			position = window->Frame().LeftTop();
3810 				// TODO: make sure the window is still on-screen if it
3811 				//	was before!
3812 		}
3813 
3814 		if (!window->InWorkspace(previousIndex)) {
3815 			// This window was not visible before, make sure its frame
3816 			// is up-to-date
3817 			if (window->Frame().LeftTop() != position) {
3818 				BPoint offset = position - window->Frame().LeftTop();
3819 				window->MoveBy((int32)offset.x, (int32)offset.y);
3820 			}
3821 			continue;
3822 		}
3823 
3824 		if (window->Frame().LeftTop() != position) {
3825 			// the window was visible before, but its on-screen location changed
3826 			BPoint offset = position - window->Frame().LeftTop();
3827 			MoveWindowBy(window, offset.x, offset.y);
3828 				// TODO: be a bit smarter than this...
3829 		} else {
3830 			// We need to remember the previous visible region of the
3831 			// window if they changed their order
3832 			ObjectDeleter<BRegion> region(new (std::nothrow)
3833 				BRegion(window->VisibleRegion()));
3834 			if (region.IsSet()) {
3835 				if (previousRegions.AddItem(region.Detach()))
3836 					windows.AddWindow(window);
3837 			}
3838 		}
3839 	}
3840 
3841 	_UpdateFronts(false);
3842 	_UpdateFloating(previousIndex, index,
3843 		movedMouseEventWindow ? movedWindow : NULL);
3844 
3845 	BRegion stillAvailableOnScreen;
3846 	_RebuildClippingForAllWindows(stillAvailableOnScreen);
3847 	_SetBackground(stillAvailableOnScreen);
3848 
3849 	for (Window* window = _Windows(index).FirstWindow(); window != NULL;
3850 			window = window->NextWindow(index)) {
3851 		// send B_WORKSPACE_ACTIVATED message
3852 		window->WorkspaceActivated(index, true);
3853 
3854 		if (!window->IsHidden()
3855 			&& window->ServerWindow()->HasDirectFrameBufferAccess()) {
3856 			window->ServerWindow()->HandleDirectConnection(
3857 				B_DIRECT_START | B_BUFFER_RESET, B_MODE_CHANGED);
3858 		}
3859 
3860 		if (window->InWorkspace(previousIndex) || window->IsHidden()
3861 			|| (window == movedWindow && movedWindow->IsNormal())
3862 			|| (!window->IsNormal()
3863 				&& window->HasInSubset(movedWindow))) {
3864 			// This window was visible before, and is already handled in the
3865 			// above loop
3866 			continue;
3867 		}
3868 
3869 		dirty.Include(&window->VisibleRegion());
3870 	}
3871 
3872 	// Catch order changes in the new workspaces window list
3873 	int32 i = 0;
3874 	for (Window* window = windows.FirstWindow(); window != NULL;
3875 			window = window->NextWindow(kWorkingList), i++) {
3876 		BRegion* region = (BRegion*)previousRegions.ItemAt(i);
3877 		region->ExclusiveInclude(&window->VisibleRegion());
3878 		dirty.Include(region);
3879 		delete region;
3880 	}
3881 
3882 	// Set new focus, but keep focus to a floating window if still visible
3883 	if (movedWindow != NULL)
3884 		SetFocusWindow(movedWindow);
3885 	else if (!_Windows(index).HasWindow(FocusWindow())
3886 		|| (FocusWindow() != NULL && !FocusWindow()->IsFloating()))
3887 		SetFocusWindow(fLastWorkspaceFocus[index]);
3888 
3889 	_WindowChanged(NULL);
3890 	MarkDirty(dirty);
3891 
3892 #if 0
3893 	// Show the dirty regions of this workspace switch
3894 	if (GetDrawingEngine()->LockParallelAccess()) {
3895 		GetDrawingEngine()->FillRegion(dirty, (rgb_color){255, 0, 0});
3896 		GetDrawingEngine()->UnlockParallelAccess();
3897 		snooze(100000);
3898 	}
3899 #endif
3900 
3901 	if (previousColor != fWorkspaces[fCurrentWorkspace].Color())
3902 		RedrawBackground();
3903 }
3904