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