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