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