xref: /haiku/src/servers/app/Window.cpp (revision b30304acc8c37e678a1bf66976d15bdab103f931)
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(fFrame.left - fTopView->Frame().left,
513 			fFrame.top - fTopView->Frame().top, NULL);
514 
515 		// make sure the size of the top view matches ours
516 		fTopView->ResizeBy(fFrame.Width() - fTopView->Frame().Width(),
517 			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->ScreenClipping(&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(&view->ScreenClipping(&fContentRegion));
710 
711 //fDrawingEngine->FillRegion(viewRegion, rgb_color{ 0, 255, 0, 255 });
712 //snooze(10000);
713 			fDirtyCause |= UPDATE_REQUEST;
714 			_TriggerContentRedraw(viewRegion);
715 		}
716 	}
717 }
718 
719 // DisableUpdateRequests
720 void
721 Window::DisableUpdateRequests()
722 {
723 	fUpdatesEnabled = false;
724 }
725 
726 
727 // EnableUpdateRequests
728 void
729 Window::EnableUpdateRequests()
730 {
731 	fUpdatesEnabled = true;
732 	if (!fUpdateRequested && fPendingUpdateSession->IsUsed())
733 		_SendUpdateMessage();
734 }
735 
736 // #pragma mark -
737 
738 
739 void
740 Window::MouseDown(BMessage* message, BPoint where, int32* _viewToken)
741 {
742 	// TODO: move into Decorator
743 	if (!fBorderRegionValid)
744 		GetBorderRegion(&fBorderRegion);
745 
746 	// default action is to drag the Window
747 	if (fBorderRegion.Contains(where)) {
748 		// clicking Window visible area
749 
750 		click_type action = DEC_DRAG;
751 
752 		if (fDecorator)
753 			action = _ActionFor(message);
754 
755 		// ignore clicks on decorator buttons if the
756 		// non-floating window doesn't have focus
757 		if (!IsFocus() && !IsFloating() && action != DEC_MOVETOBACK
758 			&& action != DEC_RESIZE && action != DEC_SLIDETAB)
759 			action = DEC_DRAG;
760 
761 		// set decorator internals
762 		switch (action) {
763 			case DEC_CLOSE:
764 				fIsClosing = true;
765 				STRACE_CLICK(("===> DEC_CLOSE\n"));
766 				break;
767 
768 			case DEC_ZOOM:
769 				fIsZooming = true;
770 				STRACE_CLICK(("===> DEC_ZOOM\n"));
771 				break;
772 
773 			case DEC_MINIMIZE:
774 				fIsMinimizing = true;
775 				STRACE_CLICK(("===> DEC_MINIMIZE\n"));
776 				break;
777 
778 			case DEC_DRAG:
779 				fIsDragging = true;
780 				fLastMousePosition = where;
781 				STRACE_CLICK(("===> DEC_DRAG\n"));
782 				break;
783 
784 			case DEC_RESIZE:
785 				fIsResizing = true;
786 				fLastMousePosition = where;
787 				STRACE_CLICK(("===> DEC_RESIZE\n"));
788 				break;
789 
790 			case DEC_SLIDETAB:
791 				fIsSlidingTab = true;
792 				fLastMousePosition = where;
793 				STRACE_CLICK(("===> DEC_SLIDETAB\n"));
794 				break;
795 
796 			default:
797 				break;
798 		}
799 
800 		// redraw decorator
801 		BRegion* visibleBorder = fRegionPool.GetRegion();
802 		GetBorderRegion(visibleBorder);
803 		visibleBorder->IntersectWith(&VisibleRegion());
804 
805 		DrawingEngine* engine = fDecorator->GetDrawingEngine();
806 		engine->LockParallelAccess();
807 		engine->ConstrainClippingRegion(visibleBorder);
808 
809 		if (fIsZooming) {
810 			fDecorator->SetZoom(true);
811 		} else if (fIsClosing) {
812 			fDecorator->SetClose(true);
813 		} else if (fIsMinimizing) {
814 			fDecorator->SetMinimize(true);
815 		}
816 
817 		engine->UnlockParallelAccess();
818 
819 		fRegionPool.Recycle(visibleBorder);
820 
821 		// based on what the Decorator returned, properly place this window.
822 		if (action == DEC_MOVETOBACK) {
823 			fDesktop->SendWindowBehind(this);
824 		} else {
825 			fDesktop->SetMouseEventWindow(this);
826 
827 			// activate window if not in FFM mode
828 			DesktopSettings desktopSettings(fDesktop);
829 			if (!desktopSettings.FocusFollowsMouse()) {
830 				fDesktop->ActivateWindow(this);
831 			} else {
832 				// actually, the window should already be
833 				// focused since the mouse would have to
834 				// be over it, but just for completeness...
835 				fDesktop->SetFocusWindow(this);
836 				if (action == DEC_DRAG) {
837 					fActivateOnMouseUp = true;
838 					fLastMoveTime = system_time();
839 				}
840 			}
841 		}
842 	} else {
843 		// click was inside the window contents
844 		if (View* view = ViewAt(where)) {
845 			if (HasModal())
846 				return;
847 
848 			// clicking a simple View
849 			if (!IsFocus()) {
850 				DesktopSettings desktopSettings(fDesktop);
851 
852 				// Activate window in case it doesn't accept first click, and
853 				// we're not in FFM mode
854 				if ((Flags() & B_WILL_ACCEPT_FIRST_CLICK) == 0
855 					&& !desktopSettings.FocusFollowsMouse())
856 					fDesktop->ActivateWindow(this);
857 
858 				// Eat the click if we don't accept first click
859 				// (B_AVOID_FOCUS never gets the focus, so they always accept
860 				// the first click)
861 				// TODO: the latter is unlike BeOS - if we really wanted to
862 				// imitate this behaviour, we would need to check if we're
863 				// the front window instead of the focus window
864 				if ((Flags() & (B_WILL_ACCEPT_FIRST_CLICK
865 						| B_AVOID_FOCUS)) == 0)
866 					return;
867 			}
868 
869 			// fill out view token for the view under the mouse
870 			*_viewToken = view->Token();
871 			view->MouseDown(message, where);
872 		}
873 	}
874 }
875 
876 
877 void
878 Window::MouseUp(BMessage* message, BPoint where, int32* _viewToken)
879 {
880 	bool invalidate = false;
881 	if (fDecorator) {
882 		click_type action = _ActionFor(message);
883 
884 		// redraw decorator
885 		BRegion* visibleBorder = fRegionPool.GetRegion();
886 		GetBorderRegion(visibleBorder);
887 		visibleBorder->IntersectWith(&VisibleRegion());
888 
889 		DrawingEngine* engine = fDecorator->GetDrawingEngine();
890 		engine->LockParallelAccess();
891 		engine->ConstrainClippingRegion(visibleBorder);
892 
893 		if (fIsZooming) {
894 			fIsZooming = false;
895 			fDecorator->SetZoom(false);
896 			if (action == DEC_ZOOM) {
897 				invalidate = true;
898 				fWindow->NotifyZoom();
899 			}
900 		}
901 		if (fIsClosing) {
902 			fIsClosing = false;
903 			fDecorator->SetClose(false);
904 			if (action == DEC_CLOSE) {
905 				invalidate = true;
906 				fWindow->NotifyQuitRequested();
907 			}
908 		}
909 		if (fIsMinimizing) {
910 			fIsMinimizing = false;
911 			fDecorator->SetMinimize(false);
912 			if (action == DEC_MINIMIZE) {
913 				invalidate = true;
914 				fWindow->NotifyMinimize(true);
915 			}
916 		}
917 
918 		engine->UnlockParallelAccess();
919 
920 		fRegionPool.Recycle(visibleBorder);
921 	}
922 
923 	// in FFM mode, activate the window and bring it
924 	// to front in case this was a drag click but the
925 	// mouse was not moved
926 	if (fActivateOnMouseUp) {
927 		fActivateOnMouseUp = false;
928 		// on R5, there is a time window for this feature
929 		// ie, click and press too long, nothing will happen
930 		if (system_time() - fLastMoveTime < 500000)
931 			fDesktop->ActivateWindow(this);
932 	}
933 
934 	fIsDragging = false;
935 	fIsResizing = false;
936 	fIsSlidingTab = false;
937 
938 	if (View* view = ViewAt(where)) {
939 		if (HasModal())
940 			return;
941 
942 		*_viewToken = view->Token();
943 		view->MouseUp(message, where);
944 	}
945 }
946 
947 
948 void
949 Window::MouseMoved(BMessage *message, BPoint where, int32* _viewToken,
950 	bool isLatestMouseMoved)
951 {
952 #if 0
953 	if (fDecorator != NULL && fTopView != NULL) {
954 		DrawingEngine* engine = fDecorator->GetDrawingEngine();
955 		engine->LockParallelAccess();
956 		engine->ConstrainClippingRegion(&VisibleRegion());
957 
958 		fTopView->MarkAt(engine, where);
959 		engine->UnlockParallelAccess();
960 	}
961 #endif
962 
963 	View* view = ViewAt(where);
964 	if (view != NULL)
965 		*_viewToken = view->Token();
966 
967 	// ignore pointer history
968 	if (!isLatestMouseMoved)
969 		return;
970 
971 	// limit the rate at which "mouse moved" events
972 	// are handled that move or resize the window
973 	if (fIsDragging || fIsResizing) {
974 		bigtime_t now = system_time();
975 		if (now - fLastMoveTime < 13333) {
976 			// TODO: add a "timed event" to query for
977 			// the then current mouse position
978 			return;
979 		}
980 		fLastMoveTime = now;
981 	}
982 
983 	if (fDecorator) {
984 		BRegion* visibleBorder = fRegionPool.GetRegion();
985 		GetBorderRegion(visibleBorder);
986 		visibleBorder->IntersectWith(&VisibleRegion());
987 
988 		DrawingEngine* engine = fDecorator->GetDrawingEngine();
989 		engine->LockParallelAccess();
990 		engine->ConstrainClippingRegion(visibleBorder);
991 
992 		if (fIsZooming) {
993 			fDecorator->SetZoom(_ActionFor(message) == DEC_ZOOM);
994 		} else if (fIsClosing) {
995 			fDecorator->SetClose(_ActionFor(message) == DEC_CLOSE);
996 		} else if (fIsMinimizing) {
997 			fDecorator->SetMinimize(_ActionFor(message) == DEC_MINIMIZE);
998 		}
999 
1000 		engine->UnlockParallelAccess();
1001 		fRegionPool.Recycle(visibleBorder);
1002 	}
1003 
1004 	BPoint delta = where - fLastMousePosition;
1005 	// NOTE: "delta" is later used to change fLastMousePosition.
1006 	// If for some reason no change should take effect, delta
1007 	// is to be set to (0, 0) so that fLastMousePosition is not
1008 	// adjusted. This way the relative mouse position to the
1009 	// item being changed (border during resizing, tab during
1010 	// sliding...) stays fixed when the mouse is moved so that
1011 	// changes are taking effect again.
1012 
1013 	// moving
1014 	if (fIsDragging) {
1015 		if (!(Flags() & B_NOT_MOVABLE)) {
1016 			BPoint oldLeftTop = fFrame.LeftTop();
1017 
1018 			fDesktop->MoveWindowBy(this, delta.x, delta.y);
1019 
1020 			// constrain delta to true change in size
1021 			delta = fFrame.LeftTop() - oldLeftTop;
1022 		} else
1023 			delta = BPoint(0, 0);
1024 	}
1025 	// resizing
1026 	if (fIsResizing) {
1027 		if (!(Flags() & B_NOT_RESIZABLE)) {
1028 			if (Flags() & B_NOT_V_RESIZABLE)
1029 				delta.y = 0;
1030 			if (Flags() & B_NOT_H_RESIZABLE)
1031 				delta.x = 0;
1032 
1033 			BPoint oldRightBottom = fFrame.RightBottom();
1034 
1035 			fDesktop->ResizeWindowBy(this, delta.x, delta.y);
1036 
1037 			// constrain delta to true change in size
1038 			delta = fFrame.RightBottom() - oldRightBottom;
1039 		} else
1040 			delta = BPoint(0, 0);
1041 	}
1042 	// sliding tab
1043 	if (fIsSlidingTab) {
1044 		float loc = TabLocation();
1045 		// TODO: change to [0:1]
1046 		loc += delta.x;
1047 		if (fDesktop->SetWindowTabLocation(this, loc))
1048 			delta.y = 0;
1049 		else
1050 			delta = BPoint(0, 0);
1051 	}
1052 
1053 	// NOTE: fLastMousePosition is currently only
1054 	// used for window moving/resizing/sliding the tab
1055 	fLastMousePosition += delta;
1056 
1057 	// the window was moved, it doesn't come to
1058 	// the front in FFM mode when the mouse is released
1059 	fActivateOnMouseUp = false;
1060 
1061 	// change focus in FFM mode
1062 	DesktopSettings desktopSettings(fDesktop);
1063 	if (desktopSettings.FocusFollowsMouse()
1064 		&& !IsFocus() && !(Flags() & B_AVOID_FOCUS)) {
1065 		fDesktop->SetFocusWindow(this);
1066 	}
1067 
1068 	// mouse cursor
1069 
1070 	if (view != NULL) {
1071 		view->MouseMoved(message, where);
1072 
1073 		// TODO: there is more for real cursor support, ie. if a window is closed,
1074 		//		new app cursor shouldn't override view cursor, ...
1075 		ServerWindow()->App()->SetCurrentCursor(view->Cursor());
1076 	}
1077 }
1078 
1079 
1080 // #pragma mark -
1081 
1082 
1083 void
1084 Window::WorkspaceActivated(int32 index, bool active)
1085 {
1086 	if (!active)
1087 		fWindow->HandleDirectConnection(B_DIRECT_STOP);
1088 
1089 	BMessage activatedMsg(B_WORKSPACE_ACTIVATED);
1090 	activatedMsg.AddInt64("when", system_time());
1091 	activatedMsg.AddInt32("workspace", index);
1092 	activatedMsg.AddBool("active", active);
1093 
1094 	ServerWindow()->SendMessageToClient(&activatedMsg);
1095 
1096 	if (active)
1097 		fWindow->HandleDirectConnection(B_DIRECT_START | B_BUFFER_RESET);
1098 }
1099 
1100 
1101 void
1102 Window::WorkspacesChanged(uint32 oldWorkspaces, uint32 newWorkspaces)
1103 {
1104 	fWorkspaces = newWorkspaces;
1105 
1106 	BMessage changedMsg(B_WORKSPACES_CHANGED);
1107 	changedMsg.AddInt64("when", system_time());
1108 	changedMsg.AddInt32("old", oldWorkspaces);
1109 	changedMsg.AddInt32("new", newWorkspaces);
1110 
1111 	ServerWindow()->SendMessageToClient(&changedMsg);
1112 }
1113 
1114 
1115 void
1116 Window::Activated(bool active)
1117 {
1118 	BMessage msg(B_WINDOW_ACTIVATED);
1119 	msg.AddBool("active", active);
1120 	ServerWindow()->SendMessageToClient(&msg);
1121 }
1122 
1123 
1124 //# pragma mark -
1125 
1126 
1127 void
1128 Window::SetTitle(const char* name, BRegion& dirty)
1129 {
1130 	// rebuild the clipping for the title area
1131 	// and redraw it.
1132 
1133 	fTitle = name;
1134 
1135 	if (fDecorator) {
1136 		fDecorator->SetTitle(name, &dirty);
1137 
1138 		fBorderRegionValid = false;
1139 			// the border very likely changed
1140 	}
1141 }
1142 
1143 
1144 void
1145 Window::SetFocus(bool focus)
1146 {
1147 	// executed from Desktop thread
1148 	// it holds the clipping write lock,
1149 	// so the window thread cannot be
1150 	// accessing fIsFocus
1151 
1152 	BRegion* dirty = fRegionPool.GetRegion(fBorderRegion);
1153 	if (dirty) {
1154 		dirty->IntersectWith(&fVisibleRegion);
1155 		fDesktop->MarkDirty(*dirty);
1156 		fRegionPool.Recycle(dirty);
1157 	}
1158 
1159 	fIsFocus = focus;
1160 	if (fDecorator)
1161 		fDecorator->SetFocus(focus);
1162 
1163 	Activated(focus);
1164 }
1165 
1166 
1167 void
1168 Window::SetHidden(bool hidden)
1169 {
1170 	// the desktop takes care of dirty regions
1171 	if (fHidden != hidden) {
1172 		fHidden = hidden;
1173 
1174 		fTopView->SetHidden(hidden);
1175 
1176 		// TODO: anything else?
1177 	}
1178 }
1179 
1180 
1181 void
1182 Window::SetMinimized(bool minimized)
1183 {
1184 	if (minimized == fMinimized)
1185 		return;
1186 
1187 	fMinimized = minimized;
1188 }
1189 
1190 
1191 bool
1192 Window::IsVisible() const
1193 {
1194 	if (IsOffscreenWindow())
1195 		return true;
1196 
1197 	if (IsHidden())
1198 		return false;
1199 
1200 /*
1201 	if (fVisibleRegion.CountRects() == 0)
1202 		return false;
1203 */
1204 	return fCurrentWorkspace >= 0 && fCurrentWorkspace < kWorkingList;
1205 }
1206 
1207 
1208 void
1209 Window::SetSizeLimits(int32 minWidth, int32 maxWidth,
1210 	int32 minHeight, int32 maxHeight)
1211 {
1212 	if (minWidth < 0)
1213 		minWidth = 0;
1214 
1215 	if (minHeight < 0)
1216 		minHeight = 0;
1217 
1218 	fMinWidth = minWidth;
1219 	fMaxWidth = maxWidth;
1220 	fMinHeight = minHeight;
1221 	fMaxHeight = maxHeight;
1222 
1223 	// give the Decorator a say in this too
1224 	if (fDecorator) {
1225 		fDecorator->GetSizeLimits(&fMinWidth, &fMinHeight,
1226 								  &fMaxWidth, &fMaxHeight);
1227 	}
1228 
1229 	_ObeySizeLimits();
1230 }
1231 
1232 
1233 void
1234 Window::GetSizeLimits(int32* minWidth, int32* maxWidth,
1235 	int32* minHeight, int32* maxHeight) const
1236 {
1237 	*minWidth = fMinWidth;
1238 	*maxWidth = fMaxWidth;
1239 	*minHeight = fMinHeight;
1240 	*maxHeight = fMaxHeight;
1241 }
1242 
1243 
1244 bool
1245 Window::SetTabLocation(float location, BRegion& dirty)
1246 {
1247 	bool ret = false;
1248 	if (fDecorator) {
1249 		ret = fDecorator->SetTabLocation(location, &dirty);
1250 		// the border region changed if ret is true
1251 		fBorderRegionValid = fBorderRegionValid && !ret;
1252 	}
1253 	return ret;
1254 }
1255 
1256 
1257 float
1258 Window::TabLocation() const
1259 {
1260 	if (fDecorator)
1261 		return fDecorator->TabLocation();
1262 	return 0.0;
1263 }
1264 
1265 
1266 bool
1267 Window::SetDecoratorSettings(const BMessage& settings, BRegion& dirty)
1268 {
1269 	bool ret = false;
1270 	if (fDecorator) {
1271 		ret = fDecorator->SetSettings(settings, &dirty);
1272 		// the border region changed if ret is true
1273 		fBorderRegionValid = fBorderRegionValid && !ret;
1274 	}
1275 	return ret;
1276 }
1277 
1278 
1279 bool
1280 Window::GetDecoratorSettings(BMessage* settings)
1281 {
1282 	if (fDecorator)
1283 		return fDecorator->GetSettings(settings);
1284 
1285 	return false;
1286 }
1287 
1288 
1289 void
1290 Window::SetLook(window_look look, BRegion* updateRegion)
1291 {
1292 	if (fDecorator == NULL && look != B_NO_BORDER_WINDOW_LOOK) {
1293 		// we need a new decorator
1294 		fDecorator = gDecorManager.AllocateDecorator(fDesktop, fDrawingEngine,
1295 			Frame(), Title(), fLook, fFlags);
1296 		if (IsFocus())
1297 			fDecorator->SetFocus(true);
1298 	}
1299 
1300 	fLook = look;
1301 
1302 	fBorderRegionValid = false;
1303 		// the border very likely changed
1304 	fContentRegionValid = false;
1305 		// mabye a resize handle was added...
1306 	fEffectiveDrawingRegionValid = false;
1307 		// ...and therefor the drawing region is
1308 		// likely not valid anymore either
1309 
1310 	if (fDecorator != NULL) {
1311 		DesktopSettings settings(fDesktop);
1312 		fDecorator->SetLook(settings, look, updateRegion);
1313 
1314 		// we might need to resize the window!
1315 		fDecorator->GetSizeLimits(&fMinWidth, &fMinHeight, &fMaxWidth, &fMaxHeight);
1316 		_ObeySizeLimits();
1317 	}
1318 
1319 	if (look == B_NO_BORDER_WINDOW_LOOK) {
1320 		// we don't need a decorator for this window
1321 		delete fDecorator;
1322 		fDecorator = NULL;
1323 	}
1324 }
1325 
1326 
1327 void
1328 Window::SetFeel(window_feel feel)
1329 {
1330 	// if the subset list is no longer needed, clear it
1331 	if ((fFeel == B_MODAL_SUBSET_WINDOW_FEEL || fFeel == B_FLOATING_SUBSET_WINDOW_FEEL)
1332 		&& (feel != B_MODAL_SUBSET_WINDOW_FEEL && feel != B_FLOATING_SUBSET_WINDOW_FEEL))
1333 		fSubsets.MakeEmpty();
1334 
1335 	fFeel = feel;
1336 
1337 	// having modal windows with B_AVOID_FRONT or B_AVOID_FOCUS doesn't
1338 	// make that much sense, so we filter those flags out on demand
1339 	fFlags = fOriginalFlags;
1340 	fFlags &= ValidWindowFlags(fFeel);
1341 
1342 	if (!IsNormal()) {
1343 		fFlags |= B_SAME_POSITION_IN_ALL_WORKSPACES;
1344 		_PropagatePosition();
1345 	}
1346 }
1347 
1348 
1349 void
1350 Window::SetFlags(uint32 flags, BRegion* updateRegion)
1351 {
1352 	fOriginalFlags = flags;
1353 	fFlags = flags & ValidWindowFlags(fFeel);
1354 	if (!IsNormal())
1355 		fFlags |= B_SAME_POSITION_IN_ALL_WORKSPACES;
1356 
1357 	if ((fFlags & B_SAME_POSITION_IN_ALL_WORKSPACES) != 0)
1358 		_PropagatePosition();
1359 
1360 	if (fDecorator == NULL)
1361 		return;
1362 
1363 	fDecorator->SetFlags(flags, updateRegion);
1364 
1365 	fBorderRegionValid = false;
1366 		// the border might have changed (smaller/larger tab)
1367 
1368 	// we might need to resize the window!
1369 	if (fDecorator) {
1370 		fDecorator->GetSizeLimits(&fMinWidth, &fMinHeight, &fMaxWidth, &fMaxHeight);
1371 		_ObeySizeLimits();
1372 	}
1373 }
1374 
1375 
1376 /*!	Returns wether or not a window is in the workspace list with the
1377 	specified \a index.
1378 */
1379 bool
1380 Window::InWorkspace(int32 index) const
1381 {
1382 	return (fWorkspaces & (1UL << index)) != 0;
1383 }
1384 
1385 
1386 bool
1387 Window::SupportsFront()
1388 {
1389 	if (fFeel == kDesktopWindowFeel
1390 		|| fFeel == kMenuWindowFeel
1391 		|| (fFlags & B_AVOID_FRONT) != 0)
1392 		return false;
1393 
1394 	return true;
1395 }
1396 
1397 
1398 bool
1399 Window::IsModal() const
1400 {
1401 	return IsModalFeel(fFeel);
1402 }
1403 
1404 
1405 bool
1406 Window::IsFloating() const
1407 {
1408 	return IsFloatingFeel(fFeel);
1409 }
1410 
1411 
1412 bool
1413 Window::IsNormal() const
1414 {
1415 	return !IsFloatingFeel(fFeel) && !IsModalFeel(fFeel);
1416 }
1417 
1418 
1419 bool
1420 Window::HasModal() const
1421 {
1422 	for (Window* window = NextWindow(fCurrentWorkspace); window != NULL;
1423 			window = window->NextWindow(fCurrentWorkspace)) {
1424 		if (window->IsHidden() || !window->IsModal())
1425 			continue;
1426 
1427 		if (window->HasInSubset(this))
1428 			return true;
1429 	}
1430 
1431 	return false;
1432 }
1433 
1434 
1435 /*!	\brief Returns the windows that's in behind of the backmost position
1436 		this window can get.
1437 		Returns NULL is this window can be the backmost window.
1438 */
1439 Window*
1440 Window::Backmost(Window* window, int32 workspace)
1441 {
1442 	if (workspace == -1)
1443 		workspace = fCurrentWorkspace;
1444 
1445 	// Desktop windows are always backmost
1446 	if (fFeel == kDesktopWindowFeel)
1447 		return NULL;
1448 
1449 	if (window == NULL)
1450 		window = PreviousWindow(workspace);
1451 
1452 	for (; window != NULL; window = window->PreviousWindow(workspace)) {
1453 		if (window->IsHidden() || window == this)
1454 			continue;
1455 
1456 		if (HasInSubset(window))
1457 			return window;
1458 	}
1459 
1460 	return NULL;
1461 }
1462 
1463 
1464 /*!	\brief Returns the windows that's in front of the frontmost position
1465 		this window can get.
1466 		Returns NULL if this window can be the frontmost window.
1467 */
1468 Window*
1469 Window::Frontmost(Window* first, int32 workspace)
1470 {
1471 	if (workspace == -1)
1472 		workspace = fCurrentWorkspace;
1473 
1474 	if (fFeel == kDesktopWindowFeel)
1475 		return first ? first : NextWindow(workspace);
1476 
1477 	if (first == NULL)
1478 		first = NextWindow(workspace);
1479 
1480 	for (Window* window = first; window != NULL;
1481 			window = window->NextWindow(workspace)) {
1482 		if (window->IsHidden() || window == this)
1483 			continue;
1484 
1485 		if (window->HasInSubset(this))
1486 			return window;
1487 	}
1488 
1489 	return NULL;
1490 }
1491 
1492 
1493 bool
1494 Window::AddToSubset(Window* window)
1495 {
1496 	return fSubsets.AddItem(window);
1497 }
1498 
1499 
1500 void
1501 Window::RemoveFromSubset(Window* window)
1502 {
1503 	fSubsets.RemoveItem(window);
1504 }
1505 
1506 
1507 bool
1508 Window::HasInSubset(const Window* window) const
1509 {
1510 	if (window == NULL || fFeel == window->Feel()
1511 		|| fFeel == B_NORMAL_WINDOW_FEEL)
1512 		return false;
1513 
1514 	// Menus are a special case: they will always be on-top of every window
1515 	// of their application
1516 	if (fFeel == kMenuWindowFeel)
1517 		return window->ServerWindow()->App() == ServerWindow()->App();
1518 	else if (window->Feel() == kMenuWindowFeel)
1519 		return false;
1520 
1521 	// we have a few special feels that have a fixed order
1522 
1523 	const int32 feel[] = {kWindowScreenFeel, B_MODAL_ALL_WINDOW_FEEL,
1524 		B_FLOATING_ALL_WINDOW_FEEL, 0};
1525 
1526 	for (int32 order = 0; feel[order]; order++) {
1527 		if (fFeel == feel[order])
1528 			return true;
1529 		if (window->Feel() == feel[order])
1530 			return false;
1531 	}
1532 
1533 	if (fFeel == B_FLOATING_APP_WINDOW_FEEL
1534 			&& window->Feel() != B_MODAL_APP_WINDOW_FEEL
1535 		|| fFeel == B_MODAL_APP_WINDOW_FEEL)
1536 		return window->ServerWindow()->App() == ServerWindow()->App();
1537 
1538 	return fSubsets.HasItem(window);
1539 }
1540 
1541 
1542 /*!	\brief Collects all workspaces views in this window and puts it into \a list
1543 */
1544 void
1545 Window::FindWorkspacesViews(BObjectList<WorkspacesView>& list) const
1546 {
1547 	int32 count = fWorkspacesViewCount;
1548 	fTopView->FindViews(kWorkspacesViewFlag, (BObjectList<View>&)list, count);
1549 }
1550 
1551 
1552 /*!	\brief Returns on which workspaces the window should be visible.
1553 
1554 	A modal or floating window may be visible on a workscreen if one
1555 	of its subset windows is visible there. Floating windows also need
1556 	to have a subset as front window to be visible.
1557 */
1558 uint32
1559 Window::SubsetWorkspaces() const
1560 {
1561 	if (fFeel == B_MODAL_ALL_WINDOW_FEEL
1562 		|| fFeel == B_FLOATING_ALL_WINDOW_FEEL)
1563 		return B_ALL_WORKSPACES;
1564 
1565 	if (fFeel == B_FLOATING_APP_WINDOW_FEEL) {
1566 		Window* front = fDesktop->FrontWindow();
1567 		if (front != NULL && front->IsNormal()
1568 			&& front->ServerWindow()->App() == ServerWindow()->App())
1569 			return ServerWindow()->App()->Workspaces();
1570 
1571 		return 0;
1572 	}
1573 
1574 	if (fFeel == B_MODAL_APP_WINDOW_FEEL) {
1575 		uint32 workspaces = ServerWindow()->App()->Workspaces();
1576 		if (workspaces == 0) {
1577 			// The application doesn't seem to have any other windows
1578 			// open or visible - but we'd like to see modal windows
1579 			// anyway, at least when they are first opened.
1580 			return 1UL << fDesktop->CurrentWorkspace();
1581 		}
1582 		return workspaces;
1583 	}
1584 
1585 	if (fFeel == B_MODAL_SUBSET_WINDOW_FEEL
1586 		|| fFeel == B_FLOATING_SUBSET_WINDOW_FEEL) {
1587 		uint32 workspaces = 0;
1588 		bool hasNormalFront = false;
1589 		for (int32 i = 0; i < fSubsets.CountItems(); i++) {
1590 			Window* window = fSubsets.ItemAt(i);
1591 
1592 			if (!window->IsHidden())
1593 				workspaces |= window->Workspaces();
1594 			if (window == fDesktop->FrontWindow() && window->IsNormal())
1595 				hasNormalFront = true;
1596 		}
1597 
1598 		if (fFeel == B_FLOATING_SUBSET_WINDOW_FEEL && !hasNormalFront)
1599 			return 0;
1600 
1601 		return workspaces;
1602 	}
1603 
1604 	return 0;
1605 }
1606 
1607 
1608 // #pragma mark - static
1609 
1610 
1611 /*static*/
1612 bool
1613 Window::IsValidLook(window_look look)
1614 {
1615 	return look == B_TITLED_WINDOW_LOOK
1616 		|| look == B_DOCUMENT_WINDOW_LOOK
1617 		|| look == B_MODAL_WINDOW_LOOK
1618 		|| look == B_FLOATING_WINDOW_LOOK
1619 		|| look == B_BORDERED_WINDOW_LOOK
1620 		|| look == B_NO_BORDER_WINDOW_LOOK
1621 		|| look == kDesktopWindowLook
1622 		|| look == kLeftTitledWindowLook;
1623 }
1624 
1625 
1626 /*static*/
1627 bool
1628 Window::IsValidFeel(window_feel feel)
1629 {
1630 	return feel == B_NORMAL_WINDOW_FEEL
1631 		|| feel == B_MODAL_SUBSET_WINDOW_FEEL
1632 		|| feel == B_MODAL_APP_WINDOW_FEEL
1633 		|| feel == B_MODAL_ALL_WINDOW_FEEL
1634 		|| feel == B_FLOATING_SUBSET_WINDOW_FEEL
1635 		|| feel == B_FLOATING_APP_WINDOW_FEEL
1636 		|| feel == B_FLOATING_ALL_WINDOW_FEEL
1637 		|| feel == kDesktopWindowFeel
1638 		|| feel == kMenuWindowFeel
1639 		|| feel == kWindowScreenFeel;
1640 }
1641 
1642 
1643 /*static*/
1644 bool
1645 Window::IsModalFeel(window_feel feel)
1646 {
1647 	return feel == B_MODAL_SUBSET_WINDOW_FEEL
1648 		|| feel == B_MODAL_APP_WINDOW_FEEL
1649 		|| feel == B_MODAL_ALL_WINDOW_FEEL;
1650 }
1651 
1652 
1653 /*static*/
1654 bool
1655 Window::IsFloatingFeel(window_feel feel)
1656 {
1657 	return feel == B_FLOATING_SUBSET_WINDOW_FEEL
1658 		|| feel == B_FLOATING_APP_WINDOW_FEEL
1659 		|| feel == B_FLOATING_ALL_WINDOW_FEEL;
1660 }
1661 
1662 
1663 /*static*/
1664 uint32
1665 Window::ValidWindowFlags()
1666 {
1667 	return B_NOT_MOVABLE | B_NOT_CLOSABLE | B_NOT_ZOOMABLE
1668 		| B_NOT_MINIMIZABLE | B_NOT_RESIZABLE
1669 		| B_NOT_H_RESIZABLE | B_NOT_V_RESIZABLE
1670 		| B_AVOID_FRONT | B_AVOID_FOCUS
1671 		| B_WILL_ACCEPT_FIRST_CLICK | B_OUTLINE_RESIZE
1672 		| B_NO_WORKSPACE_ACTIVATION
1673 		| B_NOT_ANCHORED_ON_ACTIVATE
1674 		| B_ASYNCHRONOUS_CONTROLS
1675 		| B_QUIT_ON_WINDOW_CLOSE
1676 		| B_SAME_POSITION_IN_ALL_WORKSPACES
1677 		| kWindowScreenFlag;
1678 }
1679 
1680 
1681 /*static*/
1682 uint32
1683 Window::ValidWindowFlags(window_feel feel)
1684 {
1685 	uint32 flags = ValidWindowFlags();
1686 	if (IsModalFeel(feel))
1687 		return flags & ~(B_AVOID_FOCUS | B_AVOID_FRONT);
1688 
1689 	return flags;
1690 }
1691 
1692 
1693 // #pragma mark - private
1694 
1695 
1696 // _ShiftPartOfRegion
1697 void
1698 Window::_ShiftPartOfRegion(BRegion* region, BRegion* regionToShift,
1699 	int32 xOffset, int32 yOffset)
1700 {
1701 	BRegion* common = fRegionPool.GetRegion(*regionToShift);
1702 	if (!common)
1703 		return;
1704 	// see if there is a common part at all
1705 	common->IntersectWith(region);
1706 	if (common->CountRects() > 0) {
1707 		// cut the common part from the region,
1708 		// offset that to destination and include again
1709 		region->Exclude(common);
1710 		common->OffsetBy(xOffset, yOffset);
1711 		region->Include(common);
1712 	}
1713 	fRegionPool.Recycle(common);
1714 }
1715 
1716 
1717 void
1718 Window::_TriggerContentRedraw(BRegion& dirtyContentRegion)
1719 {
1720 	if (IsVisible() && dirtyContentRegion.CountRects() > 0) {
1721 		// put this into the pending dirty region
1722 		// to eventually trigger a client redraw
1723 		bool wasExpose = fPendingUpdateSession->IsExpose();
1724 		BRegion* backgroundClearingRegion = &dirtyContentRegion;
1725 
1726 		_TransferToUpdateSession(&dirtyContentRegion);
1727 
1728 		if (fPendingUpdateSession->IsExpose()) {
1729 			if (!fContentRegionValid)
1730 				_UpdateContentRegion();
1731 
1732 			if (!wasExpose) {
1733 				// there was suddenly added a dirty region
1734 				// caused by exposing content, we need to clear
1735 				// the entire background
1736 				backgroundClearingRegion
1737 					= &(fPendingUpdateSession->DirtyRegion());
1738 			}
1739 
1740 			if (fDrawingEngine->LockParallelAccess()) {
1741 				bool copyToFrontEnabled = fDrawingEngine->CopyToFrontEnabled();
1742 				fDrawingEngine->SetCopyToFrontEnabled(true);
1743 				fDrawingEngine->SuspendAutoSync();
1744 
1745 				fTopView->Draw(fDrawingEngine, backgroundClearingRegion,
1746 					&fContentRegion, true);
1747 
1748 				fDrawingEngine->Sync();
1749 				fDrawingEngine->SetCopyToFrontEnabled(copyToFrontEnabled);
1750 				fDrawingEngine->UnlockParallelAccess();
1751 			}
1752 		}
1753 	}
1754 }
1755 
1756 
1757 void
1758 Window::_DrawBorder()
1759 {
1760 	// this is executed in the window thread, but only
1761 	// in respond to a REDRAW message having been received, the
1762 	// clipping lock is held for reading
1763 
1764 	if (!fDecorator)
1765 		return;
1766 
1767 	// construct the region of the border that needs redrawing
1768 	BRegion* dirtyBorderRegion = fRegionPool.GetRegion();
1769 	if (!dirtyBorderRegion)
1770 		return;
1771 	GetBorderRegion(dirtyBorderRegion);
1772 	// intersect with our visible region
1773 	dirtyBorderRegion->IntersectWith(&fVisibleRegion);
1774 	// intersect with the dirty region
1775 	dirtyBorderRegion->IntersectWith(&fDirtyRegion);
1776 
1777 	DrawingEngine* engine = fDecorator->GetDrawingEngine();
1778 	if (dirtyBorderRegion->CountRects() > 0 && engine->LockParallelAccess()) {
1779 		engine->ConstrainClippingRegion(dirtyBorderRegion);
1780 		bool copyToFrontEnabled = engine->CopyToFrontEnabled();
1781 		engine->SetCopyToFrontEnabled(true);
1782 
1783 		fDecorator->Draw(dirtyBorderRegion->Frame());
1784 
1785 		engine->SetCopyToFrontEnabled(copyToFrontEnabled);
1786 
1787 // TODO: remove this once the DrawState stuff is handled
1788 // more cleanly. The reason why this is needed is that
1789 // when the decorator draws strings, a draw state is set
1790 // on the Painter object, and this is were it might get
1791 // out of sync with what the ServerWindow things is the
1792 // current DrawState set on the Painter
1793 fWindow->ResyncDrawState();
1794 
1795 		engine->UnlockParallelAccess();
1796 	}
1797 	fRegionPool.Recycle(dirtyBorderRegion);
1798 }
1799 
1800 
1801 //static rgb_color sPendingColor;
1802 //static rgb_color sCurrentColor;
1803 
1804 /*!
1805 	pre: the clipping is readlocked (this function is
1806 	only called from _TriggerContentRedraw()), which
1807 	in turn is only called from MessageReceived() with
1808 	the clipping lock held
1809 */
1810 void
1811 Window::_TransferToUpdateSession(BRegion* contentDirtyRegion)
1812 {
1813 	if (contentDirtyRegion->CountRects() <= 0)
1814 		return;
1815 
1816 //fDrawingEngine->FillRegion(*contentDirtyRegion, sPendingColor);
1817 //snooze(10000);
1818 
1819 	// add to pending
1820 	fPendingUpdateSession->SetUsed(true);
1821 //	if (!fPendingUpdateSession->IsExpose())
1822 	fPendingUpdateSession->AddCause(fDirtyCause);
1823 	fPendingUpdateSession->Include(contentDirtyRegion);
1824 
1825 	if (!fUpdateRequested) {
1826 		// send this to client
1827 		_SendUpdateMessage();
1828 		// the pending region is now the current,
1829 		// though the update does not start until
1830 		// we received BEGIN_UPDATE from the client
1831 	}
1832 }
1833 
1834 // _SendUpdateMessage
1835 void
1836 Window::_SendUpdateMessage()
1837 {
1838 	if (!fUpdatesEnabled)
1839 		return;
1840 
1841 	BMessage message(_UPDATE_);
1842 	ServerWindow()->SendMessageToClient(&message);
1843 
1844 	fUpdateRequested = true;
1845 	fEffectiveDrawingRegionValid = false;
1846 }
1847 
1848 
1849 void
1850 Window::BeginUpdate(BPrivate::PortLink& link)
1851 {
1852 	// NOTE: since we might "shift" parts of the
1853 	// internal dirty regions from the desktop thread
1854 	// in response to Window::ResizeBy(), which
1855 	// might move arround views, the user of this function
1856 	// needs to hold the global clipping lock so that the internal
1857 	// dirty regions are not messed with from the Desktop thread
1858 	// and ServerWindow thread at the same time.
1859 
1860 	if (!fUpdateRequested) {
1861 		link.StartMessage(B_ERROR);
1862 		link.Flush();
1863 		fprintf(stderr, "Window::BeginUpdate() - no update requested!\n");
1864 		return;
1865 	}
1866 
1867 	// make the pending update session the current update session
1868 	// (toggle the pointers)
1869 	UpdateSession* temp = fCurrentUpdateSession;
1870 	fCurrentUpdateSession = fPendingUpdateSession;
1871 	fPendingUpdateSession = temp;
1872 	fPendingUpdateSession->SetUsed(false);
1873 	// all drawing command from the client
1874 	// will have the dirty region from the update
1875 	// session enforced
1876 	fInUpdate = true;
1877 	fEffectiveDrawingRegionValid = false;
1878 
1879 	// TODO: each view could be drawn individually
1880 	// right before carrying out the first drawing
1881 	// command from the client during an update
1882 	// (View::IsBackgroundDirty() can be used
1883 	// for this)
1884 	if (!fContentRegionValid)
1885 		_UpdateContentRegion();
1886 
1887 	BRegion* dirty = fRegionPool.GetRegion(
1888 		fCurrentUpdateSession->DirtyRegion());
1889 	if (!dirty) {
1890 		link.StartMessage(B_ERROR);
1891 		link.Flush();
1892 		return;
1893 	}
1894 
1895 	dirty->IntersectWith(&VisibleContentRegion());
1896 
1897 //sCurrentColor.red = rand() % 255;
1898 //sCurrentColor.green = rand() % 255;
1899 //sCurrentColor.blue = rand() % 255;
1900 //sPendingColor.red = rand() % 255;
1901 //sPendingColor.green = rand() % 255;
1902 //sPendingColor.blue = rand() % 255;
1903 //fDrawingEngine->FillRegion(*dirty, sCurrentColor);
1904 //snooze(10000);
1905 
1906 	link.StartMessage(B_OK);
1907 	// append the current window geometry to the
1908 	// message, the client will need it
1909 	link.Attach<BPoint>(fFrame.LeftTop());
1910 	link.Attach<float>(fFrame.Width());
1911 	link.Attach<float>(fFrame.Height());
1912 	// append he update rect in screen coords
1913 	link.Attach<BRect>(dirty->Frame());
1914 	// find and attach all views that intersect with
1915 	// the dirty region
1916 	fTopView->AddTokensForViewsInRegion(link, *dirty, &fContentRegion);
1917 	// mark the end of the token "list"
1918 	link.Attach<int32>(B_NULL_TOKEN);
1919 	link.Flush();
1920 
1921 	// supress back to front buffer copies in the drawing engine
1922 	fDrawingEngine->SetCopyToFrontEnabled(false);
1923 
1924 	if (!fCurrentUpdateSession->IsExpose() && fDrawingEngine->LockParallelAccess()) {
1925 //fDrawingEngine->FillRegion(dirty, (rgb_color){ 255, 0, 0, 255 });
1926 		fDrawingEngine->SuspendAutoSync();
1927 
1928 		fTopView->Draw(fDrawingEngine, dirty, &fContentRegion, true);
1929 
1930 		fDrawingEngine->Sync();
1931 		fDrawingEngine->UnlockParallelAccess();
1932 	} // else the background was cleared already
1933 
1934 	fRegionPool.Recycle(dirty);
1935 }
1936 
1937 
1938 void
1939 Window::EndUpdate()
1940 {
1941 	// NOTE: see comment in _BeginUpdate()
1942 
1943 	if (fInUpdate) {
1944 		// reenable copy to front
1945 		fDrawingEngine->SetCopyToFrontEnabled(true);
1946 
1947 		BRegion* dirty = fRegionPool.GetRegion(
1948 			fCurrentUpdateSession->DirtyRegion());
1949 
1950 		if (dirty) {
1951 			dirty->IntersectWith(&VisibleContentRegion());
1952 
1953 			fDrawingEngine->CopyToFront(*dirty);
1954 			fRegionPool.Recycle(dirty);
1955 		}
1956 
1957 		fCurrentUpdateSession->SetUsed(false);
1958 
1959 		fInUpdate = false;
1960 		fEffectiveDrawingRegionValid = false;
1961 	}
1962 	if (fPendingUpdateSession->IsUsed()) {
1963 		// send this to client
1964 		_SendUpdateMessage();
1965 	} else {
1966 		fUpdateRequested = false;
1967 	}
1968 }
1969 
1970 
1971 void
1972 Window::_UpdateContentRegion()
1973 {
1974 	fContentRegion.Set(fFrame);
1975 
1976 	// resize handle
1977 	if (fDecorator) {
1978 		if (!fBorderRegionValid)
1979 			GetBorderRegion(&fBorderRegion);
1980 
1981 		fContentRegion.Exclude(&fBorderRegion);
1982 	}
1983 
1984 	fContentRegionValid = true;
1985 }
1986 
1987 
1988 click_type
1989 Window::_ActionFor(const BMessage* msg) const
1990 {
1991 	if (fDecorator == NULL)
1992 		return DEC_NONE;
1993 
1994 	BPoint where;
1995 	if (msg->FindPoint("where", &where) != B_OK)
1996 		return DEC_NONE;
1997 
1998 	int32 buttons;
1999 	if (msg->FindInt32("buttons", &buttons) != B_OK)
2000 		buttons = 0;
2001 
2002 	int32 modifiers;
2003 	if (msg->FindInt32("modifiers", &modifiers) != B_OK)
2004 		modifiers = 0;
2005 
2006 	return fDecorator->Clicked(where, buttons, modifiers);
2007 }
2008 
2009 
2010 void
2011 Window::_ObeySizeLimits()
2012 {
2013 	// make sure we even have valid size limits
2014 	if (fMaxWidth < fMinWidth)
2015 		fMaxWidth = fMinWidth;
2016 
2017 	if (fMaxHeight < fMinHeight)
2018 		fMaxHeight = fMinHeight;
2019 
2020 	// Automatically resize the window to fit these new limits
2021 	// if it does not already.
2022 
2023 	// On R5, Windows don't automatically resize, but since
2024 	// BWindow::ResizeTo() even honors the limits, I would guess
2025 	// this is a bug that we don't have to adopt.
2026 	// Note that most current apps will do unnecessary resizing
2027 	// after having set the limits, but the overhead is neglible.
2028 
2029 	float minWidthDiff = fMinWidth - fFrame.Width();
2030 	float minHeightDiff = fMinHeight - fFrame.Height();
2031 	float maxWidthDiff = fMaxWidth - fFrame.Width();
2032 	float maxHeightDiff = fMaxHeight - fFrame.Height();
2033 
2034 	float xDiff = 0.0;
2035 	if (minWidthDiff > 0.0)	// we're currently smaller than minWidth
2036 		xDiff = minWidthDiff;
2037 	else if (maxWidthDiff < 0.0) // we're currently larger than maxWidth
2038 		xDiff = maxWidthDiff;
2039 
2040 	float yDiff = 0.0;
2041 	if (minHeightDiff > 0.0) // we're currently smaller than minHeight
2042 		yDiff = minHeightDiff;
2043 	else if (maxHeightDiff < 0.0) // we're currently larger than maxHeight
2044 		yDiff = maxHeightDiff;
2045 
2046 	if (fDesktop)
2047 		fDesktop->ResizeWindowBy(this, xDiff, yDiff);
2048 	else
2049 		ResizeBy(xDiff, yDiff, NULL);
2050 }
2051 
2052 
2053 // #pragma mark - UpdateSession
2054 
2055 
2056 Window::UpdateSession::UpdateSession()
2057 	: fDirtyRegion(),
2058 	  fInUse(false),
2059 	  fCause(0)
2060 {
2061 }
2062 
2063 
2064 Window::UpdateSession::~UpdateSession()
2065 {
2066 }
2067 
2068 
2069 void
2070 Window::UpdateSession::Include(BRegion* additionalDirty)
2071 {
2072 	fDirtyRegion.Include(additionalDirty);
2073 }
2074 
2075 
2076 void
2077 Window::UpdateSession::Exclude(BRegion* dirtyInNextSession)
2078 {
2079 	fDirtyRegion.Exclude(dirtyInNextSession);
2080 }
2081 
2082 
2083 void
2084 Window::UpdateSession::MoveBy(int32 x, int32 y)
2085 {
2086 	fDirtyRegion.OffsetBy(x, y);
2087 }
2088 
2089 
2090 void
2091 Window::UpdateSession::SetUsed(bool used)
2092 {
2093 	fInUse = used;
2094 	if (!fInUse) {
2095 		fDirtyRegion.MakeEmpty();
2096 		fCause = 0;
2097 	}
2098 }
2099 
2100 
2101 void
2102 Window::UpdateSession::AddCause(uint8 cause)
2103 {
2104 	fCause |= cause;
2105 }
2106 
2107 
2108