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