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