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