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