xref: /haiku/src/servers/app/Window.cpp (revision b5ba4badb3098efc0ef2fb32cf8347823379dd75)
1 /*
2  * Copyright 2001-2020, Haiku, Inc.
3  * Distributed under the terms of the MIT license.
4  *
5  * Authors:
6  *		DarkWyrm, bpmagic@columbus.rr.com
7  *		Adi Oanca, adioanca@gmail.com
8  *		Stephan Aßmus, superstippi@gmx.de
9  *		Axel Dörfler, axeld@pinc-software.de
10  *		Brecht Machiels, brecht@mos6581.org
11  *		Clemens Zeidler, haiku@clemens-zeidler.de
12  *		Tri-Edge AI
13  *		Jacob Secunda, secundja@gmail.com
14  */
15 
16 
17 #include "Window.h"
18 
19 #include <new>
20 #include <stdio.h>
21 
22 #include <Debug.h>
23 
24 #include <DirectWindow.h>
25 #include <PortLink.h>
26 #include <View.h>
27 #include <ViewPrivate.h>
28 #include <WindowPrivate.h>
29 
30 #include "ClickTarget.h"
31 #include "Decorator.h"
32 #include "DecorManager.h"
33 #include "Desktop.h"
34 #include "DrawingEngine.h"
35 #include "HWInterface.h"
36 #include "MessagePrivate.h"
37 #include "PortLink.h"
38 #include "ServerApp.h"
39 #include "ServerWindow.h"
40 #include "WindowBehaviour.h"
41 #include "Workspace.h"
42 #include "WorkspacesView.h"
43 
44 
45 // Toggle debug output
46 //#define DEBUG_WINDOW
47 
48 #ifdef DEBUG_WINDOW
49 #	define STRACE(x) printf x
50 #else
51 #	define STRACE(x) ;
52 #endif
53 
54 // IMPORTANT: nested LockSingleWindow()s are not supported (by MultiLocker)
55 
56 using std::nothrow;
57 
58 // if the background clearing is delayed until
59 // the client draws the view, we have less flickering
60 // when contents have to be redrawn because of resizing
61 // a window or because the client invalidates parts.
62 // when redrawing something that has been exposed from underneath
63 // other windows, the other window will be seen longer at
64 // its previous position though if the exposed parts are not
65 // cleared right away. maybe there ought to be a flag in
66 // the update session, which tells us the cause of the update
67 
68 
69 //static rgb_color sPendingColor = (rgb_color){ 255, 255, 0, 255 };
70 //static rgb_color sCurrentColor = (rgb_color){ 255, 0, 255, 255 };
71 
72 
Window(const BRect & frame,const char * name,window_look look,window_feel feel,uint32 flags,uint32 workspaces,::ServerWindow * window,DrawingEngine * drawingEngine)73 Window::Window(const BRect& frame, const char *name,
74 		window_look look, window_feel feel, uint32 flags, uint32 workspaces,
75 		::ServerWindow* window, DrawingEngine* drawingEngine)
76 	:
77 	fTitle(name),
78 	fFrame(frame),
79 	fScreen(NULL),
80 
81 	fVisibleRegion(),
82 	fVisibleContentRegion(),
83 	fDirtyRegion(),
84 
85 	fContentRegion(),
86 	fEffectiveDrawingRegion(),
87 
88 	fVisibleContentRegionValid(false),
89 	fContentRegionValid(false),
90 	fEffectiveDrawingRegionValid(false),
91 
92 	fRegionPool(),
93 
94 	fWindow(window),
95 	fDrawingEngine(drawingEngine),
96 	fDesktop(window->Desktop()),
97 
98 	fCurrentUpdateSession(&fUpdateSessions[0]),
99 	fPendingUpdateSession(&fUpdateSessions[1]),
100 	fUpdateRequested(false),
101 	fInUpdate(false),
102 	fUpdatesEnabled(false),
103 
104 	// Windows start hidden
105 	fHidden(true),
106 	// Hidden is 1 or more
107 	fShowLevel(1),
108 	fMinimized(false),
109 	fIsFocus(false),
110 
111 	fLook(look),
112 	fFeel(feel),
113 	fWorkspaces(workspaces),
114 	fCurrentWorkspace(-1),
115 
116 	fMinWidth(1),
117 	fMaxWidth(32768),
118 	fMinHeight(1),
119 	fMaxHeight(32768),
120 
121 	fWorkspacesViewCount(0)
122 {
123 	_InitWindowStack();
124 
125 	// make sure our arguments are valid
126 	if (!IsValidLook(fLook))
127 		fLook = B_TITLED_WINDOW_LOOK;
128 	if (!IsValidFeel(fFeel))
129 		fFeel = B_NORMAL_WINDOW_FEEL;
130 
131 	SetFlags(flags, NULL);
132 
133 	if (fLook != B_NO_BORDER_WINDOW_LOOK && fCurrentStack.IsSet()) {
134 		// allocates a decorator
135 		::Decorator* decorator = Decorator();
136 		if (decorator != NULL) {
137 			decorator->GetSizeLimits(&fMinWidth, &fMinHeight, &fMaxWidth,
138 				&fMaxHeight);
139 		}
140 	}
141 	if (fFeel != kOffscreenWindowFeel)
142 		fWindowBehaviour.SetTo(gDecorManager.AllocateWindowBehaviour(this));
143 
144 	// do we need to change our size to let the decorator fit?
145 	// _ResizeBy() will adapt the frame for validity before resizing
146 	if (feel == kDesktopWindowFeel) {
147 		// the desktop window spans over the whole screen
148 		// TODO: this functionality should be moved somewhere else
149 		//  (so that it is always used when the workspace is changed)
150 		uint16 width, height;
151 		uint32 colorSpace;
152 		float frequency;
153 		if (Screen() != NULL) {
154 			Screen()->GetMode(width, height, colorSpace, frequency);
155 // TODO: MOVE THIS AWAY!!! ResizeBy contains calls to virtual methods!
156 // Also, there is no TopView()!
157 			fFrame.OffsetTo(B_ORIGIN);
158 //			ResizeBy(width - frame.Width(), height - frame.Height(), NULL);
159 		}
160 	}
161 
162 	STRACE(("Window %p, %s:\n", this, Name()));
163 	STRACE(("\tFrame: (%.1f, %.1f, %.1f, %.1f)\n", fFrame.left, fFrame.top,
164 		fFrame.right, fFrame.bottom));
165 	STRACE(("\tWindow %s\n", window ? window->Title() : "NULL"));
166 }
167 
168 
~Window()169 Window::~Window()
170 {
171 	if (fTopView.IsSet()) {
172 		fTopView->DetachedFromWindow();
173 	}
174 
175 	DetachFromWindowStack(false);
176 
177 	gDecorManager.CleanupForWindow(this);
178 }
179 
180 
181 status_t
InitCheck() const182 Window::InitCheck() const
183 {
184 	if (GetDrawingEngine() == NULL
185 		|| (fFeel != kOffscreenWindowFeel && !fWindowBehaviour.IsSet()))
186 		return B_NO_MEMORY;
187 	// TODO: anything else?
188 	return B_OK;
189 }
190 
191 
192 void
SetClipping(BRegion * stillAvailableOnScreen)193 Window::SetClipping(BRegion* stillAvailableOnScreen)
194 {
195 	// this function is only called from the Desktop thread
196 
197 	// start from full region (as if the window was fully visible)
198 	GetFullRegion(&fVisibleRegion);
199 	// clip to region still available on screen
200 	fVisibleRegion.IntersectWith(stillAvailableOnScreen);
201 
202 	fVisibleContentRegionValid = false;
203 	fEffectiveDrawingRegionValid = false;
204 }
205 
206 
207 void
GetFullRegion(BRegion * region)208 Window::GetFullRegion(BRegion* region)
209 {
210 	// TODO: if someone needs to call this from
211 	// the outside, the clipping needs to be readlocked!
212 
213 	// start from the decorator border, extend to use the frame
214 	GetBorderRegion(region);
215 	region->Include(fFrame);
216 }
217 
218 
219 void
GetBorderRegion(BRegion * region)220 Window::GetBorderRegion(BRegion* region)
221 {
222 	// TODO: if someone needs to call this from
223 	// the outside, the clipping needs to be readlocked!
224 
225 	::Decorator* decorator = Decorator();
226 	if (decorator)
227 		*region = decorator->GetFootprint();
228 	else
229 		region->MakeEmpty();
230 }
231 
232 
233 void
GetContentRegion(BRegion * region)234 Window::GetContentRegion(BRegion* region)
235 {
236 	// TODO: if someone needs to call this from
237 	// the outside, the clipping needs to be readlocked!
238 
239 	if (!fContentRegionValid) {
240 		_UpdateContentRegion();
241 	}
242 
243 	*region = fContentRegion;
244 }
245 
246 
247 BRegion&
VisibleContentRegion()248 Window::VisibleContentRegion()
249 {
250 	// TODO: if someone needs to call this from
251 	// the outside, the clipping needs to be readlocked!
252 
253 	// regions expected to be locked
254 	if (!fVisibleContentRegionValid) {
255 		GetContentRegion(&fVisibleContentRegion);
256 		fVisibleContentRegion.IntersectWith(&fVisibleRegion);
257 	}
258 	return fVisibleContentRegion;
259 }
260 
261 
262 // #pragma mark -
263 
264 
265 void
_PropagatePosition()266 Window::_PropagatePosition()
267 {
268 	if ((fFlags & B_SAME_POSITION_IN_ALL_WORKSPACES) == 0)
269 		return;
270 
271 	for (int32 i = 0; i < kListCount; i++) {
272 		Anchor(i).position = fFrame.LeftTop();
273 	}
274 }
275 
276 
277 void
MoveBy(int32 x,int32 y,bool moveStack)278 Window::MoveBy(int32 x, int32 y, bool moveStack)
279 {
280 	// this function is only called from the desktop thread
281 
282 	if (x == 0 && y == 0)
283 		return;
284 
285 	fFrame.OffsetBy(x, y);
286 	_PropagatePosition();
287 
288 	// take along the dirty region which is not
289 	// processed yet
290 	fDirtyRegion.OffsetBy(x, y);
291 	fExposeRegion.OffsetBy(x, y);
292 
293 	if (fContentRegionValid)
294 		fContentRegion.OffsetBy(x, y);
295 
296 	if (fCurrentUpdateSession->IsUsed())
297 		fCurrentUpdateSession->MoveBy(x, y);
298 	if (fPendingUpdateSession->IsUsed())
299 		fPendingUpdateSession->MoveBy(x, y);
300 
301 	fEffectiveDrawingRegionValid = false;
302 
303 	if (fTopView.IsSet()) {
304 		fTopView->MoveBy(x, y, NULL);
305 		fTopView->UpdateOverlay();
306 	}
307 
308 	::Decorator* decorator = Decorator();
309 	if (moveStack && decorator)
310 		decorator->MoveBy(x, y);
311 
312 	WindowStack* stack = GetWindowStack();
313 	if (moveStack && stack) {
314 		for (int32 i = 0; i < stack->CountWindows(); i++) {
315 			Window* window = stack->WindowList().ItemAt(i);
316 			if (window == this)
317 				continue;
318 			window->MoveBy(x, y, false);
319 		}
320 	}
321 
322 	// the desktop will take care of dirty regions
323 
324 	// dispatch a message to the client informing about the changed size
325 	BMessage msg(B_WINDOW_MOVED);
326 	msg.AddInt64("when", system_time());
327 	msg.AddPoint("where", fFrame.LeftTop());
328 	fWindow->SendMessageToClient(&msg);
329 }
330 
331 
332 void
ResizeBy(int32 x,int32 y,BRegion * dirtyRegion,bool resizeStack)333 Window::ResizeBy(int32 x, int32 y, BRegion* dirtyRegion, bool resizeStack)
334 {
335 	// this function is only called from the desktop thread
336 
337 	int32 wantWidth = fFrame.IntegerWidth() + x;
338 	int32 wantHeight = fFrame.IntegerHeight() + y;
339 
340 	// enforce size limits
341 	WindowStack* stack = GetWindowStack();
342 	if (resizeStack && stack) {
343 		for (int32 i = 0; i < stack->CountWindows(); i++) {
344 			Window* window = stack->WindowList().ItemAt(i);
345 
346 			if (wantWidth < window->fMinWidth)
347 				wantWidth = window->fMinWidth;
348 			if (wantWidth > window->fMaxWidth)
349 				wantWidth = window->fMaxWidth;
350 
351 			if (wantHeight < window->fMinHeight)
352 				wantHeight = window->fMinHeight;
353 			if (wantHeight > window->fMaxHeight)
354 				wantHeight = window->fMaxHeight;
355 		}
356 	}
357 
358 	x = wantWidth - fFrame.IntegerWidth();
359 	y = wantHeight - fFrame.IntegerHeight();
360 
361 	if (x == 0 && y == 0)
362 		return;
363 
364 	fFrame.right += x;
365 	fFrame.bottom += y;
366 
367 	fContentRegionValid = false;
368 	fEffectiveDrawingRegionValid = false;
369 
370 	if (fTopView.IsSet()) {
371 		fTopView->ResizeBy(x, y, dirtyRegion);
372 		fTopView->UpdateOverlay();
373 	}
374 
375 	::Decorator* decorator = Decorator();
376 	if (decorator && resizeStack)
377 		decorator->ResizeBy(x, y, dirtyRegion);
378 
379 	if (resizeStack && stack) {
380 		for (int32 i = 0; i < stack->CountWindows(); i++) {
381 			Window* window = stack->WindowList().ItemAt(i);
382 			if (window == this)
383 				continue;
384 			window->ResizeBy(x, y, dirtyRegion, false);
385 		}
386 	}
387 
388 	// send a message to the client informing about the changed size
389 	BRect frame(Frame());
390 	BMessage msg(B_WINDOW_RESIZED);
391 	msg.AddInt64("when", system_time());
392 	msg.AddInt32("width", frame.IntegerWidth());
393 	msg.AddInt32("height", frame.IntegerHeight());
394 	fWindow->SendMessageToClient(&msg);
395 }
396 
397 
398 void
SetOutlinesDelta(BPoint delta,BRegion * dirtyRegion)399 Window::SetOutlinesDelta(BPoint delta, BRegion* dirtyRegion)
400 {
401 	float wantWidth = fFrame.IntegerWidth() + delta.x;
402 	float wantHeight = fFrame.IntegerHeight() + delta.y;
403 
404 	// enforce size limits
405 	WindowStack* stack = GetWindowStack();
406 	if (stack != NULL) {
407 		for (int32 i = 0; i < stack->CountWindows(); i++) {
408 			Window* window = stack->WindowList().ItemAt(i);
409 
410 			if (wantWidth < window->fMinWidth)
411 				wantWidth = window->fMinWidth;
412 			if (wantWidth > window->fMaxWidth)
413 				wantWidth = window->fMaxWidth;
414 
415 			if (wantHeight < window->fMinHeight)
416 				wantHeight = window->fMinHeight;
417 			if (wantHeight > window->fMaxHeight)
418 				wantHeight = window->fMaxHeight;
419 		}
420 
421 		delta.x = wantWidth - fFrame.IntegerWidth();
422 		delta.y = wantHeight - fFrame.IntegerHeight();
423 	}
424 
425 	::Decorator* decorator = Decorator();
426 
427 	if (decorator != NULL)
428 		decorator->SetOutlinesDelta(delta, dirtyRegion);
429 
430 	_UpdateContentRegion();
431 }
432 
433 
434 void
ScrollViewBy(View * view,int32 dx,int32 dy)435 Window::ScrollViewBy(View* view, int32 dx, int32 dy)
436 {
437 	// this is executed in ServerWindow with the Readlock
438 	// held
439 
440 	if (!view || view == fTopView.Get() || (dx == 0 && dy == 0))
441 		return;
442 
443 	BRegion* dirty = fRegionPool.GetRegion();
444 	if (!dirty)
445 		return;
446 
447 	view->ScrollBy(dx, dy, dirty);
448 
449 //fDrawingEngine->FillRegion(*dirty, (rgb_color){ 255, 0, 255, 255 });
450 //snooze(20000);
451 
452 	if (!IsOffscreenWindow() && IsVisible() && view->IsVisible()) {
453 		dirty->IntersectWith(&VisibleContentRegion());
454 		_TriggerContentRedraw(*dirty);
455 	}
456 
457 	fRegionPool.Recycle(dirty);
458 }
459 
460 
461 //! Takes care of invalidating parts that could not be copied
462 void
CopyContents(BRegion * region,int32 xOffset,int32 yOffset)463 Window::CopyContents(BRegion* region, int32 xOffset, int32 yOffset)
464 {
465 	// executed in ServerWindow thread with the read lock held
466 	if (!IsVisible())
467 		return;
468 
469 	BRegion* newDirty = fRegionPool.GetRegion(*region);
470 
471 	// clip the region to the visible contents at the
472 	// source and destination location (note that VisibleContentRegion()
473 	// is used once to make sure it is valid, then fVisibleContentRegion
474 	// is used directly)
475 	region->IntersectWith(&VisibleContentRegion());
476 	if (region->CountRects() > 0) {
477 		// Constrain to content region at destination
478 		region->OffsetBy(xOffset, yOffset);
479 		region->IntersectWith(&fVisibleContentRegion);
480 		if (region->CountRects() > 0) {
481 			// if the region still contains any rects
482 			// offset to source location again
483 			region->OffsetBy(-xOffset, -yOffset);
484 
485 			BRegion* allDirtyRegions = fRegionPool.GetRegion(fDirtyRegion);
486 			if (allDirtyRegions != NULL) {
487 				if (fPendingUpdateSession->IsUsed()) {
488 					allDirtyRegions->Include(
489 						&fPendingUpdateSession->DirtyRegion());
490 				}
491 				if (fCurrentUpdateSession->IsUsed()) {
492 					allDirtyRegions->Include(
493 						&fCurrentUpdateSession->DirtyRegion());
494 				}
495 				// Get just the part of the dirty regions which is semantically
496 				// copied along
497 				allDirtyRegions->IntersectWith(region);
498 			}
499 
500 			BRegion* copyRegion = fRegionPool.GetRegion(*region);
501 			if (copyRegion != NULL) {
502 				// never copy what's already dirty
503 				if (allDirtyRegions != NULL)
504 					copyRegion->Exclude(allDirtyRegions);
505 
506 				if (fDrawingEngine->LockParallelAccess()) {
507 					fDrawingEngine->CopyRegion(copyRegion, xOffset, yOffset);
508 					fDrawingEngine->UnlockParallelAccess();
509 
510 					// Prevent those parts from being added to the dirty region...
511 					newDirty->Exclude(copyRegion);
512 
513 					// The parts that could be copied are not dirty (at the
514 					// target location!)
515 					copyRegion->OffsetBy(xOffset, yOffset);
516 					// ... and even exclude them from the pending dirty region!
517 					if (fPendingUpdateSession->IsUsed())
518 						fPendingUpdateSession->DirtyRegion().Exclude(copyRegion);
519 				}
520 
521 				fRegionPool.Recycle(copyRegion);
522 			} else {
523 				// Fallback, should never be here.
524 				if (fDrawingEngine->LockParallelAccess()) {
525 					fDrawingEngine->CopyRegion(region, xOffset, yOffset);
526 					fDrawingEngine->UnlockParallelAccess();
527 				}
528 			}
529 
530 			if (allDirtyRegions != NULL)
531 				fRegionPool.Recycle(allDirtyRegions);
532 		}
533 	}
534 	// what is left visible from the original region
535 	// at the destination after the region which could be
536 	// copied has been excluded, is considered dirty
537 	// NOTE: it may look like dirty regions are not moved
538 	// if no region could be copied, but that's alright,
539 	// since these parts will now be in newDirty anyways
540 	// (with the right offset)
541 	newDirty->OffsetBy(xOffset, yOffset);
542 	newDirty->IntersectWith(&fVisibleContentRegion);
543 	if (newDirty->CountRects() > 0)
544 		ProcessDirtyRegion(*newDirty);
545 
546 	fRegionPool.Recycle(newDirty);
547 }
548 
549 
550 // #pragma mark -
551 
552 
553 void
SetTopView(View * topView)554 Window::SetTopView(View* topView)
555 {
556 	if (fTopView.IsSet()) {
557 		fTopView->DetachedFromWindow();
558 	}
559 
560 	fTopView.SetTo(topView);
561 
562 	if (fTopView.IsSet()) {
563 		// the top view is special, it has a coordinate system
564 		// as if it was attached directly to the desktop, therefor,
565 		// the coordinate conversion through the view tree works
566 		// as expected, since the top view has no "parent" but has
567 		// fFrame as if it had
568 
569 		// make sure the location of the top view on screen matches ours
570 		fTopView->MoveBy((int32)(fFrame.left - fTopView->Frame().left),
571 			(int32)(fFrame.top - fTopView->Frame().top), NULL);
572 
573 		// make sure the size of the top view matches ours
574 		fTopView->ResizeBy((int32)(fFrame.Width() - fTopView->Frame().Width()),
575 			(int32)(fFrame.Height() - fTopView->Frame().Height()), NULL);
576 
577 		fTopView->AttachedToWindow(this);
578 	}
579 }
580 
581 
582 View*
ViewAt(const BPoint & where)583 Window::ViewAt(const BPoint& where)
584 {
585 	return fTopView->ViewAt(where);
586 }
587 
588 
589 window_anchor&
Anchor(int32 index)590 Window::Anchor(int32 index)
591 {
592 	return fAnchor[index];
593 }
594 
595 
596 Window*
NextWindow(int32 index) const597 Window::NextWindow(int32 index) const
598 {
599 	return fAnchor[index].next;
600 }
601 
602 
603 Window*
PreviousWindow(int32 index) const604 Window::PreviousWindow(int32 index) const
605 {
606 	return fAnchor[index].previous;
607 }
608 
609 
610 ::Decorator*
Decorator() const611 Window::Decorator() const
612 {
613 	if (!fCurrentStack.IsSet())
614 		return NULL;
615 	return fCurrentStack->Decorator();
616 }
617 
618 
619 bool
ReloadDecor()620 Window::ReloadDecor()
621 {
622 	::Decorator* decorator = NULL;
623 	WindowBehaviour* windowBehaviour = NULL;
624 	WindowStack* stack = GetWindowStack();
625 	if (stack == NULL)
626 		return false;
627 
628 	// only reload the window at the first position
629 	if (stack->WindowAt(0) != this)
630 		return true;
631 
632 	if (fLook != B_NO_BORDER_WINDOW_LOOK) {
633 		// we need a new decorator
634 		decorator = gDecorManager.AllocateDecorator(this);
635 		if (decorator == NULL)
636 			return false;
637 
638 		// add all tabs to the decorator
639 		for (int32 i = 1; i < stack->CountWindows(); i++) {
640 			Window* window = stack->WindowAt(i);
641 			BRegion dirty;
642 			DesktopSettings settings(fDesktop);
643 			if (decorator->AddTab(settings, window->Title(), window->Look(),
644 				window->Flags(), -1, &dirty) == NULL) {
645 				delete decorator;
646 				return false;
647 			}
648 		}
649 	} else
650 		return true;
651 
652 	windowBehaviour = gDecorManager.AllocateWindowBehaviour(this);
653 	if (windowBehaviour == NULL) {
654 		delete decorator;
655 		return false;
656 	}
657 
658 	stack->SetDecorator(decorator);
659 
660 	fWindowBehaviour.SetTo(windowBehaviour);
661 
662 	// set the correct focus and top layer tab
663 	for (int32 i = 0; i < stack->CountWindows(); i++) {
664 		Window* window = stack->WindowAt(i);
665 		if (window->IsFocus())
666 			decorator->SetFocus(i, true);
667 		if (window == stack->TopLayerWindow())
668 			decorator->SetTopTab(i);
669 	}
670 
671 	return true;
672 }
673 
674 
675 void
SetScreen(const::Screen * screen)676 Window::SetScreen(const ::Screen* screen)
677 {
678 	// TODO this assert fails in Desktop::ShowWindow
679 	//ASSERT_MULTI_WRITE_LOCKED(fDesktop->ScreenLocker());
680 	fScreen = screen;
681 }
682 
683 
684 const ::Screen*
Screen() const685 Window::Screen() const
686 {
687 	// TODO this assert also fails
688 	//ASSERT_MULTI_READ_LOCKED(fDesktop->ScreenLocker());
689 	return fScreen;
690 }
691 
692 
693 // #pragma mark -
694 
695 
696 void
GetEffectiveDrawingRegion(View * view,BRegion & region)697 Window::GetEffectiveDrawingRegion(View* view, BRegion& region)
698 {
699 	if (!fEffectiveDrawingRegionValid) {
700 		fEffectiveDrawingRegion = VisibleContentRegion();
701 		if (fUpdateRequested && !fInUpdate) {
702 			// We requested an update, but the client has not started it yet,
703 			// so it is only allowed to draw outside the pending update sessions
704 			// region
705 			fEffectiveDrawingRegion.Exclude(
706 				&fPendingUpdateSession->DirtyRegion());
707 		} else if (fInUpdate) {
708 			// enforce the dirty region of the update session
709 			fEffectiveDrawingRegion.IntersectWith(
710 				&fCurrentUpdateSession->DirtyRegion());
711 		} else {
712 			// not in update, the view can draw everywhere
713 //printf("Window(%s)::GetEffectiveDrawingRegion(for %s) - outside update\n", Title(), view->Name());
714 		}
715 
716 		fEffectiveDrawingRegionValid = true;
717 	}
718 
719 	// TODO: this is a region that needs to be cached later in the server
720 	// when the current view in ServerWindow is set, and we are currently
721 	// in an update (fInUpdate), than we can set this region and remember
722 	// it for the comming drawing commands until the current view changes
723 	// again or fEffectiveDrawingRegionValid is suddenly false.
724 	region = fEffectiveDrawingRegion;
725 	if (!fContentRegionValid)
726 		_UpdateContentRegion();
727 
728 	region.IntersectWith(&view->ScreenAndUserClipping(&fContentRegion));
729 }
730 
731 
732 bool
DrawingRegionChanged(View * view) const733 Window::DrawingRegionChanged(View* view) const
734 {
735 	return !fEffectiveDrawingRegionValid || !view->IsScreenClippingValid();
736 }
737 
738 
739 void
ProcessDirtyRegion(const BRegion & dirtyRegion,const BRegion & exposeRegion)740 Window::ProcessDirtyRegion(const BRegion& dirtyRegion, const BRegion& exposeRegion)
741 {
742 	// if this is executed in the desktop thread,
743 	// it means that the window thread currently
744 	// blocks to get the read lock, if it is
745 	// executed from the window thread, it should
746 	// have the read lock and the desktop thread
747 	// is blocking to get the write lock. IAW, this
748 	// is only executed in one thread.
749 	if (fDirtyRegion.CountRects() == 0) {
750 		// the window needs to be informed
751 		// when the dirty region was empty.
752 		// NOTE: when the window thread has processed
753 		// the dirty region in MessageReceived(),
754 		// it will make the region empty again,
755 		// when it is empty here, we need to send
756 		// the message to initiate the next update round.
757 		// Until the message is processed in the window
758 		// thread, the desktop thread can add parts to
759 		// the region as it likes.
760 		ServerWindow()->RequestRedraw();
761 	}
762 
763 	fDirtyRegion.Include(&dirtyRegion);
764 	fExposeRegion.Include(&exposeRegion);
765 }
766 
767 
768 void
RedrawDirtyRegion()769 Window::RedrawDirtyRegion()
770 {
771 	if (TopLayerStackWindow() != this) {
772 		fDirtyRegion.MakeEmpty();
773 		fExposeRegion.MakeEmpty();
774 		return;
775 	}
776 
777 	// executed from ServerWindow with the read lock held
778 	if (IsVisible()) {
779 		_DrawBorder();
780 
781 		BRegion* dirtyContentRegion = fRegionPool.GetRegion(VisibleContentRegion());
782 		BRegion* exposeContentRegion = fRegionPool.GetRegion(VisibleContentRegion());
783 		dirtyContentRegion->IntersectWith(&fDirtyRegion);
784 		exposeContentRegion->IntersectWith(&fExposeRegion);
785 
786 		_TriggerContentRedraw(*dirtyContentRegion, *exposeContentRegion);
787 
788 		fRegionPool.Recycle(dirtyContentRegion);
789 		fRegionPool.Recycle(exposeContentRegion);
790 	}
791 
792 	// reset the dirty region, since
793 	// we're fully clean. If the desktop
794 	// thread wanted to mark something
795 	// dirty in the mean time, it was
796 	// blocking on the global region lock to
797 	// get write access, since we're holding
798 	// the read lock for the whole time.
799 	fDirtyRegion.MakeEmpty();
800 	fExposeRegion.MakeEmpty();
801 }
802 
803 
804 void
MarkDirty(BRegion & regionOnScreen)805 Window::MarkDirty(BRegion& regionOnScreen)
806 {
807 	// for marking any part of the desktop dirty
808 	// this will get write access to the global
809 	// region lock, and result in ProcessDirtyRegion()
810 	// to be called for any windows affected
811 	if (fDesktop)
812 		fDesktop->MarkDirty(regionOnScreen);
813 }
814 
815 
816 void
MarkContentDirty(BRegion & dirtyRegion,BRegion & exposeRegion)817 Window::MarkContentDirty(BRegion& dirtyRegion, BRegion& exposeRegion)
818 {
819 	// for triggering AS_REDRAW
820 	// since this won't affect other windows, read locking
821 	// is sufficient. If there was no dirty region before,
822 	// an update message is triggered
823 	if (fHidden || IsOffscreenWindow())
824 		return;
825 
826 	dirtyRegion.IntersectWith(&VisibleContentRegion());
827 	exposeRegion.IntersectWith(&VisibleContentRegion());
828 	_TriggerContentRedraw(dirtyRegion, exposeRegion);
829 }
830 
831 
832 void
MarkContentDirtyAsync(BRegion & dirtyRegion)833 Window::MarkContentDirtyAsync(BRegion& dirtyRegion)
834 {
835 	// NOTE: see comments in ProcessDirtyRegion()
836 	if (fHidden || IsOffscreenWindow())
837 		return;
838 
839 	dirtyRegion.IntersectWith(&VisibleContentRegion());
840 
841 	if (fDirtyRegion.CountRects() == 0) {
842 		ServerWindow()->RequestRedraw();
843 	}
844 
845 	fDirtyRegion.Include(&dirtyRegion);
846 }
847 
848 
849 void
InvalidateView(View * view,BRegion & viewRegion)850 Window::InvalidateView(View* view, BRegion& viewRegion)
851 {
852 	if (view && IsVisible() && view->IsVisible()) {
853 		if (!fContentRegionValid)
854 			_UpdateContentRegion();
855 
856 		view->LocalToScreenTransform().Apply(&viewRegion);
857 		viewRegion.IntersectWith(&VisibleContentRegion());
858 		if (viewRegion.CountRects() > 0) {
859 			viewRegion.IntersectWith(
860 				&view->ScreenAndUserClipping(&fContentRegion));
861 
862 //fDrawingEngine->FillRegion(viewRegion, rgb_color{ 0, 255, 0, 255 });
863 //snooze(10000);
864 			_TriggerContentRedraw(viewRegion);
865 		}
866 	}
867 }
868 
869 // DisableUpdateRequests
870 void
DisableUpdateRequests()871 Window::DisableUpdateRequests()
872 {
873 	fUpdatesEnabled = false;
874 }
875 
876 
877 // EnableUpdateRequests
878 void
EnableUpdateRequests()879 Window::EnableUpdateRequests()
880 {
881 	fUpdatesEnabled = true;
882 	if (!fUpdateRequested && fPendingUpdateSession->IsUsed())
883 		_SendUpdateMessage();
884 }
885 
886 // #pragma mark -
887 
888 
889 /*!	\brief Handles a mouse-down message for the window.
890 
891 	\param message The message.
892 	\param where The point where the mouse click happened.
893 	\param lastClickTarget The target of the previous click.
894 	\param clickCount The number of subsequent, no longer than double-click
895 		interval separated clicks that have happened so far. This number doesn't
896 		necessarily match the value in the message. It has already been
897 		pre-processed in order to avoid erroneous multi-clicks (e.g. when a
898 		different button has been used or a different window was targeted). This
899 		is an in-out variable. The method can reset the value to 1, if it
900 		doesn't want this event handled as a multi-click. Returning a different
901 		click target will also make the caller reset the click count.
902 	\param _clickTarget Set by the method to a value identifying the clicked
903 		element. If not explicitly set, an invalid click target is assumed.
904 */
905 void
MouseDown(BMessage * message,BPoint where,const ClickTarget & lastClickTarget,int32 & clickCount,ClickTarget & _clickTarget)906 Window::MouseDown(BMessage* message, BPoint where,
907 	const ClickTarget& lastClickTarget, int32& clickCount,
908 	ClickTarget& _clickTarget)
909 {
910 	// If the previous click hit our decorator, get the hit region.
911 	int32 windowToken = fWindow->ServerToken();
912 	int32 lastHitRegion = 0;
913 	if (lastClickTarget.GetType() == ClickTarget::TYPE_WINDOW_DECORATOR
914 		&& lastClickTarget.WindowToken() == windowToken) {
915 		lastHitRegion = lastClickTarget.WindowElement();
916 	}
917 
918 	// Let the window behavior process the mouse event.
919 	int32 hitRegion = 0;
920 	bool eventEaten = fWindowBehaviour->MouseDown(message, where, lastHitRegion,
921 		clickCount, hitRegion);
922 
923 	if (eventEaten) {
924 		// click on the decorator (or equivalent)
925 		_clickTarget = ClickTarget(ClickTarget::TYPE_WINDOW_DECORATOR,
926 			windowToken, (int32)hitRegion);
927 	} else {
928 		// click was inside the window contents
929 		int32 viewToken = B_NULL_TOKEN;
930 		if (View* view = ViewAt(where)) {
931 			if (HasModal())
932 				return;
933 
934 			// clicking a simple View
935 			if (!IsFocus()) {
936 				bool acceptFirstClick
937 					= (Flags() & B_WILL_ACCEPT_FIRST_CLICK) != 0;
938 
939 				// Activate or focus the window in case it doesn't accept first
940 				// click, depending on the mouse mode
941 				if (!acceptFirstClick) {
942 					bool avoidFocus = (Flags() & B_AVOID_FOCUS) != 0;
943 					DesktopSettings desktopSettings(fDesktop);
944 					if (desktopSettings.MouseMode() == B_NORMAL_MOUSE)
945 						fDesktop->ActivateWindow(this);
946 					else if (!avoidFocus)
947 						fDesktop->SetFocusWindow(this);
948 
949 					// Eat the click if we don't accept first click
950 					// (B_AVOID_FOCUS never gets the focus, so they always accept
951 					// the first click)
952 					// TODO: the latter is unlike BeOS - if we really wanted to
953 					// imitate this behaviour, we would need to check if we're
954 					// the front window instead of the focus window
955 					if (!desktopSettings.AcceptFirstClick() && !avoidFocus)
956 						return;
957 				}
958 			}
959 
960 			// fill out view token for the view under the mouse
961 			viewToken = view->Token();
962 			view->MouseDown(message, where);
963 		}
964 
965 		_clickTarget = ClickTarget(ClickTarget::TYPE_WINDOW_CONTENTS,
966 			windowToken, viewToken);
967 	}
968 }
969 
970 
971 void
MouseUp(BMessage * message,BPoint where,int32 * _viewToken)972 Window::MouseUp(BMessage* message, BPoint where, int32* _viewToken)
973 {
974 	fWindowBehaviour->MouseUp(message, where);
975 
976 	if (View* view = ViewAt(where)) {
977 		if (HasModal())
978 			return;
979 
980 		*_viewToken = view->Token();
981 		view->MouseUp(message, where);
982 	}
983 }
984 
985 
986 void
MouseMoved(BMessage * message,BPoint where,int32 * _viewToken,bool isLatestMouseMoved,bool isFake)987 Window::MouseMoved(BMessage *message, BPoint where, int32* _viewToken,
988 	bool isLatestMouseMoved, bool isFake)
989 {
990 	View* view = ViewAt(where);
991 	if (view != NULL)
992 		*_viewToken = view->Token();
993 
994 	// ignore pointer history
995 	if (!isLatestMouseMoved)
996 		return;
997 
998 	fWindowBehaviour->MouseMoved(message, where, isFake);
999 
1000 	// mouse cursor
1001 
1002 	if (view != NULL) {
1003 		view->MouseMoved(message, where);
1004 
1005 		// TODO: there is more for real cursor support, ie. if a window is closed,
1006 		//		new app cursor shouldn't override view cursor, ...
1007 		ServerWindow()->App()->SetCurrentCursor(view->Cursor());
1008 	}
1009 }
1010 
1011 
1012 void
ModifiersChanged(int32 modifiers)1013 Window::ModifiersChanged(int32 modifiers)
1014 {
1015 	fWindowBehaviour->ModifiersChanged(modifiers);
1016 }
1017 
1018 
1019 // #pragma mark -
1020 
1021 
1022 void
WorkspaceActivated(int32 index,bool active)1023 Window::WorkspaceActivated(int32 index, bool active)
1024 {
1025 	BMessage activatedMsg(B_WORKSPACE_ACTIVATED);
1026 	activatedMsg.AddInt64("when", system_time());
1027 	activatedMsg.AddInt32("workspace", index);
1028 	activatedMsg.AddBool("active", active);
1029 
1030 	ServerWindow()->SendMessageToClient(&activatedMsg);
1031 }
1032 
1033 
1034 void
WorkspacesChanged(uint32 oldWorkspaces,uint32 newWorkspaces)1035 Window::WorkspacesChanged(uint32 oldWorkspaces, uint32 newWorkspaces)
1036 {
1037 	fWorkspaces = newWorkspaces;
1038 
1039 	BMessage changedMsg(B_WORKSPACES_CHANGED);
1040 	changedMsg.AddInt64("when", system_time());
1041 	changedMsg.AddInt32("old", oldWorkspaces);
1042 	changedMsg.AddInt32("new", newWorkspaces);
1043 
1044 	ServerWindow()->SendMessageToClient(&changedMsg);
1045 }
1046 
1047 
1048 void
Activated(bool active)1049 Window::Activated(bool active)
1050 {
1051 	BMessage msg(B_WINDOW_ACTIVATED);
1052 	msg.AddBool("active", active);
1053 	ServerWindow()->SendMessageToClient(&msg);
1054 }
1055 
1056 
1057 //# pragma mark -
1058 
1059 
1060 void
SetTitle(const char * name,BRegion & dirty)1061 Window::SetTitle(const char* name, BRegion& dirty)
1062 {
1063 	// rebuild the clipping for the title area
1064 	// and redraw it.
1065 
1066 	fTitle = name;
1067 
1068 	::Decorator* decorator = Decorator();
1069 	if (decorator) {
1070 		int32 index = PositionInStack();
1071 		decorator->SetTitle(index, name, &dirty);
1072 	}
1073 }
1074 
1075 
1076 void
SetFocus(bool focus)1077 Window::SetFocus(bool focus)
1078 {
1079 	::Decorator* decorator = Decorator();
1080 
1081 	// executed from Desktop thread
1082 	// it holds the clipping write lock,
1083 	// so the window thread cannot be
1084 	// accessing fIsFocus
1085 
1086 	BRegion* dirty = NULL;
1087 	if (decorator)
1088 		dirty = fRegionPool.GetRegion(decorator->GetFootprint());
1089 	if (dirty) {
1090 		dirty->IntersectWith(&fVisibleRegion);
1091 		fDesktop->MarkDirty(*dirty);
1092 		fRegionPool.Recycle(dirty);
1093 	}
1094 
1095 	fIsFocus = focus;
1096 	if (decorator) {
1097 		int32 index = PositionInStack();
1098 		decorator->SetFocus(index, focus);
1099 	}
1100 
1101 	Activated(focus);
1102 }
1103 
1104 
1105 void
SetHidden(bool hidden)1106 Window::SetHidden(bool hidden)
1107 {
1108 	// the desktop takes care of dirty regions
1109 	if (fHidden != hidden) {
1110 		fHidden = hidden;
1111 
1112 		fTopView->SetHidden(hidden);
1113 
1114 		// TODO: anything else?
1115 	}
1116 }
1117 
1118 
1119 void
SetShowLevel(int32 showLevel)1120 Window::SetShowLevel(int32 showLevel)
1121 {
1122 	if (showLevel == fShowLevel)
1123 		return;
1124 
1125 	fShowLevel = showLevel;
1126 }
1127 
1128 
1129 void
SetMinimized(bool minimized)1130 Window::SetMinimized(bool minimized)
1131 {
1132 	if (minimized == fMinimized)
1133 		return;
1134 
1135 	fMinimized = minimized;
1136 }
1137 
1138 
1139 bool
IsVisible() const1140 Window::IsVisible() const
1141 {
1142 	if (IsOffscreenWindow())
1143 		return true;
1144 
1145 	if (IsHidden())
1146 		return false;
1147 
1148 /*
1149 	if (fVisibleRegion.CountRects() == 0)
1150 		return false;
1151 */
1152 	return fCurrentWorkspace >= 0 && fCurrentWorkspace < kWorkingList;
1153 }
1154 
1155 
1156 bool
IsDragging() const1157 Window::IsDragging() const
1158 {
1159 	if (!fWindowBehaviour.IsSet())
1160 		return false;
1161 	return fWindowBehaviour->IsDragging();
1162 }
1163 
1164 
1165 bool
IsResizing() const1166 Window::IsResizing() const
1167 {
1168 	if (!fWindowBehaviour.IsSet())
1169 		return false;
1170 	return fWindowBehaviour->IsResizing();
1171 }
1172 
1173 
1174 void
SetSizeLimits(int32 minWidth,int32 maxWidth,int32 minHeight,int32 maxHeight)1175 Window::SetSizeLimits(int32 minWidth, int32 maxWidth, int32 minHeight,
1176 	int32 maxHeight)
1177 {
1178 	if (minWidth < 0)
1179 		minWidth = 0;
1180 
1181 	if (minHeight < 0)
1182 		minHeight = 0;
1183 
1184 	fMinWidth = minWidth;
1185 	fMaxWidth = maxWidth;
1186 	fMinHeight = minHeight;
1187 	fMaxHeight = maxHeight;
1188 
1189 	// give the Decorator a say in this too
1190 	::Decorator* decorator = Decorator();
1191 	if (decorator) {
1192 		decorator->GetSizeLimits(&fMinWidth, &fMinHeight, &fMaxWidth,
1193 			&fMaxHeight);
1194 	}
1195 
1196 	_ObeySizeLimits();
1197 }
1198 
1199 
1200 void
GetSizeLimits(int32 * minWidth,int32 * maxWidth,int32 * minHeight,int32 * maxHeight) const1201 Window::GetSizeLimits(int32* minWidth, int32* maxWidth,
1202 	int32* minHeight, int32* maxHeight) const
1203 {
1204 	*minWidth = fMinWidth;
1205 	*maxWidth = fMaxWidth;
1206 	*minHeight = fMinHeight;
1207 	*maxHeight = fMaxHeight;
1208 }
1209 
1210 
1211 bool
SetTabLocation(float location,bool isShifting,BRegion & dirty)1212 Window::SetTabLocation(float location, bool isShifting, BRegion& dirty)
1213 {
1214 	::Decorator* decorator = Decorator();
1215 	if (decorator) {
1216 		int32 index = PositionInStack();
1217 		return decorator->SetTabLocation(index, location, isShifting, &dirty);
1218 	}
1219 
1220 	return false;
1221 }
1222 
1223 
1224 float
TabLocation() const1225 Window::TabLocation() const
1226 {
1227 	::Decorator* decorator = Decorator();
1228 	if (decorator) {
1229 		int32 index = PositionInStack();
1230 		return decorator->TabLocation(index);
1231 	}
1232 	return 0.0;
1233 }
1234 
1235 
1236 bool
SetDecoratorSettings(const BMessage & settings,BRegion & dirty)1237 Window::SetDecoratorSettings(const BMessage& settings, BRegion& dirty)
1238 {
1239 	if (settings.what == 'prVu') {
1240 		// 'prVu' == preview a decorator!
1241 		BString path;
1242 		if (settings.FindString("preview", &path) == B_OK)
1243 			return gDecorManager.PreviewDecorator(path, this) == B_OK;
1244 		return false;
1245 	}
1246 
1247 	::Decorator* decorator = Decorator();
1248 	if (decorator)
1249 		return decorator->SetSettings(settings, &dirty);
1250 
1251 	return false;
1252 }
1253 
1254 
1255 bool
GetDecoratorSettings(BMessage * settings)1256 Window::GetDecoratorSettings(BMessage* settings)
1257 {
1258 	if (fDesktop)
1259 		fDesktop->GetDecoratorSettings(this, *settings);
1260 
1261 	::Decorator* decorator = Decorator();
1262 	if (decorator)
1263 		return decorator->GetSettings(settings);
1264 
1265 	return false;
1266 }
1267 
1268 
1269 void
FontsChanged(BRegion * updateRegion)1270 Window::FontsChanged(BRegion* updateRegion)
1271 {
1272 	::Decorator* decorator = Decorator();
1273 	if (decorator != NULL) {
1274 		DesktopSettings settings(fDesktop);
1275 		decorator->FontsChanged(settings, updateRegion);
1276 	}
1277 }
1278 
1279 
1280 void
ColorsChanged(BRegion * updateRegion)1281 Window::ColorsChanged(BRegion* updateRegion)
1282 {
1283 	::Decorator* decorator = Decorator();
1284 	if (decorator != NULL) {
1285 		DesktopSettings settings(fDesktop);
1286 		decorator->ColorsChanged(settings, updateRegion);
1287 	}
1288 }
1289 
1290 
1291 void
SetLook(window_look look,BRegion * updateRegion)1292 Window::SetLook(window_look look, BRegion* updateRegion)
1293 {
1294 	fLook = look;
1295 
1296 	fContentRegionValid = false;
1297 		// mabye a resize handle was added...
1298 	fEffectiveDrawingRegionValid = false;
1299 		// ...and therefor the drawing region is
1300 		// likely not valid anymore either
1301 
1302 	if (!fCurrentStack.IsSet())
1303 		return;
1304 
1305 	int32 stackPosition = PositionInStack();
1306 
1307 	::Decorator* decorator = Decorator();
1308 	if (decorator == NULL && look != B_NO_BORDER_WINDOW_LOOK) {
1309 		// we need a new decorator
1310 		decorator = gDecorManager.AllocateDecorator(this);
1311 		fCurrentStack->SetDecorator(decorator);
1312 		if (IsFocus())
1313 			decorator->SetFocus(stackPosition, true);
1314 	}
1315 
1316 	if (decorator != NULL) {
1317 		DesktopSettings settings(fDesktop);
1318 		decorator->SetLook(stackPosition, settings, look, updateRegion);
1319 
1320 		// we might need to resize the window!
1321 		decorator->GetSizeLimits(&fMinWidth, &fMinHeight, &fMaxWidth,
1322 			&fMaxHeight);
1323 		_ObeySizeLimits();
1324 	}
1325 
1326 	if (look == B_NO_BORDER_WINDOW_LOOK) {
1327 		// we don't need a decorator for this window
1328 		fCurrentStack->SetDecorator(NULL);
1329 	}
1330 }
1331 
1332 
1333 void
SetFeel(window_feel feel)1334 Window::SetFeel(window_feel feel)
1335 {
1336 	// if the subset list is no longer needed, clear it
1337 	if ((fFeel == B_MODAL_SUBSET_WINDOW_FEEL
1338 			|| fFeel == B_FLOATING_SUBSET_WINDOW_FEEL)
1339 		&& feel != B_MODAL_SUBSET_WINDOW_FEEL
1340 		&& feel != B_FLOATING_SUBSET_WINDOW_FEEL)
1341 		fSubsets.MakeEmpty();
1342 
1343 	fFeel = feel;
1344 
1345 	// having modal windows with B_AVOID_FRONT or B_AVOID_FOCUS doesn't
1346 	// make that much sense, so we filter those flags out on demand
1347 	fFlags = fOriginalFlags;
1348 	fFlags &= ValidWindowFlags(fFeel);
1349 
1350 	if (!IsNormal()) {
1351 		fFlags |= B_SAME_POSITION_IN_ALL_WORKSPACES;
1352 		_PropagatePosition();
1353 	}
1354 }
1355 
1356 
1357 void
SetFlags(uint32 flags,BRegion * updateRegion)1358 Window::SetFlags(uint32 flags, BRegion* updateRegion)
1359 {
1360 	fOriginalFlags = flags;
1361 	fFlags = flags & ValidWindowFlags(fFeel);
1362 	if (!IsNormal())
1363 		fFlags |= B_SAME_POSITION_IN_ALL_WORKSPACES;
1364 
1365 	if ((fFlags & B_SAME_POSITION_IN_ALL_WORKSPACES) != 0)
1366 		_PropagatePosition();
1367 
1368 	::Decorator* decorator = Decorator();
1369 	if (decorator == NULL)
1370 		return;
1371 
1372 	int32 stackPosition = PositionInStack();
1373 	decorator->SetFlags(stackPosition, flags, updateRegion);
1374 
1375 	// we might need to resize the window!
1376 	decorator->GetSizeLimits(&fMinWidth, &fMinHeight, &fMaxWidth, &fMaxHeight);
1377 	_ObeySizeLimits();
1378 
1379 // TODO: not sure if we want to do this
1380 #if 0
1381 	if ((fOriginalFlags & kWindowScreenFlag) != (flags & kWindowScreenFlag)) {
1382 		// TODO: disabling needs to be nestable (or we might lose the previous
1383 		// update state)
1384 		if ((flags & kWindowScreenFlag) != 0)
1385 			DisableUpdateRequests();
1386 		else
1387 			EnableUpdateRequests();
1388 	}
1389 #endif
1390 }
1391 
1392 
1393 /*!	Returns whether or not a window is in the workspace list with the
1394 	specified \a index.
1395 */
1396 bool
InWorkspace(int32 index) const1397 Window::InWorkspace(int32 index) const
1398 {
1399 	return (fWorkspaces & (1UL << index)) != 0;
1400 }
1401 
1402 
1403 bool
SupportsFront()1404 Window::SupportsFront()
1405 {
1406 	if (fFeel == kDesktopWindowFeel
1407 		|| fFeel == kMenuWindowFeel
1408 		|| (fFlags & B_AVOID_FRONT) != 0)
1409 		return false;
1410 
1411 	return true;
1412 }
1413 
1414 
1415 bool
IsModal() const1416 Window::IsModal() const
1417 {
1418 	return IsModalFeel(fFeel);
1419 }
1420 
1421 
1422 bool
IsFloating() const1423 Window::IsFloating() const
1424 {
1425 	return IsFloatingFeel(fFeel);
1426 }
1427 
1428 
1429 bool
IsNormal() const1430 Window::IsNormal() const
1431 {
1432 	return !IsFloatingFeel(fFeel) && !IsModalFeel(fFeel);
1433 }
1434 
1435 
1436 bool
HasModal() const1437 Window::HasModal() const
1438 {
1439 	for (Window* window = NextWindow(fCurrentWorkspace); window != NULL;
1440 			window = window->NextWindow(fCurrentWorkspace)) {
1441 		if (window->IsHidden() || !window->IsModal())
1442 			continue;
1443 
1444 		if (window->HasInSubset(this))
1445 			return true;
1446 	}
1447 
1448 	return false;
1449 }
1450 
1451 
1452 /*!	\brief Returns the windows that's in behind of the backmost position
1453 		this window can get.
1454 	Returns NULL is this window can be the backmost window.
1455 
1456 	\param workspace the workspace on which this check should be made. If
1457 		the value is -1, the window's current workspace will be used.
1458 */
1459 Window*
Backmost(Window * window,int32 workspace)1460 Window::Backmost(Window* window, int32 workspace)
1461 {
1462 	if (workspace == -1)
1463 		workspace = fCurrentWorkspace;
1464 
1465 	ASSERT(workspace != -1);
1466 	if (workspace == -1)
1467 		return NULL;
1468 
1469 	// Desktop windows are always backmost
1470 	if (fFeel == kDesktopWindowFeel)
1471 		return NULL;
1472 
1473 	if (window == NULL)
1474 		window = PreviousWindow(workspace);
1475 
1476 	for (; window != NULL; window = window->PreviousWindow(workspace)) {
1477 		if (window->IsHidden() || window == this)
1478 			continue;
1479 
1480 		if (HasInSubset(window))
1481 			return window;
1482 	}
1483 
1484 	return NULL;
1485 }
1486 
1487 
1488 /*!	\brief Returns the window that's in front of the frontmost position
1489 		this window can get.
1490 	Returns NULL if this window can be the frontmost window.
1491 
1492 	\param workspace the workspace on which this check should be made. If
1493 		the value is -1, the window's current workspace will be used.
1494 */
1495 Window*
Frontmost(Window * first,int32 workspace)1496 Window::Frontmost(Window* first, int32 workspace)
1497 {
1498 	if (workspace == -1)
1499 		workspace = fCurrentWorkspace;
1500 
1501 	ASSERT(workspace != -1);
1502 	if (workspace == -1)
1503 		return NULL;
1504 
1505 	if (fFeel == kDesktopWindowFeel)
1506 		return first ? first : NextWindow(workspace);
1507 
1508 	if (first == NULL)
1509 		first = NextWindow(workspace);
1510 
1511 	for (Window* window = first; window != NULL;
1512 			window = window->NextWindow(workspace)) {
1513 		if (window->IsHidden() || window == this)
1514 			continue;
1515 
1516 		if (window->HasInSubset(this))
1517 			return window;
1518 	}
1519 
1520 	return NULL;
1521 }
1522 
1523 
1524 bool
AddToSubset(Window * window)1525 Window::AddToSubset(Window* window)
1526 {
1527 	return fSubsets.AddItem(window);
1528 }
1529 
1530 
1531 void
RemoveFromSubset(Window * window)1532 Window::RemoveFromSubset(Window* window)
1533 {
1534 	fSubsets.RemoveItem(window);
1535 }
1536 
1537 
1538 /*!	Returns whether or not a window is in the subset of this window.
1539 	If a window is in the subset of this window, it means it should always
1540 	appear behind this window.
1541 */
1542 bool
HasInSubset(const Window * window) const1543 Window::HasInSubset(const Window* window) const
1544 {
1545 	if (window == NULL || fFeel == window->Feel()
1546 		|| fFeel == B_NORMAL_WINDOW_FEEL)
1547 		return false;
1548 
1549 	// Menus are a special case: they will always be on-top of every window
1550 	// of their application
1551 	if (fFeel == kMenuWindowFeel)
1552 		return window->ServerWindow()->App() == ServerWindow()->App();
1553 	if (window->Feel() == kMenuWindowFeel)
1554 		return false;
1555 
1556 	// we have a few special feels that have a fixed order
1557 
1558 	const int32 kFeels[] = {kPasswordWindowFeel, kWindowScreenFeel,
1559 		B_MODAL_ALL_WINDOW_FEEL, B_FLOATING_ALL_WINDOW_FEEL};
1560 
1561 	for (uint32 order = 0;
1562 			order < sizeof(kFeels) / sizeof(kFeels[0]); order++) {
1563 		if (fFeel == kFeels[order])
1564 			return true;
1565 		if (window->Feel() == kFeels[order])
1566 			return false;
1567 	}
1568 
1569 	if ((fFeel == B_FLOATING_APP_WINDOW_FEEL
1570 			&& window->Feel() != B_MODAL_APP_WINDOW_FEEL)
1571 		|| fFeel == B_MODAL_APP_WINDOW_FEEL)
1572 		return window->ServerWindow()->App() == ServerWindow()->App();
1573 
1574 	return fSubsets.HasItem(window);
1575 }
1576 
1577 
1578 /*!	\brief Collects all workspaces views in this window and puts it into \a list
1579 */
1580 void
FindWorkspacesViews(BObjectList<WorkspacesView> & list) const1581 Window::FindWorkspacesViews(BObjectList<WorkspacesView>& list) const
1582 {
1583 	int32 count = fWorkspacesViewCount;
1584 	fTopView->FindViews(kWorkspacesViewFlag, (BObjectList<View>&)list, count);
1585 }
1586 
1587 
1588 /*!	\brief Returns on which workspaces the window should be visible.
1589 
1590 	A modal or floating window may be visible on a workspace if one
1591 	of its subset windows is visible there. Floating windows also need
1592 	to have a subset as front window to be visible.
1593 */
1594 uint32
SubsetWorkspaces() const1595 Window::SubsetWorkspaces() const
1596 {
1597 	if (fFeel == B_MODAL_ALL_WINDOW_FEEL
1598 		|| fFeel == B_FLOATING_ALL_WINDOW_FEEL)
1599 		return B_ALL_WORKSPACES;
1600 
1601 	if (fFeel == B_FLOATING_APP_WINDOW_FEEL) {
1602 		Window* front = fDesktop->FrontWindow();
1603 		if (front != NULL && front->IsNormal()
1604 			&& front->ServerWindow()->App() == ServerWindow()->App())
1605 			return ServerWindow()->App()->Workspaces();
1606 
1607 		return 0;
1608 	}
1609 
1610 	if (fFeel == B_MODAL_APP_WINDOW_FEEL) {
1611 		uint32 workspaces = ServerWindow()->App()->Workspaces();
1612 		if (workspaces == 0) {
1613 			// The application doesn't seem to have any other windows
1614 			// open or visible - but we'd like to see modal windows
1615 			// anyway, at least when they are first opened.
1616 			return 1UL << fDesktop->CurrentWorkspace();
1617 		}
1618 		return workspaces;
1619 	}
1620 
1621 	if (fFeel == B_MODAL_SUBSET_WINDOW_FEEL
1622 		|| fFeel == B_FLOATING_SUBSET_WINDOW_FEEL) {
1623 		uint32 workspaces = 0;
1624 		bool hasNormalFront = false;
1625 		for (int32 i = 0; i < fSubsets.CountItems(); i++) {
1626 			Window* window = fSubsets.ItemAt(i);
1627 
1628 			if (!window->IsHidden())
1629 				workspaces |= window->Workspaces();
1630 			if (window == fDesktop->FrontWindow() && window->IsNormal())
1631 				hasNormalFront = true;
1632 		}
1633 
1634 		if (fFeel == B_FLOATING_SUBSET_WINDOW_FEEL && !hasNormalFront)
1635 			return 0;
1636 
1637 		return workspaces;
1638 	}
1639 
1640 	return 0;
1641 }
1642 
1643 
1644 /*!	Returns whether or not a window is in the subset workspace list with the
1645 	specified \a index.
1646 	See SubsetWorkspaces().
1647 */
1648 bool
InSubsetWorkspace(int32 index) const1649 Window::InSubsetWorkspace(int32 index) const
1650 {
1651 	return (SubsetWorkspaces() & (1UL << index)) != 0;
1652 }
1653 
1654 
1655 // #pragma mark - static
1656 
1657 
1658 /*static*/ bool
IsValidLook(window_look look)1659 Window::IsValidLook(window_look look)
1660 {
1661 	return look == B_TITLED_WINDOW_LOOK
1662 		|| look == B_DOCUMENT_WINDOW_LOOK
1663 		|| look == B_MODAL_WINDOW_LOOK
1664 		|| look == B_FLOATING_WINDOW_LOOK
1665 		|| look == B_BORDERED_WINDOW_LOOK
1666 		|| look == B_NO_BORDER_WINDOW_LOOK
1667 		|| look == kDesktopWindowLook
1668 		|| look == kLeftTitledWindowLook;
1669 }
1670 
1671 
1672 /*static*/ bool
IsValidFeel(window_feel feel)1673 Window::IsValidFeel(window_feel feel)
1674 {
1675 	return feel == B_NORMAL_WINDOW_FEEL
1676 		|| feel == B_MODAL_SUBSET_WINDOW_FEEL
1677 		|| feel == B_MODAL_APP_WINDOW_FEEL
1678 		|| feel == B_MODAL_ALL_WINDOW_FEEL
1679 		|| feel == B_FLOATING_SUBSET_WINDOW_FEEL
1680 		|| feel == B_FLOATING_APP_WINDOW_FEEL
1681 		|| feel == B_FLOATING_ALL_WINDOW_FEEL
1682 		|| feel == kDesktopWindowFeel
1683 		|| feel == kMenuWindowFeel
1684 		|| feel == kWindowScreenFeel
1685 		|| feel == kPasswordWindowFeel
1686 		|| feel == kOffscreenWindowFeel;
1687 }
1688 
1689 
1690 /*static*/ bool
IsModalFeel(window_feel feel)1691 Window::IsModalFeel(window_feel feel)
1692 {
1693 	return feel == B_MODAL_SUBSET_WINDOW_FEEL
1694 		|| feel == B_MODAL_APP_WINDOW_FEEL
1695 		|| feel == B_MODAL_ALL_WINDOW_FEEL;
1696 }
1697 
1698 
1699 /*static*/ bool
IsFloatingFeel(window_feel feel)1700 Window::IsFloatingFeel(window_feel feel)
1701 {
1702 	return feel == B_FLOATING_SUBSET_WINDOW_FEEL
1703 		|| feel == B_FLOATING_APP_WINDOW_FEEL
1704 		|| feel == B_FLOATING_ALL_WINDOW_FEEL;
1705 }
1706 
1707 
1708 /*static*/ uint32
ValidWindowFlags()1709 Window::ValidWindowFlags()
1710 {
1711 	return B_NOT_MOVABLE
1712 		| B_NOT_CLOSABLE
1713 		| B_NOT_ZOOMABLE
1714 		| B_NOT_MINIMIZABLE
1715 		| B_NOT_RESIZABLE
1716 		| B_NOT_H_RESIZABLE
1717 		| B_NOT_V_RESIZABLE
1718 		| B_AVOID_FRONT
1719 		| B_AVOID_FOCUS
1720 		| B_WILL_ACCEPT_FIRST_CLICK
1721 		| B_OUTLINE_RESIZE
1722 		| B_NO_WORKSPACE_ACTIVATION
1723 		| B_NOT_ANCHORED_ON_ACTIVATE
1724 		| B_ASYNCHRONOUS_CONTROLS
1725 		| B_QUIT_ON_WINDOW_CLOSE
1726 		| B_SAME_POSITION_IN_ALL_WORKSPACES
1727 		| B_AUTO_UPDATE_SIZE_LIMITS
1728 		| B_CLOSE_ON_ESCAPE
1729 		| B_NO_SERVER_SIDE_WINDOW_MODIFIERS
1730 		| kWindowScreenFlag
1731 		| kAcceptKeyboardFocusFlag;
1732 }
1733 
1734 
1735 /*static*/ uint32
ValidWindowFlags(window_feel feel)1736 Window::ValidWindowFlags(window_feel feel)
1737 {
1738 	uint32 flags = ValidWindowFlags();
1739 	if (IsModalFeel(feel))
1740 		return flags & ~(B_AVOID_FOCUS | B_AVOID_FRONT);
1741 
1742 	return flags;
1743 }
1744 
1745 
1746 // #pragma mark - private
1747 
1748 
1749 void
_ShiftPartOfRegion(BRegion * region,BRegion * regionToShift,int32 xOffset,int32 yOffset)1750 Window::_ShiftPartOfRegion(BRegion* region, BRegion* regionToShift,
1751 	int32 xOffset, int32 yOffset)
1752 {
1753 	BRegion* common = fRegionPool.GetRegion(*regionToShift);
1754 	if (!common)
1755 		return;
1756 	// see if there is a common part at all
1757 	common->IntersectWith(region);
1758 	if (common->CountRects() > 0) {
1759 		// cut the common part from the region,
1760 		// offset that to destination and include again
1761 		region->Exclude(common);
1762 		common->OffsetBy(xOffset, yOffset);
1763 		region->Include(common);
1764 	}
1765 	fRegionPool.Recycle(common);
1766 }
1767 
1768 
1769 void
_TriggerContentRedraw(BRegion & dirty,const BRegion & expose)1770 Window::_TriggerContentRedraw(BRegion& dirty, const BRegion& expose)
1771 {
1772 	if (!IsVisible() || dirty.CountRects() == 0 || (fFlags & kWindowScreenFlag) != 0)
1773 		return;
1774 
1775 	// put this into the pending dirty region
1776 	// to eventually trigger a client redraw
1777 	_TransferToUpdateSession(&dirty);
1778 
1779 	if (expose.CountRects() > 0) {
1780 		// draw exposed region background right now to avoid stamping artifacts
1781 		if (fDrawingEngine->LockParallelAccess()) {
1782 			bool copyToFrontEnabled = fDrawingEngine->CopyToFrontEnabled();
1783 			fDrawingEngine->SetCopyToFrontEnabled(true);
1784 			fDrawingEngine->SuspendAutoSync();
1785 			fTopView->Draw(fDrawingEngine.Get(), &expose, &fContentRegion, true);
1786 			fDrawingEngine->Sync();
1787 			fDrawingEngine->SetCopyToFrontEnabled(copyToFrontEnabled);
1788 			fDrawingEngine->UnlockParallelAccess();
1789 		}
1790 	}
1791 }
1792 
1793 
1794 void
_DrawBorder()1795 Window::_DrawBorder()
1796 {
1797 	// this is executed in the window thread, but only
1798 	// in respond to a REDRAW message having been received, the
1799 	// clipping lock is held for reading
1800 	::Decorator* decorator = Decorator();
1801 	if (!decorator)
1802 		return;
1803 
1804 	// construct the region of the border that needs redrawing
1805 	BRegion* dirtyBorderRegion = fRegionPool.GetRegion();
1806 	if (!dirtyBorderRegion)
1807 		return;
1808 	GetBorderRegion(dirtyBorderRegion);
1809 	// intersect with our visible region
1810 	dirtyBorderRegion->IntersectWith(&fVisibleRegion);
1811 	// intersect with the dirty region
1812 	dirtyBorderRegion->IntersectWith(&fDirtyRegion);
1813 
1814 	DrawingEngine* engine = decorator->GetDrawingEngine();
1815 	if (dirtyBorderRegion->CountRects() > 0 && engine->LockParallelAccess()) {
1816 		engine->ConstrainClippingRegion(dirtyBorderRegion);
1817 		bool copyToFrontEnabled = engine->CopyToFrontEnabled();
1818 		engine->SetCopyToFrontEnabled(false);
1819 
1820 		decorator->Draw(dirtyBorderRegion->Frame());
1821 
1822 		engine->SetCopyToFrontEnabled(copyToFrontEnabled);
1823 		engine->CopyToFront(*dirtyBorderRegion);
1824 
1825 // TODO: remove this once the DrawState stuff is handled
1826 // more cleanly. The reason why this is needed is that
1827 // when the decorator draws strings, a draw state is set
1828 // on the Painter object, and this is were it might get
1829 // out of sync with what the ServerWindow things is the
1830 // current DrawState set on the Painter
1831 fWindow->ResyncDrawState();
1832 
1833 		engine->UnlockParallelAccess();
1834 	}
1835 	fRegionPool.Recycle(dirtyBorderRegion);
1836 }
1837 
1838 
1839 /*!	pre: the clipping is readlocked (this function is
1840 	only called from _TriggerContentRedraw()), which
1841 	in turn is only called from MessageReceived() with
1842 	the clipping lock held
1843 */
1844 void
_TransferToUpdateSession(BRegion * contentDirtyRegion)1845 Window::_TransferToUpdateSession(BRegion* contentDirtyRegion)
1846 {
1847 	if (contentDirtyRegion->CountRects() <= 0)
1848 		return;
1849 
1850 //fDrawingEngine->FillRegion(*contentDirtyRegion, sPendingColor);
1851 //snooze(20000);
1852 
1853 	// add to pending
1854 	fPendingUpdateSession->SetUsed(true);
1855 	fPendingUpdateSession->Include(contentDirtyRegion);
1856 
1857 	if (!fUpdateRequested) {
1858 		// send this to client
1859 		_SendUpdateMessage();
1860 		// the pending region is now the current,
1861 		// though the update does not start until
1862 		// we received BEGIN_UPDATE from the client
1863 	}
1864 }
1865 
1866 
1867 void
_SendUpdateMessage()1868 Window::_SendUpdateMessage()
1869 {
1870 	if (!fUpdatesEnabled)
1871 		return;
1872 
1873 	BMessage message(_UPDATE_);
1874 	if (ServerWindow()->SendMessageToClient(&message) != B_OK) {
1875 		// If sending the message failed, we'll just keep adding to the dirty
1876 		// region until sending was successful.
1877 		// TODO: we might want to automatically resend this message in this case
1878 		return;
1879 	}
1880 
1881 	fUpdateRequested = true;
1882 	fEffectiveDrawingRegionValid = false;
1883 }
1884 
1885 
1886 void
BeginUpdate(BPrivate::PortLink & link)1887 Window::BeginUpdate(BPrivate::PortLink& link)
1888 {
1889 	// NOTE: since we might "shift" parts of the
1890 	// internal dirty regions from the desktop thread
1891 	// in response to Window::ResizeBy(), which
1892 	// might move arround views, the user of this function
1893 	// needs to hold the global clipping lock so that the internal
1894 	// dirty regions are not messed with from the Desktop thread
1895 	// and ServerWindow thread at the same time.
1896 
1897 	if (!fUpdateRequested) {
1898 		link.StartMessage(B_ERROR);
1899 		link.Flush();
1900 		fprintf(stderr, "Window::BeginUpdate() - no update requested!\n");
1901 		return;
1902 	}
1903 
1904 	// make the pending update session the current update session
1905 	// (toggle the pointers)
1906 	UpdateSession* temp = fCurrentUpdateSession;
1907 	fCurrentUpdateSession = fPendingUpdateSession;
1908 	fPendingUpdateSession = temp;
1909 	fPendingUpdateSession->SetUsed(false);
1910 	// all drawing command from the client
1911 	// will have the dirty region from the update
1912 	// session enforced
1913 	fInUpdate = true;
1914 	fEffectiveDrawingRegionValid = false;
1915 
1916 	// TODO: each view could be drawn individually
1917 	// right before carrying out the first drawing
1918 	// command from the client during an update
1919 	// (View::IsBackgroundDirty() can be used
1920 	// for this)
1921 	if (!fContentRegionValid)
1922 		_UpdateContentRegion();
1923 
1924 	BRegion* dirty = fRegionPool.GetRegion(
1925 		fCurrentUpdateSession->DirtyRegion());
1926 	if (!dirty) {
1927 		link.StartMessage(B_ERROR);
1928 		link.Flush();
1929 		return;
1930 	}
1931 
1932 	dirty->IntersectWith(&VisibleContentRegion());
1933 
1934 //if (!fCurrentUpdateSession->IsExpose()) {
1935 ////sCurrentColor.red = rand() % 255;
1936 ////sCurrentColor.green = rand() % 255;
1937 ////sCurrentColor.blue = rand() % 255;
1938 ////sPendingColor.red = rand() % 255;
1939 ////sPendingColor.green = rand() % 255;
1940 ////sPendingColor.blue = rand() % 255;
1941 //fDrawingEngine->FillRegion(*dirty, sCurrentColor);
1942 //snooze(10000);
1943 //}
1944 
1945 	link.StartMessage(B_OK);
1946 	// append the current window geometry to the
1947 	// message, the client will need it
1948 	link.Attach<BPoint>(fFrame.LeftTop());
1949 	link.Attach<float>(fFrame.Width());
1950 	link.Attach<float>(fFrame.Height());
1951 	// find and attach all views that intersect with
1952 	// the dirty region
1953 	fTopView->AddTokensForViewsInRegion(link, *dirty, &fContentRegion);
1954 	// mark the end of the token "list"
1955 	link.Attach<int32>(B_NULL_TOKEN);
1956 	link.Flush();
1957 
1958 	// supress back to front buffer copies in the drawing engine
1959 	fDrawingEngine->SetCopyToFrontEnabled(false);
1960 
1961 	if (fDrawingEngine->LockParallelAccess()) {
1962 		fDrawingEngine->SuspendAutoSync();
1963 
1964 		fTopView->Draw(GetDrawingEngine(), dirty, &fContentRegion, true);
1965 
1966 		fDrawingEngine->Sync();
1967 		fDrawingEngine->UnlockParallelAccess();
1968 	} // else the background was cleared already
1969 
1970 	fRegionPool.Recycle(dirty);
1971 }
1972 
1973 
1974 void
EndUpdate()1975 Window::EndUpdate()
1976 {
1977 	// NOTE: see comment in _BeginUpdate()
1978 
1979 	if (fInUpdate) {
1980 		// reenable copy to front
1981 		fDrawingEngine->SetCopyToFrontEnabled(true);
1982 
1983 		BRegion* dirty = fRegionPool.GetRegion(
1984 			fCurrentUpdateSession->DirtyRegion());
1985 
1986 		if (dirty) {
1987 			dirty->IntersectWith(&VisibleContentRegion());
1988 
1989 			fDrawingEngine->CopyToFront(*dirty);
1990 			fRegionPool.Recycle(dirty);
1991 		}
1992 
1993 		fCurrentUpdateSession->SetUsed(false);
1994 
1995 		fInUpdate = false;
1996 		fEffectiveDrawingRegionValid = false;
1997 	}
1998 	if (fPendingUpdateSession->IsUsed()) {
1999 		// send this to client
2000 		_SendUpdateMessage();
2001 	} else {
2002 		fUpdateRequested = false;
2003 	}
2004 }
2005 
2006 
2007 void
_UpdateContentRegion()2008 Window::_UpdateContentRegion()
2009 {
2010 	fContentRegion.Set(fFrame);
2011 
2012 	// resize handle
2013 	::Decorator* decorator = Decorator();
2014 	if (decorator)
2015 		fContentRegion.Exclude(&decorator->GetFootprint());
2016 
2017 	fContentRegionValid = true;
2018 }
2019 
2020 
2021 void
_ObeySizeLimits()2022 Window::_ObeySizeLimits()
2023 {
2024 	// make sure we even have valid size limits
2025 	if (fMaxWidth < fMinWidth)
2026 		fMaxWidth = fMinWidth;
2027 
2028 	if (fMaxHeight < fMinHeight)
2029 		fMaxHeight = fMinHeight;
2030 
2031 	// Automatically resize the window to fit these new limits
2032 	// if it does not already.
2033 
2034 	// On R5, Windows don't automatically resize, but since
2035 	// BWindow::ResizeTo() even honors the limits, I would guess
2036 	// this is a bug that we don't have to adopt.
2037 	// Note that most current apps will do unnecessary resizing
2038 	// after having set the limits, but the overhead is neglible.
2039 
2040 	float minWidthDiff = fMinWidth - fFrame.Width();
2041 	float minHeightDiff = fMinHeight - fFrame.Height();
2042 	float maxWidthDiff = fMaxWidth - fFrame.Width();
2043 	float maxHeightDiff = fMaxHeight - fFrame.Height();
2044 
2045 	float xDiff = 0.0;
2046 	if (minWidthDiff > 0.0)	// we're currently smaller than minWidth
2047 		xDiff = minWidthDiff;
2048 	else if (maxWidthDiff < 0.0) // we're currently larger than maxWidth
2049 		xDiff = maxWidthDiff;
2050 
2051 	float yDiff = 0.0;
2052 	if (minHeightDiff > 0.0) // we're currently smaller than minHeight
2053 		yDiff = minHeightDiff;
2054 	else if (maxHeightDiff < 0.0) // we're currently larger than maxHeight
2055 		yDiff = maxHeightDiff;
2056 
2057 	if (fDesktop)
2058 		fDesktop->ResizeWindowBy(this, xDiff, yDiff);
2059 	else
2060 		ResizeBy((int32)xDiff, (int32)yDiff, NULL);
2061 }
2062 
2063 
2064 // #pragma mark - UpdateSession
2065 
2066 
UpdateSession()2067 Window::UpdateSession::UpdateSession()
2068 	:
2069 	fDirtyRegion(),
2070 	fInUse(false)
2071 {
2072 }
2073 
2074 
2075 void
Include(BRegion * additionalDirty)2076 Window::UpdateSession::Include(BRegion* additionalDirty)
2077 {
2078 	fDirtyRegion.Include(additionalDirty);
2079 }
2080 
2081 
2082 void
Exclude(BRegion * dirtyInNextSession)2083 Window::UpdateSession::Exclude(BRegion* dirtyInNextSession)
2084 {
2085 	fDirtyRegion.Exclude(dirtyInNextSession);
2086 }
2087 
2088 
2089 void
MoveBy(int32 x,int32 y)2090 Window::UpdateSession::MoveBy(int32 x, int32 y)
2091 {
2092 	fDirtyRegion.OffsetBy(x, y);
2093 }
2094 
2095 
2096 void
SetUsed(bool used)2097 Window::UpdateSession::SetUsed(bool used)
2098 {
2099 	fInUse = used;
2100 	if (!fInUse)
2101 		fDirtyRegion.MakeEmpty();
2102 }
2103 
2104 
2105 int32
PositionInStack() const2106 Window::PositionInStack() const
2107 {
2108 	if (!fCurrentStack.IsSet())
2109 		return -1;
2110 	return fCurrentStack->WindowList().IndexOf(this);
2111 }
2112 
2113 
2114 bool
DetachFromWindowStack(bool ownStackNeeded)2115 Window::DetachFromWindowStack(bool ownStackNeeded)
2116 {
2117 	// The lock must normally be held but is not held when closing the window.
2118 	//ASSERT_MULTI_WRITE_LOCKED(fDesktop->WindowLocker());
2119 
2120 	if (!fCurrentStack.IsSet())
2121 		return false;
2122 	if (fCurrentStack->CountWindows() == 1)
2123 		return true;
2124 
2125 	int32 index = PositionInStack();
2126 
2127 	if (fCurrentStack->RemoveWindow(this) == false)
2128 		return false;
2129 
2130 	BRegion invalidatedRegion;
2131 	::Decorator* decorator = fCurrentStack->Decorator();
2132 	if (decorator != NULL) {
2133 		decorator->RemoveTab(index, &invalidatedRegion);
2134 		decorator->SetTopTab(fCurrentStack->LayerOrder().CountItems() - 1);
2135 	}
2136 
2137 	Window* remainingTop = fCurrentStack->TopLayerWindow();
2138 	if (remainingTop != NULL) {
2139 		if (decorator != NULL)
2140 			decorator->SetDrawingEngine(remainingTop->GetDrawingEngine());
2141 		// propagate focus to the decorator
2142 		remainingTop->SetFocus(remainingTop->IsFocus());
2143 		remainingTop->SetLook(remainingTop->Look(), NULL);
2144 	}
2145 
2146 	fCurrentStack = NULL;
2147 	if (ownStackNeeded == true)
2148 		_InitWindowStack();
2149 	// propagate focus to the new decorator
2150 	SetFocus(IsFocus());
2151 
2152 	if (remainingTop != NULL) {
2153 		invalidatedRegion.Include(&remainingTop->VisibleRegion());
2154 		fDesktop->RebuildAndRedrawAfterWindowChange(remainingTop,
2155 			invalidatedRegion);
2156 	}
2157 	return true;
2158 }
2159 
2160 
2161 bool
AddWindowToStack(Window * window)2162 Window::AddWindowToStack(Window* window)
2163 {
2164 	ASSERT_MULTI_WRITE_LOCKED(fDesktop->WindowLocker());
2165 
2166 	WindowStack* stack = GetWindowStack();
2167 	if (stack == NULL)
2168 		return false;
2169 
2170 	BRegion dirty;
2171 	// move window to the own position
2172 	BRect ownFrame = Frame();
2173 	BRect frame = window->Frame();
2174 	float deltaToX = round(ownFrame.left - frame.left);
2175 	float deltaToY = round(ownFrame.top - frame.top);
2176 	frame.OffsetBy(deltaToX, deltaToY);
2177 	float deltaByX = round(ownFrame.right - frame.right);
2178 	float deltaByY = round(ownFrame.bottom - frame.bottom);
2179 	dirty.Include(&window->VisibleRegion());
2180 	window->MoveBy(deltaToX, deltaToY, false);
2181 	window->ResizeBy(deltaByX, deltaByY, &dirty, false);
2182 
2183 	// first collect dirt from the window to add
2184 	::Decorator* otherDecorator = window->Decorator();
2185 	if (otherDecorator != NULL)
2186 		dirty.Include(otherDecorator->TitleBarRect());
2187 	::Decorator* decorator = stack->Decorator();
2188 	if (decorator != NULL)
2189 		dirty.Include(decorator->TitleBarRect());
2190 
2191 	int32 position = PositionInStack() + 1;
2192 	if (position >= stack->CountWindows())
2193 		position = -1;
2194 	if (stack->AddWindow(window, position) == false)
2195 		return false;
2196 	window->DetachFromWindowStack(false);
2197 	window->fCurrentStack.SetTo(stack);
2198 
2199 	if (decorator != NULL) {
2200 		DesktopSettings settings(fDesktop);
2201 		decorator->AddTab(settings, window->Title(), window->Look(),
2202 			window->Flags(), position, &dirty);
2203 	}
2204 
2205 	window->SetLook(window->Look(), &dirty);
2206 	fDesktop->RebuildAndRedrawAfterWindowChange(TopLayerStackWindow(), dirty);
2207 	window->SetFocus(window->IsFocus());
2208 	return true;
2209 }
2210 
2211 
2212 Window*
StackedWindowAt(const BPoint & where)2213 Window::StackedWindowAt(const BPoint& where)
2214 {
2215 	::Decorator* decorator = Decorator();
2216 	if (decorator == NULL)
2217 		return this;
2218 
2219 	int tab = decorator->TabAt(where);
2220 	// if we have a decorator we also have a stack
2221 	Window* window = fCurrentStack->WindowAt(tab);
2222 	if (window != NULL)
2223 		return window;
2224 	return this;
2225 }
2226 
2227 
2228 Window*
TopLayerStackWindow()2229 Window::TopLayerStackWindow()
2230 {
2231 	if (!fCurrentStack.IsSet())
2232 		return this;
2233 	return fCurrentStack->TopLayerWindow();
2234 }
2235 
2236 
2237 WindowStack*
GetWindowStack()2238 Window::GetWindowStack()
2239 {
2240 	if (!fCurrentStack.IsSet())
2241 		return _InitWindowStack();
2242 	return fCurrentStack;
2243 }
2244 
2245 
2246 bool
MoveToTopStackLayer()2247 Window::MoveToTopStackLayer()
2248 {
2249 	::Decorator* decorator = Decorator();
2250 	if (decorator == NULL)
2251 		return false;
2252 	decorator->SetDrawingEngine(GetDrawingEngine());
2253 	SetLook(Look(), NULL);
2254 	decorator->SetTopTab(PositionInStack());
2255 	return fCurrentStack->MoveToTopLayer(this);
2256 }
2257 
2258 
2259 bool
MoveToStackPosition(int32 to,bool isMoving)2260 Window::MoveToStackPosition(int32 to, bool isMoving)
2261 {
2262 	if (!fCurrentStack.IsSet())
2263 		return false;
2264 	int32 index = PositionInStack();
2265 	if (fCurrentStack->Move(index, to) == false)
2266 		return false;
2267 
2268 	BRegion dirty;
2269 	::Decorator* decorator = Decorator();
2270 	if (decorator && decorator->MoveTab(index, to, isMoving, &dirty) == false)
2271 		return false;
2272 
2273 	fDesktop->RebuildAndRedrawAfterWindowChange(this, dirty);
2274 	return true;
2275 }
2276 
2277 
2278 WindowStack*
_InitWindowStack()2279 Window::_InitWindowStack()
2280 {
2281 	fCurrentStack = NULL;
2282 	::Decorator* decorator = NULL;
2283 	if (fLook != B_NO_BORDER_WINDOW_LOOK)
2284 		decorator = gDecorManager.AllocateDecorator(this);
2285 
2286 	WindowStack* stack = new(std::nothrow) WindowStack(decorator);
2287 	if (stack == NULL)
2288 		return NULL;
2289 
2290 	if (stack->AddWindow(this) != true) {
2291 		delete stack;
2292 		return NULL;
2293 	}
2294 	fCurrentStack.SetTo(stack, true);
2295 	return stack;
2296 }
2297 
2298 
WindowStack(::Decorator * decorator)2299 WindowStack::WindowStack(::Decorator* decorator)
2300 	:
2301 	fDecorator(decorator)
2302 {
2303 
2304 }
2305 
2306 
~WindowStack()2307 WindowStack::~WindowStack()
2308 {
2309 }
2310 
2311 
2312 void
SetDecorator(::Decorator * decorator)2313 WindowStack::SetDecorator(::Decorator* decorator)
2314 {
2315 	fDecorator.SetTo(decorator);
2316 }
2317 
2318 
2319 ::Decorator*
Decorator()2320 WindowStack::Decorator()
2321 {
2322 	return fDecorator.Get();
2323 }
2324 
2325 
2326 Window*
TopLayerWindow() const2327 WindowStack::TopLayerWindow() const
2328 {
2329 	return fWindowLayerOrder.ItemAt(fWindowLayerOrder.CountItems() - 1);
2330 }
2331 
2332 
2333 int32
CountWindows()2334 WindowStack::CountWindows()
2335 {
2336 	return fWindowList.CountItems();
2337 }
2338 
2339 
2340 Window*
WindowAt(int32 index)2341 WindowStack::WindowAt(int32 index)
2342 {
2343 	return fWindowList.ItemAt(index);
2344 }
2345 
2346 
2347 bool
AddWindow(Window * window,int32 position)2348 WindowStack::AddWindow(Window* window, int32 position)
2349 {
2350 	if (position >= 0) {
2351 		if (fWindowList.AddItem(window, position) == false)
2352 			return false;
2353 	} else if (fWindowList.AddItem(window) == false)
2354 		return false;
2355 
2356 	if (fWindowLayerOrder.AddItem(window) == false) {
2357 		fWindowList.RemoveItem(window);
2358 		return false;
2359 	}
2360 	return true;
2361 }
2362 
2363 
2364 bool
RemoveWindow(Window * window)2365 WindowStack::RemoveWindow(Window* window)
2366 {
2367 	if (fWindowList.RemoveItem(window) == false)
2368 		return false;
2369 
2370 	fWindowLayerOrder.RemoveItem(window);
2371 	return true;
2372 }
2373 
2374 
2375 bool
MoveToTopLayer(Window * window)2376 WindowStack::MoveToTopLayer(Window* window)
2377 {
2378 	int32 index = fWindowLayerOrder.IndexOf(window);
2379 	return fWindowLayerOrder.MoveItem(index,
2380 		fWindowLayerOrder.CountItems() - 1);
2381 }
2382 
2383 
2384 bool
Move(int32 from,int32 to)2385 WindowStack::Move(int32 from, int32 to)
2386 {
2387 	return fWindowList.MoveItem(from, to);
2388 }
2389