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