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