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