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