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