xref: /haiku/src/servers/app/Desktop.cpp (revision 4f00613311d0bd6b70fa82ce19931c41f071ea4e)
1 /*
2  * Copyright 2001-2005, 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 "ServerScreen.h"
24 #include "ServerWindow.h"
25 #include "WindowLayer.h"
26 #include "Workspace.h"
27 #include "WorkspacesLayer.h"
28 
29 #include <WindowInfo.h>
30 #include <ServerProtocol.h>
31 
32 #include <Entry.h>
33 #include <Message.h>
34 #include <MessageFilter.h>
35 #include <Region.h>
36 
37 #include <stdio.h>
38 
39 #if TEST_MODE
40 #	include "EventStream.h"
41 #endif
42 
43 //#define DEBUG_DESKTOP
44 #ifdef DEBUG_DESKTOP
45 #	define STRACE(a) printf(a)
46 #else
47 #	define STRACE(a) ;
48 #endif
49 
50 
51 class KeyboardFilter : public EventFilter {
52 	public:
53 		KeyboardFilter(Desktop* desktop);
54 
55 		virtual filter_result Filter(BMessage* message, EventTarget** _target,
56 			int32* _viewToken);
57 
58 	private:
59 		Desktop*		fDesktop;
60 		EventTarget*	fLastFocus;
61 		bigtime_t		fTimestamp;
62 };
63 
64 class MouseFilter : public EventFilter {
65 	public:
66 		MouseFilter(Desktop* desktop);
67 
68 		virtual filter_result Filter(BMessage* message, EventTarget** _target,
69 			int32* _viewToken);
70 
71 	private:
72 		Desktop*	fDesktop;
73 };
74 
75 
76 //	#pragma mark -
77 
78 
79 KeyboardFilter::KeyboardFilter(Desktop* desktop)
80 	:
81 	fDesktop(desktop),
82 	fLastFocus(NULL),
83 	fTimestamp(0)
84 {
85 }
86 
87 
88 filter_result
89 KeyboardFilter::Filter(BMessage* message, EventTarget** _target,
90 	int32* /*_viewToken*/)
91 {
92 	int32 key;
93 	int32 modifiers;
94 
95 	if (message->what != B_KEY_DOWN
96 		|| message->FindInt32("key", &key) != B_OK
97 		|| message->FindInt32("modifiers", &modifiers) != B_OK)
98 		return B_DISPATCH_MESSAGE;
99 
100 	// Check for safe video mode (F12 + l-cmd + l-ctrl + l-shift)
101 	if (key == 0x0d
102 		&& (modifiers & (B_LEFT_COMMAND_KEY
103 				| B_LEFT_CONTROL_KEY | B_LEFT_SHIFT_KEY)) != 0)
104 	{
105 		// TODO: Set to Safe Mode in KeyboardEventHandler:B_KEY_DOWN.
106 		STRACE(("Safe Video Mode invoked - code unimplemented\n"));
107 		return B_SKIP_MESSAGE;
108 	}
109 
110 	if (key > 0x01 && key < 0x0e) {
111 		// workspace change, F1-F12
112 
113 #if !TEST_MODE
114 		if (modifiers & B_COMMAND_KEY)
115 #else
116 		if (modifiers & B_CONTROL_KEY)
117 #endif
118 		{
119 			STRACE(("Set Workspace %ld\n", key - 1));
120 
121 			fDesktop->SetWorkspace(key - 2);
122 			return B_SKIP_MESSAGE;
123 		}
124 	}
125 
126 	// TODO: this should be moved client side!
127 	// (that's how it is done in BeOS, clients could need this key for
128 	// different purposes - also, it's preferrable to let the client
129 	// write the dump within his own environment)
130 	if (key == 0xe) {
131 		// screen dump, PrintScreen
132 		char filename[128];
133 		BEntry entry;
134 
135 		int32 index = 1;
136 		do {
137 			sprintf(filename, "/boot/home/screen%ld.png", index++);
138 			entry.SetTo(filename);
139 		} while(entry.Exists());
140 
141 		fDesktop->GetDrawingEngine()->DumpToFile(filename);
142 		return B_SKIP_MESSAGE;
143 	}
144 
145 	bigtime_t now = system_time();
146 
147 	if (!fDesktop->LockSingleWindow())
148 		return B_DISPATCH_MESSAGE;
149 
150 	EventTarget* focus = NULL;
151 	if (fDesktop->FocusWindow() != NULL)
152 		focus = &fDesktop->FocusWindow()->EventTarget();
153 
154 	// TODO: this is a try to not steal focus from the current window
155 	//	in case you enter some text and a window pops up you haven't
156 	//	triggered yourself (like a pop-up window in your browser while
157 	//	you're typing a password in another window) - maybe this should
158 	//	be done differently, though (using something like B_LOCK_WINDOW_FOCUS)
159 	//	(at least B_WINDOW_ACTIVATED must be postponed)
160 
161 	if (focus != fLastFocus && now - fTimestamp > 100000) {
162 		// if the time span between the key presses is very short
163 		// we keep our previous focus alive - this is save even
164 		// if the target doesn't exist anymore, as we don't reset
165 		// it, and the event focus passed in is always valid (or NULL)
166 		*_target = focus;
167 		fLastFocus = focus;
168 	}
169 
170 	fDesktop->UnlockSingleWindow();
171 
172 	// we always allow to switch focus after the enter key has pressed
173 	if (key == B_ENTER)
174 		fTimestamp = 0;
175 	else
176 		fTimestamp = now;
177 
178 	return B_DISPATCH_MESSAGE;
179 }
180 
181 
182 //	#pragma mark -
183 
184 
185 MouseFilter::MouseFilter(Desktop* desktop)
186 	:
187 	fDesktop(desktop)
188 {
189 }
190 
191 
192 filter_result
193 MouseFilter::Filter(BMessage* message, EventTarget** _target, int32* _viewToken)
194 {
195 	BPoint where;
196 	if (message->FindPoint("where", &where) != B_OK)
197 		return B_DISPATCH_MESSAGE;
198 
199 	if (!fDesktop->LockAllWindows())
200 		return B_DISPATCH_MESSAGE;
201 
202 	WindowLayer* window = fDesktop->MouseEventWindow();
203 	if (window == NULL)
204 		window = fDesktop->WindowAt(where);
205 
206 	if (window != NULL) {
207 		// dispatch event in the window layers
208 		switch (message->what) {
209 			case B_MOUSE_DOWN:
210 				window->MouseDown(message, where, _viewToken);
211 				break;
212 
213 			case B_MOUSE_UP:
214 				window->MouseUp(message, where, _viewToken);
215 				fDesktop->SetMouseEventWindow(NULL);
216 				break;
217 
218 			case B_MOUSE_MOVED:
219 				window->MouseMoved(message, where, _viewToken);
220 				break;
221 		}
222 
223 		if (*_viewToken != B_NULL_TOKEN)
224 			*_target = &window->EventTarget();
225 		else
226 			*_target = NULL;
227 	} else
228 		*_target = NULL;
229 
230 	fDesktop->UnlockAllWindows();
231 
232 	return B_DISPATCH_MESSAGE;
233 }
234 
235 
236 //	#pragma mark -
237 
238 
239 static inline uint32
240 workspace_to_workspaces(int32 index)
241 {
242 	return 1UL << index;
243 }
244 
245 
246 static inline bool
247 workspace_in_workspaces(int32 index, uint32 workspaces)
248 {
249 	return (workspaces & (1UL << index)) != 0;
250 }
251 
252 
253 //	#pragma mark -
254 
255 
256 Desktop::Desktop(uid_t userID)
257 	: MessageLooper("desktop"),
258 
259 	fUserID(userID),
260 	fSettings(new DesktopSettings::Private()),
261 	fApplicationsLock("application list"),
262 	fShutdownSemaphore(-1),
263 	fAllWindows(kAllWindowList),
264 	fSubsetWindows(kSubsetList),
265 	fWorkspacesLayer(NULL),
266 	fActiveScreen(NULL),
267 	fWindowLock("window lock")
268 {
269 	char name[B_OS_NAME_LENGTH];
270 	Desktop::_GetLooperName(name, sizeof(name));
271 
272 	for (int32 i = 0; i < kMaxWorkspaces; i++) {
273 		_Windows(i).SetIndex(i);
274 	}
275 
276 	fMessagePort = create_port(DEFAULT_MONITOR_PORT_SIZE, name);
277 	if (fMessagePort < B_OK)
278 		return;
279 
280 	fLink.SetReceiverPort(fMessagePort);
281 	gFontManager->AttachUser(fUserID);
282 }
283 
284 
285 Desktop::~Desktop()
286 {
287 	delete fSettings;
288 
289 	delete_port(fMessagePort);
290 	gFontManager->DetachUser(fUserID);
291 }
292 
293 
294 void
295 Desktop::Init()
296 {
297 	fVirtualScreen.RestoreConfiguration(*this, fSettings->WorkspacesMessage(0));
298 
299 	// TODO: temporary workaround, fActiveScreen will be removed
300 	fActiveScreen = fVirtualScreen.ScreenAt(0);
301 
302 #if TEST_MODE
303 	gInputManager->AddStream(new InputServerStream);
304 #endif
305 	fEventDispatcher.SetTo(gInputManager->GetStream());
306 	fEventDispatcher.SetHWInterface(fVirtualScreen.HWInterface());
307 
308 	fEventDispatcher.SetMouseFilter(new MouseFilter(this));
309 	fEventDispatcher.SetKeyboardFilter(new KeyboardFilter(this));
310 
311 	// take care of setting the default cursor
312 	ServerCursor *cursor = fCursorManager.GetCursor(B_CURSOR_DEFAULT);
313 	if (cursor)
314 		fVirtualScreen.HWInterface()->SetCursor(cursor);
315 
316 	fVirtualScreen.HWInterface()->MoveCursorTo(fVirtualScreen.Frame().Width() / 2,
317 		fVirtualScreen.Frame().Height() / 2);
318 	fVirtualScreen.HWInterface()->SetCursorVisible(true);
319 
320 	// draw the background
321 
322 	fScreenRegion = fVirtualScreen.Frame();
323 
324 	BRegion stillAvailableOnScreen;
325 	_RebuildClippingForAllWindows(stillAvailableOnScreen);
326 	_SetBackground(stillAvailableOnScreen);
327 }
328 
329 
330 void
331 Desktop::_GetLooperName(char* name, size_t length)
332 {
333 	snprintf(name, length, "d:%d:%s", /*id*/0, /*name*/"baron");
334 }
335 
336 
337 void
338 Desktop::_PrepareQuit()
339 {
340 	// let's kill all remaining applications
341 
342 	fApplicationsLock.Lock();
343 
344 	int32 count = fApplications.CountItems();
345 	for (int32 i = 0; i < count; i++) {
346 		ServerApp *app = fApplications.ItemAt(i);
347 		team_id clientTeam = app->ClientTeam();
348 
349 		app->Quit();
350 		kill_team(clientTeam);
351 	}
352 
353 	// wait for the last app to die
354 	if (count > 0)
355 		acquire_sem_etc(fShutdownSemaphore, fShutdownCount, B_RELATIVE_TIMEOUT, 250000);
356 
357 	fApplicationsLock.Unlock();
358 }
359 
360 
361 void
362 Desktop::_DispatchMessage(int32 code, BPrivate::LinkReceiver &link)
363 {
364 	switch (code) {
365 		case AS_CREATE_APP:
366 		{
367 			// Create the ServerApp to node monitor a new BApplication
368 
369 			// Attached data:
370 			// 1) port_id - receiver port of a regular app
371 			// 2) port_id - client looper port - for sending messages to the client
372 			// 2) team_id - app's team ID
373 			// 3) int32 - handler token of the regular app
374 			// 4) char * - signature of the regular app
375 
376 			// Find the necessary data
377 			team_id	clientTeamID = -1;
378 			port_id	clientLooperPort = -1;
379 			port_id clientReplyPort = -1;
380 			int32 htoken = B_NULL_TOKEN;
381 			char *appSignature = NULL;
382 
383 			link.Read<port_id>(&clientReplyPort);
384 			link.Read<port_id>(&clientLooperPort);
385 			link.Read<team_id>(&clientTeamID);
386 			link.Read<int32>(&htoken);
387 			if (link.ReadString(&appSignature) != B_OK)
388 				break;
389 
390 			ServerApp *app = new ServerApp(this, clientReplyPort,
391 				clientLooperPort, clientTeamID, htoken, appSignature);
392 			if (app->InitCheck() == B_OK
393 				&& app->Run()) {
394 				// add the new ServerApp to the known list of ServerApps
395 				fApplicationsLock.Lock();
396 				fApplications.AddItem(app);
397 				fApplicationsLock.Unlock();
398 			} else {
399 				delete app;
400 
401 				// if everything went well, ServerApp::Run() will notify
402 				// the client - but since it didn't, we do it here
403 				BPrivate::LinkSender reply(clientReplyPort);
404 				reply.StartMessage(SERVER_FALSE);
405 				reply.Flush();
406 			}
407 
408 			// This is necessary because BPortLink::ReadString allocates memory
409 			free(appSignature);
410 			break;
411 		}
412 
413 		case AS_DELETE_APP:
414 		{
415 			// Delete a ServerApp. Received only from the respective ServerApp when a
416 			// BApplication asks it to quit.
417 
418 			// Attached Data:
419 			// 1) thread_id - thread ID of the ServerApp to be deleted
420 
421 			thread_id thread = -1;
422 			if (link.Read<thread_id>(&thread) < B_OK)
423 				break;
424 
425 			fApplicationsLock.Lock();
426 
427 			// Run through the list of apps and nuke the proper one
428 
429 			int32 count = fApplications.CountItems();
430 			ServerApp *removeApp = NULL;
431 
432 			for (int32 i = 0; i < count; i++) {
433 				ServerApp *app = fApplications.ItemAt(i);
434 
435 				if (app != NULL && app->Thread() == thread) {
436 					fApplications.RemoveItemAt(i);
437 					removeApp = app;
438 					break;
439 				}
440 			}
441 
442 			fApplicationsLock.Unlock();
443 
444 			if (removeApp != NULL)
445 				removeApp->Quit(fShutdownSemaphore);
446 
447 			if (fQuitting && count <= 1) {
448 				// wait for the last app to die
449 				acquire_sem_etc(fShutdownSemaphore, fShutdownCount, B_RELATIVE_TIMEOUT, 500000);
450 				PostMessage(kMsgQuitLooper);
451 			}
452 			break;
453 		}
454 
455 		case AS_ACTIVATE_APP:
456 		{
457 			// Someone is requesting to activation of a certain app.
458 
459 			// Attached data:
460 			// 1) port_id reply port
461 			// 2) team_id team
462 
463 			status_t status;
464 
465 			// get the parameters
466 			port_id replyPort;
467 			team_id team;
468 			if (link.Read(&replyPort) == B_OK
469 				&& link.Read(&team) == B_OK)
470 				status = _ActivateApp(team);
471 			else
472 				status = B_ERROR;
473 
474 			// send the reply
475 			BPrivate::PortLink replyLink(replyPort);
476 			replyLink.StartMessage(status);
477 			replyLink.Flush();
478 			break;
479 		}
480 
481 		case AS_SET_SYSCURSOR_DEFAULTS:
482 		{
483 			GetCursorManager().SetDefaults();
484 			break;
485 		}
486 
487 		case B_QUIT_REQUESTED:
488 			// We've been asked to quit, so (for now) broadcast to all
489 			// test apps to quit. This situation will occur only when the server
490 			// is compiled as a regular Be application.
491 
492 			fApplicationsLock.Lock();
493 			fShutdownSemaphore = create_sem(0, "desktop shutdown");
494 			fShutdownCount = fApplications.CountItems();
495 			fApplicationsLock.Unlock();
496 
497 			fQuitting = true;
498 			BroadcastToAllApps(AS_QUIT_APP);
499 
500 			// We now need to process the remaining AS_DELETE_APP messages and
501 			// wait for the kMsgShutdownServer message.
502 			// If an application does not quit as asked, the picasso thread
503 			// will send us this message in 2-3 seconds.
504 
505 			// if there are no apps to quit, shutdown directly
506 			if (fShutdownCount == 0)
507 				PostMessage(kMsgQuitLooper);
508 			break;
509 
510 		default:
511 			printf("Desktop %d:%s received unexpected code %ld\n", 0, "baron", code);
512 
513 			if (link.NeedsReply()) {
514 				// the client is now blocking and waiting for a reply!
515 				fLink.StartMessage(B_ERROR);
516 				fLink.Flush();
517 			}
518 			break;
519 	}
520 }
521 
522 
523 /*!
524 	\brief activate one of the app's windows.
525 */
526 status_t
527 Desktop::_ActivateApp(team_id team)
528 {
529 	status_t status = B_BAD_TEAM_ID;
530 
531 	// search for an unhidden window to give focus to
532 
533 	for (WindowLayer* window = fAllWindows.FirstWindow(); window != NULL;
534 			window = window->NextWindow(kAllWindowList)) {
535 		// if window is a normal window of the team, and not hidden,
536 		// we've found our target
537 		if (!window->IsHidden() && window->IsNormal()
538 			&& window->ServerWindow()->ClientTeam() == team) {
539 			ActivateWindow(window);
540 			return B_OK;
541 		}
542 	}
543 
544 	return status;
545 }
546 
547 
548 /*!
549 	\brief Send a quick (no attachments) message to all applications
550 
551 	Quite useful for notification for things like server shutdown, system
552 	color changes, etc.
553 */
554 void
555 Desktop::BroadcastToAllApps(int32 code)
556 {
557 	BAutolock locker(fApplicationsLock);
558 
559 	for (int32 i = 0; i < fApplications.CountItems(); i++) {
560 		fApplications.ItemAt(i)->PostMessage(code);
561 	}
562 }
563 
564 
565 void
566 Desktop::UpdateWorkspaces()
567 {
568 	// TODO: maybe this should be replaced by a SetWorkspacesCount() method
569 
570 	_WindowChanged(NULL);
571 }
572 
573 
574 void
575 Desktop::SetWorkspace(int32 index)
576 {
577 	LockAllWindows();
578 	DesktopSettings settings(this);
579 
580 	if (index < 0 || index >= settings.WorkspacesCount() || index == fCurrentWorkspace) {
581 		UnlockAllWindows();
582 		return;
583 	}
584 
585 	int32 previousIndex = fCurrentWorkspace;
586 
587 	if (fMouseEventWindow != NULL) {
588 		if (!fMouseEventWindow->InWorkspace(index)) {
589 			// the window currently being dragged will follow us to this workspace
590 			// if it's not already on it
591 			if (fMouseEventWindow->IsNormal()) {
592 				// but only normal windows are following
593 				_Windows(index).AddWindow(fMouseEventWindow);
594 				_Windows(previousIndex).RemoveWindow(fMouseEventWindow);
595 			}
596 		} else {
597 			// make sure it's frontmost
598 			_Windows(index).RemoveWindow(fMouseEventWindow);
599 			_Windows(index).AddWindow(fMouseEventWindow,
600 				fMouseEventWindow->Frontmost(_Windows(index).FirstWindow(), index));
601 		}
602 
603 		fMouseEventWindow->Anchor(index).position = fMouseEventWindow->Frame().LeftTop();
604 	}
605 
606 	// build region of windows that are no longer visible in the new workspace
607 
608 	BRegion dirty;
609 
610 	for (WindowLayer* window = _CurrentWindows().FirstWindow();
611 			window != NULL; window = window->NextWindow(previousIndex)) {
612 		// store current position in Workspace anchor
613 		window->Anchor(previousIndex).position = window->Frame().LeftTop();
614 
615 		if (window->InWorkspace(index))
616 			continue;
617 
618 		if (!window->IsHidden()) {
619 			// this window will no longer be visible
620 			dirty.Include(&window->VisibleRegion());
621 		}
622 
623 		window->SetCurrentWorkspace(-1);
624 	}
625 
626 	fCurrentWorkspace = index;
627 
628 	// show windows, and include them in the changed region - but only
629 	// those that were not visible before (or whose position changed)
630 
631 	for (WindowLayer* window = _Windows(index).FirstWindow();
632 			window != NULL; window = window->NextWindow(index)) {
633 		BPoint position = window->Anchor(index).position;
634 
635 		window->SetCurrentWorkspace(index);
636 
637 		if (window->IsHidden())
638 			continue;
639 
640 		if (position == kInvalidWindowPosition) {
641 			// if you enter a workspace for the first time, the position
642 			// of the window in the previous workspace is adopted
643 			position = window->Frame().LeftTop();
644 				// TODO: make sure the window is still on-screen if it
645 				//	was before!
646 		}
647 
648 		if (!window->InWorkspace(previousIndex)) {
649 			// this window was not visible before
650 			continue;
651 		}
652 
653 		if (window->Frame().LeftTop() != position) {
654 			// the window was visible before, but its on-screen location changed
655 			BPoint offset = position - window->Frame().LeftTop();
656 			MoveWindowBy(window, offset.x, offset.y);
657 				// TODO: be a bit smarter than this...
658 		}
659 	}
660 
661 	BRegion stillAvailableOnScreen;
662 	_RebuildClippingForAllWindows(stillAvailableOnScreen);
663 	_SetBackground(stillAvailableOnScreen);
664 
665 	for (WindowLayer* window = _Windows(index).FirstWindow(); window != NULL;
666 			window = window->NextWindow(index)) {
667 		if (window->InWorkspace(previousIndex) || window == fMouseEventWindow) {
668 			// this window was visible before, and is already handled in the above loop
669 			continue;
670 		}
671 
672 		dirty.Include(&window->VisibleRegion());
673 	}
674 
675 	_UpdateFronts(false);
676 	_UpdateFloating(previousIndex, index);
677 
678 	// Set new focus to the front window, but keep focus to a floating
679 	// window if still visible
680 	if (!_Windows(index).HasWindow(FocusWindow()) || !FocusWindow()->IsFloating())
681 		SetFocusWindow(FrontWindow());
682 
683 	_WindowChanged(NULL);
684 	MarkDirty(dirty);
685 
686 	UnlockAllWindows();
687 }
688 
689 
690 void
691 Desktop::ScreenChanged(Screen* screen)
692 {
693 	// TODO: confirm that everywhere this is used,
694 	// the Window WriteLock is held
695 
696 	// the entire screen is dirty, because we're actually
697 	// operating on an all new buffer in memory
698 	BRegion dirty(screen->Frame());
699 	// update our cached screen region
700 	fScreenRegion.Set(screen->Frame());
701 
702 	BRegion background;
703 	_RebuildClippingForAllWindows(background);
704 
705 	fBackgroundRegion.MakeEmpty();
706 		// makes sure that the complete background is redrawn
707 	_SetBackground(background);
708 
709 	// figure out dirty region
710 	dirty.Exclude(&background);
711 	_TriggerWindowRedrawing(dirty);
712 
713 	// send B_SCREEN_CHANGED to windows on that screen
714 	BMessage update(B_SCREEN_CHANGED);
715 	update.AddInt64("when", real_time_clock_usecs());
716 	update.AddRect("frame", screen->Frame());
717 	update.AddInt32("mode", screen->ColorSpace());
718 
719 	// TODO: currently ignores the screen argument!
720 	for (WindowLayer* window = fAllWindows.FirstWindow(); window != NULL;
721 			window = window->NextWindow(kAllWindowList)) {
722 		window->ServerWindow()->SendMessageToClient(&update);
723 	}
724 }
725 
726 
727 //	#pragma mark - Methods for WindowLayer manipulation
728 
729 
730 WindowList&
731 Desktop::_CurrentWindows()
732 {
733 	return fWorkspaces[fCurrentWorkspace].Windows();
734 }
735 
736 
737 WindowList&
738 Desktop::_Windows(int32 index)
739 {
740 	return fWorkspaces[index].Windows();
741 }
742 
743 
744 void
745 Desktop::_UpdateFloating(int32 previousWorkspace, int32 nextWorkspace)
746 {
747 	if (fFront == NULL)
748 		return;
749 
750 	if (previousWorkspace == -1)
751 		previousWorkspace = fCurrentWorkspace;
752 	if (nextWorkspace == -1)
753 		nextWorkspace = previousWorkspace;
754 
755 	for (WindowLayer* floating = fSubsetWindows.FirstWindow(); floating != NULL;
756 			floating = floating->NextWindow(kSubsetList)) {
757 		// we only care about app/subset floating windows
758 		if (floating->Feel() != B_FLOATING_SUBSET_WINDOW_FEEL
759 			&& floating->Feel() != B_FLOATING_APP_WINDOW_FEEL)
760 			continue;
761 
762 		if (fFront->IsNormal() && floating->HasInSubset(fFront)) {
763 			// is now visible
764 			if (_Windows(previousWorkspace).HasWindow(floating)
765 				&& previousWorkspace != nextWorkspace) {
766 				// but no longer on the previous workspace
767 				_Windows(previousWorkspace).RemoveWindow(floating);
768 				floating->SetCurrentWorkspace(-1);
769 			}
770 			if (!_Windows(nextWorkspace).HasWindow(floating)) {
771 				// but wasn't before
772 				_Windows(nextWorkspace).AddWindow(floating,
773 					floating->Frontmost(_Windows(nextWorkspace).FirstWindow(), nextWorkspace));
774 				floating->SetCurrentWorkspace(nextWorkspace);
775 				_ShowWindow(floating);
776 
777 				// TODO:
778 				// put the floating last in the floating window list to preserve
779 				// the on screen window order
780 			}
781 		} else if (_Windows(previousWorkspace).HasWindow(floating)) {
782 			// was visible, but is no longer
783 			_Windows(previousWorkspace).RemoveWindow(floating);
784 			floating->SetCurrentWorkspace(-1);
785 			_HideWindow(floating);
786 		}
787 	}
788 }
789 
790 
791 /*!
792 	Search the visible windows for a valid back window
793 	(only normal windows can be back windows)
794 */
795 void
796 Desktop::_UpdateBack()
797 {
798 	fBack = NULL;
799 
800 	for (WindowLayer* window = _CurrentWindows().FirstWindow();
801 			window != NULL; window = window->NextWindow(fCurrentWorkspace)) {
802 		if (window->IsHidden() || !window->SupportsFront())
803 			continue;
804 
805 		fBack = window;
806 		break;
807 	}
808 }
809 
810 
811 /*!
812 	Search the visible windows for a valid front window
813 	(only normal and modal windows can be front windows)
814 
815 	The only place where you don't want to update floating windows is
816 	during a workspace change - because then you'll call _UpdateFloating()
817 	yourself.
818 */
819 void
820 Desktop::_UpdateFront(bool updateFloating)
821 {
822 	fFront = NULL;
823 
824 	for (WindowLayer* window = _CurrentWindows().LastWindow();
825 			window != NULL; window = window->PreviousWindow(fCurrentWorkspace)) {
826 		if (window->IsHidden() || window->IsFloating() || !window->SupportsFront())
827 			continue;
828 
829 		fFront = window;
830 		break;
831 	}
832 
833 	if (updateFloating)
834 		_UpdateFloating();
835 }
836 
837 
838 void
839 Desktop::_UpdateFronts(bool updateFloating)
840 {
841 	_UpdateBack();
842 	_UpdateFront(updateFloating);
843 }
844 
845 
846 bool
847 Desktop::_WindowHasModal(WindowLayer* window)
848 {
849 	if (window == NULL || window->IsFloating())
850 		return false;
851 
852 	for (WindowLayer* modal = fSubsetWindows.FirstWindow(); modal != NULL;
853 			modal = modal->NextWindow(kSubsetList)) {
854 		// only visible modal windows count
855 		if (!modal->IsModal() || modal->IsHidden())
856 			continue;
857 
858 		if (modal->HasInSubset(window))
859 			return true;
860 	}
861 
862 	return false;
863 }
864 
865 
866 void
867 Desktop::_WindowChanged(WindowLayer* window)
868 {
869 	if (fWorkspacesLayer == NULL)
870 		return;
871 
872 	fWorkspacesLayer->WindowChanged(window);
873 }
874 
875 
876 void
877 Desktop::SetFocusWindow(WindowLayer* focus)
878 {
879 	if (!LockAllWindows())
880 		return;
881 
882 	bool hasModal = _WindowHasModal(focus);
883 
884 	// TODO: test for FFM and B_LOCK_WINDOW_FOCUS
885 
886 	if (focus == fFocus && focus != NULL && (focus->Flags() & B_AVOID_FOCUS) == 0
887 		&& !hasModal) {
888 		// the window that is supposed to get focus already has focus
889 		UnlockAllWindows();
890 		return;
891 	}
892 
893 	if (focus == NULL || hasModal) {
894 		focus = FrontWindow();
895 		if (focus == NULL) {
896 			// there might be no front window in case of only a single
897 			// window with B_FLOATING_ALL_WINDOW_FEEL
898 			focus = _CurrentWindows().LastWindow();
899 		}
900 	}
901 
902 	// make sure no window is chosen that doesn't want focus or cannot have it
903 	while (focus != NULL
904 		&& ((focus->Flags() & B_AVOID_FOCUS) != 0
905 			|| _WindowHasModal(focus)
906 			|| focus->IsHidden())) {
907 		focus = focus->PreviousWindow(fCurrentWorkspace);
908 	}
909 
910 	if (fFocus != NULL)
911 		fFocus->SetFocus(false);
912 
913 	fFocus = focus;
914 
915 	if (focus != NULL)
916 		focus->SetFocus(true);
917 
918 	UnlockAllWindows();
919 }
920 
921 
922 void
923 Desktop::_BringWindowsToFront(WindowList& windows, int32 list,
924 	bool wereVisible)
925 {
926 	// we don't need to redraw what is currently
927 	// visible of the window
928 	BRegion clean;
929 
930 	for (WindowLayer* window = windows.FirstWindow(); window != NULL;
931 			window = window->NextWindow(list)) {
932 		if (wereVisible)
933 			clean.Include(&window->VisibleRegion());
934 
935 		_CurrentWindows().AddWindow(window,
936 			window->Frontmost(_CurrentWindows().FirstWindow(),
937 				fCurrentWorkspace));
938 
939 		_WindowChanged(window);
940 	}
941 
942 	BRegion dummy;
943 	_RebuildClippingForAllWindows(dummy);
944 
945 	// redraw what became visible of the window(s)
946 
947 	BRegion dirty;
948 	for (WindowLayer* window = windows.FirstWindow(); window != NULL;
949 			window = window->NextWindow(list)) {
950 		dirty.Include(&window->VisibleRegion());
951 	}
952 
953 	dirty.Exclude(&clean);
954 	MarkDirty(dirty);
955 
956 	_UpdateFront();
957 
958 	if (windows.FirstWindow() == fBack || fBack == NULL)
959 		_UpdateBack();
960 }
961 
962 
963 /*!
964 	\brief Tries to move the specified window to the front of the screen,
965 		and make it the focus window.
966 
967 	If there are any modal windows on this screen, it might not actually
968 	become the frontmost window, though, as modal windows stay in front
969 	of their subset.
970 */
971 void
972 Desktop::ActivateWindow(WindowLayer* window)
973 {
974 //	printf("ActivateWindow(%p, %s)\n", window, window ? window->Title() : "<none>");
975 
976 	if (window == NULL) {
977 		fBack = NULL;
978 		fFront = NULL;
979 		return;
980 	}
981 
982 	// TODO: support B_NO_WORKSPACE_ACTIVATION
983 	// TODO: support B_NOT_ANCHORED_ON_ACTIVATE
984 	// TODO: take care about floating windows
985 
986 	if (!LockAllWindows())
987 		return;
988 
989 	if (window == FrontWindow()) {
990 		SetFocusWindow(window);
991 
992 		UnlockAllWindows();
993 		return;
994 	}
995 
996 	// we don't need to redraw what is currently
997 	// visible of the window
998 	BRegion clean(window->VisibleRegion());
999 	WindowList windows(kWorkingList);
1000 
1001 	WindowLayer* frontmost = window->Frontmost();
1002 
1003 	_CurrentWindows().RemoveWindow(window);
1004 	windows.AddWindow(window);
1005 
1006 	if (frontmost != NULL && frontmost->IsModal()) {
1007 		// all modal windows follow their subsets to the front
1008 		// (ie. they are staying in front of them, but they are
1009 		// not supposed to change their order because of that)
1010 
1011 		WindowLayer* nextModal;
1012 		for (WindowLayer* modal = frontmost; modal != NULL; modal = nextModal) {
1013 			// get the next modal window
1014 			nextModal = modal->NextWindow(fCurrentWorkspace);
1015 			while (nextModal != NULL && !nextModal->IsModal()) {
1016 				nextModal = nextModal->NextWindow(fCurrentWorkspace);
1017 			}
1018 			if (nextModal != NULL && !nextModal->HasInSubset(window))
1019 				nextModal = NULL;
1020 
1021 			_CurrentWindows().RemoveWindow(modal);
1022 			windows.AddWindow(modal);
1023 		}
1024 	}
1025 
1026 	_BringWindowsToFront(windows, kWorkingList, true);
1027 	SetFocusWindow(window);
1028 
1029 	UnlockAllWindows();
1030 }
1031 
1032 
1033 void
1034 Desktop::SendWindowBehind(WindowLayer* window, WindowLayer* behindOf)
1035 {
1036 	if (window == BackWindow() || !LockAllWindows())
1037 		return;
1038 
1039 	// Is this a valid behindOf window?
1040 	if (behindOf != NULL && window->HasInSubset(behindOf))
1041 		behindOf = NULL;
1042 
1043 	// what is currently visible of the window
1044 	// might be dirty after the window is send to back
1045 	BRegion dirty(window->VisibleRegion());
1046 
1047 	// detach window and re-attach at desired position
1048 	WindowLayer* backmost = window->Backmost(behindOf);
1049 
1050 	_CurrentWindows().RemoveWindow(window);
1051 	_CurrentWindows().AddWindow(window, backmost
1052 		? backmost->NextWindow(fCurrentWorkspace) : BackWindow());
1053 
1054 	BRegion dummy;
1055 	_RebuildClippingForAllWindows(dummy);
1056 
1057 	// mark everything dirty that is no longer visible
1058 	BRegion clean(window->VisibleRegion());
1059 	dirty.Exclude(&clean);
1060 	MarkDirty(dirty);
1061 
1062 	// TODO: if this window has any floating windows, remove them here
1063 
1064 	_UpdateFronts();
1065 	SetFocusWindow(FrontWindow());
1066 	//_WindowsChanged();
1067 
1068 	UnlockAllWindows();
1069 }
1070 
1071 
1072 void
1073 Desktop::ShowWindow(WindowLayer* window)
1074 {
1075 	if (!window->IsHidden())
1076 		return;
1077 
1078 	LockAllWindows();
1079 
1080 	window->SetHidden(false);
1081 
1082 	if (window->InWorkspace(fCurrentWorkspace)) {
1083 		_ShowWindow(window, true);
1084 		_UpdateSubsetWorkspaces(window);
1085 		ActivateWindow(window);
1086 	} else {
1087 		// then we don't need to send the fake mouse event either
1088 		UnlockAllWindows();
1089 		return;
1090 	}
1091 
1092 	if (WorkspacesLayer* layer = dynamic_cast<WorkspacesLayer*>(window->TopLayer()))
1093 		fWorkspacesLayer = layer;
1094 
1095 	UnlockAllWindows();
1096 
1097 	// If the mouse cursor is directly over the newly visible window,
1098 	// we'll send a fake mouse moved message to the window, so that
1099 	// it knows the mouse is over it.
1100 
1101 	BPoint where;
1102 	int32 buttons;
1103 	EventDispatcher().GetMouse(where, buttons);
1104 
1105 	int32 viewToken = B_NULL_TOKEN;
1106 
1107 	LockAllWindows();
1108 
1109 	if (WindowAt(where) == window) {
1110 		ViewLayer* view = window->ViewAt(where);
1111 		if (view != NULL)
1112 			viewToken = view->Token();
1113 	}
1114 	UnlockAllWindows();
1115 
1116 	if (viewToken != B_NULL_TOKEN)
1117 		EventDispatcher().SendFakeMouseMoved(window->EventTarget(), viewToken);
1118 }
1119 
1120 
1121 void
1122 Desktop::HideWindow(WindowLayer* window)
1123 {
1124 	if (window->IsHidden())
1125 		return;
1126 
1127 	if (!LockAllWindows())
1128 		return;
1129 
1130 	window->SetHidden(true);
1131 
1132 	if (window->InWorkspace(fCurrentWorkspace)) {
1133 		_UpdateSubsetWorkspaces(window);
1134 		_HideWindow(window);
1135 		_UpdateFronts();
1136 
1137 		if (FocusWindow() == window)
1138 			SetFocusWindow(FrontWindow());
1139 	}
1140 
1141 	if (dynamic_cast<WorkspacesLayer*>(window->TopLayer()) != NULL)
1142 		fWorkspacesLayer = NULL;
1143 
1144 	UnlockAllWindows();
1145 }
1146 
1147 
1148 /*!
1149 	Shows the window on the screen - it does this independently of the
1150 	WindowLayer::IsHidden() state.
1151 */
1152 void
1153 Desktop::_ShowWindow(WindowLayer* window, bool affectsOtherWindows)
1154 {
1155 	BRegion background;
1156 	_RebuildClippingForAllWindows(background);
1157 	_SetBackground(background);
1158 	_WindowChanged(window);
1159 
1160 	BRegion dirty(window->VisibleRegion());
1161 
1162 	if (!affectsOtherWindows) {
1163 		// everything that is now visible in the
1164 		// window needs a redraw, but other windows
1165 		// are not affected, we can call ProcessDirtyRegion()
1166 		// of the window, and don't have to use MarkDirty()
1167 		window->ProcessDirtyRegion(dirty);
1168 	} else
1169 		MarkDirty(dirty);
1170 }
1171 
1172 
1173 /*!
1174 	Hides the window from the screen - it does this independently of the
1175 	WindowLayer::IsHidden() state.
1176 */
1177 void
1178 Desktop::_HideWindow(WindowLayer* window)
1179 {
1180 	// after rebuilding the clipping,
1181 	// this window will not have a visible
1182 	// region anymore, so we need to remember
1183 	// it now
1184 	// (actually that's not true, since
1185 	// hidden windows are excluded from the
1186 	// clipping calculation, but anyways)
1187 	BRegion dirty(window->VisibleRegion());
1188 
1189 	BRegion background;
1190 	_RebuildClippingForAllWindows(background);
1191 	_SetBackground(background);
1192 	_WindowChanged(window);
1193 
1194 	MarkDirty(dirty);
1195 }
1196 
1197 
1198 void
1199 Desktop::MoveWindowBy(WindowLayer* window, float x, float y)
1200 {
1201 	if (!LockAllWindows())
1202 		return;
1203 
1204 	// the dirty region starts with the visible area of the window being moved
1205 	BRegion newDirtyRegion(window->VisibleRegion());
1206 
1207 	window->MoveBy(x, y);
1208 
1209 	BRegion background;
1210 	_RebuildClippingForAllWindows(background);
1211 
1212 	// construct the region that is possible to be blitted
1213 	// to move the contents of the window
1214 	BRegion copyRegion(window->VisibleRegion());
1215 	copyRegion.OffsetBy(-x, -y);
1216 	copyRegion.IntersectWith(&newDirtyRegion);
1217 
1218 	// include the the new visible region of the window being
1219 	// moved into the dirty region (for now)
1220 	newDirtyRegion.Include(&window->VisibleRegion());
1221 
1222 	if (GetDrawingEngine()->Lock()) {
1223 		GetDrawingEngine()->CopyRegion(&copyRegion, x, y);
1224 
1225 		// in the dirty region, exclude the parts that we
1226 		// could move by blitting
1227 		copyRegion.OffsetBy(x, y);
1228 		newDirtyRegion.Exclude(&copyRegion);
1229 
1230 		GetDrawingEngine()->Unlock();
1231 	}
1232 
1233 	MarkDirty(newDirtyRegion);
1234 	_SetBackground(background);
1235 	_WindowChanged(window);
1236 
1237 	UnlockAllWindows();
1238 }
1239 
1240 
1241 void
1242 Desktop::ResizeWindowBy(WindowLayer* window, float x, float y)
1243 {
1244 	if (!LockAllWindows())
1245 		return;
1246 
1247 	BRegion newDirtyRegion;
1248 	BRegion previouslyOccupiedRegion(window->VisibleRegion());
1249 
1250 	window->ResizeBy(x, y, &newDirtyRegion);
1251 
1252 	BRegion background;
1253 	_RebuildClippingForAllWindows(background);
1254 
1255 	previouslyOccupiedRegion.Exclude(&window->VisibleRegion());
1256 
1257 	newDirtyRegion.IntersectWith(&window->VisibleRegion());
1258 	newDirtyRegion.Include(&previouslyOccupiedRegion);
1259 
1260 	MarkDirty(newDirtyRegion);
1261 	_SetBackground(background);
1262 	_WindowChanged(window);
1263 
1264 	UnlockAllWindows();
1265 }
1266 
1267 
1268 /*!
1269 	Updates the workspaces of all subset windows with regard to the
1270 	specifed window.
1271 */
1272 void
1273 Desktop::_UpdateSubsetWorkspaces(WindowLayer* window)
1274 {
1275 	// if the window is hidden, the subset windows are up-to-date already
1276 	if (!window->IsNormal() || window->IsHidden())
1277 		return;
1278 
1279 	for (WindowLayer* subset = fSubsetWindows.FirstWindow(); subset != NULL;
1280 			subset = subset->NextWindow(kSubsetList)) {
1281 		if (subset->Feel() == B_MODAL_ALL_WINDOW_FEEL
1282 			|| subset->Feel() == B_FLOATING_ALL_WINDOW_FEEL) {
1283 			// These windows are always visible on all workspaces,
1284 			// no need to update them.
1285 			continue;
1286 		}
1287 
1288 		if (subset->IsFloating()) {
1289 			// Floating windows are inserted and removed to the current
1290 			// workspace as the need arises - they are not handled here
1291 			// but in _UpdateFront()
1292 			continue;
1293 		}
1294 
1295 		if (subset->HasInSubset(window)) {
1296 			// adopt the workspace change
1297 			SetWindowWorkspaces(subset, subset->SubsetWorkspaces());
1298 		}
1299 	}
1300 }
1301 
1302 
1303 /*!
1304 	\brief Adds or removes the window to or from the workspaces it's on.
1305 */
1306 void
1307 Desktop::_ChangeWindowWorkspaces(WindowLayer* window, uint32 oldWorkspaces,
1308 	uint32 newWorkspaces)
1309 {
1310 	// apply changes to the workspaces' window lists
1311 
1312 	LockAllWindows();
1313 
1314 	for (int32 i = 0; i < kMaxWorkspaces; i++) {
1315 		if (workspace_in_workspaces(i, oldWorkspaces)) {
1316 			// window is on this workspace, is it anymore?
1317 			if (!workspace_in_workspaces(i, newWorkspaces)) {
1318 				_Windows(i).RemoveWindow(window);
1319 
1320 				if (i == CurrentWorkspace()) {
1321 					// remove its appearance from the current workspace
1322 					window->SetCurrentWorkspace(-1);
1323 
1324 					if (!window->IsHidden())
1325 						_HideWindow(window);
1326 				}
1327 			}
1328 		} else {
1329 			// window was not on this workspace, is it now?
1330 			if (workspace_in_workspaces(i, newWorkspaces)) {
1331 				_Windows(i).AddWindow(window,
1332 					window->Frontmost(_Windows(i).FirstWindow(), i));
1333 
1334 				if (i == CurrentWorkspace()) {
1335 					// make the window visible in current workspace
1336 					window->SetCurrentWorkspace(fCurrentWorkspace);
1337 
1338 					if (!window->IsHidden()) {
1339 						// this only affects other windows if this windows has floating or
1340 						// modal windows that need to be shown as well
1341 						// TODO: take care of this
1342 						_ShowWindow(window, FrontWindow() == window);
1343 					}
1344 				}
1345 			}
1346 		}
1347 	}
1348 
1349 	// take care about modals and floating windows
1350 	_UpdateSubsetWorkspaces(window);
1351 
1352 	UnlockAllWindows();
1353 }
1354 
1355 
1356 void
1357 Desktop::SetWindowWorkspaces(WindowLayer* window, uint32 workspaces)
1358 {
1359 	LockAllWindows();
1360 
1361 	if (window->IsNormal() && workspaces == B_CURRENT_WORKSPACE)
1362 		workspaces = workspace_to_workspaces(CurrentWorkspace());
1363 
1364 	_ChangeWindowWorkspaces(window, window->Workspaces(), workspaces);
1365 	UnlockAllWindows();
1366 }
1367 
1368 
1369 void
1370 Desktop::AddWindow(WindowLayer *window)
1371 {
1372 	LockAllWindows();
1373 
1374 	fAllWindows.AddWindow(window);
1375 	if (!window->IsNormal())
1376 		fSubsetWindows.AddWindow(window);
1377 
1378 	if (window->IsNormal()) {
1379 		if (window->Workspaces() == B_CURRENT_WORKSPACE)
1380 			window->SetWorkspaces(workspace_to_workspaces(CurrentWorkspace()));
1381 	} else {
1382 		// subset windows are visible on all workspaces their subset is on
1383 		window->SetWorkspaces(window->SubsetWorkspaces());
1384 	}
1385 
1386 	_ChangeWindowWorkspaces(window, 0, window->Workspaces());
1387 	UnlockAllWindows();
1388 }
1389 
1390 
1391 void
1392 Desktop::RemoveWindow(WindowLayer *window)
1393 {
1394 	LockAllWindows();
1395 
1396 	if (!window->IsHidden())
1397 		HideWindow(window);
1398 
1399 	fAllWindows.RemoveWindow(window);
1400 	if (!window->IsNormal())
1401 		fSubsetWindows.RemoveWindow(window);
1402 
1403 	_ChangeWindowWorkspaces(window, window->Workspaces(), 0);
1404 	UnlockAllWindows();
1405 
1406 	// make sure this window won't get any events anymore
1407 
1408 	EventDispatcher().RemoveTarget(window->EventTarget());
1409 }
1410 
1411 
1412 bool
1413 Desktop::AddWindowToSubset(WindowLayer* subset, WindowLayer* window)
1414 {
1415 	if (!subset->AddToSubset(window))
1416 		return false;
1417 
1418 	_ChangeWindowWorkspaces(subset, subset->Workspaces(), subset->SubsetWorkspaces());
1419 	return true;
1420 }
1421 
1422 
1423 void
1424 Desktop::RemoveWindowFromSubset(WindowLayer* subset, WindowLayer* window)
1425 {
1426 	subset->RemoveFromSubset(window);
1427 	_ChangeWindowWorkspaces(subset, subset->Workspaces(), subset->SubsetWorkspaces());
1428 }
1429 
1430 
1431 void
1432 Desktop::SetWindowLook(WindowLayer *window, window_look newLook)
1433 {
1434 	if (window->Look() == newLook)
1435 		return;
1436 
1437 	if (!LockAllWindows())
1438 		return;
1439 
1440 	BRegion dirty;
1441 	window->SetLook(newLook, &dirty);
1442 		// TODO: test what happens when the window
1443 		// finds out it needs to resize itself...
1444 
1445 	BRegion stillAvailableOnScreen;
1446 	_RebuildClippingForAllWindows(stillAvailableOnScreen);
1447 	_SetBackground(stillAvailableOnScreen);
1448 	_WindowChanged(window);
1449 
1450 	_TriggerWindowRedrawing(dirty);
1451 
1452 	UnlockAllWindows();
1453 }
1454 
1455 
1456 void
1457 Desktop::SetWindowFeel(WindowLayer *window, window_feel newFeel)
1458 {
1459 	if (window->Feel() == newFeel)
1460 		return;
1461 
1462 	LockAllWindows();
1463 
1464 	bool wasNormal = window->IsNormal();
1465 
1466 	window->SetFeel(newFeel);
1467 
1468 	// move the window out of or into the subset window list as needed
1469 	if (window->IsNormal() && !wasNormal)
1470 		fSubsetWindows.RemoveWindow(window);
1471 	else if (!window->IsNormal() && wasNormal)
1472 		fSubsetWindows.AddWindow(window);
1473 
1474 	// A normal window that was once a floating or modal window will
1475 	// adopt the window's current workspaces
1476 
1477 	if (!window->IsNormal())
1478 		_ChangeWindowWorkspaces(window, window->Workspaces(), window->SubsetWorkspaces());
1479 
1480 	// make sure the window has the correct position in the window lists
1481 	//	(ie. all floating windows have to be on the top, ...)
1482 
1483 	for (int32 i = 0; i < kMaxWorkspaces; i++) {
1484 		if (!workspace_in_workspaces(i, window->Workspaces()))
1485 			continue;
1486 
1487 		WindowLayer* frontmost = window->Frontmost(_Windows(i).FirstWindow(), i);
1488 		if (frontmost == NULL)
1489 			continue;
1490 
1491 		// check if the frontmost window is really in front of it
1492 
1493 		WindowLayer* next = window->NextWindow(i);
1494 		while (next != NULL) {
1495 			if (next == frontmost)
1496 				break;
1497 
1498 			next = next->NextWindow(i);
1499 		}
1500 
1501 		if (next == NULL) {
1502 			// need to reinsert window behind its frontmost window
1503 			_Windows(i).RemoveWindow(window);
1504 			_Windows(i).AddWindow(window, frontmost);
1505 		}
1506 	}
1507 
1508 	_UpdateFronts();
1509 
1510 	if (window == FocusWindow() && !window->IsVisible())
1511 		SetFocusWindow(FrontWindow());
1512 
1513 	UnlockAllWindows();
1514 }
1515 
1516 
1517 void
1518 Desktop::SetWindowFlags(WindowLayer *window, uint32 newFlags)
1519 {
1520 	if (window->Flags() == newFlags)
1521 		return;
1522 
1523 	if (!LockAllWindows())
1524 		return;
1525 
1526 	BRegion dirty;
1527 	window->SetFlags(newFlags, &dirty);
1528 		// TODO: test what happens when the window
1529 		// finds out it needs to resize itself...
1530 
1531 	BRegion stillAvailableOnScreen;
1532 	_RebuildClippingForAllWindows(stillAvailableOnScreen);
1533 	_SetBackground(stillAvailableOnScreen);
1534 	_WindowChanged(window);
1535 
1536 	_TriggerWindowRedrawing(dirty);
1537 
1538 
1539 	UnlockAllWindows();
1540 }
1541 
1542 
1543 void
1544 Desktop::SetWindowTitle(WindowLayer *window, const char* title)
1545 {
1546 	if (!LockAllWindows())
1547 		return;
1548 
1549 	BRegion dirty;
1550 	window->SetTitle(title, dirty);
1551 
1552 	if (window->IsVisible() && dirty.CountRects() > 0) {
1553 		BRegion stillAvailableOnScreen;
1554 		_RebuildClippingForAllWindows(stillAvailableOnScreen);
1555 		_SetBackground(stillAvailableOnScreen);
1556 
1557 		_TriggerWindowRedrawing(dirty);
1558 	}
1559 
1560 	UnlockAllWindows();
1561 }
1562 
1563 
1564 /*!
1565 	Returns the window under the mouse cursor.
1566 	You need to have the window write lock acquired when calling this method.
1567 */
1568 WindowLayer*
1569 Desktop::WindowAt(BPoint where)
1570 {
1571 	for (WindowLayer* window = _CurrentWindows().LastWindow(); window;
1572 			window = window->PreviousWindow(fCurrentWorkspace)) {
1573 		if (window->VisibleRegion().Contains(where))
1574 			return window;
1575 	}
1576 
1577 	return NULL;
1578 }
1579 
1580 
1581 void
1582 Desktop::SetMouseEventWindow(WindowLayer* window)
1583 {
1584 	fMouseEventWindow = window;
1585 }
1586 
1587 
1588 WindowLayer *
1589 Desktop::FindWindowLayerByClientToken(int32 token, team_id teamID)
1590 {
1591 	LockSingleWindow();
1592 
1593 	for (WindowLayer *window = fAllWindows.FirstWindow(); window != NULL;
1594 			window = window->NextWindow(kAllWindowList)) {
1595 		if (window->ServerWindow()->ClientToken() == token
1596 			&& window->ServerWindow()->ClientTeam() == teamID) {
1597 			UnlockSingleWindow();
1598 			return window;
1599 		}
1600 	}
1601 
1602 	UnlockSingleWindow();
1603 	return NULL;
1604 }
1605 
1606 
1607 void
1608 Desktop::WriteWindowList(team_id team, BPrivate::LinkSender& sender)
1609 {
1610 	BAutolock locker(fWindowLock);
1611 
1612 	// compute the number of windows
1613 
1614 	int32 count = 0;
1615 
1616 	for (WindowLayer *window = fAllWindows.FirstWindow(); window != NULL;
1617 			window = window->NextWindow(kAllWindowList)) {
1618 		if (team < B_OK || window->ServerWindow()->ClientTeam() == team)
1619 			count++;
1620 	}
1621 
1622 	// write list
1623 
1624 	sender.StartMessage(B_OK);
1625 	sender.Attach<int32>(count);
1626 
1627 	for (WindowLayer *window = fAllWindows.FirstWindow(); window != NULL;
1628 			window = window->NextWindow(kAllWindowList)) {
1629 		if (team >= B_OK && window->ServerWindow()->ClientTeam() != team)
1630 			continue;
1631 
1632 		sender.Attach<int32>(window->ServerWindow()->ServerToken());
1633 	}
1634 
1635 	sender.Flush();
1636 }
1637 
1638 
1639 void
1640 Desktop::WriteWindowInfo(int32 serverToken, BPrivate::LinkSender& sender)
1641 {
1642 	BAutolock locker(fWindowLock);
1643 	BAutolock tokenLocker(BPrivate::gDefaultTokens);
1644 
1645 	::ServerWindow* window;
1646 	if (BPrivate::gDefaultTokens.GetToken(serverToken,
1647 			B_SERVER_TOKEN, (void**)&window) != B_OK) {
1648 		sender.StartMessage(B_ENTRY_NOT_FOUND);
1649 		sender.Flush();
1650 		return;
1651 	}
1652 
1653 	window_info info;
1654 	window->GetInfo(info);
1655 
1656 	int32 length = window->Title() ? strlen(window->Title()) : 0;
1657 
1658 	sender.StartMessage(B_OK);
1659 	sender.Attach<int32>(sizeof(window_info) + length + 1);
1660 	sender.Attach(&info, sizeof(window_info));
1661 	if (length > 0)
1662 		sender.Attach(window->Title(), length + 1);
1663 	else
1664 		sender.Attach<char>('\0');
1665 	sender.Flush();
1666 }
1667 
1668 
1669 void
1670 Desktop::MarkDirty(BRegion& region)
1671 {
1672 	if (region.CountRects() == 0)
1673 		return;
1674 
1675 	if (LockAllWindows()) {
1676 		// send redraw messages to all windows intersecting the dirty region
1677 		_TriggerWindowRedrawing(region);
1678 
1679 		UnlockAllWindows();
1680 	}
1681 }
1682 
1683 
1684 void
1685 Desktop::_RebuildClippingForAllWindows(BRegion& stillAvailableOnScreen)
1686 {
1687 	// the available region on screen starts with the entire screen area
1688 	// each window on the screen will take a portion from that area
1689 
1690 	// figure out what the entire screen area is
1691 	stillAvailableOnScreen = fScreenRegion;
1692 
1693 	// set clipping of each window
1694 	for (WindowLayer* window = _CurrentWindows().LastWindow(); window != NULL;
1695 			window = window->PreviousWindow(fCurrentWorkspace)) {
1696 		if (!window->IsHidden()) {
1697 			window->SetClipping(&stillAvailableOnScreen);
1698 			// that windows region is not available on screen anymore
1699 			stillAvailableOnScreen.Exclude(&window->VisibleRegion());
1700 		}
1701 	}
1702 }
1703 
1704 
1705 void
1706 Desktop::_TriggerWindowRedrawing(BRegion& newDirtyRegion)
1707 {
1708 	// send redraw messages to all windows intersecting the dirty region
1709 	for (WindowLayer* window = _CurrentWindows().LastWindow(); window != NULL;
1710 			window = window->PreviousWindow(fCurrentWorkspace)) {
1711 		if (!window->IsHidden()
1712 			&& newDirtyRegion.Intersects(window->VisibleRegion().Frame()))
1713 			window->ProcessDirtyRegion(newDirtyRegion);
1714 	}
1715 }
1716 
1717 
1718 void
1719 Desktop::_SetBackground(BRegion& background)
1720 {
1721 	// NOTE: the drawing operation is caried out
1722 	// in the clipping region rebuild, but it is
1723 	// ok actually, because it also avoids trails on
1724 	// moving windows
1725 
1726 	// remember the region not covered by any windows
1727 	// and redraw the dirty background
1728 	BRegion dirtyBackground(background);
1729 	dirtyBackground.Exclude(&fBackgroundRegion);
1730 	dirtyBackground.IntersectWith(&background);
1731 	fBackgroundRegion = background;
1732 	if (dirtyBackground.Frame().IsValid()) {
1733 		if (GetDrawingEngine()->Lock()) {
1734 			GetDrawingEngine()->ConstrainClippingRegion(NULL);
1735 			GetDrawingEngine()->FillRegion(dirtyBackground,
1736 				fWorkspaces[fCurrentWorkspace].Color());
1737 
1738 			GetDrawingEngine()->Unlock();
1739 		}
1740 	}
1741 }
1742