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