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