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