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