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