xref: /haiku/src/servers/app/Desktop.cpp (revision 4909eb2091b751e2cee8eac11ecabf3315544a99)
1 /*
2  * Copyright 2001-2020, Haiku.
3  * Distributed under the terms of the MIT License.
4  *
5  * Authors:
6  *		Adrian Oanca, adioanca@cotty.iren.ro
7  *		Stephan Aßmus, superstippi@gmx.de
8  *		Axel Dörfler, axeld@pinc-software.de
9  *		Andrej Spielmann, andrej.spielmann@seh.ox.ac.uk
10  *		Brecht Machiels, brecht@mos6581.org
11  *		Clemens Zeidler, haiku@clemens-zeidler.de
12  *		Ingo Weinhold, ingo_weinhold@gmx.de
13  *		Joseph Groover, looncraz@looncraz.net
14  *		Tri-Edge AI
15  *		Jacob Secunda, secundja@gmail.com
16  */
17 
18 
19 /*!	Class used to encapsulate desktop management */
20 
21 
22 #include "Desktop.h"
23 
24 #include <stdio.h>
25 #include <string.h>
26 #include <syslog.h>
27 
28 #include <Debug.h>
29 #include <debugger.h>
30 #include <DirectWindow.h>
31 #include <Entry.h>
32 #include <FindDirectory.h>
33 #include <Message.h>
34 #include <MessageFilter.h>
35 #include <Path.h>
36 #include <Region.h>
37 #include <Roster.h>
38 
39 #include <PrivateScreen.h>
40 #include <ServerProtocol.h>
41 #include <ViewPrivate.h>
42 #include <WindowInfo.h>
43 
44 #include "AppServer.h"
45 #include "ClickTarget.h"
46 #include "DecorManager.h"
47 #include "DesktopSettingsPrivate.h"
48 #include "DrawingEngine.h"
49 #include "GlobalFontManager.h"
50 #include "HWInterface.h"
51 #include "InputManager.h"
52 #include "Screen.h"
53 #include "ScreenManager.h"
54 #include "ServerApp.h"
55 #include "ServerConfig.h"
56 #include "ServerCursor.h"
57 #include "ServerWindow.h"
58 #include "SystemPalette.h"
59 #include "WindowPrivate.h"
60 #include "Window.h"
61 #include "Workspace.h"
62 #include "WorkspacesView.h"
63 
64 #if TEST_MODE
65 #	include "EventStream.h"
66 #endif
67 
68 
69 //#define DEBUG_DESKTOP
70 #ifdef DEBUG_DESKTOP
71 #	define STRACE(a) printf a
72 #else
73 #	define STRACE(a) ;
74 #endif
75 
76 
77 static inline float
square_vector_length(float x,float y)78 square_vector_length(float x, float y)
79 {
80 	return x * x + y * y;
81 }
82 
83 
84 static inline float
square_distance(const BPoint & a,const BPoint & b)85 square_distance(const BPoint& a, const BPoint& b)
86 {
87 	return square_vector_length(a.x - b.x, a.y - b.y);
88 }
89 
90 
91 class KeyboardFilter : public EventFilter {
92 	public:
93 		KeyboardFilter(Desktop* desktop);
94 
95 		virtual filter_result Filter(BMessage* message, EventTarget** _target,
96 			int32* _viewToken, BMessage* latestMouseMoved);
97 		virtual void RemoveTarget(EventTarget* target);
98 
99 	private:
100 		void _UpdateFocus(int32 key, uint32 modifiers, EventTarget** _target);
101 
102 		Desktop*		fDesktop;
103 		EventTarget*	fLastFocus;
104 		bigtime_t		fTimestamp;
105 };
106 
107 
108 class MouseFilter : public EventFilter {
109 public:
110 	MouseFilter(Desktop* desktop);
111 
112 	virtual filter_result Filter(BMessage* message, EventTarget** _target,
113 		int32* _viewToken, BMessage* latestMouseMoved);
114 
115 private:
116 	Desktop*	fDesktop;
117 	int32		fLastClickButtons;
118 	int32		fLastClickModifiers;
119 	int32		fResetClickCount;
120 	BPoint		fLastClickPoint;
121 	ClickTarget	fLastClickTarget;
122 };
123 
124 
125 //	#pragma mark -
126 
127 
KeyboardFilter(Desktop * desktop)128 KeyboardFilter::KeyboardFilter(Desktop* desktop)
129 	:
130 	fDesktop(desktop),
131 	fLastFocus(NULL),
132 	fTimestamp(0)
133 {
134 }
135 
136 
137 void
_UpdateFocus(int32 key,uint32 modifiers,EventTarget ** _target)138 KeyboardFilter::_UpdateFocus(int32 key, uint32 modifiers, EventTarget** _target)
139 {
140 	if (!fDesktop->LockSingleWindow())
141 		return;
142 
143 	EventTarget* focus = fDesktop->KeyboardEventTarget();
144 
145 #if 0
146 	bigtime_t now = system_time();
147 
148 	// TODO: this is a try to not steal focus from the current window
149 	//	in case you enter some text and a window pops up you haven't
150 	//	triggered yourself (like a pop-up window in your browser while
151 	//	you're typing a password in another window) - maybe this should
152 	//	be done differently, though (using something like B_LOCK_WINDOW_FOCUS)
153 	//	(at least B_WINDOW_ACTIVATED must be postponed)
154 
155 	if (fLastFocus == NULL
156 		|| (focus != fLastFocus && now - fTimestamp > 100000)) {
157 		// if the time span between the key presses is very short
158 		// we keep our previous focus alive - this is safe even
159 		// if the target doesn't exist anymore, as we don't reset
160 		// it, and the event focus passed in is always valid (or NULL)
161 		*_target = focus;
162 		fLastFocus = focus;
163 	}
164 #endif
165 	*_target = focus;
166 	fLastFocus = focus;
167 
168 	fDesktop->UnlockSingleWindow();
169 
170 #if 0
171 	// we always allow to switch focus after the enter key has pressed
172 	if (key == B_ENTER || modifiers == B_COMMAND_KEY
173 		|| modifiers == B_CONTROL_KEY || modifiers == B_OPTION_KEY)
174 		fTimestamp = 0;
175 	else
176 		fTimestamp = now;
177 #endif
178 }
179 
180 
181 filter_result
Filter(BMessage * message,EventTarget ** _target,int32 *,BMessage *)182 KeyboardFilter::Filter(BMessage* message, EventTarget** _target,
183 	int32* /*_viewToken*/, BMessage* /*latestMouseMoved*/)
184 {
185 	int32 key = 0;
186 	int32 modifiers = 0;
187 
188 	message->FindInt32("key", &key);
189 	message->FindInt32("modifiers", &modifiers);
190 
191 	if ((message->what == B_KEY_DOWN || message->what == B_UNMAPPED_KEY_DOWN)) {
192 		// Check for safe video mode (shift + cmd + ctrl + escape)
193 		if (key == 0x01 && (modifiers & B_COMMAND_KEY) != 0
194 			&& (modifiers & B_CONTROL_KEY) != 0
195 			&& (modifiers & B_SHIFT_KEY) != 0) {
196 			system("screenmode --fall-back &");
197 			return B_SKIP_MESSAGE;
198 		}
199 
200 		bool takeWindow = (modifiers & B_SHIFT_KEY) != 0
201 			|| fDesktop->MouseEventWindow() != NULL;
202 		if (key >= B_F1_KEY && key <= B_F12_KEY) {
203 			// workspace change
204 
205 #if !TEST_MODE
206 			if ((modifiers & (B_COMMAND_KEY | B_CONTROL_KEY | B_OPTION_KEY))
207 					== B_COMMAND_KEY)
208 #else
209 			if ((modifiers & B_CONTROL_KEY) != 0)
210 #endif
211 			{
212 				STRACE(("Set Workspace %" B_PRId32 "\n", key - 1));
213 
214 				fDesktop->SetWorkspaceAsync(key - B_F1_KEY, takeWindow);
215 				return B_SKIP_MESSAGE;
216 			}
217 		} else if (key == 0x11
218 			&& (modifiers & (B_COMMAND_KEY | B_CONTROL_KEY | B_OPTION_KEY))
219 					== B_COMMAND_KEY) {
220 			// switch to previous workspace (command + `)
221 			fDesktop->SetWorkspaceAsync(-1, takeWindow);
222 			return B_SKIP_MESSAGE;
223 		}
224 	}
225 
226 	if (message->what == B_KEY_DOWN
227 		|| message->what == B_MODIFIERS_CHANGED
228 		|| message->what == B_UNMAPPED_KEY_DOWN
229 		|| message->what == B_INPUT_METHOD_EVENT)
230 		_UpdateFocus(key, modifiers, _target);
231 
232 	return fDesktop->KeyEvent(message->what, key, modifiers);
233 }
234 
235 
236 void
RemoveTarget(EventTarget * target)237 KeyboardFilter::RemoveTarget(EventTarget* target)
238 {
239 	if (target == fLastFocus)
240 		fLastFocus = NULL;
241 }
242 
243 
244 //	#pragma mark -
245 
246 
MouseFilter(Desktop * desktop)247 MouseFilter::MouseFilter(Desktop* desktop)
248 	:
249 	fDesktop(desktop),
250 	fLastClickButtons(0),
251 	fLastClickModifiers(0),
252 	fResetClickCount(0),
253 	fLastClickPoint(),
254 	fLastClickTarget()
255 {
256 }
257 
258 
259 filter_result
Filter(BMessage * message,EventTarget ** _target,int32 * _viewToken,BMessage * latestMouseMoved)260 MouseFilter::Filter(BMessage* message, EventTarget** _target, int32* _viewToken,
261 	BMessage* latestMouseMoved)
262 {
263 	BPoint where;
264 	if (message->FindPoint("where", &where) != B_OK)
265 		return B_DISPATCH_MESSAGE;
266 
267 	int32 buttons;
268 	if (message->FindInt32("buttons", &buttons) != B_OK)
269 		buttons = 0;
270 
271 	if (!fDesktop->LockAllWindows())
272 		return B_DISPATCH_MESSAGE;
273 
274 	int32 viewToken = B_NULL_TOKEN;
275 
276 	Window* window = fDesktop->MouseEventWindow();
277 	if (window == NULL)
278 		window = fDesktop->WindowAt(where);
279 
280 	if (window != NULL) {
281 		// dispatch event to the window
282 		switch (message->what) {
283 			case B_MOUSE_DOWN:
284 			{
285 				int32 windowToken = window->ServerWindow()->ServerToken();
286 
287 				// First approximation of click count validation. We reset the
288 				// click count when modifiers or pressed buttons have changed
289 				// or when we've got a different click target, or when the
290 				// previous click location is too far from the new one. We can
291 				// only check the window of the click target here; we'll recheck
292 				// after asking the window.
293 				int32 modifiers = message->FindInt32("modifiers");
294 
295 				int32 originalClickCount = message->FindInt32("clicks");
296 				if (originalClickCount <= 0)
297 					originalClickCount = 1;
298 
299 				int32 clickCount = originalClickCount;
300 				if (clickCount > 1) {
301 					if (modifiers != fLastClickModifiers
302 						|| buttons != fLastClickButtons
303 						|| !fLastClickTarget.IsValid()
304 						|| fLastClickTarget.WindowToken() != windowToken
305 						|| square_distance(where, fLastClickPoint) >= 16
306 						|| clickCount - fResetClickCount < 1) {
307 						clickCount = 1;
308 					} else
309 						clickCount -= fResetClickCount;
310 				}
311 
312 				// notify the window
313 				ClickTarget clickTarget;
314 				window->MouseDown(message, where, fLastClickTarget, clickCount,
315 					clickTarget);
316 
317 				// If the click target changed, always reset the click count.
318 				if (clickCount != 1 && clickTarget != fLastClickTarget)
319 					clickCount = 1;
320 
321 				// update our click count management attributes
322 				fResetClickCount = originalClickCount - clickCount;
323 				fLastClickTarget = clickTarget;
324 				fLastClickButtons = buttons;
325 				fLastClickModifiers = modifiers;
326 				fLastClickPoint = where;
327 
328 				// get the view token from the click target
329 				if (clickTarget.GetType() == ClickTarget::TYPE_WINDOW_CONTENTS)
330 					viewToken = clickTarget.WindowElement();
331 
332 				// update the message's "clicks" field, if necessary
333 				if (clickCount != originalClickCount) {
334 					if (message->HasInt32("clicks"))
335 						message->ReplaceInt32("clicks", clickCount);
336 					else
337 						message->AddInt32("clicks", clickCount);
338 				}
339 
340 				// notify desktop listeners
341 				fDesktop->NotifyMouseDown(window, message, where);
342 				break;
343 			}
344 
345 			case B_MOUSE_UP:
346 				window->MouseUp(message, where, &viewToken);
347 				if (buttons == 0)
348 					fDesktop->SetMouseEventWindow(NULL);
349 				fDesktop->NotifyMouseUp(window, message, where);
350 				break;
351 
352 			case B_MOUSE_MOVED:
353 				window->MouseMoved(message, where, &viewToken,
354 					latestMouseMoved == NULL || latestMouseMoved == message,
355 					false);
356 				fDesktop->NotifyMouseMoved(window, message, where);
357 				break;
358 		}
359 
360 		if (viewToken != B_NULL_TOKEN) {
361 			fDesktop->SetViewUnderMouse(window, viewToken);
362 
363 			*_viewToken = viewToken;
364 			*_target = &window->EventTarget();
365 		}
366 	} else if (message->what == B_MOUSE_DOWN) {
367 		// the mouse-down didn't hit a window -- reset the click target
368 		fResetClickCount = 0;
369 		fLastClickTarget = ClickTarget();
370 		fLastClickButtons = message->FindInt32("buttons");
371 		fLastClickModifiers = message->FindInt32("modifiers");
372 		fLastClickPoint = where;
373 	}
374 
375 	if (window == NULL || viewToken == B_NULL_TOKEN) {
376 		// mouse is not over a window or over a decorator
377 		fDesktop->SetViewUnderMouse(window, B_NULL_TOKEN);
378 		fDesktop->SetCursor(NULL);
379 
380 		*_target = NULL;
381 	}
382 
383 	fDesktop->SetLastMouseState(where, buttons, window);
384 
385 	fDesktop->NotifyMouseEvent(message);
386 
387 	fDesktop->UnlockAllWindows();
388 
389 	return B_DISPATCH_MESSAGE;
390 }
391 
392 
393 //	#pragma mark -
394 
395 
396 static inline uint32
workspace_to_workspaces(int32 index)397 workspace_to_workspaces(int32 index)
398 {
399 	return 1UL << index;
400 }
401 
402 
403 static inline bool
workspace_in_workspaces(int32 index,uint32 workspaces)404 workspace_in_workspaces(int32 index, uint32 workspaces)
405 {
406 	return (workspaces & (1UL << index)) != 0;
407 }
408 
409 
410 //	#pragma mark -
411 
412 
Desktop(uid_t userID,const char * targetScreen)413 Desktop::Desktop(uid_t userID, const char* targetScreen)
414 	:
415 	MessageLooper("desktop"),
416 
417 	fUserID(userID),
418 	fTargetScreen(strdup(targetScreen)),
419 	fSettings(NULL),
420 	fSharedReadOnlyArea(-1),
421 	fApplicationsLock("application list"),
422 	fShutdownSemaphore(-1),
423 	fShutdownCount(0),
424 	fScreenLock("screen lock"),
425 	fDirectScreenLock("direct screen lock"),
426 	fDirectScreenTeam(-1),
427 	fCurrentWorkspace(0),
428 	fPreviousWorkspace(0),
429 	fAllWindows(kAllWindowList),
430 	fSubsetWindows(kSubsetList),
431 	fFocusList(kFocusList),
432 	fWorkspacesViews(false),
433 
434 	fWorkspacesLock("workspaces list"),
435 	fWindowLock("window lock"),
436 
437 	fMouseEventWindow(NULL),
438 	fWindowUnderMouse(NULL),
439 	fLockedFocusWindow(NULL),
440 	fViewUnderMouse(B_NULL_TOKEN),
441 	fLastMousePosition(B_ORIGIN),
442 	fLastMouseButtons(0),
443 
444 	fFocus(NULL),
445 	fFront(NULL),
446 	fBack(NULL)
447 {
448 	memset(fLastWorkspaceFocus, 0, sizeof(fLastWorkspaceFocus));
449 
450 	char name[B_OS_NAME_LENGTH];
451 	Desktop::_GetLooperName(name, sizeof(name));
452 
453 	fMessagePort = create_port(DEFAULT_MONITOR_PORT_SIZE, name);
454 	if (fMessagePort < B_OK)
455 		return;
456 
457 	fLink.SetReceiverPort(fMessagePort);
458 
459 	// register listeners
460 	RegisterListener(&fStackAndTile);
461 
462 	const DesktopListenerList& newListeners
463 		= gDecorManager.GetDesktopListeners();
464 	for (int i = 0; i < newListeners.CountItems(); i++)
465  		RegisterListener(newListeners.ItemAt(i));
466 }
467 
468 
~Desktop()469 Desktop::~Desktop()
470 {
471 	delete_area(fSharedReadOnlyArea);
472 	delete_port(fMessagePort);
473 
474 	free(fTargetScreen);
475 }
476 
477 
478 void
RegisterListener(DesktopListener * listener)479 Desktop::RegisterListener(DesktopListener* listener)
480 {
481 	DesktopObservable::RegisterListener(listener, this);
482 }
483 
484 
485 /*!	This method is allowed to throw exceptions.
486 */
487 status_t
Init()488 Desktop::Init()
489 {
490 	if (fMessagePort < B_OK)
491 		return fMessagePort;
492 
493 	// the system palette needs to be initialized before the
494 	// desktop settings, since it is used there already
495 	InitializeColorMap();
496 
497 	const size_t areaSize = B_PAGE_SIZE;
498 	char name[B_OS_NAME_LENGTH];
499 	snprintf(name, sizeof(name), "d:%d:shared read only", fUserID);
500 	fSharedReadOnlyArea = create_area(name, (void **)&fServerReadOnlyMemory,
501 		B_ANY_ADDRESS, areaSize, B_NO_LOCK, B_READ_AREA | B_WRITE_AREA | B_CLONEABLE_AREA);
502 	if (fSharedReadOnlyArea < B_OK)
503 		return fSharedReadOnlyArea;
504 
505 	fSettings.SetTo(new DesktopSettingsPrivate(fServerReadOnlyMemory));
506 
507 	for (int32 i = 0; i < kMaxWorkspaces; i++) {
508 		_Windows(i).SetIndex(i);
509 		fWorkspaces[i].RestoreConfiguration(*fSettings->WorkspacesMessage(i));
510 	}
511 
512 	status_t status = fVirtualScreen.SetConfiguration(*this,
513 		fWorkspaces[0].CurrentScreenConfiguration());
514 	if (status != B_OK) {
515 		debug_printf("app_server: Failed to initialize virtual screen configuration: %s\n",
516 			strerror(status));
517 		return status;
518 	}
519 
520 	if (fVirtualScreen.HWInterface() == NULL) {
521 		debug_printf("Could not initialize graphics output. Exiting.\n");
522 		return B_ERROR;
523 	}
524 
525 	// now that the mode is set, see if we should increase the default font size
526 	if (fSettings->DefaultPlainFont() == *gFontManager->DefaultPlainFont()
527 			&& !fSettings->DidLoadFontSettings()) {
528 		float fontSize = fSettings->DefaultPlainFont().Size();
529 		gScreenManager->Lock();
530 		Screen* screen = gScreenManager->ScreenAt(0);
531 		if (screen != NULL) {
532 			display_mode mode;
533 			screen->GetMode(mode);
534 
535 			if (mode.virtual_width > 3840 && mode.virtual_height > 2160)
536 				fontSize *= 2.0f;
537 			else if (mode.virtual_width > 1920 && mode.virtual_height > 1080)
538 				fontSize *= 1.5f;
539 		}
540 		gScreenManager->Unlock();
541 
542 		// modify settings without saving them
543 		const_cast<ServerFont&>(fSettings->DefaultPlainFont()).SetSize(fontSize);
544 		const_cast<ServerFont&>(fSettings->DefaultBoldFont()).SetSize(fontSize);
545 		const_cast<ServerFont&>(fSettings->DefaultFixedFont()).SetSize(fontSize);
546 		const_cast<menu_info&>(fSettings->MenuInfo()).font_size = fontSize;
547 	}
548 
549 	HWInterface()->SetDPMSMode(B_DPMS_ON);
550 
551 	float brightness = fWorkspaces[0].StoredScreenConfiguration().Brightness(0);
552 	if (brightness > 0)
553 		HWInterface()->SetBrightness(brightness);
554 
555 	fVirtualScreen.HWInterface()->MoveCursorTo(
556 		fVirtualScreen.Frame().Width() / 2,
557 		fVirtualScreen.Frame().Height() / 2);
558 
559 #if TEST_MODE
560 	gInputManager->AddStream(new InputServerStream);
561 #endif
562 
563 	EventStream* stream = fVirtualScreen.HWInterface()->CreateEventStream();
564 	if (stream == NULL)
565 		stream = gInputManager->GetStream();
566 
567 	fEventDispatcher.SetDesktop(this);
568 	fEventDispatcher.SetTo(stream);
569 	if (fEventDispatcher.InitCheck() != B_OK)
570 		_LaunchInputServer();
571 
572 	fEventDispatcher.SetHWInterface(fVirtualScreen.HWInterface());
573 
574 	fEventDispatcher.SetMouseFilter(new MouseFilter(this));
575 	fEventDispatcher.SetKeyboardFilter(new KeyboardFilter(this));
576 
577 	// draw the background
578 
579 	fScreenRegion = fVirtualScreen.Frame();
580 
581 	BRegion stillAvailableOnScreen;
582 	_RebuildClippingForAllWindows(stillAvailableOnScreen);
583 	_SetBackground(stillAvailableOnScreen);
584 
585 	SetCursor(NULL);
586 		// this will set the default cursor
587 
588 	fVirtualScreen.HWInterface()->SetCursorVisible(true);
589 
590 	return B_OK;
591 }
592 
593 
594 /*!	\brief Send a quick (no attachments) message to all applications.
595 
596 	Quite useful for notification for things like server shutdown, system
597 	color changes, etc.
598 */
599 void
BroadcastToAllApps(int32 code)600 Desktop::BroadcastToAllApps(int32 code)
601 {
602 	BAutolock locker(fApplicationsLock);
603 
604 	for (int32 i = fApplications.CountItems(); i-- > 0;) {
605 		fApplications.ItemAt(i)->PostMessage(code);
606 	}
607 }
608 
609 
610 /*!	\brief Send a quick (no attachments) message to all windows.
611 */
612 void
BroadcastToAllWindows(int32 code)613 Desktop::BroadcastToAllWindows(int32 code)
614 {
615 	AutoReadLocker _(fWindowLock);
616 
617 	for (Window* window = fAllWindows.FirstWindow(); window != NULL;
618 			window = window->NextWindow(kAllWindowList)) {
619 		window->ServerWindow()->PostMessage(code);
620 	}
621 }
622 
623 
624 int32
GetAllWindowTargets(DelayedMessage & message)625 Desktop::GetAllWindowTargets(DelayedMessage& message)
626 {
627 	AutoReadLocker _(fWindowLock);
628 	int32 count = 0;
629 
630 	for (Window* window = fAllWindows.FirstWindow(); window != NULL;
631 			window = window->NextWindow(kAllWindowList)) {
632 		message.AddTarget(window->ServerWindow()->MessagePort());
633 		++count;
634 	}
635 
636 	return count;
637 }
638 
639 
640 int32
GetAllAppTargets(DelayedMessage & message)641 Desktop::GetAllAppTargets(DelayedMessage& message)
642 {
643 	BAutolock _(fApplicationsLock);
644 
645 	for (int32 index = 0; index < fApplications.CountItems(); ++index)
646 		message.AddTarget(fApplications.ItemAt(index)->MessagePort());
647 
648 	return fApplications.CountItems();
649 }
650 
651 
652 filter_result
KeyEvent(uint32 what,int32 key,int32 modifiers)653 Desktop::KeyEvent(uint32 what, int32 key, int32 modifiers)
654 {
655 	filter_result result = B_DISPATCH_MESSAGE;
656 	if (LockAllWindows()) {
657 		Window* window = MouseEventWindow();
658 		if (window == NULL)
659 			window = WindowAt(fLastMousePosition);
660 
661 		if (window != NULL) {
662 			if (what == B_MODIFIERS_CHANGED)
663 				window->ModifiersChanged(modifiers);
664 		}
665 
666 		if (NotifyKeyPressed(what, key, modifiers))
667 			result = B_SKIP_MESSAGE;
668 
669 		UnlockAllWindows();
670 	}
671 
672 	return result;
673 }
674 
675 
676 // #pragma mark - Mouse and cursor methods
677 
678 
679 void
SetCursor(ServerCursor * newCursor)680 Desktop::SetCursor(ServerCursor* newCursor)
681 {
682 	if (newCursor == NULL)
683 		newCursor = fCursorManager.GetCursor(B_CURSOR_ID_SYSTEM_DEFAULT);
684 
685 	if (newCursor == fCursor)
686 		return;
687 
688 	fCursor.SetTo(newCursor, false);
689 
690 	if (!fManagementCursor.IsSet())
691 		HWInterface()->SetCursor(newCursor);
692 }
693 
694 
695 ServerCursorReference
Cursor() const696 Desktop::Cursor() const
697 {
698 	return fCursor;
699 }
700 
701 
702 void
SetManagementCursor(ServerCursor * newCursor)703 Desktop::SetManagementCursor(ServerCursor* newCursor)
704 {
705 	if (newCursor == fManagementCursor)
706 		return;
707 
708 	fManagementCursor.SetTo(newCursor, false);
709 
710 	HWInterface()->SetCursor(newCursor != NULL ? newCursor : fCursor.Get());
711 }
712 
713 
714 void
SetLastMouseState(const BPoint & position,int32 buttons,Window * windowUnderMouse)715 Desktop::SetLastMouseState(const BPoint& position, int32 buttons,
716 	Window* windowUnderMouse)
717 {
718 	// The all-window-lock is write-locked.
719 	fLastMousePosition = position;
720 	fLastMouseButtons = buttons;
721 
722 	if (fLastMouseButtons == 0 && fLockedFocusWindow) {
723 		fLockedFocusWindow = NULL;
724 		if (fSettings->FocusFollowsMouse())
725 			SetFocusWindow(windowUnderMouse);
726 	}
727 }
728 
729 
730 void
GetLastMouseState(BPoint * position,int32 * buttons) const731 Desktop::GetLastMouseState(BPoint* position, int32* buttons) const
732 {
733 	*position = fLastMousePosition;
734 	*buttons = fLastMouseButtons;
735 }
736 
737 
738 //	#pragma mark - Screen methods
739 
740 
741 status_t
SetScreenMode(int32 workspace,int32 id,const display_mode & mode,bool makeDefault)742 Desktop::SetScreenMode(int32 workspace, int32 id, const display_mode& mode,
743 	bool makeDefault)
744 {
745 	AutoWriteLocker _(fWindowLock);
746 
747 	if (workspace == B_CURRENT_WORKSPACE_INDEX)
748 		workspace = fCurrentWorkspace;
749 
750 	if (workspace < 0 || workspace >= kMaxWorkspaces)
751 		return B_BAD_VALUE;
752 
753 	Screen* screen = fVirtualScreen.ScreenByID(id);
754 	if (screen == NULL)
755 		return B_NAME_NOT_FOUND;
756 
757 	// Check if the mode has actually changed
758 
759 	if (workspace == fCurrentWorkspace) {
760 		// retrieve from current screen
761 		display_mode oldMode;
762 		screen->GetMode(oldMode);
763 
764 		if (!memcmp(&oldMode, &mode, sizeof(display_mode)))
765 			return B_OK;
766 
767 		// Set the new one
768 
769 		_SuspendDirectFrameBufferAccess();
770 
771 		AutoWriteLocker locker(fScreenLock);
772 
773 		status_t status = screen->SetMode(mode);
774 		if (status != B_OK) {
775 			locker.Unlock();
776 
777 			_ResumeDirectFrameBufferAccess();
778 			return status;
779 		}
780 	} else {
781 		// retrieve from settings
782 		screen_configuration* configuration
783 			= fWorkspaces[workspace].CurrentScreenConfiguration().CurrentByID(
784 				screen->ID());
785 		if (configuration != NULL
786 			&& !memcmp(&configuration->mode, &mode, sizeof(display_mode)))
787 			return B_OK;
788 	}
789 
790 	// Update our configurations
791 
792 	monitor_info info;
793 	bool hasInfo = screen->GetMonitorInfo(info) == B_OK;
794 
795 	fWorkspaces[workspace].CurrentScreenConfiguration().Set(id,
796 		hasInfo ? &info : NULL, screen->Frame(), mode);
797 	if (makeDefault) {
798 		fWorkspaces[workspace].StoredScreenConfiguration().Set(id,
799 			hasInfo ? &info : NULL, screen->Frame(), mode);
800 		StoreWorkspaceConfiguration(workspace);
801 	}
802 
803 	_ScreenChanged(screen);
804 	if (workspace == fCurrentWorkspace)
805 		_ResumeDirectFrameBufferAccess();
806 
807 	return B_OK;
808 }
809 
810 
811 status_t
GetScreenMode(int32 workspace,int32 id,display_mode & mode)812 Desktop::GetScreenMode(int32 workspace, int32 id, display_mode& mode)
813 {
814 	AutoReadLocker _(fScreenLock);
815 
816 	if (workspace == B_CURRENT_WORKSPACE_INDEX)
817 		workspace = fCurrentWorkspace;
818 
819 	if (workspace < 0 || workspace >= kMaxWorkspaces)
820 		return B_BAD_VALUE;
821 
822 	if (workspace == fCurrentWorkspace) {
823 		// retrieve from current screen
824 		Screen* screen = fVirtualScreen.ScreenByID(id);
825 		if (screen == NULL)
826 			return B_NAME_NOT_FOUND;
827 
828 		screen->GetMode(mode);
829 		return B_OK;
830 	}
831 
832 	// retrieve from settings
833 	screen_configuration* configuration
834 		= fWorkspaces[workspace].CurrentScreenConfiguration().CurrentByID(id);
835 	if (configuration == NULL)
836 		return B_NAME_NOT_FOUND;
837 
838 	mode = configuration->mode;
839 	return B_OK;
840 }
841 
842 
843 status_t
GetScreenFrame(int32 workspace,int32 id,BRect & frame)844 Desktop::GetScreenFrame(int32 workspace, int32 id, BRect& frame)
845 {
846 	AutoReadLocker _(fScreenLock);
847 
848 	if (workspace == B_CURRENT_WORKSPACE_INDEX)
849 		workspace = fCurrentWorkspace;
850 
851 	if (workspace < 0 || workspace >= kMaxWorkspaces)
852 		return B_BAD_VALUE;
853 
854 	if (workspace == fCurrentWorkspace) {
855 		// retrieve from current screen
856 		Screen* screen = fVirtualScreen.ScreenByID(id);
857 		if (screen == NULL)
858 			return B_NAME_NOT_FOUND;
859 
860 		frame = screen->Frame();
861 		return B_OK;
862 	}
863 
864 	// retrieve from settings
865 	screen_configuration* configuration
866 		= fWorkspaces[workspace].CurrentScreenConfiguration().CurrentByID(id);
867 	if (configuration == NULL)
868 		return B_NAME_NOT_FOUND;
869 
870 	frame = configuration->frame;
871 	return B_OK;
872 }
873 
874 
875 void
RevertScreenModes(uint32 workspaces)876 Desktop::RevertScreenModes(uint32 workspaces)
877 {
878 	if (workspaces == 0)
879 		return;
880 
881 	AutoWriteLocker _(fWindowLock);
882 
883 	for (int32 workspace = 0; workspace < kMaxWorkspaces; workspace++) {
884 		if ((workspaces & (1U << workspace)) == 0)
885 			continue;
886 
887 		// Revert all screens on this workspace
888 
889 		// TODO: ideally, we would know which screens to revert - this way, too
890 		// many of them could be reverted
891 
892 		for (int32 index = 0; index < fVirtualScreen.CountScreens(); index++) {
893 			Screen* screen = fVirtualScreen.ScreenAt(index);
894 
895 			// retrieve configurations
896 			screen_configuration* stored = fWorkspaces[workspace]
897 				.StoredScreenConfiguration().CurrentByID(screen->ID());
898 			screen_configuration* current = fWorkspaces[workspace]
899 				.CurrentScreenConfiguration().CurrentByID(screen->ID());
900 
901 			if ((stored != NULL && current != NULL
902 					&& !memcmp(&stored->mode, &current->mode,
903 							sizeof(display_mode)))
904 				|| (stored == NULL && current == NULL))
905 				continue;
906 
907 			if (stored == NULL) {
908 				fWorkspaces[workspace].CurrentScreenConfiguration()
909 					.Remove(current);
910 
911 				if (workspace == fCurrentWorkspace) {
912 					_SuspendDirectFrameBufferAccess();
913 					_SetCurrentWorkspaceConfiguration();
914 					_ResumeDirectFrameBufferAccess();
915 				}
916 			} else
917 				SetScreenMode(workspace, screen->ID(), stored->mode, false);
918 		}
919 	}
920 }
921 
922 
923 status_t
SetBrightness(int32 id,float brightness)924 Desktop::SetBrightness(int32 id, float brightness)
925 {
926 	status_t result = HWInterface()->SetBrightness(brightness);
927 
928 	if (result == B_OK) {
929 		if (fWorkspaces[0].StoredScreenConfiguration().CurrentByID(id) == NULL) {
930 			// store the current configuration if empty
931 			screen_configuration* current
932 				= fWorkspaces[0].CurrentScreenConfiguration().CurrentByID(id);
933 			fWorkspaces[0].StoredScreenConfiguration().Set(id,
934 				current->has_info ? &current->info : NULL, current->frame, current->mode);
935 		}
936 		fWorkspaces[0].StoredScreenConfiguration().SetBrightness(id,
937 			brightness);
938 		// Save brightness for next boot
939 		StoreWorkspaceConfiguration(0);
940 	}
941 
942 	return result;
943 }
944 
945 
946 status_t
LockDirectScreen(team_id team)947 Desktop::LockDirectScreen(team_id team)
948 {
949 	// TODO: BWindowScreens should use the same mechanism as BDirectWindow,
950 	// which would make this method superfluous.
951 
952 	status_t status = fDirectScreenLock.LockWithTimeout(1000000L);
953 	if (status == B_OK)
954 		fDirectScreenTeam = team;
955 
956 	return status;
957 }
958 
959 
960 status_t
UnlockDirectScreen(team_id team)961 Desktop::UnlockDirectScreen(team_id team)
962 {
963 	if (fDirectScreenTeam == team) {
964 		fDirectScreenLock.Unlock();
965 		fDirectScreenTeam = -1;
966 		return B_OK;
967 	}
968 
969 	return B_PERMISSION_DENIED;
970 }
971 
972 
973 // #pragma mark - Workspaces methods
974 
975 
976 /*!	Changes the current workspace to the one specified by \a index.
977 */
978 void
SetWorkspaceAsync(int32 index,bool moveFocusWindow)979 Desktop::SetWorkspaceAsync(int32 index, bool moveFocusWindow)
980 {
981 	BPrivate::LinkSender link(MessagePort());
982 	link.StartMessage(AS_ACTIVATE_WORKSPACE);
983 	link.Attach<int32>(index);
984 	link.Attach<bool>(moveFocusWindow);
985 	link.Flush();
986 }
987 
988 
989 /*!	Changes the current workspace to the one specified by \a index.
990 	You must not hold any window lock when calling this method.
991 */
992 void
SetWorkspace(int32 index,bool moveFocusWindow)993 Desktop::SetWorkspace(int32 index, bool moveFocusWindow)
994 {
995 	LockAllWindows();
996 	DesktopSettings settings(this);
997 
998 	if (index < 0 || index >= settings.WorkspacesCount()
999 		|| index == fCurrentWorkspace) {
1000 		UnlockAllWindows();
1001 		return;
1002 	}
1003 
1004 	_SetWorkspace(index, moveFocusWindow);
1005 	UnlockAllWindows();
1006 
1007 	_SendFakeMouseMoved();
1008 }
1009 
1010 
1011 status_t
SetWorkspacesLayout(int32 newColumns,int32 newRows)1012 Desktop::SetWorkspacesLayout(int32 newColumns, int32 newRows)
1013 {
1014 	int32 newCount = newColumns * newRows;
1015 	if (newCount < 1 || newCount > kMaxWorkspaces)
1016 		return B_BAD_VALUE;
1017 
1018 	if (!LockAllWindows())
1019 		return B_ERROR;
1020 
1021 	fSettings->SetWorkspacesLayout(newColumns, newRows);
1022 
1023 	// either update the workspaces window, or switch to
1024 	// the last available workspace - which will update
1025 	// the workspaces window automatically
1026 	bool workspaceChanged = CurrentWorkspace() >= newCount;
1027 	if (workspaceChanged)
1028 		_SetWorkspace(newCount - 1);
1029 	else
1030 		_WindowChanged(NULL);
1031 
1032 	UnlockAllWindows();
1033 
1034 	if (workspaceChanged)
1035 		_SendFakeMouseMoved();
1036 
1037 	return B_OK;
1038 }
1039 
1040 
1041 /*!	Returns the virtual screen frame of the workspace specified by \a index.
1042 */
1043 BRect
WorkspaceFrame(int32 index) const1044 Desktop::WorkspaceFrame(int32 index) const
1045 {
1046 	BRect frame;
1047 	if (index == fCurrentWorkspace)
1048 		frame = fVirtualScreen.Frame();
1049 	else if (index >= 0 && index < fSettings->WorkspacesCount()) {
1050 		BMessage screenData;
1051 		if (fSettings->WorkspacesMessage(index)->FindMessage("screen",
1052 				&screenData) != B_OK
1053 			|| screenData.FindRect("frame", &frame) != B_OK) {
1054 			frame = fVirtualScreen.Frame();
1055 		}
1056 	}
1057 
1058 	return frame;
1059 }
1060 
1061 
1062 /*!	\brief Stores the workspace configuration.
1063 	You must hold the window lock when calling this method.
1064 */
1065 void
StoreWorkspaceConfiguration(int32 index)1066 Desktop::StoreWorkspaceConfiguration(int32 index)
1067 {
1068 	// Retrieve settings
1069 
1070 	BMessage settings;
1071 	fWorkspaces[index].StoreConfiguration(settings);
1072 
1073 	// and store them
1074 
1075 	fSettings->SetWorkspacesMessage(index, settings);
1076 	fSettings->Save(kWorkspacesSettings);
1077 }
1078 
1079 
1080 void
AddWorkspacesView(WorkspacesView * view)1081 Desktop::AddWorkspacesView(WorkspacesView* view)
1082 {
1083 	if (view->Window() == NULL || view->Window()->IsHidden())
1084 		return;
1085 
1086 	BAutolock _(fWorkspacesLock);
1087 
1088 	if (!fWorkspacesViews.HasItem(view))
1089 		fWorkspacesViews.AddItem(view);
1090 }
1091 
1092 
1093 void
RemoveWorkspacesView(WorkspacesView * view)1094 Desktop::RemoveWorkspacesView(WorkspacesView* view)
1095 {
1096 	BAutolock _(fWorkspacesLock);
1097 	fWorkspacesViews.RemoveItem(view);
1098 }
1099 
1100 
1101 //	#pragma mark - Methods for Window manipulation
1102 
1103 
1104 /*!	\brief Activates or focusses the window based on the pointer position.
1105 */
1106 void
SelectWindow(Window * window)1107 Desktop::SelectWindow(Window* window)
1108 {
1109 	if (fSettings->ClickToFocusMouse()) {
1110 		// Only bring the window to front when it is not the window under the
1111 		// mouse pointer. This should result in sensible behaviour.
1112 		if (window != fWindowUnderMouse
1113 			|| (window == fWindowUnderMouse && window != FocusWindow()))
1114 			ActivateWindow(window);
1115 		else
1116 			SetFocusWindow(window);
1117 	} else
1118 		ActivateWindow(window);
1119 }
1120 
1121 
1122 /*!	\brief Tries to move the specified window to the front of the screen,
1123 		and make it the focus window.
1124 
1125 	If there are any modal windows on this screen, it might not actually
1126 	become the frontmost window, though, as modal windows stay in front
1127 	of their subset.
1128 */
1129 void
ActivateWindow(Window * window)1130 Desktop::ActivateWindow(Window* window)
1131 {
1132 	STRACE(("ActivateWindow(%p, %s)\n", window, window
1133 		? window->Title() : "<none>"));
1134 
1135 	if (window == NULL) {
1136 		fBack = NULL;
1137 		fFront = NULL;
1138 		return;
1139 	}
1140 	if (window->Workspaces() == 0 && window->IsNormal())
1141 		return;
1142 
1143 	AutoWriteLocker allWindowLocker(fWindowLock);
1144 
1145 	NotifyWindowActivated(window);
1146 
1147 	bool windowOnOtherWorkspace = !window->InWorkspace(fCurrentWorkspace);
1148 	if (windowOnOtherWorkspace
1149 		&& (window->Flags() & B_NOT_ANCHORED_ON_ACTIVATE) == 0) {
1150 		if ((window->Flags() & B_NO_WORKSPACE_ACTIVATION) == 0) {
1151 			// Switch to the workspace on which this window is
1152 			// (we'll take the first one that the window is on)
1153 			uint32 workspaces = window->Workspaces();
1154 			for (int32 i = 0; i < fSettings->WorkspacesCount(); i++) {
1155 				uint32 workspace = workspace_to_workspaces(i);
1156 				if (workspaces & workspace) {
1157 					SetWorkspace(i);
1158 					windowOnOtherWorkspace = false;
1159 					break;
1160 				}
1161 			}
1162 		} else
1163 			return;
1164 	}
1165 
1166 	if (windowOnOtherWorkspace) {
1167 		if (!window->IsNormal()) {
1168 			// Bring a window to front that this floating window belongs to
1169 			Window* front = _LastFocusSubsetWindow(window);
1170 			if (front == NULL) {
1171 				// We can't do anything about those.
1172 				return;
1173 			}
1174 
1175 			ActivateWindow(front);
1176 
1177 			if (!window->InWorkspace(fCurrentWorkspace)) {
1178 				// This window can't be made active
1179 				return;
1180 			}
1181 		} else {
1182 			// Bring the window to the current workspace
1183 			// TODO: what if this window is on multiple workspaces?!?
1184 			uint32 workspaces = workspace_to_workspaces(fCurrentWorkspace);
1185 			SetWindowWorkspaces(window, workspaces);
1186 		}
1187 	}
1188 
1189 	if (window->IsMinimized()) {
1190 		// Unlike WindowAction(), this is called from the application itself,
1191 		// so we will just unminimize the window here.
1192 		window->SetMinimized(false);
1193 		ShowWindow(window);
1194 	}
1195 
1196 	if (window == FrontWindow()) {
1197 		// see if there is a normal B_AVOID_FRONT window still in front of us
1198 		Window* avoidsFront = window->NextWindow(fCurrentWorkspace);
1199 		while (avoidsFront && avoidsFront->IsNormal()
1200 			&& (avoidsFront->Flags() & B_AVOID_FRONT) == 0) {
1201 			avoidsFront = avoidsFront->NextWindow(fCurrentWorkspace);
1202 		}
1203 
1204 		if (avoidsFront == NULL) {
1205 			// we're already the frontmost window, we might just not have focus
1206 			// yet
1207 			if ((window->Flags() & B_AVOID_FOCUS) == 0)
1208 				SetFocusWindow(window);
1209 			return;
1210 		}
1211 	}
1212 
1213 	WindowList windows(kWorkingList);
1214 	Window* frontmost = window->Frontmost();
1215 	const Window* lastWindowUnderMouse = fWindowUnderMouse;
1216 
1217 	CurrentWindows().RemoveWindow(window);
1218 	windows.AddWindow(window);
1219 	window->MoveToTopStackLayer();
1220 
1221 	if (frontmost != NULL && frontmost->IsModal()) {
1222 		// all modal windows follow their subsets to the front
1223 		// (ie. they are staying in front of them, but they are
1224 		// not supposed to change their order because of that)
1225 
1226 		Window* nextModal;
1227 		for (Window* modal = frontmost; modal != NULL; modal = nextModal) {
1228 			// get the next modal window
1229 			nextModal = modal->NextWindow(fCurrentWorkspace);
1230 			while (nextModal != NULL && !nextModal->IsModal()) {
1231 				nextModal = nextModal->NextWindow(fCurrentWorkspace);
1232 			}
1233 			if (nextModal != NULL && !nextModal->HasInSubset(window))
1234 				nextModal = NULL;
1235 
1236 			CurrentWindows().RemoveWindow(modal);
1237 			windows.AddWindow(modal);
1238 		}
1239 	}
1240 
1241 	_BringWindowsToFront(windows, kWorkingList, true);
1242 
1243 	if ((window->Flags() & B_AVOID_FOCUS) == 0)
1244 		SetFocusWindow(window);
1245 
1246 	bool sendFakeMouseMoved = _CheckSendFakeMouseMoved(lastWindowUnderMouse);
1247 
1248 	allWindowLocker.Unlock();
1249 
1250 	if (sendFakeMouseMoved)
1251 		_SendFakeMouseMoved();
1252 }
1253 
1254 
1255 void
SendWindowBehind(Window * window,Window * behindOf,bool sendStack)1256 Desktop::SendWindowBehind(Window* window, Window* behindOf, bool sendStack)
1257 {
1258 	if (!LockAllWindows())
1259 		return;
1260 
1261 	Window* orgWindow = window;
1262 	WindowStack* stack = window->GetWindowStack();
1263 	if (sendStack && stack != NULL)
1264 		window = stack->TopLayerWindow();
1265 
1266 	// TODO: should the "not in current workspace" be handled anyway?
1267 	//	(the code below would have to be changed then, though)
1268 	if (window == BackWindow()
1269 		|| !window->InWorkspace(fCurrentWorkspace)
1270 		|| (behindOf != NULL && !behindOf->InWorkspace(fCurrentWorkspace))) {
1271 		UnlockAllWindows();
1272 		return;
1273 	}
1274 
1275 	// Is this a valid behindOf window?
1276 	if (behindOf != NULL && window->HasInSubset(behindOf))
1277 		behindOf = NULL;
1278 
1279 	// what is currently visible of the window
1280 	// might be dirty after the window is send to back
1281 	BRegion dirty(window->VisibleRegion());
1282 
1283 	Window* backmost = window->Backmost(behindOf);
1284 	const Window* lastWindowUnderMouse = fWindowUnderMouse;
1285 
1286 	CurrentWindows().RemoveWindow(window);
1287 	CurrentWindows().AddWindow(window, backmost
1288 		? backmost->NextWindow(fCurrentWorkspace) : BackWindow());
1289 
1290 	BRegion dummy;
1291 	_RebuildClippingForAllWindows(dummy);
1292 
1293 	// only redraw the top layer window to avoid flicker
1294 	if (sendStack) {
1295 		// mark everything dirty that is no longer visible
1296 		BRegion clean(window->VisibleRegion());
1297 		dirty.Exclude(&clean);
1298 		MarkDirty(dirty);
1299 	}
1300 
1301 	_UpdateFronts();
1302 	if (fSettings->FocusFollowsMouse())
1303 		SetFocusWindow(WindowAt(fLastMousePosition));
1304 	else if (fSettings->NormalMouse())
1305 		SetFocusWindow(NULL);
1306 
1307 	_WindowChanged(window);
1308 
1309 	if (sendStack && stack != NULL) {
1310 		for (int32 i = 0; i < stack->CountWindows(); i++) {
1311 			Window* stackWindow = stack->LayerOrder().ItemAt(i);
1312 			if (stackWindow == window)
1313 				continue;
1314 			SendWindowBehind(stackWindow, behindOf, false);
1315 		}
1316 	}
1317 
1318 	bool sendFakeMouseMoved = _CheckSendFakeMouseMoved(lastWindowUnderMouse);
1319 	NotifyWindowSentBehind(orgWindow, behindOf);
1320 
1321 	UnlockAllWindows();
1322 
1323 	if (sendFakeMouseMoved)
1324 		_SendFakeMouseMoved();
1325 }
1326 
1327 
1328 void
ShowWindow(Window * window)1329 Desktop::ShowWindow(Window* window)
1330 {
1331 	if (!window->IsHidden())
1332 		return;
1333 
1334 	AutoWriteLocker locker(fWindowLock);
1335 
1336 	window->SetHidden(false);
1337 	fFocusList.AddWindow(window);
1338 
1339 	// If the window is on the current workspace, we'll show it. Special
1340 	// handling for floating windows, as they can only be shown if their
1341 	// subset is.
1342 	if (window->InWorkspace(fCurrentWorkspace)
1343 		|| (window->IsFloating() && _LastFocusSubsetWindow(window) != NULL)) {
1344 		_ShowWindow(window, true);
1345 		_UpdateSubsetWorkspaces(window);
1346 		ActivateWindow(window);
1347 	} else {
1348 		// then we don't need to send the fake mouse event either
1349 		_WindowChanged(window);
1350 		return;
1351 	}
1352 
1353 	if (window->HasWorkspacesViews()) {
1354 		// find workspaces views in view hierarchy
1355 		BAutolock _(fWorkspacesLock);
1356 		window->FindWorkspacesViews(fWorkspacesViews);
1357 	}
1358 
1359 	// If the mouse cursor is directly over the newly visible window,
1360 	// we'll send a fake mouse moved message to the window, so that
1361 	// it knows the mouse is over it.
1362 
1363 	_SendFakeMouseMoved(window);
1364 }
1365 
1366 
1367 void
HideWindow(Window * window,bool fromMinimize)1368 Desktop::HideWindow(Window* window, bool fromMinimize)
1369 {
1370 	if (window->IsHidden())
1371 		return;
1372 
1373 	if (!LockAllWindows())
1374 		return;
1375 
1376 	window->SetHidden(true);
1377 	fFocusList.RemoveWindow(window);
1378 
1379 	if (fMouseEventWindow == window) {
1380 		// Make its decorator lose the current mouse action
1381 		BMessage message;
1382 		int32 viewToken;
1383 		window->MouseUp(&message, fLastMousePosition, &viewToken);
1384 
1385 		fMouseEventWindow = NULL;
1386 	}
1387 
1388 	if (fLockedFocusWindow == window) {
1389 		// Remove the focus lock so the focus can be changed below
1390 		fLockedFocusWindow = NULL;
1391 	}
1392 
1393 	if (window->InWorkspace(fCurrentWorkspace)) {
1394 		_UpdateSubsetWorkspaces(window);
1395 		_HideWindow(window);
1396 		_UpdateFronts();
1397 	} else
1398 		_WindowChanged(window);
1399 
1400 	if (FocusWindow() == window)
1401 		SetFocusWindow();
1402 
1403 	_WindowRemoved(window);
1404 
1405 	if (window->HasWorkspacesViews()) {
1406 		// remove workspaces views from this window
1407 		BObjectList<WorkspacesView> list(false);
1408 		window->FindWorkspacesViews(list);
1409 
1410 		BAutolock _(fWorkspacesLock);
1411 
1412 		while (WorkspacesView* view = list.RemoveItemAt(0)) {
1413 			fWorkspacesViews.RemoveItem(view);
1414 		}
1415 	}
1416 
1417 	NotifyWindowHidden(window, fromMinimize);
1418 
1419 	UnlockAllWindows();
1420 
1421 	if (window == fWindowUnderMouse)
1422 		_SendFakeMouseMoved();
1423 }
1424 
1425 
1426 void
MinimizeWindow(Window * window,bool minimize)1427 Desktop::MinimizeWindow(Window* window, bool minimize)
1428 {
1429 	if (!LockAllWindows())
1430 		return;
1431 
1432 	if (minimize && !window->IsHidden()) {
1433 		HideWindow(window, true);
1434 		window->SetMinimized(minimize);
1435 		NotifyWindowMinimized(window, minimize);
1436 	} else if (!minimize && window->IsHidden()) {
1437 		ActivateWindow(window);
1438 			// this will unminimize the window for us
1439 		NotifyWindowMinimized(window, minimize);
1440 	}
1441 
1442 	UnlockAllWindows();
1443 }
1444 
1445 
1446 void
MoveWindowBy(Window * window,float x,float y,int32 workspace)1447 Desktop::MoveWindowBy(Window* window, float x, float y, int32 workspace)
1448 {
1449 	if (x == 0 && y == 0)
1450 		return;
1451 
1452 	AutoWriteLocker _(fWindowLock);
1453 
1454 	Window* topWindow = window->TopLayerStackWindow();
1455 	if (topWindow != NULL)
1456 		window = topWindow;
1457 
1458 	if (workspace == -1)
1459 		workspace = fCurrentWorkspace;
1460 	if (!window->IsVisible() || workspace != fCurrentWorkspace) {
1461 		if (workspace != fCurrentWorkspace) {
1462 			WindowStack* stack = window->GetWindowStack();
1463 			if (stack != NULL) {
1464 				for (int32 s = 0; s < stack->CountWindows(); s++) {
1465 					Window* stackWindow = stack->WindowAt(s);
1466 					// move the window on another workspace - this doesn't
1467 					// change it's current position
1468 					if (stackWindow->Anchor(workspace).position
1469 						== kInvalidWindowPosition) {
1470 						stackWindow->Anchor(workspace).position
1471 							= stackWindow->Frame().LeftTop();
1472 					}
1473 
1474 					stackWindow->Anchor(workspace).position += BPoint(x, y);
1475 					stackWindow->SetCurrentWorkspace(workspace);
1476 					_WindowChanged(stackWindow);
1477 				}
1478 			}
1479 		} else
1480 			window->MoveBy((int32)x, (int32)y);
1481 
1482 		NotifyWindowMoved(window);
1483 		return;
1484 	}
1485 
1486 	// the dirty region starts with the visible area of the window being moved
1487 	BRegion newDirtyRegion(window->VisibleRegion());
1488 
1489 	// stop direct frame buffer access
1490 	bool direct = false;
1491 	if (window->ServerWindow()->IsDirectlyAccessing()) {
1492 		window->ServerWindow()->HandleDirectConnection(B_DIRECT_STOP);
1493 		direct = true;
1494 	}
1495 
1496 	window->MoveBy((int32)x, (int32)y);
1497 
1498 	BRegion background;
1499 	_RebuildClippingForAllWindows(background);
1500 
1501 	// construct the region that is possible to be blitted
1502 	// to move the contents of the window
1503 	BRegion copyRegion(window->VisibleRegion());
1504 	copyRegion.OffsetBy((int32)-x, (int32)-y);
1505 	copyRegion.IntersectWith(&newDirtyRegion);
1506 		// newDirtyRegion == the windows old visible region
1507 
1508 	// include the the new visible region of the window being
1509 	// moved into the dirty region (for now)
1510 	newDirtyRegion.Include(&window->VisibleRegion());
1511 
1512 	// NOTE: Having all windows locked should prevent any
1513 	// problems with locking the drawing engine here.
1514 	if (GetDrawingEngine()->LockParallelAccess()) {
1515 		GetDrawingEngine()->CopyRegion(&copyRegion, (int32)x, (int32)y);
1516 		GetDrawingEngine()->UnlockParallelAccess();
1517 	}
1518 
1519 	// in the dirty region, exclude the parts that we
1520 	// could move by blitting
1521 	copyRegion.OffsetBy((int32)x, (int32)y);
1522 	newDirtyRegion.Exclude(&copyRegion);
1523 
1524 	MarkDirty(newDirtyRegion);
1525 	_SetBackground(background);
1526 	_WindowChanged(window);
1527 
1528 	// resume direct frame buffer access
1529 	if (direct) {
1530 		// TODO: the clipping actually only changes when we move our window
1531 		// off screen, or behind some other window
1532 		window->ServerWindow()->HandleDirectConnection(
1533 			B_DIRECT_START | B_BUFFER_MOVED | B_CLIPPING_MODIFIED);
1534 	}
1535 
1536 	NotifyWindowMoved(window);
1537 }
1538 
1539 
1540 void
ResizeWindowBy(Window * window,float x,float y)1541 Desktop::ResizeWindowBy(Window* window, float x, float y)
1542 {
1543 	if (x == 0 && y == 0)
1544 		return;
1545 
1546 	AutoWriteLocker _(fWindowLock);
1547 
1548 	Window* topWindow = window->TopLayerStackWindow();
1549 	if (topWindow)
1550 		window = topWindow;
1551 
1552 	if (!window->IsVisible()) {
1553 		window->ResizeBy((int32)x, (int32)y, NULL);
1554 		NotifyWindowResized(window);
1555 		return;
1556 	}
1557 
1558 	// The dirty region for the inside of the window is constructed by the window itself in
1559 	// ResizeBy()
1560 	BRegion newDirtyRegion;
1561 	// Track the dirty region outside the window in case it is shrunk in "previouslyOccupiedRegion"
1562 	BRegion previouslyOccupiedRegion(window->VisibleRegion());
1563 	// Track the region that was drawn in previous update sessions, so we can compute the newly
1564 	// exposed areas by excluding this from the update region.
1565 	BRegion previousVisibleContentRegion(window->VisibleContentRegion());
1566 
1567 	// stop direct frame buffer access
1568 	bool direct = false;
1569 	if (window->ServerWindow()->IsDirectlyAccessing()) {
1570 		window->ServerWindow()->HandleDirectConnection(B_DIRECT_STOP);
1571 		direct = true;
1572 	}
1573 
1574 	window->ResizeBy((int32)x, (int32)y, &newDirtyRegion);
1575 
1576 	BRegion background;
1577 	_RebuildClippingForAllWindows(background);
1578 
1579 	// we just care for the region outside the window
1580 	previouslyOccupiedRegion.Exclude(&window->VisibleRegion());
1581 
1582 	// make sure the window cannot mark stuff dirty outside
1583 	// its visible region...
1584 	newDirtyRegion.IntersectWith(&window->VisibleRegion());
1585 	// ...because we do this ourselves
1586 	newDirtyRegion.Include(&previouslyOccupiedRegion);
1587 
1588 	// calculate old expose region as window visible region difference
1589 	BRegion exposeRegion(previouslyOccupiedRegion);
1590 	exposeRegion.Exclude(&window->VisibleRegion());
1591 	// ...and new expose region as window content visible region difference
1592 	BRegion tmp(window->VisibleContentRegion());
1593 	tmp.Exclude(&previousVisibleContentRegion);
1594 	exposeRegion.Include(&tmp);
1595 
1596 	MarkDirty(newDirtyRegion, exposeRegion);
1597 	_SetBackground(background);
1598 	_WindowChanged(window);
1599 
1600 	// resume direct frame buffer access
1601 	if (direct) {
1602 		window->ServerWindow()->HandleDirectConnection(
1603 			B_DIRECT_START | B_BUFFER_RESIZED | B_CLIPPING_MODIFIED);
1604 	}
1605 
1606 	NotifyWindowResized(window);
1607 }
1608 
1609 
1610 void
SetWindowOutlinesDelta(Window * window,BPoint delta)1611 Desktop::SetWindowOutlinesDelta(Window* window, BPoint delta)
1612 {
1613 	AutoWriteLocker _(fWindowLock);
1614 
1615 	if (!window->IsVisible())
1616 		return;
1617 
1618 	BRegion newDirtyRegion;
1619 	window->SetOutlinesDelta(delta, &newDirtyRegion);
1620 
1621 	BRegion background;
1622 	_RebuildClippingForAllWindows(background);
1623 
1624 	MarkDirty(newDirtyRegion);
1625 	_SetBackground(background);
1626 }
1627 
1628 
1629 bool
SetWindowTabLocation(Window * window,float location,bool isShifting)1630 Desktop::SetWindowTabLocation(Window* window, float location, bool isShifting)
1631 {
1632 	AutoWriteLocker _(fWindowLock);
1633 
1634 	BRegion dirty;
1635 	bool changed = window->SetTabLocation(location, isShifting, dirty);
1636 	if (changed)
1637 		RebuildAndRedrawAfterWindowChange(window, dirty);
1638 
1639 	NotifyWindowTabLocationChanged(window, location, isShifting);
1640 
1641 	return changed;
1642 }
1643 
1644 
1645 bool
SetWindowDecoratorSettings(Window * window,const BMessage & settings)1646 Desktop::SetWindowDecoratorSettings(Window* window, const BMessage& settings)
1647 {
1648 	AutoWriteLocker _(fWindowLock);
1649 
1650 	BRegion dirty;
1651 	bool changed = window->SetDecoratorSettings(settings, dirty);
1652 	bool listenerChanged = SetDecoratorSettings(window, settings);
1653 	if (changed || listenerChanged)
1654 		RebuildAndRedrawAfterWindowChange(window, dirty);
1655 
1656 	return changed;
1657 }
1658 
1659 
1660 void
SetWindowWorkspaces(Window * window,uint32 workspaces)1661 Desktop::SetWindowWorkspaces(Window* window, uint32 workspaces)
1662 {
1663 	LockAllWindows();
1664 
1665 	if (window->IsNormal() && workspaces == B_CURRENT_WORKSPACE)
1666 		workspaces = workspace_to_workspaces(CurrentWorkspace());
1667 
1668 	WindowStack* stack = window->GetWindowStack();
1669 	if (stack != NULL) {
1670 		for (int32 s = 0; s < stack->CountWindows(); s++) {
1671 			window = stack->LayerOrder().ItemAt(s);
1672 
1673 			uint32 oldWorkspaces = window->Workspaces();
1674 			window->WorkspacesChanged(oldWorkspaces, workspaces);
1675 			_ChangeWindowWorkspaces(window, oldWorkspaces, workspaces);
1676 		}
1677 	}
1678 	UnlockAllWindows();
1679 }
1680 
1681 
1682 /*!	\brief Adds the window to the desktop.
1683 	At this point, the window is still hidden and must be shown explicitly
1684 	via ShowWindow().
1685 */
1686 void
AddWindow(Window * window)1687 Desktop::AddWindow(Window *window)
1688 {
1689 	LockAllWindows();
1690 
1691 	fAllWindows.AddWindow(window);
1692 	if (!window->IsNormal())
1693 		fSubsetWindows.AddWindow(window);
1694 
1695 	if (window->IsNormal()) {
1696 		if (window->Workspaces() == B_CURRENT_WORKSPACE)
1697 			window->SetWorkspaces(workspace_to_workspaces(CurrentWorkspace()));
1698 	} else {
1699 		// subset windows are visible on all workspaces their subset is on
1700 		window->SetWorkspaces(window->SubsetWorkspaces());
1701 	}
1702 
1703 	_ChangeWindowWorkspaces(window, 0, window->Workspaces());
1704 
1705 	NotifyWindowAdded(window);
1706 
1707 	UnlockAllWindows();
1708 }
1709 
1710 
1711 void
RemoveWindow(Window * window)1712 Desktop::RemoveWindow(Window *window)
1713 {
1714 	LockAllWindows();
1715 
1716 	if (!window->IsHidden())
1717 		HideWindow(window);
1718 
1719 	fAllWindows.RemoveWindow(window);
1720 	if (!window->IsNormal())
1721 		fSubsetWindows.RemoveWindow(window);
1722 
1723 	_ChangeWindowWorkspaces(window, window->Workspaces(), 0);
1724 
1725 	NotifyWindowRemoved(window);
1726 
1727 	UnlockAllWindows();
1728 
1729 	// make sure this window won't get any events anymore
1730 
1731 	EventDispatcher().RemoveTarget(window->EventTarget());
1732 }
1733 
1734 
1735 bool
AddWindowToSubset(Window * subset,Window * window)1736 Desktop::AddWindowToSubset(Window* subset, Window* window)
1737 {
1738 	if (!subset->AddToSubset(window))
1739 		return false;
1740 
1741 	_ChangeWindowWorkspaces(subset, subset->Workspaces(),
1742 		subset->SubsetWorkspaces());
1743 	return true;
1744 }
1745 
1746 
1747 void
RemoveWindowFromSubset(Window * subset,Window * window)1748 Desktop::RemoveWindowFromSubset(Window* subset, Window* window)
1749 {
1750 	subset->RemoveFromSubset(window);
1751 	_ChangeWindowWorkspaces(subset, subset->Workspaces(),
1752 		subset->SubsetWorkspaces());
1753 }
1754 
1755 
1756 void
FontsChanged(Window * window)1757 Desktop::FontsChanged(Window* window)
1758 {
1759 	AutoWriteLocker _(fWindowLock);
1760 
1761 	BRegion dirty;
1762 	window->FontsChanged(&dirty);
1763 
1764 	RebuildAndRedrawAfterWindowChange(window, dirty);
1765 }
1766 
1767 
1768 void
ColorUpdated(Window * window,color_which which,rgb_color color)1769 Desktop::ColorUpdated(Window* window, color_which which, rgb_color color)
1770 {
1771 	AutoWriteLocker _(fWindowLock);
1772 
1773 	window->TopView()->ColorUpdated(which, color);
1774 
1775 	switch (which) {
1776 		case B_WINDOW_TAB_COLOR:
1777 		case B_WINDOW_TEXT_COLOR:
1778 		case B_WINDOW_INACTIVE_TAB_COLOR:
1779 		case B_WINDOW_INACTIVE_TEXT_COLOR:
1780 		case B_WINDOW_BORDER_COLOR:
1781 		case B_WINDOW_INACTIVE_BORDER_COLOR:
1782 			break;
1783 		default:
1784 			return;
1785 	}
1786 
1787 	BRegion dirty;
1788 	window->ColorsChanged(&dirty);
1789 	RebuildAndRedrawAfterWindowChange(window, dirty);
1790 }
1791 
1792 
1793 void
SetWindowLook(Window * window,window_look newLook)1794 Desktop::SetWindowLook(Window* window, window_look newLook)
1795 {
1796 	if (window->Look() == newLook)
1797 		return;
1798 
1799 	AutoWriteLocker _(fWindowLock);
1800 
1801 	BRegion dirty;
1802 	window->SetLook(newLook, &dirty);
1803 		// TODO: test what happens when the window
1804 		// finds out it needs to resize itself...
1805 
1806 	RebuildAndRedrawAfterWindowChange(window, dirty);
1807 
1808 	NotifyWindowLookChanged(window, newLook);
1809 }
1810 
1811 
1812 void
SetWindowFeel(Window * window,window_feel newFeel)1813 Desktop::SetWindowFeel(Window* window, window_feel newFeel)
1814 {
1815 	if (window->Feel() == newFeel)
1816 		return;
1817 
1818 	LockAllWindows();
1819 
1820 	bool wasNormal = window->IsNormal();
1821 
1822 	window->SetFeel(newFeel);
1823 
1824 	// move the window out of or into the subset window list as needed
1825 	if (window->IsNormal() && !wasNormal)
1826 		fSubsetWindows.RemoveWindow(window);
1827 	else if (!window->IsNormal() && wasNormal)
1828 		fSubsetWindows.AddWindow(window);
1829 
1830 	// A normal window that was once a floating or modal window will
1831 	// adopt the window's current workspaces
1832 
1833 	if (!window->IsNormal()) {
1834 		_ChangeWindowWorkspaces(window, window->Workspaces(),
1835 			window->SubsetWorkspaces());
1836 	}
1837 
1838 	// make sure the window has the correct position in the window lists
1839 	// (ie. all floating windows have to be on the top, ...)
1840 
1841 	for (int32 i = 0; i < kMaxWorkspaces; i++) {
1842 		if (!workspace_in_workspaces(i, window->Workspaces()))
1843 			continue;
1844 
1845 		bool changed = false;
1846 		BRegion visibleBefore;
1847 		if (i == fCurrentWorkspace && window->IsVisible())
1848 			visibleBefore = window->VisibleRegion();
1849 
1850 		Window* backmost = window->Backmost(_Windows(i).LastWindow(), i);
1851 		if (backmost != NULL) {
1852 			// check if the backmost window is really behind it
1853 			Window* previous = window->PreviousWindow(i);
1854 			while (previous != NULL) {
1855 				if (previous == backmost)
1856 					break;
1857 
1858 				previous = previous->PreviousWindow(i);
1859 			}
1860 
1861 			if (previous == NULL) {
1862 				// need to reinsert window before its backmost window
1863 				_Windows(i).RemoveWindow(window);
1864 				_Windows(i).AddWindow(window, backmost->NextWindow(i));
1865 				changed = true;
1866 			}
1867 		}
1868 
1869 		Window* frontmost = window->Frontmost(_Windows(i).FirstWindow(), i);
1870 		if (frontmost != NULL) {
1871 			// check if the frontmost window is really in front of it
1872 			Window* next = window->NextWindow(i);
1873 			while (next != NULL) {
1874 				if (next == frontmost)
1875 					break;
1876 
1877 				next = next->NextWindow(i);
1878 			}
1879 
1880 			if (next == NULL) {
1881 				// need to reinsert window behind its frontmost window
1882 				_Windows(i).RemoveWindow(window);
1883 				_Windows(i).AddWindow(window, frontmost);
1884 				changed = true;
1885 			}
1886 		}
1887 
1888 		if (i == fCurrentWorkspace && changed) {
1889 			BRegion dummy;
1890 			_RebuildClippingForAllWindows(dummy);
1891 
1892 			// mark everything dirty that is no longer visible, or
1893 			// is now visible and wasn't before
1894 			BRegion visibleAfter(window->VisibleRegion());
1895 			BRegion dirty(visibleAfter);
1896 			dirty.Exclude(&visibleBefore);
1897 			visibleBefore.Exclude(&visibleAfter);
1898 			dirty.Include(&visibleBefore);
1899 
1900 			MarkDirty(dirty);
1901 		}
1902 	}
1903 
1904 	_UpdateFronts();
1905 
1906 	if (window == FocusWindow() && !window->IsVisible())
1907 		SetFocusWindow();
1908 
1909 	NotifyWindowFeelChanged(window, newFeel);
1910 
1911 	UnlockAllWindows();
1912 }
1913 
1914 
1915 void
SetWindowFlags(Window * window,uint32 newFlags)1916 Desktop::SetWindowFlags(Window *window, uint32 newFlags)
1917 {
1918 	if (window->Flags() == newFlags)
1919 		return;
1920 
1921 	AutoWriteLocker _(fWindowLock);
1922 
1923 	BRegion dirty;
1924 	window->SetFlags(newFlags, &dirty);
1925 		// TODO: test what happens when the window
1926 		// finds out it needs to resize itself...
1927 
1928 	RebuildAndRedrawAfterWindowChange(window, dirty);
1929 }
1930 
1931 
1932 void
SetWindowTitle(Window * window,const char * title)1933 Desktop::SetWindowTitle(Window *window, const char* title)
1934 {
1935 	AutoWriteLocker _(fWindowLock);
1936 
1937 	BRegion dirty;
1938 	window->SetTitle(title, dirty);
1939 
1940 	RebuildAndRedrawAfterWindowChange(window, dirty);
1941 }
1942 
1943 
1944 /*!	Returns the window under the mouse cursor.
1945 	You need to have acquired the All Windows lock when calling this method.
1946 */
1947 Window*
WindowAt(BPoint where)1948 Desktop::WindowAt(BPoint where)
1949 {
1950 	for (Window* window = CurrentWindows().LastWindow(); window;
1951 			window = window->PreviousWindow(fCurrentWorkspace)) {
1952 		if (window->IsVisible() && window->VisibleRegion().Contains(where))
1953 			return window->StackedWindowAt(where);
1954 	}
1955 
1956 	return NULL;
1957 }
1958 
1959 
1960 void
SetMouseEventWindow(Window * window)1961 Desktop::SetMouseEventWindow(Window* window)
1962 {
1963 	fMouseEventWindow = window;
1964 }
1965 
1966 
1967 void
SetViewUnderMouse(const Window * window,int32 viewToken)1968 Desktop::SetViewUnderMouse(const Window* window, int32 viewToken)
1969 {
1970 	fWindowUnderMouse = window;
1971 	fViewUnderMouse = viewToken;
1972 }
1973 
1974 
1975 int32
ViewUnderMouse(const Window * window)1976 Desktop::ViewUnderMouse(const Window* window)
1977 {
1978 	if (window != NULL && fWindowUnderMouse == window)
1979 		return fViewUnderMouse;
1980 
1981 	return B_NULL_TOKEN;
1982 }
1983 
1984 
1985 /*!	Returns the current keyboard event target candidate - which is either the
1986 	top-most window (in case it has the kAcceptKeyboardFocusFlag flag set), or
1987 	the one having focus.
1988 	The window lock must be held when calling this function.
1989 */
1990 EventTarget*
KeyboardEventTarget()1991 Desktop::KeyboardEventTarget()
1992 {
1993 	// Get the top most non-hidden window
1994 	Window* window = CurrentWindows().LastWindow();
1995 	while (window != NULL && window->IsHidden()) {
1996 		window = window->PreviousWindow(fCurrentWorkspace);
1997 	}
1998 
1999 	if (window != NULL && (window->Flags() & kAcceptKeyboardFocusFlag) != 0)
2000 		return &window->EventTarget();
2001 
2002 	if (FocusWindow() != NULL)
2003 		return &FocusWindow()->EventTarget();
2004 
2005 	return NULL;
2006 }
2007 
2008 
2009 /*!	Tries to set the focus to the specified \a focus window. It will make sure,
2010 	however, that the window actually can have focus. You are allowed to pass
2011 	in a NULL pointer for \a focus.
2012 
2013 	Besides the B_AVOID_FOCUS flag, a modal window, or a BWindowScreen can both
2014 	prevent it from getting focus.
2015 
2016 	In any case, this method makes sure that there is a focus window, if there
2017 	is any window at all, that is.
2018 */
2019 void
SetFocusWindow(Window * nextFocus)2020 Desktop::SetFocusWindow(Window* nextFocus)
2021 {
2022 	if (!LockAllWindows())
2023 		return;
2024 
2025 	// test for B_LOCK_WINDOW_FOCUS
2026 	if (fLockedFocusWindow && nextFocus != fLockedFocusWindow) {
2027 		UnlockAllWindows();
2028 		return;
2029 	}
2030 
2031 	bool hasModal = _WindowHasModal(nextFocus);
2032 	bool hasWindowScreen = false;
2033 
2034 	if (!hasModal && nextFocus != NULL) {
2035 		// Check whether or not a window screen is in front of the window
2036 		// (if it has a modal, the right thing is done, anyway)
2037 		Window* window = nextFocus;
2038 		while (true) {
2039 			window = window->NextWindow(fCurrentWorkspace);
2040 			if (window == NULL || window->Feel() == kWindowScreenFeel)
2041 				break;
2042 		}
2043 		if (window != NULL)
2044 			hasWindowScreen = true;
2045 	}
2046 
2047 	if (nextFocus == fFocus && nextFocus != NULL && !nextFocus->IsHidden()
2048 		&& (nextFocus->Flags() & B_AVOID_FOCUS) == 0
2049 		&& !hasModal && !hasWindowScreen) {
2050 		// the window that is supposed to get focus already has focus
2051 		UnlockAllWindows();
2052 		return;
2053 	}
2054 
2055 	uint32 listIndex = fCurrentWorkspace;
2056 	WindowList* list = &_Windows(fCurrentWorkspace);
2057 	if (!fSettings->NormalMouse()) {
2058 		listIndex = kFocusList;
2059 		list = &fFocusList;
2060 	}
2061 
2062 	if (nextFocus == NULL || hasModal || hasWindowScreen) {
2063 		nextFocus = list->LastWindow();
2064 
2065 		if (fSettings->NormalMouse()) {
2066 			// If the last window having focus is a window that cannot make it
2067 			// to the front, we use that as the next focus
2068 			Window* lastFocus = fFocusList.LastWindow();
2069 			if (lastFocus != NULL && !lastFocus->SupportsFront()
2070 				&& _WindowCanHaveFocus(lastFocus)) {
2071 				nextFocus = lastFocus;
2072 			}
2073 		}
2074 	}
2075 
2076 	// make sure no window is chosen that doesn't want focus or cannot have it
2077 	while (nextFocus != NULL && !_WindowCanHaveFocus(nextFocus)) {
2078 		nextFocus = nextFocus->PreviousWindow(listIndex);
2079 	}
2080 
2081 	if (fFocus == nextFocus) {
2082 		// turns out the window that is supposed to get focus now already has it
2083 		UnlockAllWindows();
2084 		return;
2085 	}
2086 
2087 	team_id oldActiveApp = -1;
2088 	team_id newActiveApp = -1;
2089 
2090 	if (fFocus != NULL) {
2091 		fFocus->SetFocus(false);
2092 		oldActiveApp = fFocus->ServerWindow()->App()->ClientTeam();
2093 	}
2094 
2095 	fFocus = nextFocus;
2096 
2097 	if (fFocus != NULL) {
2098 		fFocus->SetFocus(true);
2099 		newActiveApp = fFocus->ServerWindow()->App()->ClientTeam();
2100 
2101 		// move current focus to the end of the focus list
2102 		fFocusList.RemoveWindow(fFocus);
2103 		fFocusList.AddWindow(fFocus);
2104 	}
2105 
2106 	if (newActiveApp == -1) {
2107 		// make sure the cursor is visible
2108 		HWInterface()->SetCursorVisible(true);
2109 	}
2110 
2111 	UnlockAllWindows();
2112 
2113 	// change the "active" app if appropriate
2114 	if (oldActiveApp == newActiveApp)
2115 		return;
2116 
2117 	BAutolock locker(fApplicationsLock);
2118 
2119 	for (int32 i = 0; i < fApplications.CountItems(); i++) {
2120 		ServerApp* app = fApplications.ItemAt(i);
2121 
2122 		if (oldActiveApp != -1 && app->ClientTeam() == oldActiveApp)
2123 			app->Activate(false);
2124 		else if (newActiveApp != -1 && app->ClientTeam() == newActiveApp)
2125 			app->Activate(true);
2126 	}
2127 }
2128 
2129 
2130 void
SetFocusLocked(const Window * window)2131 Desktop::SetFocusLocked(const Window* window)
2132 {
2133 	AutoWriteLocker _(fWindowLock);
2134 
2135 	if (window != NULL) {
2136 		// Don't allow this to be set when no mouse buttons
2137 		// are pressed. (BView::SetMouseEventMask() should only be called
2138 		// from mouse hooks.)
2139 		if (fLastMouseButtons == 0)
2140 			return;
2141 	}
2142 
2143 	fLockedFocusWindow = window;
2144 }
2145 
2146 
2147 Window*
FindWindowByClientToken(int32 token,team_id teamID)2148 Desktop::FindWindowByClientToken(int32 token, team_id teamID)
2149 {
2150 	for (Window *window = fAllWindows.FirstWindow(); window != NULL;
2151 			window = window->NextWindow(kAllWindowList)) {
2152 		if (window->ServerWindow()->ClientToken() == token
2153 			&& window->ServerWindow()->ClientTeam() == teamID) {
2154 			return window;
2155 		}
2156 	}
2157 
2158 	return NULL;
2159 }
2160 
2161 
2162 ::EventTarget*
FindTarget(BMessenger & messenger)2163 Desktop::FindTarget(BMessenger& messenger)
2164 {
2165 	for (Window *window = fAllWindows.FirstWindow(); window != NULL;
2166 			window = window->NextWindow(kAllWindowList)) {
2167 		if (window->EventTarget().Messenger() == messenger)
2168 			return &window->EventTarget();
2169 	}
2170 
2171 	return NULL;
2172 }
2173 
2174 
2175 void
MarkDirty(BRegion & dirtyRegion,BRegion & exposeRegion)2176 Desktop::MarkDirty(BRegion& dirtyRegion, BRegion& exposeRegion)
2177 {
2178 	if (dirtyRegion.CountRects() == 0)
2179 		return;
2180 
2181 	if (LockAllWindows()) {
2182 		// send redraw messages to all windows intersecting the dirty region
2183 		_TriggerWindowRedrawing(dirtyRegion, exposeRegion);
2184 
2185 		UnlockAllWindows();
2186 	}
2187 }
2188 
2189 
2190 void
Redraw()2191 Desktop::Redraw()
2192 {
2193 	BRegion dirty(fVirtualScreen.Frame());
2194 	MarkDirty(dirty);
2195 }
2196 
2197 
2198 /*!	\brief Redraws the background (ie. the desktop window, if any).
2199 */
2200 void
RedrawBackground()2201 Desktop::RedrawBackground()
2202 {
2203 	LockAllWindows();
2204 
2205 	BRegion redraw;
2206 
2207 	Window* window = CurrentWindows().FirstWindow();
2208 	if (window != NULL && window->Feel() == kDesktopWindowFeel) {
2209 		redraw = window->VisibleContentRegion();
2210 
2211 		// look for desktop background view, and update its background color
2212 		// TODO: is there a better way to do this?
2213 		View* view = window->TopView();
2214 		if (view != NULL)
2215 			view = view->FirstChild();
2216 
2217 		while (view != NULL) {
2218 			if (view->IsDesktopBackground()) {
2219 				view->SetViewColor(fWorkspaces[fCurrentWorkspace].Color());
2220 				break;
2221 			}
2222 			view = view->NextSibling();
2223 		}
2224 
2225 		window->ProcessDirtyRegion(redraw);
2226 	} else {
2227 		redraw = BackgroundRegion();
2228 		fBackgroundRegion.MakeEmpty();
2229 		_SetBackground(redraw);
2230 	}
2231 
2232 	_WindowChanged(NULL);
2233 		// update workspaces view as well
2234 
2235 	UnlockAllWindows();
2236 }
2237 
2238 
2239 bool
ReloadDecor(DecorAddOn * oldDecor)2240 Desktop::ReloadDecor(DecorAddOn* oldDecor)
2241 {
2242 	AutoWriteLocker _(fWindowLock);
2243 
2244 	bool returnValue = true;
2245 
2246 	if (oldDecor != NULL) {
2247 		const DesktopListenerList* oldListeners
2248 			= &oldDecor->GetDesktopListeners();
2249 		for (int i = 0; i < oldListeners->CountItems(); i++)
2250 			UnregisterListener(oldListeners->ItemAt(i));
2251 	}
2252 
2253 	for (Window* window = fAllWindows.FirstWindow(); window != NULL;
2254 			window = window->NextWindow(kAllWindowList)) {
2255 		BRegion oldBorder;
2256 		window->GetBorderRegion(&oldBorder);
2257 
2258 		if (!window->ReloadDecor()) {
2259 			// prevent unloading previous add-on
2260 			returnValue = false;
2261 		}
2262 
2263 		BRegion border;
2264 		window->GetBorderRegion(&border);
2265 
2266 		border.Include(&oldBorder);
2267 		RebuildAndRedrawAfterWindowChange(window, border);
2268 	}
2269 
2270 	// register new listeners
2271 	const DesktopListenerList& newListeners
2272 		= gDecorManager.GetDesktopListeners();
2273 	for (int i = 0; i < newListeners.CountItems(); i++)
2274  		RegisterListener(newListeners.ItemAt(i));
2275 
2276  	return returnValue;
2277 }
2278 
2279 
2280 void
MinimizeApplication(team_id team)2281 Desktop::MinimizeApplication(team_id team)
2282 {
2283 	AutoWriteLocker locker(fWindowLock);
2284 
2285 	// Just minimize all windows of that application
2286 
2287 	for (Window *window = fAllWindows.FirstWindow(); window != NULL;
2288 			window = window->NextWindow(kAllWindowList)) {
2289 		if (window->ServerWindow()->ClientTeam() != team)
2290 			continue;
2291 
2292 		window->ServerWindow()->NotifyMinimize(true);
2293 	}
2294 }
2295 
2296 
2297 void
BringApplicationToFront(team_id team)2298 Desktop::BringApplicationToFront(team_id team)
2299 {
2300 	AutoWriteLocker locker(fWindowLock);
2301 
2302 	// TODO: for now, just maximize all windows of that application
2303 	// TODO: have the ability to lock the current workspace
2304 
2305 	for (Window *window = fAllWindows.FirstWindow(); window != NULL;
2306 			window = window->NextWindow(kAllWindowList)) {
2307 		if (window->ServerWindow()->ClientTeam() != team)
2308 			continue;
2309 
2310 		window->ServerWindow()->NotifyMinimize(false);
2311 	}
2312 }
2313 
2314 
2315 void
WindowAction(int32 windowToken,int32 action)2316 Desktop::WindowAction(int32 windowToken, int32 action)
2317 {
2318 	if (action != B_MINIMIZE_WINDOW && action != B_BRING_TO_FRONT)
2319 		return;
2320 
2321 	LockAllWindows();
2322 
2323 	::ServerWindow* serverWindow;
2324 	Window* window;
2325 	if (BPrivate::gDefaultTokens.GetToken(windowToken,
2326 			B_SERVER_TOKEN, (void**)&serverWindow) != B_OK
2327 		|| (window = serverWindow->Window()) == NULL) {
2328 		UnlockAllWindows();
2329 		return;
2330 	}
2331 
2332 	if (action == B_BRING_TO_FRONT && !window->IsMinimized()) {
2333 		// the window is visible, we just need to make it the front window
2334 		ActivateWindow(window);
2335 	} else {
2336 		// if not, ask the window if it wants to be unminimized
2337 		serverWindow->NotifyMinimize(action == B_MINIMIZE_WINDOW);
2338 	}
2339 
2340 	UnlockAllWindows();
2341 }
2342 
2343 
2344 void
WriteWindowList(team_id team,BPrivate::LinkSender & sender)2345 Desktop::WriteWindowList(team_id team, BPrivate::LinkSender& sender)
2346 {
2347 	AutoWriteLocker locker(fWindowLock);
2348 
2349 	// compute the number of windows
2350 
2351 	int32 count = 0;
2352 
2353 	for (Window *window = fAllWindows.FirstWindow(); window != NULL;
2354 			window = window->NextWindow(kAllWindowList)) {
2355 		if (team < B_OK || window->ServerWindow()->ClientTeam() == team)
2356 			count++;
2357 	}
2358 
2359 	// write list
2360 
2361 	sender.StartMessage(B_OK);
2362 	sender.Attach<int32>(count);
2363 
2364 	// first write the windows of the current workspace correctly ordered
2365 	for (Window *window = CurrentWindows().LastWindow(); window != NULL;
2366 			window = window->PreviousWindow(fCurrentWorkspace)) {
2367 		if (team >= B_OK && window->ServerWindow()->ClientTeam() != team)
2368 			continue;
2369 
2370 		sender.Attach<int32>(window->ServerWindow()->ServerToken());
2371 	}
2372 
2373 	// then write all the other windows
2374 	for (Window *window = fAllWindows.FirstWindow(); window != NULL;
2375 			window = window->NextWindow(kAllWindowList)) {
2376 		if ((team >= B_OK && window->ServerWindow()->ClientTeam() != team)
2377 			|| window->InWorkspace(fCurrentWorkspace))
2378 			continue;
2379 
2380 		sender.Attach<int32>(window->ServerWindow()->ServerToken());
2381 	}
2382 
2383 	sender.Flush();
2384 }
2385 
2386 
2387 void
WriteWindowInfo(int32 serverToken,BPrivate::LinkSender & sender)2388 Desktop::WriteWindowInfo(int32 serverToken, BPrivate::LinkSender& sender)
2389 {
2390 	AutoWriteLocker locker(fWindowLock);
2391 	BAutolock tokenLocker(BPrivate::gDefaultTokens);
2392 
2393 	::ServerWindow* window;
2394 	if (BPrivate::gDefaultTokens.GetToken(serverToken,
2395 			B_SERVER_TOKEN, (void**)&window) != B_OK) {
2396 		sender.StartMessage(B_ENTRY_NOT_FOUND);
2397 		sender.Flush();
2398 		return;
2399 	}
2400 
2401 	window_info info;
2402 	window->GetInfo(info);
2403 
2404 	float tabSize = 0.0;
2405 	float borderSize = 0.0;
2406 	::Window* tmp = window->Window();
2407 	if (tmp) {
2408 		BMessage message;
2409 		if (tmp->GetDecoratorSettings(&message)) {
2410 			BRect tabFrame;
2411 			message.FindRect("tab frame", &tabFrame);
2412 			tabSize = tabFrame.bottom - tabFrame.top;
2413 			message.FindFloat("border width", &borderSize);
2414 		}
2415 	}
2416 
2417 	int32 length = window->Title() ? strlen(window->Title()) : 0;
2418 
2419 	sender.StartMessage(B_OK);
2420 	sender.Attach<int32>(sizeof(client_window_info) + length + 1);
2421 	sender.Attach(&info, sizeof(window_info));
2422 	sender.Attach<float>(tabSize);
2423 	sender.Attach<float>(borderSize);
2424 
2425 	if (length > 0)
2426 		sender.Attach(window->Title(), length + 1);
2427 	else
2428 		sender.Attach<char>('\0');
2429 
2430 	sender.Flush();
2431 }
2432 
2433 
2434 void
WriteWindowOrder(int32 workspace,BPrivate::LinkSender & sender)2435 Desktop::WriteWindowOrder(int32 workspace, BPrivate::LinkSender& sender)
2436 {
2437 	LockSingleWindow();
2438 
2439 	if (workspace < 0)
2440 		workspace = fCurrentWorkspace;
2441 	else if (workspace >= kMaxWorkspaces) {
2442 		sender.StartMessage(B_BAD_VALUE);
2443 		sender.Flush();
2444 		UnlockSingleWindow();
2445 		return;
2446 	}
2447 
2448 	int32 count = _Windows(workspace).Count();
2449 
2450 	// write list
2451 
2452 	sender.StartMessage(B_OK);
2453 	sender.Attach<int32>(count);
2454 
2455 	for (Window *window = _Windows(workspace).LastWindow(); window != NULL;
2456 			window = window->PreviousWindow(workspace)) {
2457 		sender.Attach<int32>(window->ServerWindow()->ServerToken());
2458 	}
2459 
2460 	sender.Flush();
2461 
2462 	UnlockSingleWindow();
2463 }
2464 
2465 
2466 void
WriteApplicationOrder(int32 workspace,BPrivate::LinkSender & sender)2467 Desktop::WriteApplicationOrder(int32 workspace, BPrivate::LinkSender& sender)
2468 {
2469 	fApplicationsLock.Lock();
2470 	LockSingleWindow();
2471 
2472 	int32 maxCount = fApplications.CountItems();
2473 
2474 	fApplicationsLock.Unlock();
2475 		// as long as we hold the window lock, no new window can appear
2476 
2477 	if (workspace < 0)
2478 		workspace = fCurrentWorkspace;
2479 	else if (workspace >= kMaxWorkspaces) {
2480 		sender.StartMessage(B_BAD_VALUE);
2481 		sender.Flush();
2482 		UnlockSingleWindow();
2483 		return;
2484 	}
2485 
2486 	// compute the list of applications on this workspace
2487 
2488 	team_id* teams = (team_id*)malloc(maxCount * sizeof(team_id));
2489 	if (teams == NULL) {
2490 		sender.StartMessage(B_NO_MEMORY);
2491 		sender.Flush();
2492 		UnlockSingleWindow();
2493 		return;
2494 	}
2495 
2496 	int32 count = 0;
2497 
2498 	for (Window *window = _Windows(workspace).LastWindow(); window != NULL;
2499 			window = window->PreviousWindow(workspace)) {
2500 		team_id team = window->ServerWindow()->ClientTeam();
2501 		if (count > 1) {
2502 			// see if we already have this team
2503 			bool found = false;
2504 			for (int32 i = 0; i < count; i++) {
2505 				if (teams[i] == team) {
2506 					found = true;
2507 					break;
2508 				}
2509 			}
2510 			if (found)
2511 				continue;
2512 		}
2513 
2514 		ASSERT(count < maxCount);
2515 		teams[count++] = team;
2516 	}
2517 
2518 	UnlockSingleWindow();
2519 
2520 	// write list
2521 
2522 	sender.StartMessage(B_OK);
2523 	sender.Attach<int32>(count);
2524 
2525 	for (int32 i = 0; i < count; i++) {
2526 		sender.Attach<int32>(teams[i]);
2527 	}
2528 
2529 	sender.Flush();
2530 	free(teams);
2531 }
2532 
2533 
2534 void
_LaunchInputServer()2535 Desktop::_LaunchInputServer()
2536 {
2537 	BRoster roster;
2538 	status_t status = roster.Launch("application/x-vnd.Be-input_server");
2539 	if (status == B_OK || status == B_ALREADY_RUNNING)
2540 		return;
2541 
2542 	// Could not load input_server by signature, try well-known location
2543 
2544 	BEntry entry;
2545 	BPath inputServerPath;
2546 	if (find_directory(B_SYSTEM_SERVERS_DIRECTORY, &inputServerPath) == B_OK
2547 		&& inputServerPath.Append("input_server") == B_OK) {
2548 		entry.SetTo(inputServerPath.Path());
2549 	} else
2550 		entry.SetTo("/system/servers/input_server");
2551 	entry_ref ref;
2552 	status_t entryStatus = entry.GetRef(&ref);
2553 	if (entryStatus == B_OK)
2554 		entryStatus = roster.Launch(&ref);
2555 	if (entryStatus == B_OK || entryStatus == B_ALREADY_RUNNING) {
2556 		syslog(LOG_ERR, "Failed to launch the input server by signature: %s!\n",
2557 			strerror(status));
2558 		return;
2559 	}
2560 
2561 	syslog(LOG_ERR, "Failed to launch the input server: %s!\n",
2562 		strerror(entryStatus));
2563 }
2564 
2565 
2566 void
_GetLooperName(char * name,size_t length)2567 Desktop::_GetLooperName(char* name, size_t length)
2568 {
2569 	snprintf(name, length, "d:%d:%s", fUserID,
2570 		fTargetScreen == NULL ? "baron" : fTargetScreen);
2571 }
2572 
2573 
2574 void
_PrepareQuit()2575 Desktop::_PrepareQuit()
2576 {
2577 	// let's kill all remaining applications
2578 
2579 	fApplicationsLock.Lock();
2580 
2581 	int32 count = fApplications.CountItems();
2582 	for (int32 i = 0; i < count; i++) {
2583 		ServerApp *app = fApplications.ItemAt(i);
2584 		team_id clientTeam = app->ClientTeam();
2585 
2586 		app->Quit();
2587 		kill_team(clientTeam);
2588 	}
2589 
2590 	// wait for the last app to die
2591 	if (count > 0) {
2592 		acquire_sem_etc(fShutdownSemaphore, fShutdownCount, B_RELATIVE_TIMEOUT,
2593 			250000);
2594 	}
2595 
2596 	fApplicationsLock.Unlock();
2597 }
2598 
2599 
2600 void
_DispatchMessage(int32 code,BPrivate::LinkReceiver & link)2601 Desktop::_DispatchMessage(int32 code, BPrivate::LinkReceiver& link)
2602 {
2603 	switch (code) {
2604 		case AS_CREATE_APP:
2605 		{
2606 			// Create the ServerApp to node monitor a new BApplication
2607 
2608 			// Attached data:
2609 			// 1) port_id - receiver port of a regular app
2610 			// 2) port_id - client looper port - for sending messages to the
2611 			//		client
2612 			// 2) team_id - app's team ID
2613 			// 3) int32 - handler token of the regular app
2614 			// 4) char * - signature of the regular app
2615 
2616 			// Find the necessary data
2617 			team_id	clientTeamID = -1;
2618 			port_id	clientLooperPort = -1;
2619 			port_id clientReplyPort = -1;
2620 			int32 htoken = B_NULL_TOKEN;
2621 			char* appSignature = NULL;
2622 
2623 			link.Read<port_id>(&clientReplyPort);
2624 			link.Read<port_id>(&clientLooperPort);
2625 			link.Read<team_id>(&clientTeamID);
2626 			link.Read<int32>(&htoken);
2627 			if (link.ReadString(&appSignature) != B_OK)
2628 				break;
2629 
2630 			ObjectDeleter<ServerApp> app(new (std::nothrow) ServerApp(this, clientReplyPort,
2631 				clientLooperPort, clientTeamID, htoken, appSignature));
2632 			status_t status = B_OK;
2633 			if (!app.IsSet())
2634 				status = B_NO_MEMORY;
2635 			if (status == B_OK)
2636 				status = app->InitCheck();
2637 			if (status == B_OK)
2638 				status = app->Run();
2639 			if (status == B_OK) {
2640 				// add the new ServerApp to the known list of ServerApps
2641 				fApplicationsLock.Lock();
2642 				fApplications.AddItem(app.Detach());
2643 				fApplicationsLock.Unlock();
2644 			} else {
2645 				// if everything went well, ServerApp::Run() will notify
2646 				// the client - but since it didn't, we do it here
2647 				BPrivate::LinkSender reply(clientReplyPort);
2648 				reply.StartMessage(status);
2649 				reply.Flush();
2650 			}
2651 
2652 			// This is necessary because BPortLink::ReadString allocates memory
2653 			free(appSignature);
2654 			break;
2655 		}
2656 
2657 		case AS_DELETE_APP:
2658 		{
2659 			// Delete a ServerApp. Received only from the respective ServerApp
2660 			// when a BApplication asks it to quit.
2661 
2662 			// Attached Data:
2663 			// 1) thread_id - thread ID of the ServerApp to be deleted
2664 
2665 			thread_id thread = -1;
2666 			if (link.Read<thread_id>(&thread) < B_OK)
2667 				break;
2668 
2669 			fApplicationsLock.Lock();
2670 
2671 			// Run through the list of apps and nuke the proper one
2672 
2673 			int32 count = fApplications.CountItems();
2674 			ServerApp* removeApp = NULL;
2675 
2676 			for (int32 i = 0; i < count; i++) {
2677 				ServerApp* app = fApplications.ItemAt(i);
2678 
2679 				if (app->Thread() == thread) {
2680 					fApplications.RemoveItemAt(i);
2681 					removeApp = app;
2682 					break;
2683 				}
2684 			}
2685 
2686 			fApplicationsLock.Unlock();
2687 
2688 			if (removeApp != NULL)
2689 				removeApp->Quit(fShutdownSemaphore);
2690 
2691 			if (fQuitting && count <= 1) {
2692 				// wait for the last app to die
2693 				acquire_sem_etc(fShutdownSemaphore, fShutdownCount,
2694 					B_RELATIVE_TIMEOUT, 500000);
2695 				PostMessage(kMsgQuitLooper);
2696 			}
2697 			break;
2698 		}
2699 
2700 		case AS_ACTIVATE_APP:
2701 		{
2702 			// Someone is requesting to activation of a certain app.
2703 
2704 			// Attached data:
2705 			// 1) port_id reply port
2706 			// 2) team_id team
2707 
2708 			status_t status;
2709 
2710 			// get the parameters
2711 			port_id replyPort;
2712 			team_id team;
2713 			if (link.Read(&replyPort) == B_OK
2714 				&& link.Read(&team) == B_OK)
2715 				status = _ActivateApp(team);
2716 			else
2717 				status = B_ERROR;
2718 
2719 			// send the reply
2720 			BPrivate::PortLink replyLink(replyPort);
2721 			replyLink.StartMessage(status);
2722 			replyLink.Flush();
2723 			break;
2724 		}
2725 
2726 		case AS_APP_CRASHED:
2727 		case AS_DUMP_ALLOCATOR:
2728 		case AS_DUMP_BITMAPS:
2729 		{
2730 			BAutolock locker(fApplicationsLock);
2731 
2732 			team_id team;
2733 			if (link.Read(&team) != B_OK)
2734 				break;
2735 
2736 			for (int32 i = 0; i < fApplications.CountItems(); i++) {
2737 				ServerApp* app = fApplications.ItemAt(i);
2738 
2739 				if (app->ClientTeam() == team)
2740 					app->PostMessage(code);
2741 			}
2742 			break;
2743 		}
2744 
2745 		case AS_EVENT_STREAM_CLOSED:
2746 			_LaunchInputServer();
2747 			break;
2748 
2749 		case B_QUIT_REQUESTED:
2750 			// We've been asked to quit, so (for now) broadcast to all
2751 			// test apps to quit. This situation will occur only when the
2752 			// server is compiled as a regular Be application.
2753 
2754 			fApplicationsLock.Lock();
2755 			fShutdownSemaphore = create_sem(0, "desktop shutdown");
2756 			fShutdownCount = fApplications.CountItems();
2757 			fApplicationsLock.Unlock();
2758 
2759 			fQuitting = true;
2760 			BroadcastToAllApps(AS_QUIT_APP);
2761 
2762 			// We now need to process the remaining AS_DELETE_APP messages.
2763 			// We quit the looper when the last app is deleted.
2764 
2765 			// if there are no apps to quit, shutdown directly
2766 			if (fShutdownCount == 0)
2767 				PostMessage(kMsgQuitLooper);
2768 
2769 			break;
2770 
2771 		case AS_ACTIVATE_WORKSPACE:
2772 		{
2773 			int32 index;
2774 			link.Read<int32>(&index);
2775 			if (index == -1)
2776 				index = fPreviousWorkspace;
2777 
2778 			bool moveFocusWindow;
2779 			link.Read<bool>(&moveFocusWindow);
2780 
2781 			SetWorkspace(index, moveFocusWindow);
2782 			break;
2783 		}
2784 
2785 		case AS_TALK_TO_DESKTOP_LISTENER:
2786 		{
2787 			port_id clientReplyPort;
2788 			if (link.Read<port_id>(&clientReplyPort) != B_OK)
2789 				break;
2790 
2791 			BPrivate::LinkSender reply(clientReplyPort);
2792 			AutoWriteLocker locker(fWindowLock);
2793 			if (MessageForListener(NULL, link, reply) != true) {
2794 				// unhandled message, at least send an error if needed
2795 				if (link.NeedsReply()) {
2796 					reply.StartMessage(B_ERROR);
2797 					reply.Flush();
2798 				}
2799 			}
2800 			break;
2801 		}
2802 
2803 		case AS_SET_UI_COLOR:
2804 		{
2805 			color_which which;
2806 			rgb_color color;
2807 
2808 			if (link.Read<color_which>(&which) == B_OK
2809 					&& link.Read<rgb_color>(&color) == B_OK) {
2810 
2811 				const char* colorName = ui_color_name(which);
2812 				fPendingColors.SetColor(colorName, color);
2813 
2814 				DelayedMessage delayed(AS_SET_UI_COLORS, DM_60HZ_DELAY);
2815 				delayed.AddTarget(MessagePort());
2816 				delayed.SetMerge(DM_MERGE_CANCEL);
2817 
2818 				delayed.Attach<bool>(true);
2819 				delayed.Flush();
2820 			}
2821 
2822 			break;
2823 		}
2824 
2825 		case AS_SET_UI_COLORS:
2826 		{
2827 			bool flushPendingOnly = false;
2828 
2829 			if (link.Read<bool>(&flushPendingOnly) != B_OK
2830 				|| (flushPendingOnly &&
2831 						fPendingColors.CountNames(B_RGB_32_BIT_TYPE) == 0)) {
2832 				break;
2833 			}
2834 
2835 			if (!flushPendingOnly) {
2836 				// Client wants to set a color map
2837 				color_which which = B_NO_COLOR;
2838 				rgb_color color;
2839 
2840 				do {
2841 					if (link.Read<color_which>(&which) != B_OK
2842 						|| link.Read<rgb_color>(&color) != B_OK)
2843 						break;
2844 
2845 					fPendingColors.SetColor(ui_color_name(which), color);
2846 				} while (which != B_NO_COLOR);
2847 			}
2848 
2849 			_FlushPendingColors();
2850 			break;
2851 		}
2852 
2853 		// ToDo: Remove this again. It is a message sent by the
2854 		// invalidate_on_exit kernel debugger add-on to trigger a redraw
2855 		// after exiting a kernel debugger session.
2856 		case 'KDLE':
2857 		{
2858 			BRegion dirty;
2859 			dirty.Include(fVirtualScreen.Frame());
2860 			MarkDirty(dirty);
2861 			break;
2862 		}
2863 
2864 		default:
2865 			printf("Desktop %d:%s received unexpected code %" B_PRId32 "\n", 0,
2866 				"baron", code);
2867 
2868 			if (link.NeedsReply()) {
2869 				// the client is now blocking and waiting for a reply!
2870 				fLink.StartMessage(B_ERROR);
2871 				fLink.Flush();
2872 			}
2873 			break;
2874 	}
2875 }
2876 
2877 
2878 WindowList&
CurrentWindows()2879 Desktop::CurrentWindows()
2880 {
2881 	return fWorkspaces[fCurrentWorkspace].Windows();
2882 }
2883 
2884 
2885 WindowList&
AllWindows()2886 Desktop::AllWindows()
2887 {
2888 	return fAllWindows;
2889 }
2890 
2891 
2892 Window*
WindowForClientLooperPort(port_id port)2893 Desktop::WindowForClientLooperPort(port_id port)
2894 {
2895 	ASSERT_MULTI_LOCKED(fWindowLock);
2896 
2897 	for (Window* window = fAllWindows.FirstWindow(); window != NULL;
2898 			window = window->NextWindow(kAllWindowList)) {
2899 		if (window->ServerWindow()->ClientLooperPort() == port)
2900 			return window;
2901 	}
2902 	return NULL;
2903 }
2904 
2905 
2906 WindowList&
_Windows(int32 index)2907 Desktop::_Windows(int32 index)
2908 {
2909 	ASSERT(index >= 0 && index < kMaxWorkspaces);
2910 	return fWorkspaces[index].Windows();
2911 }
2912 
2913 
2914 void
_FlushPendingColors()2915 Desktop::_FlushPendingColors()
2916 {
2917 	// Update all windows while we are holding the write lock.
2918 
2919 	int32 count = fPendingColors.CountNames(B_RGB_32_BIT_TYPE);
2920 	if (count == 0)
2921 		return;
2922 
2923 	bool changed[count];
2924 	LockedDesktopSettings settings(this);
2925 	settings.SetUIColors(fPendingColors, &changed[0]);
2926 
2927 	int32 index = 0;
2928 	char* name = NULL;
2929 	type_code type = B_RGB_32_BIT_TYPE;
2930 	rgb_color color;
2931 	color_which which = B_NO_COLOR;
2932 	BMessage clientMessage(B_COLORS_UPDATED);
2933 
2934 	while (fPendingColors.GetInfo(type, index, &name, &type) == B_OK) {
2935 		which = which_ui_color(name);
2936 		if (which == B_NO_COLOR || fPendingColors.FindColor(name,
2937 				&color) != B_OK || !changed[index]) {
2938 			++index;
2939 			continue;
2940 		}
2941 
2942 		for (Window* window = fAllWindows.FirstWindow(); window != NULL;
2943 				window = window->NextWindow(kAllWindowList)) {
2944 			ColorUpdated(window, which, color);
2945 		}
2946 
2947 		// Ensure client only gets list of changed colors
2948 		clientMessage.AddColor(name, color);
2949 		++index;
2950 	}
2951 
2952 	// Notify client applications
2953 	BAutolock appListLock(fApplicationsLock);
2954 	for (int32 index = 0; index < fApplications.CountItems(); ++index) {
2955 		fApplications.ItemAt(index)->SendMessageToClient(&clientMessage);
2956 	}
2957 
2958 	fPendingColors.MakeEmpty();
2959 }
2960 
2961 
2962 void
_UpdateFloating(int32 previousWorkspace,int32 nextWorkspace,Window * mouseEventWindow)2963 Desktop::_UpdateFloating(int32 previousWorkspace, int32 nextWorkspace,
2964 	Window* mouseEventWindow)
2965 {
2966 	if (previousWorkspace == -1)
2967 		previousWorkspace = fCurrentWorkspace;
2968 	if (nextWorkspace == -1)
2969 		nextWorkspace = previousWorkspace;
2970 
2971 	for (Window* floating = fSubsetWindows.FirstWindow(); floating != NULL;
2972 			floating = floating->NextWindow(kSubsetList)) {
2973 		// we only care about app/subset floating windows
2974 		if (floating->Feel() != B_FLOATING_SUBSET_WINDOW_FEEL
2975 			&& floating->Feel() != B_FLOATING_APP_WINDOW_FEEL)
2976 			continue;
2977 
2978 		if (fFront != NULL && fFront->IsNormal()
2979 			&& floating->HasInSubset(fFront)) {
2980 			// is now visible
2981 			if (_Windows(previousWorkspace).HasWindow(floating)
2982 				&& previousWorkspace != nextWorkspace
2983 				&& !floating->InSubsetWorkspace(previousWorkspace)) {
2984 				// but no longer on the previous workspace
2985 				_Windows(previousWorkspace).RemoveWindow(floating);
2986 				floating->SetCurrentWorkspace(-1);
2987 			}
2988 
2989 			if (!_Windows(nextWorkspace).HasWindow(floating)) {
2990 				// but wasn't before
2991 				_Windows(nextWorkspace).AddWindow(floating,
2992 					floating->Frontmost(_Windows(nextWorkspace).FirstWindow(),
2993 					nextWorkspace));
2994 				floating->SetCurrentWorkspace(nextWorkspace);
2995 				if (mouseEventWindow != fFront)
2996 					_ShowWindow(floating);
2997 
2998 				// TODO: put the floating last in the floating window list to
2999 				// preserve the on screen window order
3000 			}
3001 		} else if (_Windows(previousWorkspace).HasWindow(floating)
3002 			&& !floating->InSubsetWorkspace(previousWorkspace)) {
3003 			// was visible, but is no longer
3004 
3005 			_Windows(previousWorkspace).RemoveWindow(floating);
3006 			floating->SetCurrentWorkspace(-1);
3007 			_HideWindow(floating);
3008 
3009 			if (FocusWindow() == floating)
3010 				SetFocusWindow();
3011 		}
3012 	}
3013 }
3014 
3015 
3016 /*!	Search the visible windows for a valid back window
3017 	(only desktop windows can't be back windows)
3018 */
3019 void
_UpdateBack()3020 Desktop::_UpdateBack()
3021 {
3022 	fBack = NULL;
3023 
3024 	for (Window* window = CurrentWindows().FirstWindow(); window != NULL;
3025 			window = window->NextWindow(fCurrentWorkspace)) {
3026 		if (window->IsHidden() || window->Feel() == kDesktopWindowFeel)
3027 			continue;
3028 
3029 		fBack = window;
3030 		break;
3031 	}
3032 }
3033 
3034 
3035 /*!	Search the visible windows for a valid front window
3036 	(only normal and modal windows can be front windows)
3037 
3038 	The only place where you don't want to update floating windows is
3039 	during a workspace change - because then you'll call _UpdateFloating()
3040 	yourself.
3041 */
3042 void
_UpdateFront(bool updateFloating)3043 Desktop::_UpdateFront(bool updateFloating)
3044 {
3045 	fFront = NULL;
3046 
3047 	for (Window* window = CurrentWindows().LastWindow(); window != NULL;
3048 			window = window->PreviousWindow(fCurrentWorkspace)) {
3049 		if (window->IsHidden() || window->IsFloating()
3050 			|| !window->SupportsFront())
3051 			continue;
3052 
3053 		fFront = window;
3054 		break;
3055 	}
3056 
3057 	if (updateFloating)
3058 		_UpdateFloating();
3059 }
3060 
3061 
3062 void
_UpdateFronts(bool updateFloating)3063 Desktop::_UpdateFronts(bool updateFloating)
3064 {
3065 	_UpdateBack();
3066 	_UpdateFront(updateFloating);
3067 }
3068 
3069 
3070 bool
_WindowHasModal(Window * window) const3071 Desktop::_WindowHasModal(Window* window) const
3072 {
3073 	if (window == NULL)
3074 		return false;
3075 
3076 	for (Window* modal = fSubsetWindows.FirstWindow(); modal != NULL;
3077 			modal = modal->NextWindow(kSubsetList)) {
3078 		// only visible modal windows count
3079 		if (!modal->IsModal() || modal->IsHidden())
3080 			continue;
3081 
3082 		if (modal->HasInSubset(window))
3083 			return true;
3084 	}
3085 
3086 	return false;
3087 }
3088 
3089 
3090 /*!	Determines whether or not the specified \a window can have focus at all.
3091 */
3092 bool
_WindowCanHaveFocus(Window * window) const3093 Desktop::_WindowCanHaveFocus(Window* window) const
3094 {
3095 	return window != NULL
3096 		&& window->InWorkspace(fCurrentWorkspace)
3097 		&& (window->Flags() & B_AVOID_FOCUS) == 0
3098 		&& !_WindowHasModal(window)
3099 		&& !window->IsHidden();
3100 }
3101 
3102 
3103 /*!	You must at least hold a single window lock when calling this method.
3104 */
3105 void
_WindowChanged(Window * window)3106 Desktop::_WindowChanged(Window* window)
3107 {
3108 	ASSERT_MULTI_LOCKED(fWindowLock);
3109 
3110 	BAutolock _(fWorkspacesLock);
3111 
3112 	for (uint32 i = fWorkspacesViews.CountItems(); i-- > 0;) {
3113 		WorkspacesView* view = fWorkspacesViews.ItemAt(i);
3114 		view->WindowChanged(window);
3115 	}
3116 }
3117 
3118 
3119 /*!	You must at least hold a single window lock when calling this method.
3120 */
3121 void
_WindowRemoved(Window * window)3122 Desktop::_WindowRemoved(Window* window)
3123 {
3124 	ASSERT_MULTI_LOCKED(fWindowLock);
3125 
3126 	BAutolock _(fWorkspacesLock);
3127 
3128 	for (uint32 i = fWorkspacesViews.CountItems(); i-- > 0;) {
3129 		WorkspacesView* view = fWorkspacesViews.ItemAt(i);
3130 		view->WindowRemoved(window);
3131 	}
3132 }
3133 
3134 
3135 /*!	Shows the window on the screen - it does this independently of the
3136 	Window::IsHidden() state.
3137 */
3138 void
_ShowWindow(Window * window,bool affectsOtherWindows)3139 Desktop::_ShowWindow(Window* window, bool affectsOtherWindows)
3140 {
3141 	BRegion background;
3142 	_RebuildClippingForAllWindows(background);
3143 	_SetBackground(background);
3144 	_WindowChanged(window);
3145 
3146 	BRegion dirty(window->VisibleRegion());
3147 
3148 	if (!affectsOtherWindows) {
3149 		// everything that is now visible in the
3150 		// window needs a redraw, but other windows
3151 		// are not affected, we can call ProcessDirtyRegion()
3152 		// of the window, and don't have to use MarkDirty()
3153 		window->ProcessDirtyRegion(dirty);
3154 	} else
3155 		MarkDirty(dirty);
3156 
3157 	if (window->ServerWindow()->HasDirectFrameBufferAccess()) {
3158 		window->ServerWindow()->HandleDirectConnection(
3159 			B_DIRECT_START | B_BUFFER_RESET);
3160 	}
3161 }
3162 
3163 
3164 /*!	Hides the window from the screen - it does this independently of the
3165 	Window::IsHidden() state.
3166 */
3167 void
_HideWindow(Window * window)3168 Desktop::_HideWindow(Window* window)
3169 {
3170 	if (window->ServerWindow()->IsDirectlyAccessing())
3171 		window->ServerWindow()->HandleDirectConnection(B_DIRECT_STOP);
3172 
3173 	// after rebuilding the clipping,
3174 	// this window will not have a visible
3175 	// region anymore, so we need to remember
3176 	// it now
3177 	// (actually that's not true, since
3178 	// hidden windows are excluded from the
3179 	// clipping calculation, but anyways)
3180 	BRegion dirty(window->VisibleRegion());
3181 
3182 	BRegion background;
3183 	_RebuildClippingForAllWindows(background);
3184 	_SetBackground(background);
3185 	_WindowChanged(window);
3186 
3187 	MarkDirty(dirty);
3188 }
3189 
3190 
3191 /*!	Updates the workspaces of all subset windows with regard to the
3192 	specifed window.
3193 	If newIndex is not -1, it will move all subset windows that belong to
3194 	the specifed window to the new workspace; this form is only called by
3195 	SetWorkspace().
3196 */
3197 void
_UpdateSubsetWorkspaces(Window * window,int32 previousIndex,int32 newIndex)3198 Desktop::_UpdateSubsetWorkspaces(Window* window, int32 previousIndex,
3199 	int32 newIndex)
3200 {
3201 	STRACE(("_UpdateSubsetWorkspaces(window %p, %s)\n", window,
3202 		window->Title()));
3203 
3204 	// if the window is hidden, the subset windows are up-to-date already
3205 	if (!window->IsNormal() || window->IsHidden())
3206 		return;
3207 
3208 	for (Window* subset = fSubsetWindows.FirstWindow(); subset != NULL;
3209 			subset = subset->NextWindow(kSubsetList)) {
3210 		if (subset->Feel() == B_MODAL_ALL_WINDOW_FEEL
3211 			|| subset->Feel() == B_FLOATING_ALL_WINDOW_FEEL) {
3212 			// These windows are always visible on all workspaces,
3213 			// no need to update them.
3214 			continue;
3215 		}
3216 
3217 		if (subset->IsFloating()) {
3218 			// Floating windows are inserted and removed to the current
3219 			// workspace as the need arises - they are not handled here
3220 			// but in _UpdateFront()
3221 			continue;
3222 		}
3223 
3224 		if (subset->HasInSubset(window)) {
3225 			// adopt the workspace change
3226 			SetWindowWorkspaces(subset, subset->SubsetWorkspaces());
3227 		}
3228 	}
3229 }
3230 
3231 
3232 /*!	\brief Adds or removes the window to or from the workspaces it's on.
3233 */
3234 void
_ChangeWindowWorkspaces(Window * window,uint32 oldWorkspaces,uint32 newWorkspaces)3235 Desktop::_ChangeWindowWorkspaces(Window* window, uint32 oldWorkspaces,
3236 	uint32 newWorkspaces)
3237 {
3238 	if (oldWorkspaces == newWorkspaces)
3239 		return;
3240 
3241 	// apply changes to the workspaces' window lists
3242 
3243 	LockAllWindows();
3244 
3245 	// NOTE: we bypass the anchor-mechanism by intention when switching
3246 	// the workspace programmatically.
3247 
3248 	for (int32 i = 0; i < kMaxWorkspaces; i++) {
3249 		if (workspace_in_workspaces(i, oldWorkspaces)) {
3250 			// window is on this workspace, is it anymore?
3251 			if (!workspace_in_workspaces(i, newWorkspaces)) {
3252 				_Windows(i).RemoveWindow(window);
3253 				if (fLastWorkspaceFocus[i] == window)
3254 					fLastWorkspaceFocus[i] = NULL;
3255 
3256 				if (i == CurrentWorkspace()) {
3257 					// remove its appearance from the current workspace
3258 					window->SetCurrentWorkspace(-1);
3259 
3260 					if (!window->IsHidden())
3261 						_HideWindow(window);
3262 				}
3263 			}
3264 		} else {
3265 			// window was not on this workspace, is it now?
3266 			if (workspace_in_workspaces(i, newWorkspaces)) {
3267 				_Windows(i).AddWindow(window,
3268 					window->Frontmost(_Windows(i).FirstWindow(), i));
3269 
3270 				if (i == CurrentWorkspace()) {
3271 					// make the window visible in current workspace
3272 					window->SetCurrentWorkspace(fCurrentWorkspace);
3273 
3274 					if (!window->IsHidden()) {
3275 						// This only affects other windows if this window has
3276 						// floating or modal windows that need to be shown as
3277 						// well
3278 						// TODO: take care of this
3279 						_ShowWindow(window, FrontWindow() == window);
3280 					}
3281 				}
3282 			}
3283 		}
3284 	}
3285 
3286 	// If the window is visible only on one workspace, we set it's current
3287 	// position in that workspace (so that WorkspacesView will find us).
3288 	int32 firstWorkspace = -1;
3289 	for (int32 i = 0; i < kMaxWorkspaces; i++) {
3290 		if ((newWorkspaces & (1L << i)) != 0) {
3291 			if (firstWorkspace != -1) {
3292 				firstWorkspace = -1;
3293 				break;
3294 			}
3295 			firstWorkspace = i;
3296 		}
3297 	}
3298 	if (firstWorkspace >= 0)
3299 		window->Anchor(firstWorkspace).position = window->Frame().LeftTop();
3300 
3301 	// take care about modals and floating windows
3302 	_UpdateSubsetWorkspaces(window);
3303 
3304 	NotifyWindowWorkspacesChanged(window, newWorkspaces);
3305 
3306 	UnlockAllWindows();
3307 }
3308 
3309 
3310 void
_BringWindowsToFront(WindowList & windows,int32 list,bool wereVisible)3311 Desktop::_BringWindowsToFront(WindowList& windows, int32 list, bool wereVisible)
3312 {
3313 	// we don't need to redraw what is currently
3314 	// visible of the window
3315 	BRegion clean;
3316 
3317 	for (Window* window = windows.FirstWindow(); window != NULL;
3318 			window = window->NextWindow(list)) {
3319 		if (wereVisible)
3320 			clean.Include(&window->VisibleRegion());
3321 
3322 		CurrentWindows().AddWindow(window,
3323 			window->Frontmost(CurrentWindows().FirstWindow(),
3324 				fCurrentWorkspace));
3325 
3326 		_WindowChanged(window);
3327 	}
3328 
3329 	BRegion dummy;
3330 	_RebuildClippingForAllWindows(dummy);
3331 
3332 	// redraw what became visible of the window(s)
3333 
3334 	BRegion dirty;
3335 	for (Window* window = windows.FirstWindow(); window != NULL;
3336 			window = window->NextWindow(list)) {
3337 		dirty.Include(&window->VisibleRegion());
3338 	}
3339 
3340 	dirty.Exclude(&clean);
3341 	MarkDirty(dirty);
3342 
3343 	_UpdateFront();
3344 
3345 	if (windows.FirstWindow() == fBack || fBack == NULL)
3346 		_UpdateBack();
3347 }
3348 
3349 
3350 /*!	Returns the last focussed non-hidden subset window belonging to the
3351 	specified \a window.
3352 */
3353 Window*
_LastFocusSubsetWindow(Window * window)3354 Desktop::_LastFocusSubsetWindow(Window* window)
3355 {
3356 	if (window == NULL)
3357 		return NULL;
3358 
3359 	for (Window* front = fFocusList.LastWindow(); front != NULL;
3360 			front = front->PreviousWindow(kFocusList)) {
3361 		if (front != window && !front->IsHidden()
3362 			&& window->HasInSubset(front))
3363 			return front;
3364 	}
3365 
3366 	return NULL;
3367 }
3368 
3369 
3370 /*!	\brief Checks whether or not a fake mouse moved message needs to be sent
3371 	to the previous mouse window.
3372 
3373 	You need to have the all window lock held when calling this method.
3374 */
3375 bool
_CheckSendFakeMouseMoved(const Window * lastWindowUnderMouse)3376 Desktop::_CheckSendFakeMouseMoved(const Window* lastWindowUnderMouse)
3377 {
3378 	Window* window = WindowAt(fLastMousePosition);
3379 	return window != lastWindowUnderMouse;
3380 }
3381 
3382 
3383 /*!	\brief Sends a fake B_MOUSE_MOVED event to the window under the mouse,
3384 		and also updates the current view under the mouse.
3385 
3386 	This has only to be done in case the view changed without mouse movement,
3387 	ie. because of a workspace change, a closing window, or programmatic window
3388 	movement.
3389 
3390 	You must not have locked any windows when calling this method.
3391 */
3392 void
_SendFakeMouseMoved(Window * window)3393 Desktop::_SendFakeMouseMoved(Window* window)
3394 {
3395 	int32 viewToken = B_NULL_TOKEN;
3396 	EventTarget* target = NULL;
3397 
3398 	LockAllWindows();
3399 
3400 	if (window == NULL)
3401 		window = WindowAt(fLastMousePosition);
3402 
3403 	if (window != NULL) {
3404 		BMessage message;
3405 		window->MouseMoved(&message, fLastMousePosition, &viewToken, true,
3406 			true);
3407 
3408 		if (viewToken != B_NULL_TOKEN)
3409 			target = &window->EventTarget();
3410 	}
3411 
3412 	if (viewToken != B_NULL_TOKEN)
3413 		SetViewUnderMouse(window, viewToken);
3414 	else {
3415 		SetViewUnderMouse(NULL, B_NULL_TOKEN);
3416 		SetCursor(NULL);
3417 	}
3418 
3419 	UnlockAllWindows();
3420 
3421 	if (target != NULL)
3422 		EventDispatcher().SendFakeMouseMoved(*target, viewToken);
3423 }
3424 
3425 
3426 Screen*
_DetermineScreenFor(BRect frame)3427 Desktop::_DetermineScreenFor(BRect frame)
3428 {
3429 	AutoReadLocker _(fScreenLock);
3430 
3431 	// TODO: choose the screen depending on where most of the area is
3432 	return fVirtualScreen.ScreenAt(0);
3433 }
3434 
3435 
3436 void
_RebuildClippingForAllWindows(BRegion & stillAvailableOnScreen)3437 Desktop::_RebuildClippingForAllWindows(BRegion& stillAvailableOnScreen)
3438 {
3439 	// the available region on screen starts with the entire screen area
3440 	// each window on the screen will take a portion from that area
3441 
3442 	// figure out what the entire screen area is
3443 	stillAvailableOnScreen = fScreenRegion;
3444 
3445 	// set clipping of each window
3446 	for (Window* window = CurrentWindows().LastWindow(); window != NULL;
3447 			window = window->PreviousWindow(fCurrentWorkspace)) {
3448 		if (!window->IsHidden()) {
3449 			window->SetClipping(&stillAvailableOnScreen);
3450 			window->SetScreen(_DetermineScreenFor(window->Frame()));
3451 
3452 			if (window->ServerWindow()->IsDirectlyAccessing()) {
3453 				window->ServerWindow()->HandleDirectConnection(
3454 					B_DIRECT_MODIFY | B_CLIPPING_MODIFIED);
3455 			}
3456 
3457 			// that windows region is not available on screen anymore
3458 			stillAvailableOnScreen.Exclude(&window->VisibleRegion());
3459 		}
3460 	}
3461 }
3462 
3463 
3464 void
_TriggerWindowRedrawing(BRegion & dirtyRegion,BRegion & exposeRegion)3465 Desktop::_TriggerWindowRedrawing(BRegion& dirtyRegion, BRegion& exposeRegion)
3466 {
3467 	// send redraw messages to all windows intersecting the dirty region
3468 	for (Window* window = CurrentWindows().LastWindow(); window != NULL;
3469 			window = window->PreviousWindow(fCurrentWorkspace)) {
3470 		if (!window->IsHidden()
3471 			&& dirtyRegion.Intersects(window->VisibleRegion().Frame()))
3472 			window->ProcessDirtyRegion(dirtyRegion, exposeRegion);
3473 	}
3474 }
3475 
3476 
3477 void
_SetBackground(BRegion & background)3478 Desktop::_SetBackground(BRegion& background)
3479 {
3480 	// NOTE: the drawing operation is caried out
3481 	// in the clipping region rebuild, but it is
3482 	// ok actually, because it also avoids trails on
3483 	// moving windows
3484 
3485 	// remember the region not covered by any windows
3486 	// and redraw the dirty background
3487 	BRegion dirtyBackground(background);
3488 	dirtyBackground.Exclude(&fBackgroundRegion);
3489 	dirtyBackground.IntersectWith(&background);
3490 	fBackgroundRegion = background;
3491 	if (dirtyBackground.Frame().IsValid()) {
3492 		if (GetDrawingEngine()->LockParallelAccess()) {
3493 			GetDrawingEngine()->FillRegion(dirtyBackground,
3494 				fWorkspaces[fCurrentWorkspace].Color());
3495 
3496 			GetDrawingEngine()->UnlockParallelAccess();
3497 		}
3498 	}
3499 }
3500 
3501 
3502 //!	The all window lock must be held when calling this function.
3503 void
RebuildAndRedrawAfterWindowChange(Window * changedWindow,BRegion & dirty)3504 Desktop::RebuildAndRedrawAfterWindowChange(Window* changedWindow,
3505 	BRegion& dirty)
3506 {
3507 	ASSERT_MULTI_WRITE_LOCKED(fWindowLock);
3508 	if (!changedWindow->IsVisible() || dirty.CountRects() == 0)
3509 		return;
3510 
3511 	// The following loop is pretty much a copy of
3512 	// _RebuildClippingForAllWindows(), but will also
3513 	// take care about restricting our dirty region.
3514 
3515 	// figure out what the entire screen area is
3516 	BRegion stillAvailableOnScreen(fScreenRegion);
3517 
3518 	// set clipping of each window
3519 	for (Window* window = CurrentWindows().LastWindow(); window != NULL;
3520 			window = window->PreviousWindow(fCurrentWorkspace)) {
3521 		if (!window->IsHidden()) {
3522 			if (window == changedWindow)
3523 				dirty.IntersectWith(&stillAvailableOnScreen);
3524 
3525 			window->SetClipping(&stillAvailableOnScreen);
3526 			window->SetScreen(_DetermineScreenFor(window->Frame()));
3527 
3528 			if (window->ServerWindow()->IsDirectlyAccessing()) {
3529 				window->ServerWindow()->HandleDirectConnection(
3530 					B_DIRECT_MODIFY | B_CLIPPING_MODIFIED);
3531 			}
3532 
3533 			// that windows region is not available on screen anymore
3534 			stillAvailableOnScreen.Exclude(&window->VisibleRegion());
3535 		}
3536 	}
3537 
3538 	_SetBackground(stillAvailableOnScreen);
3539 	_WindowChanged(changedWindow);
3540 
3541 	_TriggerWindowRedrawing(dirty, dirty);
3542 }
3543 
3544 
3545 //! Suspend all windows with direct access to the frame buffer
3546 void
_SuspendDirectFrameBufferAccess()3547 Desktop::_SuspendDirectFrameBufferAccess()
3548 {
3549 	ASSERT_MULTI_LOCKED(fWindowLock);
3550 
3551 	for (Window* window = fAllWindows.FirstWindow(); window != NULL;
3552 			window = window->NextWindow(kAllWindowList)) {
3553 		if (window->ServerWindow()->IsDirectlyAccessing())
3554 			window->ServerWindow()->HandleDirectConnection(B_DIRECT_STOP);
3555 	}
3556 }
3557 
3558 
3559 //! Resume all windows with direct access to the frame buffer
3560 void
_ResumeDirectFrameBufferAccess()3561 Desktop::_ResumeDirectFrameBufferAccess()
3562 {
3563 	ASSERT_MULTI_LOCKED(fWindowLock);
3564 
3565 	for (Window* window = fAllWindows.FirstWindow(); window != NULL;
3566 			window = window->NextWindow(kAllWindowList)) {
3567 		if (window->IsHidden() || !window->InWorkspace(fCurrentWorkspace))
3568 			continue;
3569 
3570 		if (window->ServerWindow()->HasDirectFrameBufferAccess()) {
3571 			window->ServerWindow()->HandleDirectConnection(
3572 				B_DIRECT_START | B_BUFFER_RESET, B_MODE_CHANGED);
3573 		}
3574 	}
3575 }
3576 
3577 
3578 void
ScreenChanged(Screen * screen)3579 Desktop::ScreenChanged(Screen* screen)
3580 {
3581 	AutoWriteLocker windowLocker(fWindowLock);
3582 
3583 	AutoWriteLocker screenLocker(fScreenLock);
3584 	screen->SetPreferredMode();
3585 	screenLocker.Unlock();
3586 
3587 	_ScreenChanged(screen);
3588 }
3589 
3590 
3591 void
_ScreenChanged(Screen * screen)3592 Desktop::_ScreenChanged(Screen* screen)
3593 {
3594 	ASSERT_MULTI_WRITE_LOCKED(fWindowLock);
3595 
3596 	// the entire screen is dirty, because we're actually
3597 	// operating on an all new buffer in memory
3598 	BRegion dirty(screen->Frame());
3599 
3600 	// update our cached screen region
3601 	fScreenRegion.Set(screen->Frame());
3602 	gInputManager->UpdateScreenBounds(screen->Frame());
3603 
3604 	BRegion background;
3605 	_RebuildClippingForAllWindows(background);
3606 
3607 	fBackgroundRegion.MakeEmpty();
3608 		// makes sure that the complete background is redrawn
3609 	_SetBackground(background);
3610 
3611 	// figure out dirty region
3612 	dirty.Exclude(&background);
3613 	_TriggerWindowRedrawing(dirty, dirty);
3614 
3615 	// send B_SCREEN_CHANGED to windows on that screen
3616 	BMessage update(B_SCREEN_CHANGED);
3617 	update.AddInt64("when", real_time_clock_usecs());
3618 	update.AddRect("frame", screen->Frame());
3619 	update.AddInt32("mode", screen->ColorSpace());
3620 
3621 	fVirtualScreen.UpdateFrame();
3622 
3623 	for (Window* window = fAllWindows.FirstWindow(); window != NULL;
3624 			window = window->NextWindow(kAllWindowList)) {
3625 		if (window->Screen() == screen)
3626 			window->ServerWindow()->ScreenChanged(&update);
3627 	}
3628 }
3629 
3630 
3631 /*!	\brief activate one of the app's windows.
3632 */
3633 status_t
_ActivateApp(team_id team)3634 Desktop::_ActivateApp(team_id team)
3635 {
3636 	// search for an unhidden window in the current workspace
3637 
3638 	AutoWriteLocker locker(fWindowLock);
3639 
3640 	for (Window* window = CurrentWindows().LastWindow(); window != NULL;
3641 			window = window->PreviousWindow(fCurrentWorkspace)) {
3642 		if (!window->IsHidden() && window->IsNormal()
3643 			&& window->ServerWindow()->ClientTeam() == team) {
3644 			ActivateWindow(window);
3645 			return B_OK;
3646 		}
3647 	}
3648 
3649 	// search for an unhidden window to give focus to
3650 
3651 	for (Window* window = fAllWindows.FirstWindow(); window != NULL;
3652 			window = window->NextWindow(kAllWindowList)) {
3653 		// if window is a normal window of the team, and not hidden,
3654 		// we've found our target
3655 		if (!window->IsHidden() && window->IsNormal()
3656 			&& window->ServerWindow()->ClientTeam() == team) {
3657 			ActivateWindow(window);
3658 			return B_OK;
3659 		}
3660 	}
3661 
3662 	// TODO: we cannot maximize minimized windows here (with the window lock
3663 	// write locked). To work-around this, we could forward the request to
3664 	// the ServerApp of this team - it maintains its own window list, and can
3665 	// therefore call ActivateWindow() without holding the window lock.
3666 	return B_BAD_VALUE;
3667 }
3668 
3669 
3670 void
_SetCurrentWorkspaceConfiguration()3671 Desktop::_SetCurrentWorkspaceConfiguration()
3672 {
3673 	ASSERT_MULTI_WRITE_LOCKED(fWindowLock);
3674 
3675 	status_t status = fDirectScreenLock.LockWithTimeout(1000000L);
3676 	if (status != B_OK) {
3677 		// The application having the direct screen lock didn't give it up in
3678 		// time, make it crash
3679 		syslog(LOG_ERR, "Team %" B_PRId32 " did not give up its direct screen "
3680 			"lock.\n", fDirectScreenTeam);
3681 
3682 		debug_thread(fDirectScreenTeam);
3683 		fDirectScreenTeam = -1;
3684 	} else
3685 		fDirectScreenLock.Unlock();
3686 
3687 	AutoWriteLocker _(fScreenLock);
3688 
3689 	uint32 changedScreens;
3690 	fVirtualScreen.SetConfiguration(*this,
3691 		fWorkspaces[fCurrentWorkspace].CurrentScreenConfiguration(),
3692 		&changedScreens);
3693 
3694 	for (int32 i = 0; changedScreens != 0; i++, changedScreens /= 2) {
3695 		if ((changedScreens & (1 << i)) != 0)
3696 			_ScreenChanged(fVirtualScreen.ScreenAt(i));
3697 	}
3698 }
3699 
3700 
3701 /*!	Changes the current workspace to the one specified by \a index.
3702 	You must hold the all window lock when calling this method.
3703 */
3704 void
_SetWorkspace(int32 index,bool moveFocusWindow)3705 Desktop::_SetWorkspace(int32 index, bool moveFocusWindow)
3706 {
3707 	ASSERT_MULTI_WRITE_LOCKED(fWindowLock);
3708 
3709 	int32 previousIndex = fCurrentWorkspace;
3710 	rgb_color previousColor = fWorkspaces[fCurrentWorkspace].Color();
3711 	bool movedMouseEventWindow = false;
3712 	Window* movedWindow = NULL;
3713 	if (moveFocusWindow) {
3714 		if (fMouseEventWindow != NULL)
3715 			movedWindow = fMouseEventWindow;
3716 		else
3717 			movedWindow = FocusWindow();
3718 	}
3719 
3720 	if (movedWindow != NULL) {
3721 		if (movedWindow->IsNormal()) {
3722 			if (!movedWindow->InWorkspace(index)) {
3723 				// The window currently being dragged will follow us to this
3724 				// workspace if it's not already on it.
3725 				// But only normal windows are following
3726 				uint32 oldWorkspaces = movedWindow->Workspaces();
3727 
3728 				WindowStack* stack = movedWindow->GetWindowStack();
3729 				if (stack != NULL) {
3730 					for (int32 s = 0; s < stack->CountWindows(); s++) {
3731 						Window* stackWindow = stack->LayerOrder().ItemAt(s);
3732 
3733 						_Windows(previousIndex).RemoveWindow(stackWindow);
3734 						_Windows(index).AddWindow(stackWindow,
3735 							stackWindow->Frontmost(
3736 								_Windows(index).FirstWindow(), index));
3737 
3738 						// send B_WORKSPACES_CHANGED message
3739 						stackWindow->WorkspacesChanged(oldWorkspaces,
3740 							stackWindow->Workspaces());
3741 					}
3742 				}
3743 				// TODO: subset windows will always flicker this way
3744 
3745 				movedMouseEventWindow = true;
3746 
3747 				NotifyWindowWorkspacesChanged(movedWindow,
3748 					movedWindow->Workspaces());
3749 			} else {
3750 				// make sure it's frontmost
3751 				_Windows(index).RemoveWindow(movedWindow);
3752 				_Windows(index).AddWindow(movedWindow,
3753 					movedWindow->Frontmost(_Windows(index).FirstWindow(),
3754 					index));
3755 			}
3756 		}
3757 
3758 		movedWindow->Anchor(index).position = movedWindow->Frame().LeftTop();
3759 	}
3760 
3761 	if (movedWindow == NULL || movedWindow->InWorkspace(previousIndex))
3762 		fLastWorkspaceFocus[previousIndex] = FocusWindow();
3763 	else
3764 		fLastWorkspaceFocus[previousIndex] = NULL;
3765 
3766 	// build region of windows that are no longer visible in the new workspace
3767 
3768 	BRegion dirty;
3769 
3770 	for (Window* window = CurrentWindows().FirstWindow();
3771 			window != NULL; window = window->NextWindow(previousIndex)) {
3772 		// store current position in Workspace anchor
3773 		window->Anchor(previousIndex).position = window->Frame().LeftTop();
3774 
3775 		if (!window->IsHidden()
3776 			&& window->ServerWindow()->IsDirectlyAccessing())
3777 			window->ServerWindow()->HandleDirectConnection(B_DIRECT_STOP);
3778 
3779 		window->WorkspaceActivated(previousIndex, false);
3780 
3781 		if (window->InWorkspace(index))
3782 			continue;
3783 
3784 		if (!window->IsHidden()) {
3785 			// this window will no longer be visible
3786 			dirty.Include(&window->VisibleRegion());
3787 		}
3788 
3789 		window->SetCurrentWorkspace(-1);
3790 	}
3791 
3792 	fPreviousWorkspace = fCurrentWorkspace;
3793 	fCurrentWorkspace = index;
3794 
3795 	// Change the display modes, if needed
3796 	_SetCurrentWorkspaceConfiguration();
3797 
3798 	// Show windows, and include them in the changed region - but only
3799 	// those that were not visible before (or whose position changed)
3800 
3801 	WindowList windows(kWorkingList);
3802 	BList previousRegions;
3803 
3804 	for (Window* window = _Windows(index).FirstWindow();
3805 			window != NULL; window = window->NextWindow(index)) {
3806 		BPoint position = window->Anchor(index).position;
3807 
3808 		window->SetCurrentWorkspace(index);
3809 
3810 		if (window->IsHidden())
3811 			continue;
3812 
3813 		if (position == kInvalidWindowPosition) {
3814 			// if you enter a workspace for the first time, the position
3815 			// of the window in the previous workspace is adopted
3816 			position = window->Frame().LeftTop();
3817 				// TODO: make sure the window is still on-screen if it
3818 				//	was before!
3819 		}
3820 
3821 		if (!window->InWorkspace(previousIndex)) {
3822 			// This window was not visible before, make sure its frame
3823 			// is up-to-date
3824 			if (window->Frame().LeftTop() != position) {
3825 				BPoint offset = position - window->Frame().LeftTop();
3826 				window->MoveBy((int32)offset.x, (int32)offset.y);
3827 			}
3828 			continue;
3829 		}
3830 
3831 		if (window->Frame().LeftTop() != position) {
3832 			// the window was visible before, but its on-screen location changed
3833 			BPoint offset = position - window->Frame().LeftTop();
3834 			MoveWindowBy(window, offset.x, offset.y);
3835 				// TODO: be a bit smarter than this...
3836 		} else {
3837 			// We need to remember the previous visible region of the
3838 			// window if they changed their order
3839 			ObjectDeleter<BRegion> region(new (std::nothrow)
3840 				BRegion(window->VisibleRegion()));
3841 			if (region.IsSet()) {
3842 				if (previousRegions.AddItem(region.Detach()))
3843 					windows.AddWindow(window);
3844 			}
3845 		}
3846 	}
3847 
3848 	_UpdateFronts(false);
3849 	_UpdateFloating(previousIndex, index,
3850 		movedMouseEventWindow ? movedWindow : NULL);
3851 
3852 	BRegion stillAvailableOnScreen;
3853 	_RebuildClippingForAllWindows(stillAvailableOnScreen);
3854 	_SetBackground(stillAvailableOnScreen);
3855 
3856 	for (Window* window = _Windows(index).FirstWindow(); window != NULL;
3857 			window = window->NextWindow(index)) {
3858 		// send B_WORKSPACE_ACTIVATED message
3859 		window->WorkspaceActivated(index, true);
3860 
3861 		if (!window->IsHidden()
3862 			&& window->ServerWindow()->HasDirectFrameBufferAccess()) {
3863 			window->ServerWindow()->HandleDirectConnection(
3864 				B_DIRECT_START | B_BUFFER_RESET, B_MODE_CHANGED);
3865 		}
3866 
3867 		if (window->InWorkspace(previousIndex) || window->IsHidden()
3868 			|| (window == movedWindow && movedWindow->IsNormal())
3869 			|| (!window->IsNormal()
3870 				&& window->HasInSubset(movedWindow))) {
3871 			// This window was visible before, and is already handled in the
3872 			// above loop
3873 			continue;
3874 		}
3875 
3876 		dirty.Include(&window->VisibleRegion());
3877 	}
3878 
3879 	// Catch order changes in the new workspaces window list
3880 	int32 i = 0;
3881 	for (Window* window = windows.FirstWindow(); window != NULL;
3882 			window = window->NextWindow(kWorkingList), i++) {
3883 		BRegion* region = (BRegion*)previousRegions.ItemAt(i);
3884 		region->ExclusiveInclude(&window->VisibleRegion());
3885 		dirty.Include(region);
3886 		delete region;
3887 	}
3888 
3889 	// Set new focus, but keep focus to a floating window if still visible
3890 	if (movedWindow != NULL)
3891 		SetFocusWindow(movedWindow);
3892 	else if (!_Windows(index).HasWindow(FocusWindow())
3893 		|| (FocusWindow() != NULL && !FocusWindow()->IsFloating()))
3894 		SetFocusWindow(fLastWorkspaceFocus[index]);
3895 
3896 	_WindowChanged(NULL);
3897 	MarkDirty(dirty);
3898 
3899 #if 0
3900 	// Show the dirty regions of this workspace switch
3901 	if (GetDrawingEngine()->LockParallelAccess()) {
3902 		GetDrawingEngine()->FillRegion(dirty, (rgb_color){255, 0, 0});
3903 		GetDrawingEngine()->UnlockParallelAccess();
3904 		snooze(100000);
3905 	}
3906 #endif
3907 
3908 	if (previousColor != fWorkspaces[fCurrentWorkspace].Color())
3909 		RedrawBackground();
3910 }
3911