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