xref: /haiku/src/servers/app/Desktop.cpp (revision 1214ef1b2100f2b3299fc9d8d6142e46f70a4c3f)
1 /*
2  * Copyright 2001-2007, 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  */
10 
11 /*!	Class used to encapsulate desktop management */
12 
13 
14 #include "Desktop.h"
15 
16 #include "AppServer.h"
17 #include "DesktopSettingsPrivate.h"
18 #include "DrawingEngine.h"
19 #include "HWInterface.h"
20 #include "InputManager.h"
21 #include "ServerApp.h"
22 #include "ServerConfig.h"
23 #include "ServerCursor.h"
24 #include "ServerScreen.h"
25 #include "ServerWindow.h"
26 #include "SystemPalette.h"
27 #include "WindowPrivate.h"
28 #include "WindowLayer.h"
29 #include "Workspace.h"
30 #include "WorkspacesLayer.h"
31 
32 #include <WindowInfo.h>
33 #include <ServerProtocol.h>
34 
35 #include <DirectWindow.h>
36 #include <Entry.h>
37 #include <Message.h>
38 #include <MessageFilter.h>
39 #include <Region.h>
40 #include <Roster.h>
41 
42 #include <stdio.h>
43 #include <string.h>
44 #include <syslog.h>
45 
46 #if TEST_MODE
47 #	include "EventStream.h"
48 #endif
49 
50 //#define DEBUG_DESKTOP
51 #ifdef DEBUG_DESKTOP
52 #	define STRACE(a) printf(a)
53 #else
54 #	define STRACE(a) ;
55 #endif
56 
57 #if !USE_MULTI_LOCKER
58 #	define AutoWriteLocker BAutolock
59 #endif
60 
61 class KeyboardFilter : public EventFilter {
62 	public:
63 		KeyboardFilter(Desktop* desktop);
64 
65 		virtual filter_result Filter(BMessage* message, EventTarget** _target,
66 			int32* _viewToken, BMessage* latestMouseMoved);
67 		virtual void RemoveTarget(EventTarget* target);
68 
69 	private:
70 		void _UpdateFocus(int32 key, EventTarget** _target);
71 
72 		Desktop*		fDesktop;
73 		EventTarget*	fLastFocus;
74 		bigtime_t		fTimestamp;
75 };
76 
77 class MouseFilter : public EventFilter {
78 	public:
79 		MouseFilter(Desktop* desktop);
80 
81 		virtual filter_result Filter(BMessage* message, EventTarget** _target,
82 			int32* _viewToken, BMessage* latestMouseMoved);
83 
84 	private:
85 		Desktop*	fDesktop;
86 };
87 
88 
89 //	#pragma mark -
90 
91 
92 KeyboardFilter::KeyboardFilter(Desktop* desktop)
93 	:
94 	fDesktop(desktop),
95 	fLastFocus(NULL),
96 	fTimestamp(0)
97 {
98 }
99 
100 
101 void
102 KeyboardFilter::_UpdateFocus(int32 key, EventTarget** _target)
103 {
104 	if (!fDesktop->LockSingleWindow())
105 		return;
106 
107 	EventTarget* focus = fDesktop->KeyboardEventTarget();
108 	bigtime_t now = system_time();
109 
110 	// TODO: this is a try to not steal focus from the current window
111 	//	in case you enter some text and a window pops up you haven't
112 	//	triggered yourself (like a pop-up window in your browser while
113 	//	you're typing a password in another window) - maybe this should
114 	//	be done differently, though (using something like B_LOCK_WINDOW_FOCUS)
115 	//	(at least B_WINDOW_ACTIVATED must be postponed)
116 
117 	if (fLastFocus == NULL || (focus != fLastFocus && now - fTimestamp > 100000)) {
118 		// if the time span between the key presses is very short
119 		// we keep our previous focus alive - this is save even
120 		// if the target doesn't exist anymore, as we don't reset
121 		// it, and the event focus passed in is always valid (or NULL)
122 		*_target = focus;
123 		fLastFocus = focus;
124 	}
125 
126 	fDesktop->UnlockSingleWindow();
127 
128 	// we always allow to switch focus after the enter key has pressed
129 	if (key == B_ENTER)
130 		fTimestamp = 0;
131 	else
132 		fTimestamp = now;
133 }
134 
135 
136 filter_result
137 KeyboardFilter::Filter(BMessage* message, EventTarget** _target,
138 	int32* /*_viewToken*/, BMessage* /*latestMouseMoved*/)
139 {
140 	int32 key = 0;
141 	int32 modifiers;
142 
143 	if (message->what == B_KEY_DOWN
144 		&& message->FindInt32("key", &key) == B_OK
145 		&& message->FindInt32("modifiers", &modifiers) == B_OK) {
146 		// TODO: for some reason, one of the above is failing when pressing
147 		//	a modifier key at least with the old BMessage implementation
148 		//	(a message dump shows all entries, though)
149 		//	Try again with BMessage4!
150 
151 		// Check for safe video mode (F12 + l-cmd + l-ctrl + l-shift)
152 		if (key == 0x0d
153 			&& (modifiers & (B_LEFT_COMMAND_KEY
154 					| B_LEFT_CONTROL_KEY | B_LEFT_SHIFT_KEY)) != 0) {
155 			// TODO: Set to Safe Mode in KeyboardEventHandler:B_KEY_DOWN.
156 			STRACE(("Safe Video Mode invoked - code unimplemented\n"));
157 			return B_SKIP_MESSAGE;
158 		}
159 
160 		if (key > 0x01 && key < 0x0e) {
161 			// workspace change, F1-F12
162 
163 #if !TEST_MODE
164 			if (modifiers & B_COMMAND_KEY)
165 #else
166 			if (modifiers & B_CONTROL_KEY)
167 #endif
168 			{
169 				STRACE(("Set Workspace %ld\n", key - 1));
170 
171 				fDesktop->SetWorkspaceAsync(key - 2);
172 				return B_SKIP_MESSAGE;
173 			}
174 		}
175 
176 		// TODO: this should be moved client side!
177 		// (that's how it is done in BeOS, clients could need this key for
178 		// different purposes - also, it's preferrable to let the client
179 		// write the dump within his own environment)
180 		if (key == 0xe) {
181 			// screen dump, PrintScreen
182 			char filename[128];
183 			BEntry entry;
184 
185 			int32 index = 1;
186 			do {
187 				sprintf(filename, "/boot/home/screen%ld.png", index++);
188 				entry.SetTo(filename);
189 			} while(entry.Exists());
190 
191 			fDesktop->GetDrawingEngine()->DumpToFile(filename);
192 			return B_SKIP_MESSAGE;
193 		}
194 	}
195 
196 	if (message->what == B_KEY_DOWN
197 		|| message->what == B_MODIFIERS_CHANGED
198 		|| message->what == B_UNMAPPED_KEY_DOWN
199 		|| message->what == B_INPUT_METHOD_EVENT)
200 		_UpdateFocus(key, _target);
201 
202 	return B_DISPATCH_MESSAGE;
203 }
204 
205 
206 void
207 KeyboardFilter::RemoveTarget(EventTarget* target)
208 {
209 	if (target == fLastFocus)
210 		fLastFocus = NULL;
211 }
212 
213 
214 //	#pragma mark -
215 
216 
217 MouseFilter::MouseFilter(Desktop* desktop)
218 	:
219 	fDesktop(desktop)
220 {
221 }
222 
223 
224 filter_result
225 MouseFilter::Filter(BMessage* message, EventTarget** _target, int32* _viewToken,
226 	BMessage* latestMouseMoved)
227 {
228 	BPoint where;
229 	if (message->FindPoint("where", &where) != B_OK)
230 		return B_DISPATCH_MESSAGE;
231 
232 	int32 buttons;
233 	if (message->FindInt32("buttons", &buttons) != B_OK)
234 		buttons = 0;
235 
236 	if (!fDesktop->LockAllWindows())
237 		return B_DISPATCH_MESSAGE;
238 
239 	int32 viewToken = B_NULL_TOKEN;
240 
241 	WindowLayer* window = fDesktop->MouseEventWindow();
242 	if (window == NULL)
243 		window = fDesktop->WindowAt(where);
244 
245 	if (window != NULL) {
246 		// dispatch event in the window layers
247 		switch (message->what) {
248 			case B_MOUSE_DOWN:
249 				window->MouseDown(message, where, &viewToken);
250 				break;
251 
252 			case B_MOUSE_UP:
253 				window->MouseUp(message, where, &viewToken);
254 				fDesktop->SetMouseEventWindow(NULL);
255 				break;
256 
257 			case B_MOUSE_MOVED:
258 				window->MouseMoved(message, where, &viewToken,
259 					latestMouseMoved == NULL || latestMouseMoved == message);
260 				break;
261 		}
262 
263 		if (viewToken != B_NULL_TOKEN) {
264 			fDesktop->SetViewUnderMouse(window, viewToken);
265 
266 			*_viewToken = viewToken;
267 			*_target = &window->EventTarget();
268 		}
269 	}
270 
271 	if (window == NULL || viewToken == B_NULL_TOKEN) {
272 		// mouse is not over a window or over a decorator
273 		fDesktop->SetViewUnderMouse(window, B_NULL_TOKEN);
274 		fDesktop->SetCursor(NULL);
275 
276 		*_target = NULL;
277 	}
278 
279 	fDesktop->SetLastMouseState(where, buttons);
280 
281 	fDesktop->UnlockAllWindows();
282 
283 	return B_DISPATCH_MESSAGE;
284 }
285 
286 
287 //	#pragma mark -
288 
289 
290 static inline uint32
291 workspace_to_workspaces(int32 index)
292 {
293 	return 1UL << index;
294 }
295 
296 
297 static inline bool
298 workspace_in_workspaces(int32 index, uint32 workspaces)
299 {
300 	return (workspaces & (1UL << index)) != 0;
301 }
302 
303 
304 //	#pragma mark -
305 
306 
307 Desktop::Desktop(uid_t userID)
308 	: MessageLooper("desktop"),
309 
310 	fUserID(userID),
311 	fSettings(NULL),
312 	fSharedReadOnlyArea(-1),
313 	fApplicationsLock("application list"),
314 	fShutdownSemaphore(-1),
315 	fCurrentWorkspace(0),
316 	fAllWindows(kAllWindowList),
317 	fSubsetWindows(kSubsetList),
318 	fWorkspacesLayer(NULL),
319 	fActiveScreen(NULL),
320 
321 	fWindowLock("window lock"),
322 
323 	fFocusFollowsMouse(false),
324 
325 	fMouseEventWindow(NULL),
326 	fWindowUnderMouse(NULL),
327 	fViewUnderMouse(B_NULL_TOKEN),
328 	fLastMousePosition(B_ORIGIN),
329 	fLastMouseButtons(0),
330 
331 	fFocus(NULL),
332 	fFront(NULL),
333 	fBack(NULL)
334 {
335 	char name[B_OS_NAME_LENGTH];
336 	Desktop::_GetLooperName(name, sizeof(name));
337 
338 	fMessagePort = create_port(DEFAULT_MONITOR_PORT_SIZE, name);
339 	if (fMessagePort < B_OK)
340 		return;
341 
342 	fLink.SetReceiverPort(fMessagePort);
343 }
344 
345 
346 Desktop::~Desktop()
347 {
348 	delete fSettings;
349 
350 	delete_area(fSharedReadOnlyArea);
351 	delete_port(fMessagePort);
352 	gFontManager->DetachUser(fUserID);
353 }
354 
355 
356 status_t
357 Desktop::Init()
358 {
359 	if (fMessagePort < B_OK)
360 		return fMessagePort;
361 
362 	// the system palette needs to be initialized before the
363 	// desktop settings, since it is used there already
364 	InitializeColorMap();
365 
366 	const size_t areaSize = B_PAGE_SIZE;
367 	char name[B_OS_NAME_LENGTH];
368 	snprintf(name, sizeof(name), "d:%d:shared read only", /*id*/0);
369 	fSharedReadOnlyArea = create_area(name, (void **)&fServerReadOnlyMemory,
370 		B_ANY_ADDRESS, areaSize, B_NO_LOCK, B_READ_AREA | B_WRITE_AREA);
371 	if (fSharedReadOnlyArea < B_OK)
372 		return fSharedReadOnlyArea;
373 
374 	gFontManager->AttachUser(fUserID);
375 
376 	fSettings = new DesktopSettingsPrivate(fServerReadOnlyMemory);
377 
378 	for (int32 i = 0; i < kMaxWorkspaces; i++) {
379 		_Windows(i).SetIndex(i);
380 		fWorkspaces[i].RestoreConfiguration(*fSettings->WorkspacesMessage(i));
381 	}
382 
383 	fVirtualScreen.RestoreConfiguration(*this, fSettings->WorkspacesMessage(0));
384 
385 	// TODO: temporary workaround, fActiveScreen will be removed
386 	fActiveScreen = fVirtualScreen.ScreenAt(0);
387 
388 	fVirtualScreen.HWInterface()->MoveCursorTo(fVirtualScreen.Frame().Width() / 2,
389 		fVirtualScreen.Frame().Height() / 2);
390 
391 #if TEST_MODE
392 	gInputManager->AddStream(new InputServerStream);
393 #endif
394 
395 	fEventDispatcher.SetDesktop(this);
396 	fEventDispatcher.SetTo(gInputManager->GetStream());
397 	if (fEventDispatcher.InitCheck() != B_OK)
398 		_LaunchInputServer();
399 
400 	fEventDispatcher.SetHWInterface(fVirtualScreen.HWInterface());
401 
402 	fEventDispatcher.SetMouseFilter(new MouseFilter(this));
403 	fEventDispatcher.SetKeyboardFilter(new KeyboardFilter(this));
404 
405 	// draw the background
406 
407 	fScreenRegion = fVirtualScreen.Frame();
408 
409 	BRegion stillAvailableOnScreen;
410 	_RebuildClippingForAllWindows(stillAvailableOnScreen);
411 	_SetBackground(stillAvailableOnScreen);
412 
413 	SetCursor(NULL);
414 		// this will set the default cursor
415 
416 	fVirtualScreen.HWInterface()->SetCursorVisible(true);
417 
418 	return B_OK;
419 }
420 
421 
422 void
423 Desktop::_LaunchInputServer()
424 {
425 	BRoster roster;
426 	status_t status = roster.Launch("application/x-vnd.Be-input_server");
427 	if (status != B_OK && status != B_ALREADY_RUNNING)
428 		syslog(LOG_ERR, "Failed to launch the input server: %s!\n", strerror(status));
429 }
430 
431 
432 void
433 Desktop::_GetLooperName(char* name, size_t length)
434 {
435 	snprintf(name, length, "d:%d:%s", /*id*/0, /*name*/"baron");
436 }
437 
438 
439 void
440 Desktop::_PrepareQuit()
441 {
442 	// let's kill all remaining applications
443 
444 	fApplicationsLock.Lock();
445 
446 	int32 count = fApplications.CountItems();
447 	for (int32 i = 0; i < count; i++) {
448 		ServerApp *app = fApplications.ItemAt(i);
449 		team_id clientTeam = app->ClientTeam();
450 
451 		app->Quit();
452 		kill_team(clientTeam);
453 	}
454 
455 	// wait for the last app to die
456 	if (count > 0)
457 		acquire_sem_etc(fShutdownSemaphore, fShutdownCount, B_RELATIVE_TIMEOUT, 250000);
458 
459 	fApplicationsLock.Unlock();
460 }
461 
462 
463 void
464 Desktop::_DispatchMessage(int32 code, BPrivate::LinkReceiver &link)
465 {
466 	switch (code) {
467 		case AS_CREATE_APP:
468 		{
469 			// Create the ServerApp to node monitor a new BApplication
470 
471 			// Attached data:
472 			// 1) port_id - receiver port of a regular app
473 			// 2) port_id - client looper port - for sending messages to the client
474 			// 2) team_id - app's team ID
475 			// 3) int32 - handler token of the regular app
476 			// 4) char * - signature of the regular app
477 
478 			// Find the necessary data
479 			team_id	clientTeamID = -1;
480 			port_id	clientLooperPort = -1;
481 			port_id clientReplyPort = -1;
482 			int32 htoken = B_NULL_TOKEN;
483 			char *appSignature = NULL;
484 
485 			link.Read<port_id>(&clientReplyPort);
486 			link.Read<port_id>(&clientLooperPort);
487 			link.Read<team_id>(&clientTeamID);
488 			link.Read<int32>(&htoken);
489 			if (link.ReadString(&appSignature) != B_OK)
490 				break;
491 
492 			ServerApp *app = new ServerApp(this, clientReplyPort,
493 				clientLooperPort, clientTeamID, htoken, appSignature);
494 			if (app->InitCheck() == B_OK
495 				&& app->Run()) {
496 				// add the new ServerApp to the known list of ServerApps
497 				fApplicationsLock.Lock();
498 				fApplications.AddItem(app);
499 				fApplicationsLock.Unlock();
500 			} else {
501 				delete app;
502 
503 				// if everything went well, ServerApp::Run() will notify
504 				// the client - but since it didn't, we do it here
505 				BPrivate::LinkSender reply(clientReplyPort);
506 				reply.StartMessage(B_ERROR);
507 				reply.Flush();
508 			}
509 
510 			// This is necessary because BPortLink::ReadString allocates memory
511 			free(appSignature);
512 			break;
513 		}
514 
515 		case AS_DELETE_APP:
516 		{
517 			// Delete a ServerApp. Received only from the respective ServerApp when a
518 			// BApplication asks it to quit.
519 
520 			// Attached Data:
521 			// 1) thread_id - thread ID of the ServerApp to be deleted
522 
523 			thread_id thread = -1;
524 			if (link.Read<thread_id>(&thread) < B_OK)
525 				break;
526 
527 			fApplicationsLock.Lock();
528 
529 			// Run through the list of apps and nuke the proper one
530 
531 			int32 count = fApplications.CountItems();
532 			ServerApp *removeApp = NULL;
533 
534 			for (int32 i = 0; i < count; i++) {
535 				ServerApp *app = fApplications.ItemAt(i);
536 
537 				if (app->Thread() == thread) {
538 					fApplications.RemoveItemAt(i);
539 					removeApp = app;
540 					break;
541 				}
542 			}
543 
544 			fApplicationsLock.Unlock();
545 
546 			if (removeApp != NULL)
547 				removeApp->Quit(fShutdownSemaphore);
548 
549 			if (fQuitting && count <= 1) {
550 				// wait for the last app to die
551 				acquire_sem_etc(fShutdownSemaphore, fShutdownCount, B_RELATIVE_TIMEOUT, 500000);
552 				PostMessage(kMsgQuitLooper);
553 			}
554 			break;
555 		}
556 
557 		case AS_ACTIVATE_APP:
558 		{
559 			// Someone is requesting to activation of a certain app.
560 
561 			// Attached data:
562 			// 1) port_id reply port
563 			// 2) team_id team
564 
565 			status_t status;
566 
567 			// get the parameters
568 			port_id replyPort;
569 			team_id team;
570 			if (link.Read(&replyPort) == B_OK
571 				&& link.Read(&team) == B_OK)
572 				status = _ActivateApp(team);
573 			else
574 				status = B_ERROR;
575 
576 			// send the reply
577 			BPrivate::PortLink replyLink(replyPort);
578 			replyLink.StartMessage(status);
579 			replyLink.Flush();
580 			break;
581 		}
582 
583 		case AS_APP_CRASHED:
584 		{
585 			BAutolock locker(fApplicationsLock);
586 
587 			team_id team;
588 			if (link.Read(&team) != B_OK)
589 				break;
590 
591 			for (int32 i = 0; i < fApplications.CountItems(); i++) {
592 				ServerApp* app = fApplications.ItemAt(i);
593 
594 				if (app->ClientTeam() == team)
595 					app->PostMessage(AS_APP_CRASHED);
596 			}
597 			break;
598 		}
599 
600 		case AS_EVENT_STREAM_CLOSED:
601 			_LaunchInputServer();
602 			break;
603 
604 		case B_QUIT_REQUESTED:
605 			// We've been asked to quit, so (for now) broadcast to all
606 			// test apps to quit. This situation will occur only when the server
607 			// is compiled as a regular Be application.
608 
609 			fApplicationsLock.Lock();
610 			fShutdownSemaphore = create_sem(0, "desktop shutdown");
611 			fShutdownCount = fApplications.CountItems();
612 			fApplicationsLock.Unlock();
613 
614 			fQuitting = true;
615 			BroadcastToAllApps(AS_QUIT_APP);
616 
617 			// We now need to process the remaining AS_DELETE_APP messages and
618 			// wait for the kMsgShutdownServer message.
619 			// If an application does not quit as asked, the picasso thread
620 			// will send us this message in 2-3 seconds.
621 
622 			// if there are no apps to quit, shutdown directly
623 			if (fShutdownCount == 0)
624 				PostMessage(kMsgQuitLooper);
625 			break;
626 
627 		case AS_ACTIVATE_WORKSPACE:
628 		{
629 			int32 index;
630 			link.Read<int32>(&index);
631 
632 			SetWorkspace(index);
633 			break;
634 		}
635 
636 		default:
637 			printf("Desktop %d:%s received unexpected code %ld\n", 0, "baron", code);
638 
639 			if (link.NeedsReply()) {
640 				// the client is now blocking and waiting for a reply!
641 				fLink.StartMessage(B_ERROR);
642 				fLink.Flush();
643 			}
644 			break;
645 	}
646 }
647 
648 
649 /*!
650 	\brief activate one of the app's windows.
651 */
652 status_t
653 Desktop::_ActivateApp(team_id team)
654 {
655 	status_t status = B_BAD_TEAM_ID;
656 
657 	// search for an unhidden window to give focus to
658 
659 	for (WindowLayer* window = fAllWindows.FirstWindow(); window != NULL;
660 			window = window->NextWindow(kAllWindowList)) {
661 		// if window is a normal window of the team, and not hidden,
662 		// we've found our target
663 		if (!window->IsHidden() && window->IsNormal()
664 			&& window->ServerWindow()->ClientTeam() == team) {
665 			ActivateWindow(window);
666 			return B_OK;
667 		}
668 	}
669 
670 	return status;
671 }
672 
673 
674 /*!
675 	\brief Send a quick (no attachments) message to all applications
676 
677 	Quite useful for notification for things like server shutdown, system
678 	color changes, etc.
679 */
680 void
681 Desktop::BroadcastToAllApps(int32 code)
682 {
683 	BAutolock locker(fApplicationsLock);
684 
685 	for (int32 i = fApplications.CountItems(); i-- > 0;) {
686 		fApplications.ItemAt(i)->PostMessage(code);
687 	}
688 }
689 
690 
691 // #pragma mark -
692 
693 
694 void
695 Desktop::SetCursor(ServerCursor* newCursor)
696 {
697 	if (newCursor == NULL)
698 		newCursor = fCursorManager.GetCursor(B_CURSOR_DEFAULT);
699 
700 	ServerCursor* oldCursor = Cursor();
701 	if (newCursor == oldCursor)
702 		return;
703 
704 	HWInterface()->SetCursor(newCursor);
705 }
706 
707 
708 ServerCursor*
709 Desktop::Cursor() const
710 {
711 	return HWInterface()->Cursor();
712 }
713 
714 
715 void
716 Desktop::SetLastMouseState(const BPoint& position, int32 buttons)
717 {
718 	fLastMousePosition = position;
719 	fLastMouseButtons = buttons;
720 }
721 
722 
723 void
724 Desktop::GetLastMouseState(BPoint* position, int32* buttons) const
725 {
726 	*position = fLastMousePosition;
727 	*buttons = fLastMouseButtons;
728 }
729 
730 
731 // #pragma mark -
732 
733 
734 /*!
735 	\brief Redraws the background (ie. the desktop window, if any).
736 */
737 void
738 Desktop::RedrawBackground()
739 {
740 	LockAllWindows();
741 
742 	BRegion redraw;
743 
744 	WindowLayer* window = _CurrentWindows().FirstWindow();
745 	if (window->Feel() == kDesktopWindowFeel) {
746 		redraw = window->VisibleContentRegion();
747 
748 		// look for desktop background view, and update its background color
749 		// TODO: is there a better way to do this?
750 		ViewLayer* view = window->TopLayer();
751 		if (view != NULL)
752 			view = view->FirstChild();
753 
754 		while (view) {
755 			if (view->IsDesktopBackground()) {
756 				view->SetViewColor(fWorkspaces[fCurrentWorkspace].Color());
757 				break;
758 			}
759 			view = view->NextSibling();
760 		}
761 
762 		window->ProcessDirtyRegion(redraw);
763 	} else {
764 		redraw = BackgroundRegion();
765 		fBackgroundRegion.MakeEmpty();
766 		_SetBackground(redraw);
767 	}
768 
769 	_WindowChanged(NULL);
770 		// update workspaces view as well
771 
772 	UnlockAllWindows();
773 }
774 
775 
776 /*!
777 	\brief Store the workspace configuration
778 */
779 void
780 Desktop::StoreWorkspaceConfiguration(int32 index)
781 {
782 	const BMessage *oldSettings = fSettings->WorkspacesMessage(index);
783 	// store settings
784 	BMessage settings;
785 	if (oldSettings)
786 		settings = *oldSettings;
787 	fWorkspaces[index].StoreConfiguration(settings);
788 	fSettings->SetWorkspacesMessage(index, settings);
789 	fSettings->Save(kWorkspacesSettings);
790 }
791 
792 
793 status_t
794 Desktop::SetWorkspacesCount(int32 newCount)
795 {
796 	if (newCount < 1 || newCount > kMaxWorkspaces)
797 		return B_BAD_VALUE;
798 
799 	if (!LockAllWindows())
800 		return B_ERROR;
801 
802 	fSettings->SetWorkspacesCount(newCount);
803 
804 	// either update the workspaces window, or switch to
805 	// the last available workspace - which will update
806 	// the workspaces window automatically
807 	bool workspaceChanged = CurrentWorkspace() >= newCount;
808 	if (workspaceChanged)
809 		_SetWorkspace(newCount - 1);
810 	else
811 		_WindowChanged(NULL);
812 
813 	UnlockAllWindows();
814 
815 	if (workspaceChanged)
816 		_SendFakeMouseMoved();
817 
818 	return B_OK;
819 }
820 
821 
822 /*!
823 	Changes the current workspace to the one specified by \a index.
824 */
825 void
826 Desktop::SetWorkspaceAsync(int32 index)
827 {
828 	BPrivate::LinkSender link(MessagePort());
829 	link.StartMessage(AS_ACTIVATE_WORKSPACE);
830 	link.Attach<int32>(index);
831 	link.Flush();
832 }
833 
834 
835 /*!
836 	Changes the current workspace to the one specified by \a index.
837 	You must not hold any window lock when calling this method.
838 */
839 void
840 Desktop::SetWorkspace(int32 index)
841 {
842 	LockAllWindows();
843 	DesktopSettings settings(this);
844 
845 	if (index < 0 || index >= settings.WorkspacesCount()
846 		|| index == fCurrentWorkspace) {
847 		UnlockAllWindows();
848 		return;
849 	}
850 
851 	_SetWorkspace(index);
852 	UnlockAllWindows();
853 
854 	_SendFakeMouseMoved();
855 }
856 
857 
858 /*!
859 	Changes the current workspace to the one specified by \a index.
860 	You must hold the all window lock when calling this method.
861 */
862 void
863 Desktop::_SetWorkspace(int32 index)
864 {
865 	int32 previousIndex = fCurrentWorkspace;
866 	rgb_color previousColor = fWorkspaces[fCurrentWorkspace].Color();
867 	bool movedMouseEventWindow = false;
868 
869 	if (fMouseEventWindow != NULL) {
870 		if (fMouseEventWindow->IsNormal()) {
871 			if (!fMouseEventWindow->InWorkspace(index)) {
872 				// The window currently being dragged will follow us to this
873 				// workspace if it's not already on it.
874 				// But only normal windows are following
875 				uint32 oldWorkspaces = fMouseEventWindow->Workspaces();
876 
877 				_Windows(index).AddWindow(fMouseEventWindow);
878 				_Windows(previousIndex).RemoveWindow(fMouseEventWindow);
879 
880 				_UpdateSubsetWorkspaces(fMouseEventWindow, previousIndex, index);
881 				movedMouseEventWindow = true;
882 
883 				// send B_WORKSPACES_CHANGED message
884 				fMouseEventWindow->WorkspacesChanged(oldWorkspaces,
885 					fMouseEventWindow->Workspaces());
886 			} else {
887 				// make sure it's frontmost
888 				_Windows(index).RemoveWindow(fMouseEventWindow);
889 				_Windows(index).AddWindow(fMouseEventWindow,
890 					fMouseEventWindow->Frontmost(_Windows(index).FirstWindow(), index));
891 			}
892 		}
893 
894 		fMouseEventWindow->Anchor(index).position = fMouseEventWindow->Frame().LeftTop();
895 	}
896 
897 	// build region of windows that are no longer visible in the new workspace
898 
899 	BRegion dirty;
900 
901 	for (WindowLayer* window = _CurrentWindows().FirstWindow();
902 			window != NULL; window = window->NextWindow(previousIndex)) {
903 		// store current position in Workspace anchor
904 		window->Anchor(previousIndex).position = window->Frame().LeftTop();
905 
906 		window->WorkspaceActivated(previousIndex, false);
907 
908 		if (window->InWorkspace(index))
909 			continue;
910 
911 		if (!window->IsHidden()) {
912 			// this window will no longer be visible
913 			dirty.Include(&window->VisibleRegion());
914 		}
915 
916 		window->SetCurrentWorkspace(-1);
917 	}
918 
919 	fCurrentWorkspace = index;
920 
921 	// show windows, and include them in the changed region - but only
922 	// those that were not visible before (or whose position changed)
923 
924 	WindowList windows(kWorkingList);
925 	BList previousRegions;
926 
927 	for (WindowLayer* window = _Windows(index).FirstWindow();
928 			window != NULL; window = window->NextWindow(index)) {
929 		BPoint position = window->Anchor(index).position;
930 
931 		window->SetCurrentWorkspace(index);
932 
933 		if (window->IsHidden())
934 			continue;
935 
936 		if (position == kInvalidWindowPosition) {
937 			// if you enter a workspace for the first time, the position
938 			// of the window in the previous workspace is adopted
939 			position = window->Frame().LeftTop();
940 				// TODO: make sure the window is still on-screen if it
941 				//	was before!
942 		}
943 
944 		if (!window->InWorkspace(previousIndex)) {
945 			// This window was not visible before, make sure its frame
946 			// is up-to-date
947 			if (window->Frame().LeftTop() != position) {
948 				BPoint offset = position - window->Frame().LeftTop();
949 				window->MoveBy(offset.x, offset.y);
950 			}
951 			continue;
952 		}
953 
954 		if (window->Frame().LeftTop() != position) {
955 			// the window was visible before, but its on-screen location changed
956 			BPoint offset = position - window->Frame().LeftTop();
957 			MoveWindowBy(window, offset.x, offset.y);
958 				// TODO: be a bit smarter than this...
959 		} else {
960 			// We need to remember the previous visible region of the
961 			// window if they changed their order
962 			BRegion* region = new (std::nothrow)
963 				BRegion(window->VisibleRegion());
964 			if (region != NULL) {
965 				if (previousRegions.AddItem(region))
966 					windows.AddWindow(window);
967 				else
968 					delete region;
969 			}
970 		}
971 	}
972 
973 	_UpdateFronts(false);
974 	_UpdateFloating(previousIndex, index,
975 		movedMouseEventWindow ? fMouseEventWindow : NULL);
976 
977 	BRegion stillAvailableOnScreen;
978 	_RebuildClippingForAllWindows(stillAvailableOnScreen);
979 	_SetBackground(stillAvailableOnScreen);
980 
981 	for (WindowLayer* window = _Windows(index).FirstWindow(); window != NULL;
982 			window = window->NextWindow(index)) {
983 		// send B_WORKSPACE_ACTIVATED message
984 		window->WorkspaceActivated(index, true);
985 
986 		if (window->InWorkspace(previousIndex) || window->IsHidden()
987 			|| (window == fMouseEventWindow && fMouseEventWindow->IsNormal())
988 			|| (!window->IsNormal() && window->HasInSubset(fMouseEventWindow))) {
989 			// this window was visible before, and is already handled in the above loop
990 			continue;
991 		}
992 
993 		dirty.Include(&window->VisibleRegion());
994 	}
995 
996 	// Catch order changes in the new workspaces window list
997 	int32 i = 0;
998 	for (WindowLayer* window = windows.FirstWindow(); window != NULL;
999 			window = window->NextWindow(kWorkingList), i++) {
1000 		BRegion* region = (BRegion*)previousRegions.ItemAt(i);
1001 		region->ExclusiveInclude(&window->VisibleRegion());
1002 		dirty.Include(region);
1003 		delete region;
1004 	}
1005 
1006 	// Set new focus to the front window, but keep focus to a floating
1007 	// window if still visible
1008 	if (!_Windows(index).HasWindow(FocusWindow()) || !FocusWindow()->IsFloating())
1009 		SetFocusWindow(FrontWindow());
1010 
1011 	_WindowChanged(NULL);
1012 	MarkDirty(dirty);
1013 
1014 #if 0
1015 	// Show the dirty regions of this workspace switch
1016 	if (GetDrawingEngine()->LockParallelAccess()) {
1017 		GetDrawingEngine()->FillRegion(dirty, (rgb_color){255, 0, 0});
1018 		GetDrawingEngine()->UnlockParallelAccess();
1019 		snooze(100000);
1020 	}
1021 #endif
1022 
1023 	if (previousColor != fWorkspaces[fCurrentWorkspace].Color())
1024 		RedrawBackground();
1025 }
1026 
1027 
1028 void
1029 Desktop::ScreenChanged(Screen* screen, bool makeDefault)
1030 {
1031 	// TODO: confirm that everywhere this is used,
1032 	// the Window WriteLock is held
1033 
1034 	// the entire screen is dirty, because we're actually
1035 	// operating on an all new buffer in memory
1036 	BRegion dirty(screen->Frame());
1037 	// update our cached screen region
1038 	fScreenRegion.Set(screen->Frame());
1039 
1040 	BRegion background;
1041 	_RebuildClippingForAllWindows(background);
1042 
1043 	fBackgroundRegion.MakeEmpty();
1044 		// makes sure that the complete background is redrawn
1045 	_SetBackground(background);
1046 
1047 	// figure out dirty region
1048 	dirty.Exclude(&background);
1049 	_TriggerWindowRedrawing(dirty);
1050 
1051 	// send B_SCREEN_CHANGED to windows on that screen
1052 	BMessage update(B_SCREEN_CHANGED);
1053 	update.AddInt64("when", real_time_clock_usecs());
1054 	update.AddRect("frame", screen->Frame());
1055 	update.AddInt32("mode", screen->ColorSpace());
1056 
1057 	// TODO: currently ignores the screen argument!
1058 	for (WindowLayer* window = fAllWindows.FirstWindow(); window != NULL;
1059 			window = window->NextWindow(kAllWindowList)) {
1060 		window->ServerWindow()->SendMessageToClient(&update);
1061 	}
1062 
1063 	fVirtualScreen.UpdateFrame();
1064 
1065 	if (makeDefault) {
1066 		// store settings
1067 		BMessage settings;
1068 		fVirtualScreen.StoreConfiguration(settings);
1069 		fWorkspaces[fCurrentWorkspace].StoreConfiguration(settings);
1070 
1071 		fSettings->SetWorkspacesMessage(fCurrentWorkspace, settings);
1072 		fSettings->Save(kWorkspacesSettings);
1073 	}
1074 }
1075 
1076 
1077 //	#pragma mark - Methods for WindowLayer manipulation
1078 
1079 
1080 WindowList&
1081 Desktop::_CurrentWindows()
1082 {
1083 	return fWorkspaces[fCurrentWorkspace].Windows();
1084 }
1085 
1086 
1087 WindowList&
1088 Desktop::_Windows(int32 index)
1089 {
1090 	return fWorkspaces[index].Windows();
1091 }
1092 
1093 
1094 void
1095 Desktop::_UpdateFloating(int32 previousWorkspace, int32 nextWorkspace,
1096 	WindowLayer* mouseEventWindow)
1097 {
1098 	if (previousWorkspace == -1)
1099 		previousWorkspace = fCurrentWorkspace;
1100 	if (nextWorkspace == -1)
1101 		nextWorkspace = previousWorkspace;
1102 
1103 	for (WindowLayer* floating = fSubsetWindows.FirstWindow(); floating != NULL;
1104 			floating = floating->NextWindow(kSubsetList)) {
1105 		// we only care about app/subset floating windows
1106 		if (floating->Feel() != B_FLOATING_SUBSET_WINDOW_FEEL
1107 			&& floating->Feel() != B_FLOATING_APP_WINDOW_FEEL)
1108 			continue;
1109 
1110 		if (fFront != NULL && fFront->IsNormal() && floating->HasInSubset(fFront)) {
1111 			// is now visible
1112 			if (_Windows(previousWorkspace).HasWindow(floating)
1113 				&& previousWorkspace != nextWorkspace) {
1114 				// but no longer on the previous workspace
1115 				_Windows(previousWorkspace).RemoveWindow(floating);
1116 				floating->SetCurrentWorkspace(-1);
1117 			}
1118 			if (!_Windows(nextWorkspace).HasWindow(floating)) {
1119 				// but wasn't before
1120 				_Windows(nextWorkspace).AddWindow(floating,
1121 					floating->Frontmost(_Windows(nextWorkspace).FirstWindow(), nextWorkspace));
1122 				floating->SetCurrentWorkspace(nextWorkspace);
1123 				if (mouseEventWindow != fFront)
1124 					_ShowWindow(floating);
1125 
1126 				// TODO:
1127 				// put the floating last in the floating window list to preserve
1128 				// the on screen window order
1129 			}
1130 		} else if (_Windows(previousWorkspace).HasWindow(floating)) {
1131 			// was visible, but is no longer
1132 
1133 			_Windows(previousWorkspace).RemoveWindow(floating);
1134 			floating->SetCurrentWorkspace(-1);
1135 			_HideWindow(floating);
1136 
1137 			if (FocusWindow() == floating)
1138 				SetFocusWindow(_CurrentWindows().LastWindow());
1139 		}
1140 	}
1141 }
1142 
1143 
1144 /*!
1145 	Search the visible windows for a valid back window
1146 	(only desktop windows can't be back windows)
1147 */
1148 void
1149 Desktop::_UpdateBack()
1150 {
1151 	fBack = NULL;
1152 
1153 	for (WindowLayer* window = _CurrentWindows().FirstWindow();
1154 			window != NULL; window = window->NextWindow(fCurrentWorkspace)) {
1155 		if (window->IsHidden() || window->Feel() == kDesktopWindowFeel)
1156 			continue;
1157 
1158 		fBack = window;
1159 		break;
1160 	}
1161 }
1162 
1163 
1164 /*!
1165 	Search the visible windows for a valid front window
1166 	(only normal and modal windows can be front windows)
1167 
1168 	The only place where you don't want to update floating windows is
1169 	during a workspace change - because then you'll call _UpdateFloating()
1170 	yourself.
1171 */
1172 void
1173 Desktop::_UpdateFront(bool updateFloating)
1174 {
1175 	fFront = NULL;
1176 
1177 	for (WindowLayer* window = _CurrentWindows().LastWindow();
1178 			window != NULL; window = window->PreviousWindow(fCurrentWorkspace)) {
1179 		if (window->IsHidden() || window->IsFloating() || !window->SupportsFront())
1180 			continue;
1181 
1182 		fFront = window;
1183 		break;
1184 	}
1185 
1186 	if (updateFloating)
1187 		_UpdateFloating();
1188 }
1189 
1190 
1191 void
1192 Desktop::_UpdateFronts(bool updateFloating)
1193 {
1194 	_UpdateBack();
1195 	_UpdateFront(updateFloating);
1196 }
1197 
1198 
1199 /*!
1200 	Returns the current keyboard event target candidate - which is either the
1201 	top-most window (in case it's a menu), or the one having focus.
1202 	The window lock must be held when calling this function.
1203 */
1204 EventTarget*
1205 Desktop::KeyboardEventTarget()
1206 {
1207 	WindowLayer* window = _CurrentWindows().LastWindow();
1208 	while (window != NULL && window->IsHidden()) {
1209 		window = window->PreviousWindow(fCurrentWorkspace);
1210 	}
1211 	if (window != NULL && window->Feel() == kMenuWindowFeel)
1212 		return &window->EventTarget();
1213 
1214 	if (FocusWindow() != NULL)
1215 		return &FocusWindow()->EventTarget();
1216 
1217 	return NULL;
1218 }
1219 
1220 
1221 bool
1222 Desktop::_WindowHasModal(WindowLayer* window)
1223 {
1224 	if (window == NULL)
1225 		return false;
1226 
1227 	for (WindowLayer* modal = fSubsetWindows.FirstWindow(); modal != NULL;
1228 			modal = modal->NextWindow(kSubsetList)) {
1229 		// only visible modal windows count
1230 		if (!modal->IsModal() || modal->IsHidden())
1231 			continue;
1232 
1233 		if (modal->HasInSubset(window))
1234 			return true;
1235 	}
1236 
1237 	return false;
1238 }
1239 
1240 
1241 /*!
1242 	You must at least hold a single window lock when calling this method.
1243 */
1244 void
1245 Desktop::_WindowChanged(WindowLayer* window)
1246 {
1247 	if (fWorkspacesLayer == NULL)
1248 		return;
1249 
1250 	fWorkspacesLayer->WindowChanged(window);
1251 }
1252 
1253 
1254 /*!
1255 	\brief Sends a fake B_MOUSE_MOVED event to the window under the mouse,
1256 		and also updates the current view under the mouse.
1257 
1258 	This has only to be done in case the view changed without user interaction,
1259 	ie. because of a workspace change or a closing window.
1260 
1261 	Windows must not be locked when calling this method.
1262 */
1263 void
1264 Desktop::_SendFakeMouseMoved(WindowLayer* window)
1265 {
1266 	int32 viewToken = B_NULL_TOKEN;
1267 	EventTarget* target = NULL;
1268 
1269 	LockAllWindows();
1270 
1271 	if (window == NULL)
1272 		window = MouseEventWindow();
1273 	if (window == NULL)
1274 		window = WindowAt(fLastMousePosition);
1275 
1276 	if (window != NULL) {
1277 		BMessage message;
1278 		window->MouseMoved(&message, fLastMousePosition, &viewToken, true);
1279 
1280 		if (viewToken != B_NULL_TOKEN)
1281 			target = &window->EventTarget();
1282 	}
1283 
1284 	if (viewToken != B_NULL_TOKEN)
1285 		SetViewUnderMouse(window, viewToken);
1286 	else {
1287 		SetViewUnderMouse(NULL, B_NULL_TOKEN);
1288 		SetCursor(NULL);
1289 	}
1290 
1291 	UnlockAllWindows();
1292 
1293 	if (target != NULL)
1294 		EventDispatcher().SendFakeMouseMoved(*target, viewToken);
1295 }
1296 
1297 
1298 void
1299 Desktop::SetFocusWindow(WindowLayer* focus)
1300 {
1301 	if (!LockAllWindows())
1302 		return;
1303 
1304 	bool hasModal = _WindowHasModal(focus);
1305 
1306 	// TODO: test for FFM and B_LOCK_WINDOW_FOCUS
1307 
1308 	if (focus == fFocus && focus != NULL && !focus->IsHidden()
1309 		&& (focus->Flags() & B_AVOID_FOCUS) == 0 && !hasModal) {
1310 		// the window that is supposed to get focus already has focus
1311 		UnlockAllWindows();
1312 		return;
1313 	}
1314 
1315 	if (focus == NULL || hasModal) {
1316 		focus = FrontWindow();
1317 		if (focus == NULL) {
1318 			// there might be no front window in case of only a single
1319 			// window with B_FLOATING_ALL_WINDOW_FEEL
1320 			focus = _CurrentWindows().LastWindow();
1321 		}
1322 	}
1323 
1324 	// make sure no window is chosen that doesn't want focus or cannot have it
1325 	while (focus != NULL
1326 		&& ((focus->Flags() & B_AVOID_FOCUS) != 0
1327 			|| _WindowHasModal(focus)
1328 			|| focus->IsHidden())) {
1329 		focus = focus->PreviousWindow(fCurrentWorkspace);
1330 	}
1331 
1332 	if (fFocus == focus) {
1333 		// turns out the window that is supposed to get focus now already has it
1334 		UnlockAllWindows();
1335 		return;
1336 	}
1337 
1338 	team_id oldActiveApp = -1;
1339 	team_id newActiveApp = -1;
1340 
1341 	if (fFocus != NULL) {
1342 		fFocus->SetFocus(false);
1343 		oldActiveApp = fFocus->ServerWindow()->App()->ClientTeam();
1344 	}
1345 
1346 	fFocus = focus;
1347 
1348 	if (fFocus != NULL) {
1349 		fFocus->SetFocus(true);
1350 		newActiveApp = fFocus->ServerWindow()->App()->ClientTeam();
1351 	}
1352 
1353 	if (newActiveApp == -1) {
1354 		// make sure the cursor is visible
1355 		HWInterface()->SetCursorVisible(true);
1356 	}
1357 
1358 	UnlockAllWindows();
1359 
1360 	// change the "active" app if appropriate
1361 	if (oldActiveApp == newActiveApp)
1362 		return;
1363 
1364 	BAutolock locker(fApplicationsLock);
1365 
1366 	for (int32 i = 0; i < fApplications.CountItems(); i++) {
1367 		ServerApp *app = fApplications.ItemAt(i);
1368 
1369 		if (oldActiveApp != -1 && app->ClientTeam() == oldActiveApp)
1370 			app->Activate(false);
1371 		else if (newActiveApp != -1 && app->ClientTeam() == newActiveApp)
1372 			app->Activate(true);
1373 	}
1374 }
1375 
1376 
1377 void
1378 Desktop::_BringWindowsToFront(WindowList& windows, int32 list,
1379 	bool wereVisible)
1380 {
1381 	// we don't need to redraw what is currently
1382 	// visible of the window
1383 	BRegion clean;
1384 
1385 	for (WindowLayer* window = windows.FirstWindow(); window != NULL;
1386 			window = window->NextWindow(list)) {
1387 		if (wereVisible)
1388 			clean.Include(&window->VisibleRegion());
1389 
1390 		_CurrentWindows().AddWindow(window,
1391 			window->Frontmost(_CurrentWindows().FirstWindow(),
1392 				fCurrentWorkspace));
1393 
1394 		_WindowChanged(window);
1395 	}
1396 
1397 	BRegion dummy;
1398 	_RebuildClippingForAllWindows(dummy);
1399 
1400 	// redraw what became visible of the window(s)
1401 
1402 	BRegion dirty;
1403 	for (WindowLayer* window = windows.FirstWindow(); window != NULL;
1404 			window = window->NextWindow(list)) {
1405 		dirty.Include(&window->VisibleRegion());
1406 	}
1407 
1408 	dirty.Exclude(&clean);
1409 	MarkDirty(dirty);
1410 
1411 	_UpdateFront();
1412 
1413 	if (windows.FirstWindow() == fBack || fBack == NULL)
1414 		_UpdateBack();
1415 }
1416 
1417 
1418 /*!
1419 	\brief Tries to move the specified window to the front of the screen,
1420 		and make it the focus window.
1421 
1422 	If there are any modal windows on this screen, it might not actually
1423 	become the frontmost window, though, as modal windows stay in front
1424 	of their subset.
1425 */
1426 void
1427 Desktop::ActivateWindow(WindowLayer* window)
1428 {
1429 	STRACE(("ActivateWindow(%p, %s)\n", window, window ? window->Title() : "<none>"));
1430 
1431 	if (window == NULL) {
1432 		fBack = NULL;
1433 		fFront = NULL;
1434 		return;
1435 	}
1436 	if (window->Workspaces() == 0)
1437 		return;
1438 
1439 	// TODO: take care about floating windows
1440 
1441 	bool windowOnOtherWorkspace = !window->InWorkspace(fCurrentWorkspace);
1442 	if (windowOnOtherWorkspace) {
1443 		if ((window->Flags() & B_NO_WORKSPACE_ACTIVATION) == 0
1444 			&& (window->Flags() & B_NOT_ANCHORED_ON_ACTIVATE) == 0) {
1445 			// switch to the workspace on which this window is
1446 			// (we'll take the first one that the window is on)
1447 			uint32 workspaces = window->Workspaces();
1448 			for (int32 i = 0; i < 32; i++) {
1449 				uint32 workspace = workspace_to_workspaces(i);
1450 				if (workspaces & workspace) {
1451 					SetWorkspace(i);
1452 					windowOnOtherWorkspace = false;
1453 					break;
1454 				}
1455 			}
1456 		} else if ((window->Flags() & B_NOT_ANCHORED_ON_ACTIVATE) == 0)
1457 			return;
1458 	}
1459 
1460 	if (!LockAllWindows())
1461 		return;
1462 
1463 	if (windowOnOtherWorkspace
1464 		&& (window->Flags() & B_NOT_ANCHORED_ON_ACTIVATE) != 0) {
1465 		// bring the window to the current workspace
1466 		// TODO: what if this window is on multiple workspaces?!?
1467 		uint32 workspaces = workspace_to_workspaces(fCurrentWorkspace);
1468 		SetWindowWorkspaces(window, workspaces);
1469 	}
1470 
1471 	if (window == FrontWindow()) {
1472 		// see if there is a normal B_AVOID_FRONT window still in front of us
1473 		WindowLayer* avoidsFront = window->NextWindow(fCurrentWorkspace);
1474 		while (avoidsFront && avoidsFront->IsNormal()
1475 			&& (avoidsFront->Flags() & B_AVOID_FRONT) == 0) {
1476 			avoidsFront = avoidsFront->NextWindow(fCurrentWorkspace);
1477 		}
1478 
1479 		if (avoidsFront == NULL) {
1480 			// we're already the frontmost window, we might just not have focus yet
1481 			if ((window->Flags() & B_AVOID_FOCUS) == 0)
1482 				SetFocusWindow(window);
1483 
1484 			UnlockAllWindows();
1485 			return;
1486 		}
1487 	}
1488 
1489 	// we don't need to redraw what is currently
1490 	// visible of the window
1491 	BRegion clean(window->VisibleRegion());
1492 	WindowList windows(kWorkingList);
1493 
1494 	WindowLayer* frontmost = window->Frontmost();
1495 
1496 	_CurrentWindows().RemoveWindow(window);
1497 	windows.AddWindow(window);
1498 
1499 	if (frontmost != NULL && frontmost->IsModal()) {
1500 		// all modal windows follow their subsets to the front
1501 		// (ie. they are staying in front of them, but they are
1502 		// not supposed to change their order because of that)
1503 
1504 		WindowLayer* nextModal;
1505 		for (WindowLayer* modal = frontmost; modal != NULL; modal = nextModal) {
1506 			// get the next modal window
1507 			nextModal = modal->NextWindow(fCurrentWorkspace);
1508 			while (nextModal != NULL && !nextModal->IsModal()) {
1509 				nextModal = nextModal->NextWindow(fCurrentWorkspace);
1510 			}
1511 			if (nextModal != NULL && !nextModal->HasInSubset(window))
1512 				nextModal = NULL;
1513 
1514 			_CurrentWindows().RemoveWindow(modal);
1515 			windows.AddWindow(modal);
1516 		}
1517 	}
1518 
1519 	_BringWindowsToFront(windows, kWorkingList, true);
1520 	if ((window->Flags() & B_AVOID_FOCUS) == 0)
1521 		SetFocusWindow(window);
1522 
1523 	UnlockAllWindows();
1524 }
1525 
1526 
1527 void
1528 Desktop::SendWindowBehind(WindowLayer* window, WindowLayer* behindOf)
1529 {
1530 	// TODO: should the "not in current workspace" be handled anyway?
1531 	//	(the code below would have to be changed then, though)
1532 	if (window == BackWindow()
1533 		|| !window->InWorkspace(fCurrentWorkspace)
1534 		|| (behindOf != NULL && !behindOf->InWorkspace(fCurrentWorkspace))
1535 		|| !LockAllWindows())
1536 		return;
1537 
1538 	// Is this a valid behindOf window?
1539 	if (behindOf != NULL && window->HasInSubset(behindOf))
1540 		behindOf = NULL;
1541 
1542 	// what is currently visible of the window
1543 	// might be dirty after the window is send to back
1544 	BRegion dirty(window->VisibleRegion());
1545 
1546 	// detach window and re-attach at desired position
1547 	WindowLayer* backmost = window->Backmost(behindOf);
1548 
1549 	_CurrentWindows().RemoveWindow(window);
1550 	_CurrentWindows().AddWindow(window, backmost
1551 		? backmost->NextWindow(fCurrentWorkspace) : BackWindow());
1552 
1553 	BRegion dummy;
1554 	_RebuildClippingForAllWindows(dummy);
1555 
1556 	// mark everything dirty that is no longer visible
1557 	BRegion clean(window->VisibleRegion());
1558 	dirty.Exclude(&clean);
1559 	MarkDirty(dirty);
1560 
1561 	_UpdateFronts();
1562 	SetFocusWindow(_CurrentWindows().LastWindow());
1563 	_WindowChanged(window);
1564 
1565 	UnlockAllWindows();
1566 }
1567 
1568 
1569 void
1570 Desktop::ShowWindow(WindowLayer* window)
1571 {
1572 	if (!window->IsHidden())
1573 		return;
1574 
1575 	LockAllWindows();
1576 
1577 	window->SetHidden(false);
1578 
1579 	if (window->InWorkspace(fCurrentWorkspace)) {
1580 		_ShowWindow(window, true);
1581 		_UpdateSubsetWorkspaces(window);
1582 		ActivateWindow(window);
1583 	} else {
1584 		// then we don't need to send the fake mouse event either
1585 		_WindowChanged(window);
1586 		UnlockAllWindows();
1587 		return;
1588 	}
1589 
1590 	if (WorkspacesLayer* layer = dynamic_cast<WorkspacesLayer*>(window->TopLayer()))
1591 		fWorkspacesLayer = layer;
1592 
1593 	UnlockAllWindows();
1594 
1595 	// If the mouse cursor is directly over the newly visible window,
1596 	// we'll send a fake mouse moved message to the window, so that
1597 	// it knows the mouse is over it.
1598 
1599 	_SendFakeMouseMoved(window);
1600 }
1601 
1602 
1603 void
1604 Desktop::HideWindow(WindowLayer* window)
1605 {
1606 	if (window->IsHidden())
1607 		return;
1608 
1609 	if (!LockAllWindows())
1610 		return;
1611 
1612 	window->SetHidden(true);
1613 	if (fMouseEventWindow == window)
1614 		fMouseEventWindow = NULL;
1615 
1616 	if (window->InWorkspace(fCurrentWorkspace)) {
1617 		_UpdateSubsetWorkspaces(window);
1618 		_HideWindow(window);
1619 		_UpdateFronts();
1620 
1621 		if (FocusWindow() == window)
1622 			SetFocusWindow(_CurrentWindows().LastWindow());
1623 	} else
1624 		_WindowChanged(window);
1625 
1626 	if (fWorkspacesLayer != NULL)
1627 		fWorkspacesLayer->WindowRemoved(window);
1628 
1629 	if (dynamic_cast<WorkspacesLayer*>(window->TopLayer()) != NULL)
1630 		fWorkspacesLayer = NULL;
1631 
1632 	UnlockAllWindows();
1633 
1634 	if (window == fWindowUnderMouse)
1635 		_SendFakeMouseMoved();
1636 }
1637 
1638 
1639 /*!
1640 	Shows the window on the screen - it does this independently of the
1641 	WindowLayer::IsHidden() state.
1642 */
1643 void
1644 Desktop::_ShowWindow(WindowLayer* window, bool affectsOtherWindows)
1645 {
1646 	BRegion background;
1647 	_RebuildClippingForAllWindows(background);
1648 	_SetBackground(background);
1649 	_WindowChanged(window);
1650 
1651 	BRegion dirty(window->VisibleRegion());
1652 
1653 	if (!affectsOtherWindows) {
1654 		// everything that is now visible in the
1655 		// window needs a redraw, but other windows
1656 		// are not affected, we can call ProcessDirtyRegion()
1657 		// of the window, and don't have to use MarkDirty()
1658 		window->ProcessDirtyRegion(dirty);
1659 	} else
1660 		MarkDirty(dirty);
1661 }
1662 
1663 
1664 /*!
1665 	Hides the window from the screen - it does this independently of the
1666 	WindowLayer::IsHidden() state.
1667 */
1668 void
1669 Desktop::_HideWindow(WindowLayer* window)
1670 {
1671 	// after rebuilding the clipping,
1672 	// this window will not have a visible
1673 	// region anymore, so we need to remember
1674 	// it now
1675 	// (actually that's not true, since
1676 	// hidden windows are excluded from the
1677 	// clipping calculation, but anyways)
1678 	BRegion dirty(window->VisibleRegion());
1679 
1680 	BRegion background;
1681 	_RebuildClippingForAllWindows(background);
1682 	_SetBackground(background);
1683 	_WindowChanged(window);
1684 
1685 	MarkDirty(dirty);
1686 }
1687 
1688 
1689 void
1690 Desktop::MoveWindowBy(WindowLayer* window, float x, float y, int32 workspace)
1691 {
1692 	if (!LockAllWindows())
1693 		return;
1694 
1695 	if (workspace == -1)
1696 		workspace = fCurrentWorkspace;
1697 
1698 	if (!window->IsVisible() || workspace != fCurrentWorkspace) {
1699 		if (workspace != fCurrentWorkspace) {
1700 			// move the window on another workspace - this doesn't change it's
1701 			// current position
1702 			if (window->Anchor(workspace).position == kInvalidWindowPosition)
1703 				window->Anchor(workspace).position = window->Frame().LeftTop();
1704 
1705 			window->Anchor(workspace).position += BPoint(x, y);
1706 			_WindowChanged(window);
1707 		} else
1708 			window->MoveBy(x, y);
1709 
1710 		UnlockAllWindows();
1711 		return;
1712 	}
1713 
1714 	// the dirty region starts with the visible area of the window being moved
1715 	BRegion newDirtyRegion(window->VisibleRegion());
1716 
1717 	// no more drawing for DirectWindows
1718 	window->ServerWindow()->HandleDirectConnection(B_DIRECT_STOP);
1719 
1720 	window->MoveBy(x, y);
1721 
1722 	BRegion background;
1723 	_RebuildClippingForAllWindows(background);
1724 
1725 	// construct the region that is possible to be blitted
1726 	// to move the contents of the window
1727 	BRegion copyRegion(window->VisibleRegion());
1728 	copyRegion.OffsetBy(-x, -y);
1729 	copyRegion.IntersectWith(&newDirtyRegion);
1730 		// newDirtyRegion == the windows old visible region
1731 
1732 	// include the the new visible region of the window being
1733 	// moved into the dirty region (for now)
1734 	newDirtyRegion.Include(&window->VisibleRegion());
1735 
1736 	GetDrawingEngine()->CopyRegion(&copyRegion, x, y);
1737 
1738 	// allow DirectWindows to draw again after the visual
1739 	// content is at the new location
1740 	window->ServerWindow()->HandleDirectConnection(B_DIRECT_START | B_BUFFER_MOVED);
1741 
1742 	// in the dirty region, exclude the parts that we
1743 	// could move by blitting
1744 	copyRegion.OffsetBy(x, y);
1745 	newDirtyRegion.Exclude(&copyRegion);
1746 
1747 	MarkDirty(newDirtyRegion);
1748 	_SetBackground(background);
1749 	_WindowChanged(window);
1750 
1751 	UnlockAllWindows();
1752 }
1753 
1754 
1755 void
1756 Desktop::ResizeWindowBy(WindowLayer* window, float x, float y)
1757 {
1758 	if (!LockAllWindows())
1759 		return;
1760 
1761 	if (!window->IsVisible()) {
1762 		window->ResizeBy(x, y, NULL);
1763 		UnlockAllWindows();
1764 		return;
1765 	}
1766 
1767 	// the dirty region for the inside of the window is
1768 	// constructed by the window itself in ResizeBy()
1769 	BRegion newDirtyRegion;
1770 	// track the dirty region outside the window in case
1771 	// it is shrunk in "previouslyOccupiedRegion"
1772 	BRegion previouslyOccupiedRegion(window->VisibleRegion());
1773 
1774 	window->ResizeBy(x, y, &newDirtyRegion);
1775 
1776 	BRegion background;
1777 	_RebuildClippingForAllWindows(background);
1778 
1779 	// we just care for the region outside the window
1780 	previouslyOccupiedRegion.Exclude(&window->VisibleRegion());
1781 
1782 	// make sure the window cannot mark stuff dirty outside
1783 	// its visible region...
1784 	newDirtyRegion.IntersectWith(&window->VisibleRegion());
1785 	// ...because we do this outself
1786 	newDirtyRegion.Include(&previouslyOccupiedRegion);
1787 
1788 	MarkDirty(newDirtyRegion);
1789 	_SetBackground(background);
1790 	_WindowChanged(window);
1791 
1792 	UnlockAllWindows();
1793 }
1794 
1795 
1796 bool
1797 Desktop::SetWindowTabLocation(WindowLayer* window, float location)
1798 {
1799 	if (!LockAllWindows())
1800 		return false;
1801 
1802 	BRegion dirty;
1803 	bool changed = window->SetTabLocation(location, dirty);
1804 
1805 	if (changed && window->IsVisible() && dirty.CountRects() > 0) {
1806 		BRegion stillAvailableOnScreen;
1807 		_RebuildClippingForAllWindows(stillAvailableOnScreen);
1808 		_SetBackground(stillAvailableOnScreen);
1809 
1810 		_WindowChanged(window);
1811 		_TriggerWindowRedrawing(dirty);
1812 	}
1813 
1814 	UnlockAllWindows();
1815 
1816 	return changed;
1817 }
1818 
1819 
1820 bool
1821 Desktop::SetWindowDecoratorSettings(WindowLayer* window,
1822 									const BMessage& settings)
1823 {
1824 	// TODO: almost exact code duplication to above function...
1825 
1826 	if (!LockAllWindows())
1827 		return false;
1828 
1829 	BRegion dirty;
1830 	bool changed = window->SetDecoratorSettings(settings, dirty);
1831 
1832 	if (changed && window->IsVisible() && dirty.CountRects() > 0) {
1833 		BRegion stillAvailableOnScreen;
1834 		_RebuildClippingForAllWindows(stillAvailableOnScreen);
1835 		_SetBackground(stillAvailableOnScreen);
1836 
1837 		_TriggerWindowRedrawing(dirty);
1838 	}
1839 
1840 	UnlockAllWindows();
1841 
1842 	return changed;
1843 }
1844 
1845 
1846 /*!
1847 	Updates the workspaces of all subset windows with regard to the
1848 	specifed window.
1849 	If newIndex is not -1, it will move all subset windows that belong to
1850 	the specifed window to the new workspace; this form is only called by
1851 	SetWorkspace().
1852 */
1853 void
1854 Desktop::_UpdateSubsetWorkspaces(WindowLayer* window, int32 previousIndex,
1855 	int32 newIndex)
1856 {
1857 	STRACE(("_UpdateSubsetWorkspaces(window %p, %s)\n", window, window->Title()));
1858 
1859 	// if the window is hidden, the subset windows are up-to-date already
1860 	if (!window->IsNormal() || window->IsHidden())
1861 		return;
1862 
1863 	for (WindowLayer* subset = fSubsetWindows.FirstWindow(); subset != NULL;
1864 			subset = subset->NextWindow(kSubsetList)) {
1865 		if (subset->Feel() == B_MODAL_ALL_WINDOW_FEEL
1866 			|| subset->Feel() == B_FLOATING_ALL_WINDOW_FEEL) {
1867 			// These windows are always visible on all workspaces,
1868 			// no need to update them.
1869 			continue;
1870 		}
1871 
1872 		if (subset->IsFloating()) {
1873 			// Floating windows are inserted and removed to the current
1874 			// workspace as the need arises - they are not handled here
1875 			// but in _UpdateFront()
1876 			continue;
1877 		}
1878 
1879 		if (subset->HasInSubset(window)) {
1880 			// adopt the workspace change
1881 			if (newIndex != -1) {
1882 				_Windows(newIndex).AddWindow(subset);
1883 				_Windows(previousIndex).RemoveWindow(subset);
1884 			} else
1885 				SetWindowWorkspaces(subset, subset->SubsetWorkspaces());
1886 		}
1887 	}
1888 }
1889 
1890 
1891 /*!
1892 	\brief Adds or removes the window to or from the workspaces it's on.
1893 */
1894 void
1895 Desktop::_ChangeWindowWorkspaces(WindowLayer* window, uint32 oldWorkspaces,
1896 	uint32 newWorkspaces)
1897 {
1898 	// apply changes to the workspaces' window lists
1899 
1900 	LockAllWindows();
1901 
1902 	for (int32 i = 0; i < kMaxWorkspaces; i++) {
1903 		if (workspace_in_workspaces(i, oldWorkspaces)) {
1904 			// window is on this workspace, is it anymore?
1905 			if (!workspace_in_workspaces(i, newWorkspaces)) {
1906 				_Windows(i).RemoveWindow(window);
1907 
1908 				if (i == CurrentWorkspace()) {
1909 					// remove its appearance from the current workspace
1910 					window->SetCurrentWorkspace(-1);
1911 
1912 					if (!window->IsHidden())
1913 						_HideWindow(window);
1914 				}
1915 			}
1916 		} else {
1917 			// window was not on this workspace, is it now?
1918 			if (workspace_in_workspaces(i, newWorkspaces)) {
1919 				_Windows(i).AddWindow(window,
1920 					window->Frontmost(_Windows(i).FirstWindow(), i));
1921 
1922 				if (i == CurrentWorkspace()) {
1923 					// make the window visible in current workspace
1924 					window->SetCurrentWorkspace(fCurrentWorkspace);
1925 
1926 					if (!window->IsHidden()) {
1927 						// this only affects other windows if this windows has floating or
1928 						// modal windows that need to be shown as well
1929 						// TODO: take care of this
1930 						_ShowWindow(window, FrontWindow() == window);
1931 					}
1932 				}
1933 			}
1934 		}
1935 	}
1936 
1937 	// take care about modals and floating windows
1938 	_UpdateSubsetWorkspaces(window);
1939 
1940 	UnlockAllWindows();
1941 }
1942 
1943 
1944 void
1945 Desktop::SetWindowWorkspaces(WindowLayer* window, uint32 workspaces)
1946 {
1947 	LockAllWindows();
1948 
1949 	if (window->IsNormal() && workspaces == B_CURRENT_WORKSPACE)
1950 		workspaces = workspace_to_workspaces(CurrentWorkspace());
1951 
1952 	uint32 oldWorkspaces = window->Workspaces();
1953 
1954 	window->WorkspacesChanged(oldWorkspaces, workspaces);
1955 	_ChangeWindowWorkspaces(window, oldWorkspaces, workspaces);
1956 
1957 	UnlockAllWindows();
1958 }
1959 
1960 
1961 /*!	\brief Adds the window to the desktop.
1962 	At this point, the window is still hidden and must be shown explicetly
1963 	via ShowWindow().
1964 */
1965 void
1966 Desktop::AddWindow(WindowLayer *window)
1967 {
1968 	LockAllWindows();
1969 
1970 	fAllWindows.AddWindow(window);
1971 	if (!window->IsNormal())
1972 		fSubsetWindows.AddWindow(window);
1973 
1974 	if (window->IsNormal()) {
1975 		if (window->Workspaces() == B_CURRENT_WORKSPACE)
1976 			window->SetWorkspaces(workspace_to_workspaces(CurrentWorkspace()));
1977 	} else {
1978 		// subset windows are visible on all workspaces their subset is on
1979 		window->SetWorkspaces(window->SubsetWorkspaces());
1980 	}
1981 
1982 	_ChangeWindowWorkspaces(window, 0, window->Workspaces());
1983 	UnlockAllWindows();
1984 }
1985 
1986 
1987 void
1988 Desktop::RemoveWindow(WindowLayer *window)
1989 {
1990 	LockAllWindows();
1991 
1992 	if (!window->IsHidden())
1993 		HideWindow(window);
1994 
1995 	fAllWindows.RemoveWindow(window);
1996 	if (!window->IsNormal())
1997 		fSubsetWindows.RemoveWindow(window);
1998 
1999 	_ChangeWindowWorkspaces(window, window->Workspaces(), 0);
2000 	UnlockAllWindows();
2001 
2002 	// make sure this window won't get any events anymore
2003 
2004 	EventDispatcher().RemoveTarget(window->EventTarget());
2005 }
2006 
2007 
2008 bool
2009 Desktop::AddWindowToSubset(WindowLayer* subset, WindowLayer* window)
2010 {
2011 	if (!subset->AddToSubset(window))
2012 		return false;
2013 
2014 	_ChangeWindowWorkspaces(subset, subset->Workspaces(), subset->SubsetWorkspaces());
2015 	return true;
2016 }
2017 
2018 
2019 void
2020 Desktop::RemoveWindowFromSubset(WindowLayer* subset, WindowLayer* window)
2021 {
2022 	subset->RemoveFromSubset(window);
2023 	_ChangeWindowWorkspaces(subset, subset->Workspaces(), subset->SubsetWorkspaces());
2024 }
2025 
2026 
2027 void
2028 Desktop::SetWindowLook(WindowLayer *window, window_look newLook)
2029 {
2030 	if (window->Look() == newLook)
2031 		return;
2032 
2033 	if (!LockAllWindows())
2034 		return;
2035 
2036 	BRegion dirty;
2037 	window->SetLook(newLook, &dirty);
2038 		// TODO: test what happens when the window
2039 		// finds out it needs to resize itself...
2040 
2041 	BRegion stillAvailableOnScreen;
2042 	_RebuildClippingForAllWindows(stillAvailableOnScreen);
2043 	_SetBackground(stillAvailableOnScreen);
2044 	_WindowChanged(window);
2045 
2046 	_TriggerWindowRedrawing(dirty);
2047 
2048 	UnlockAllWindows();
2049 }
2050 
2051 
2052 void
2053 Desktop::SetWindowFeel(WindowLayer *window, window_feel newFeel)
2054 {
2055 	if (window->Feel() == newFeel)
2056 		return;
2057 
2058 	LockAllWindows();
2059 
2060 	bool wasNormal = window->IsNormal();
2061 
2062 	window->SetFeel(newFeel);
2063 
2064 	// move the window out of or into the subset window list as needed
2065 	if (window->IsNormal() && !wasNormal)
2066 		fSubsetWindows.RemoveWindow(window);
2067 	else if (!window->IsNormal() && wasNormal)
2068 		fSubsetWindows.AddWindow(window);
2069 
2070 	// A normal window that was once a floating or modal window will
2071 	// adopt the window's current workspaces
2072 
2073 	if (!window->IsNormal())
2074 		_ChangeWindowWorkspaces(window, window->Workspaces(), window->SubsetWorkspaces());
2075 
2076 	// make sure the window has the correct position in the window lists
2077 	//	(ie. all floating windows have to be on the top, ...)
2078 
2079 	for (int32 i = 0; i < kMaxWorkspaces; i++) {
2080 		if (!workspace_in_workspaces(i, window->Workspaces()))
2081 			continue;
2082 
2083 		bool changed = false;
2084 		BRegion visibleBefore;
2085 		if (i == fCurrentWorkspace && window->IsVisible())
2086 			visibleBefore = window->VisibleRegion();
2087 
2088 		WindowLayer* backmost = window->Backmost(_Windows(i).LastWindow(), i);
2089 		if (backmost != NULL) {
2090 			// check if the backmost window is really behind it
2091 			WindowLayer* previous = window->PreviousWindow(i);
2092 			while (previous != NULL) {
2093 				if (previous == backmost)
2094 					break;
2095 
2096 				previous = previous->PreviousWindow(i);
2097 			}
2098 
2099 			if (previous == NULL) {
2100 				// need to reinsert window before its backmost window
2101 				_Windows(i).RemoveWindow(window);
2102 				_Windows(i).AddWindow(window, backmost->NextWindow(i));
2103 				changed = true;
2104 			}
2105 		}
2106 
2107 		WindowLayer* frontmost = window->Frontmost(_Windows(i).FirstWindow(), i);
2108 		if (frontmost != NULL) {
2109 			// check if the frontmost window is really in front of it
2110 			WindowLayer* next = window->NextWindow(i);
2111 			while (next != NULL) {
2112 				if (next == frontmost)
2113 					break;
2114 
2115 				next = next->NextWindow(i);
2116 			}
2117 
2118 			if (next == NULL) {
2119 				// need to reinsert window behind its frontmost window
2120 				_Windows(i).RemoveWindow(window);
2121 				_Windows(i).AddWindow(window, frontmost);
2122 				changed = true;
2123 			}
2124 		}
2125 
2126 		if (i == fCurrentWorkspace && changed) {
2127 			BRegion dummy;
2128 			_RebuildClippingForAllWindows(dummy);
2129 
2130 			// mark everything dirty that is no longer visible, or
2131 			// is now visible and wasn't before
2132 			BRegion visibleAfter(window->VisibleRegion());
2133 			BRegion dirty(visibleAfter);
2134 			dirty.Exclude(&visibleBefore);
2135 			visibleBefore.Exclude(&visibleAfter);
2136 			dirty.Include(&visibleBefore);
2137 
2138 			MarkDirty(dirty);
2139 		}
2140 	}
2141 
2142 	_UpdateFronts();
2143 
2144 	if (window == FocusWindow() && !window->IsVisible())
2145 		SetFocusWindow(_CurrentWindows().LastWindow());
2146 
2147 	UnlockAllWindows();
2148 }
2149 
2150 
2151 void
2152 Desktop::SetWindowFlags(WindowLayer *window, uint32 newFlags)
2153 {
2154 	if (window->Flags() == newFlags)
2155 		return;
2156 
2157 	if (!LockAllWindows())
2158 		return;
2159 
2160 	BRegion dirty;
2161 	window->SetFlags(newFlags, &dirty);
2162 		// TODO: test what happens when the window
2163 		// finds out it needs to resize itself...
2164 
2165 	BRegion stillAvailableOnScreen;
2166 	_RebuildClippingForAllWindows(stillAvailableOnScreen);
2167 	_SetBackground(stillAvailableOnScreen);
2168 	_WindowChanged(window);
2169 
2170 	_TriggerWindowRedrawing(dirty);
2171 
2172 	UnlockAllWindows();
2173 }
2174 
2175 
2176 void
2177 Desktop::SetWindowTitle(WindowLayer *window, const char* title)
2178 {
2179 	if (!LockAllWindows())
2180 		return;
2181 
2182 	BRegion dirty;
2183 	window->SetTitle(title, dirty);
2184 
2185 	if (window->IsVisible() && dirty.CountRects() > 0) {
2186 		BRegion stillAvailableOnScreen;
2187 		_RebuildClippingForAllWindows(stillAvailableOnScreen);
2188 		_SetBackground(stillAvailableOnScreen);
2189 
2190 		_TriggerWindowRedrawing(dirty);
2191 	}
2192 
2193 	UnlockAllWindows();
2194 }
2195 
2196 
2197 /*!
2198 	Returns the window under the mouse cursor.
2199 	You need to have acquired the All Windows lock when calling this method.
2200 */
2201 WindowLayer*
2202 Desktop::WindowAt(BPoint where)
2203 {
2204 	for (WindowLayer* window = _CurrentWindows().LastWindow(); window;
2205 			window = window->PreviousWindow(fCurrentWorkspace)) {
2206 		if (window->IsVisible() && window->VisibleRegion().Contains(where))
2207 			return window;
2208 	}
2209 
2210 	return NULL;
2211 }
2212 
2213 
2214 void
2215 Desktop::SetMouseEventWindow(WindowLayer* window)
2216 {
2217 	fMouseEventWindow = window;
2218 }
2219 
2220 
2221 void
2222 Desktop::SetViewUnderMouse(const WindowLayer* window, int32 viewToken)
2223 {
2224 	fWindowUnderMouse = window;
2225 	fViewUnderMouse = viewToken;
2226 }
2227 
2228 
2229 int32
2230 Desktop::ViewUnderMouse(const WindowLayer* window)
2231 {
2232 	if (window != NULL && fWindowUnderMouse == window)
2233 		return fViewUnderMouse;
2234 
2235 	return B_NULL_TOKEN;
2236 }
2237 
2238 
2239 WindowLayer *
2240 Desktop::FindWindowLayerByClientToken(int32 token, team_id teamID)
2241 {
2242 	for (WindowLayer *window = fAllWindows.FirstWindow(); window != NULL;
2243 			window = window->NextWindow(kAllWindowList)) {
2244 		if (window->ServerWindow()->ClientToken() == token
2245 			&& window->ServerWindow()->ClientTeam() == teamID) {
2246 			return window;
2247 		}
2248 	}
2249 
2250 	return NULL;
2251 }
2252 
2253 
2254 void
2255 Desktop::MinimizeApplication(team_id team)
2256 {
2257 	AutoWriteLocker locker(fWindowLock);
2258 
2259 	// Just minimize all windows of that application
2260 
2261 	for (WindowLayer *window = fAllWindows.FirstWindow(); window != NULL;
2262 			window = window->NextWindow(kAllWindowList)) {
2263 		if (window->ServerWindow()->ClientTeam() != team)
2264 			continue;
2265 
2266 		window->ServerWindow()->NotifyMinimize(true);
2267 	}
2268 }
2269 
2270 
2271 void
2272 Desktop::BringApplicationToFront(team_id team)
2273 {
2274 	AutoWriteLocker locker(fWindowLock);
2275 
2276 	// TODO: for now, just maximize all windows of that application
2277 
2278 	for (WindowLayer *window = fAllWindows.FirstWindow(); window != NULL;
2279 			window = window->NextWindow(kAllWindowList)) {
2280 		if (window->ServerWindow()->ClientTeam() != team)
2281 			continue;
2282 
2283 		window->ServerWindow()->NotifyMinimize(false);
2284 	}
2285 }
2286 
2287 
2288 void
2289 Desktop::WindowAction(int32 windowToken, int32 action)
2290 {
2291 	if (action != B_MINIMIZE_WINDOW && action != B_BRING_TO_FRONT)
2292 		return;
2293 
2294 	LockAllWindows();
2295 
2296 	::ServerWindow* serverWindow;
2297 	WindowLayer* window;
2298 	if (BPrivate::gDefaultTokens.GetToken(windowToken,
2299 			B_SERVER_TOKEN, (void**)&serverWindow) != B_OK
2300 		|| (window = serverWindow->Window()) == NULL) {
2301 		UnlockAllWindows();
2302 		return;
2303 	}
2304 
2305 	if (action == B_BRING_TO_FRONT
2306 		&& !window->IsMinimized()) {
2307 		// the window is visible, we just need to make it the front window
2308 		ActivateWindow(window);
2309 	} else
2310 		serverWindow->NotifyMinimize(action == B_MINIMIZE_WINDOW);
2311 
2312 	UnlockAllWindows();
2313 }
2314 
2315 
2316 void
2317 Desktop::WriteWindowList(team_id team, BPrivate::LinkSender& sender)
2318 {
2319 	AutoWriteLocker locker(fWindowLock);
2320 
2321 	// compute the number of windows
2322 
2323 	int32 count = 0;
2324 
2325 	for (WindowLayer *window = fAllWindows.FirstWindow(); window != NULL;
2326 			window = window->NextWindow(kAllWindowList)) {
2327 		if (team < B_OK || window->ServerWindow()->ClientTeam() == team)
2328 			count++;
2329 	}
2330 
2331 	// write list
2332 
2333 	sender.StartMessage(B_OK);
2334 	sender.Attach<int32>(count);
2335 
2336 	for (WindowLayer *window = fAllWindows.FirstWindow(); window != NULL;
2337 			window = window->NextWindow(kAllWindowList)) {
2338 		if (team >= B_OK && window->ServerWindow()->ClientTeam() != team)
2339 			continue;
2340 
2341 		sender.Attach<int32>(window->ServerWindow()->ServerToken());
2342 	}
2343 
2344 	sender.Flush();
2345 }
2346 
2347 
2348 void
2349 Desktop::WriteWindowInfo(int32 serverToken, BPrivate::LinkSender& sender)
2350 {
2351 	AutoWriteLocker locker(fWindowLock);
2352 	BAutolock tokenLocker(BPrivate::gDefaultTokens);
2353 
2354 	::ServerWindow* window;
2355 	if (BPrivate::gDefaultTokens.GetToken(serverToken,
2356 			B_SERVER_TOKEN, (void**)&window) != B_OK) {
2357 		sender.StartMessage(B_ENTRY_NOT_FOUND);
2358 		sender.Flush();
2359 		return;
2360 	}
2361 
2362 	window_info info;
2363 	window->GetInfo(info);
2364 
2365 	int32 length = window->Title() ? strlen(window->Title()) : 0;
2366 
2367 	sender.StartMessage(B_OK);
2368 	sender.Attach<int32>(sizeof(window_info) + length + 1);
2369 	sender.Attach(&info, sizeof(window_info));
2370 	if (length > 0)
2371 		sender.Attach(window->Title(), length + 1);
2372 	else
2373 		sender.Attach<char>('\0');
2374 	sender.Flush();
2375 }
2376 
2377 
2378 void
2379 Desktop::MarkDirty(BRegion& region)
2380 {
2381 	if (region.CountRects() == 0)
2382 		return;
2383 
2384 	if (LockAllWindows()) {
2385 		// send redraw messages to all windows intersecting the dirty region
2386 		_TriggerWindowRedrawing(region);
2387 
2388 		UnlockAllWindows();
2389 	}
2390 }
2391 
2392 
2393 void
2394 Desktop::_RebuildClippingForAllWindows(BRegion& stillAvailableOnScreen)
2395 {
2396 	// the available region on screen starts with the entire screen area
2397 	// each window on the screen will take a portion from that area
2398 
2399 	// figure out what the entire screen area is
2400 	stillAvailableOnScreen = fScreenRegion;
2401 
2402 	// set clipping of each window
2403 	for (WindowLayer* window = _CurrentWindows().LastWindow(); window != NULL;
2404 			window = window->PreviousWindow(fCurrentWorkspace)) {
2405 		if (!window->IsHidden()) {
2406 			window->SetClipping(&stillAvailableOnScreen);
2407 			// that windows region is not available on screen anymore
2408 			stillAvailableOnScreen.Exclude(&window->VisibleRegion());
2409 		}
2410 	}
2411 }
2412 
2413 
2414 void
2415 Desktop::_TriggerWindowRedrawing(BRegion& newDirtyRegion)
2416 {
2417 	// send redraw messages to all windows intersecting the dirty region
2418 	for (WindowLayer* window = _CurrentWindows().LastWindow(); window != NULL;
2419 			window = window->PreviousWindow(fCurrentWorkspace)) {
2420 		if (!window->IsHidden()
2421 			&& newDirtyRegion.Intersects(window->VisibleRegion().Frame()))
2422 			window->ProcessDirtyRegion(newDirtyRegion);
2423 	}
2424 }
2425 
2426 
2427 void
2428 Desktop::_SetBackground(BRegion& background)
2429 {
2430 	// NOTE: the drawing operation is caried out
2431 	// in the clipping region rebuild, but it is
2432 	// ok actually, because it also avoids trails on
2433 	// moving windows
2434 
2435 	// remember the region not covered by any windows
2436 	// and redraw the dirty background
2437 	BRegion dirtyBackground(background);
2438 	dirtyBackground.Exclude(&fBackgroundRegion);
2439 	dirtyBackground.IntersectWith(&background);
2440 	fBackgroundRegion = background;
2441 	if (dirtyBackground.Frame().IsValid()) {
2442 		if (GetDrawingEngine()->LockParallelAccess()) {
2443 			GetDrawingEngine()->FillRegion(dirtyBackground,
2444 				fWorkspaces[fCurrentWorkspace].Color());
2445 
2446 			GetDrawingEngine()->UnlockParallelAccess();
2447 		}
2448 	}
2449 }
2450