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