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