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