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