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