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