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