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