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