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