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