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