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