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