windowing/windowserver/nga/SERVER/openwfc/ScreenRedraw.cpp
changeset 0 5d03bc08d59c
child 121 d72fc2aace31
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/windowing/windowserver/nga/SERVER/openwfc/ScreenRedraw.cpp	Tue Feb 02 01:47:50 2010 +0200
@@ -0,0 +1,1228 @@
+// Copyright (c) 2006-2009 Nokia Corporation and/or its subsidiary(-ies).
+// All rights reserved.
+// This component and the accompanying materials are made available
+// under the terms of "Eclipse Public License v1.0"
+// which accompanies this distribution, and is available
+// at the URL "http://www.eclipse.org/legal/epl-v10.html".
+//
+// Initial Contributors:
+// Nokia Corporation - initial contribution.
+//
+// Contributors:
+//
+// Description:
+//
+
+#include "ScreenRedraw.h"
+
+#include <hal.h>
+
+#include "debugbar.h"
+#include "inifile.h"
+#include "screen.h"
+#include "pointer.h"
+#include "rootwin.h"
+#include "walkwindowtree.h"
+#include "wstop.h"
+#include "WsMemMgr.h"
+#include "renderstagemanager.h"
+#include "Graphics/WsRenderStageFactory.h"
+#include "Graphics/WsRenderStage.h"
+#include "Graphics/wsgraphicscontext.h"
+#include "EVENT.H"
+
+#ifdef USE_DEBUG_FRAME_CAPTURE
+#include <graphics/wsscreendevice.h>
+#include "../debuglog/debuglog.h"
+#endif
+
+GLREF_D CDebugLogBase *wsDebugLog;
+
+#ifdef USE_DEBUG_REGIONS
+#	define DEBUG_REGION(col,fill,reg) DebugRegion(col,fill,reg)
+#	define DEBUG_RECT(col,fill,rect) DebugRect(col,fill,rect)
+#else
+#	define DEBUG_REGION(col,fill,reg)
+#	define DEBUG_RECT(col,fill,rect)
+#endif
+
+#ifdef _DEBUG
+# define LOG_SCREEN_REDRAW_START {if (wsDebugLog) {_LIT(KLogScreenRedrawStart, ">> CScreenRedraw::OnAnimation()"); wsDebugLog->MiscMessage(CDebugLogBase::ELogEverything, KLogScreenRedrawStart);}}
+# define LOG_SCREEN_REDRAW_END {if (wsDebugLog) {_LIT(KLogScreenRedrawEnd, "<< CScreenRedraw::OnAnimation()"); wsDebugLog->MiscMessage(CDebugLogBase::ELogEverything, KLogScreenRedrawEnd);}}
+#else
+# define LOG_SCREEN_REDRAW_START
+# define LOG_SCREEN_REDRAW_END
+#endif
+
+
+#ifdef USE_DEBUG_FRAME_CAPTURE
+#	ifdef __WINS__
+	_LIT(KDefaultFrameCaptureLocation, "c:\\");
+#	else
+	_LIT(KDefaultFrameCaptureLocation, "e:\\");
+#	endif
+#endif //USE_DEBUG_FRAME_CAPTURE
+
+CScreenRedraw::TTimedRect::TTimedRect(TAnimType aType, const TRect& aRect, const TTime& aTime, CWsWindow* aWindow)
+	: iType(aType), iRect(aRect), iTime(aTime), iWindow(aWindow)
+	{
+	}
+
+TInt CScreenRedraw::TTimedRect::Compare(const TTimedRect& aOne,const TTimedRect& aOther)
+	{
+	if(aOne.iTime < aOther.iTime)
+		return -1;
+	else if(aOne.iTime > aOther.iTime)
+		return 1;
+	else
+		return 0;
+	}
+
+CScreenRedraw * CScreenRedraw::NewL(CScreen& aScreen)
+	{
+	CScreenRedraw * self = new (ELeave) CScreenRedraw(aScreen);
+	CleanupStack::PushL(self);
+	self->ConstructL();
+	CleanupStack::Pop(self);
+	return self;
+	}
+	
+CScreenRedraw::CScreenRedraw(CScreen& aScreen): iScreen(aScreen)
+	{
+	}
+
+CScreenRedraw::~CScreenRedraw()
+	{
+	CRenderStageManager::Release(iRenderStages);
+	iRenderStages = NULL;
+	iTimedDrawRect.Close();
+	iInvalid.Reset();
+	iTopElement.Reset();
+	iQuickFadeList.Reset();
+#ifdef USE_DEBUG_FRAME_CAPTURE
+	delete iDebugBitmap;
+	delete iDebugBitmapDevice;
+	delete iDebugBitmapGc;
+#endif //USE_DEBUG_FRAME_CAPTURE
+	}
+
+void CScreenRedraw::ConstructL()
+	{
+	
+	LEAVE_LOG_INSTALL_C;
+	
+	iRenderStages = CRenderStageManager::ConnectL(
+						iScreen.ScreenNumber(),
+						static_cast<MWsScreen*>(&iScreen),
+						this
+						);
+	
+	LEAVE_LOG_UNINSTALL_C;
+	
+	WS_ASSERT_ALWAYS(iRenderStages, EWsPanicNoRenderStagePipeline);
+	iRenderStageTextCursor = static_cast<MWsTextCursor*>(iRenderStages->ResolveObjectInterface(KMWsTextCursor));
+	WS_ASSERT_ALWAYS(iRenderStageTextCursor, EWsPanicTextCursorInterfaceMissing);
+
+#ifdef USE_DEBUG_FRAME_CAPTURE
+	_LIT(KWSERVIniFileVarFrameCapture,"FRAMECAPTURE");
+	iFrameCapture = WsIniFile->FindVar(KWSERVIniFileVarFrameCapture);
+
+	// Location to save captured images
+	if (!WsIniFile->FindVar(KWSERVIniFileVarFrameCapture, iFrameCaptureLocation) || iFrameCaptureLocation.Length() == 0)
+		{
+		iFrameCaptureLocation.Set(KDefaultFrameCaptureLocation);
+		}
+#endif //USE_DEBUG_FRAME_CAPTURE
+	}
+
+MWsTextCursor* CScreenRedraw::RenderStageTextCursor() const
+	{
+	return iRenderStageTextCursor;
+	}
+
+const TTime& CScreenRedraw::Now() const
+	{
+	if(!iAnimating)
+		{
+		iNow.UniversalTime();
+		}
+	return iNow;
+	}
+
+void CScreenRedraw::ScheduleRender(const TTimeIntervalMicroSeconds& aFromNow)
+	{
+	iRenderScheduled = ETrue;
+	TTime then(Now() + aFromNow);
+	if ((!iScheduled) || then < iNext)
+		iNext = then;
+	iScheduled = ETrue;
+	CWsTop::WindowServer()->AnimationScheduler()->ScheduleAnimation(iScreen,iNext);
+	}
+
+void CScreenRedraw::ScheduleRedraw()
+	{
+	iNext = Now();
+	iScheduled = ETrue;
+		
+	// The other scheduler also removes future animations which this one encompasses.
+	// We choose not to do the region calculations needed to achieve that here.
+	CWindowServer* server = CWsTop::WindowServer();
+	ASSERT(server);
+	MWsAnimationScheduler* sched = server->AnimationScheduler();
+	if(sched)
+		sched->ScheduleRedraw(iScreen,iNext);
+	else
+		RDebug::Printf("CWsTop::WindowServer()->RedrawScheduler() is NULL!!!");			
+	}
+
+/**
+@param aRect in screen coordinates 
+*/
+void CScreenRedraw::ScheduleAnimation(TAnimType aType, const TRect& aRect,const TTimeIntervalMicroSeconds& aFromNow,const TTimeIntervalMicroSeconds& /*aFreq*/,const TTimeIntervalMicroSeconds& /*aStop*/, CWsWindow* aWindow)
+	{
+	TRect scheduledRect(aRect);
+	TRect test(aRect);
+	// In changetracking mode, animation is either scheduled via a window dirty region (aWindow non-NULL)
+	// or via a sprite manager dirty region (aWindow NULL) if scheduling a floating sprite
+	if (iScreen.ChangeTracking())
+		{
+		if (aWindow)
+			{
+			// scheduling a window dirty rect for a window/anim/sprite
+			test.Intersection(aWindow->Abs());
+			scheduledRect.Move(-aWindow->Origin()); // convert to window coordinates
+			}
+		// else, // scheduling a sprite manager dirty rect for a floating sprite
+		}
+	else
+		{
+		// scheduling a screen dirty rect
+		test.Intersection(iScreen.DrawableArea());
+		aWindow = NULL; // ensure all future timed draw screen rects are checked below
+		}
+	if(!test.IsEmpty())
+		{
+		const TTime then(Now() + aFromNow);
+		TTimedRect tRect(aType, scheduledRect, then, aWindow);
+
+		const TInt error = iTimedDrawRect.InsertInOrderAllowRepeats(tRect,TTimedRect::Compare);
+		if (KErrNone == error)
+	   		{
+			if (iScheduled)
+				{
+				if (then < iNext)
+					{
+					iNext = then;
+					}
+				}
+			else
+				{
+				iNext = then;
+				iScheduled = ETrue;
+				}
+			// remove further futures that are completely contained
+			TInt count = iTimedDrawRect.Count();
+			for(TInt i=0; i<count; i++)
+				{
+				const TTimedRect& future = iTimedDrawRect[i];
+				if (future.iWindow != aWindow) // only check futures for the window/floating sprite/screen we're scheduling
+					continue;
+				if(future.iTime.Int64() > then.Int64())
+					{
+					TRect rect(scheduledRect);
+					rect.BoundingRect(future.iRect);
+					if(rect == scheduledRect) // future is completely contained within scheduledRect
+						{
+						iTimedDrawRect.Remove(i);
+						count--;
+						i--;
+						}
+					}
+				}
+			CWsTop::WindowServer()->AnimationScheduler()->ScheduleAnimation(iScreen,iNext);
+
+			// Blue rectangles for scheduled animations
+			DEBUG_RECT(TRgb(0x00, 0x00, 0xFF),TRgb(0x00, 0x00, 0xFF, 0x20),&aRect);
+			}
+		}
+	}
+
+TBool CScreenRedraw::IsScheduled(TAnimType aType, const TRect& aRect, CWsWindow* aWindow) const
+	{
+	TRect rect(aRect);
+	if(aWindow)
+		{
+		rect.Move(-aWindow->Origin()); //convert to window coordinates
+		}
+	
+	const TInt count(iTimedDrawRect.Count());
+	for(TInt i=0; i<count; i++)
+		{
+		if ((iTimedDrawRect[i].iType == aType) && 
+				(iTimedDrawRect[i].iRect == rect) && 
+				(iTimedDrawRect[i].iWindow == aWindow))
+			{
+			return ETrue;
+			}
+		}
+	return EFalse;
+	}
+
+// This adds a region to the stored invalid region.
+// The invalid region is the area of the screen that needs to be redrawn in addition to any animations.
+// The draw region is the area of the screen on which only the top window needs to be redrawn.
+// If the top window has transparency, this can only be true when it has been made newly visible.
+// The value of aSchedule could be determined automatically from iAnimating, but passing it this way
+// allows us to have the assert, which is a very valuable assert.
+void CScreenRedraw::AddRedrawRegion(const TRegion& aRegion, TBool aSchedule, TRedrawDepth aDepth)
+	{
+	WS_ASSERT_DEBUG(!iAnimating || !aSchedule, EWsPanicScheduledRedraw);
+
+	if(aRegion.CheckError())
+		{
+		iInvalid.ForceError();
+
+		if (aSchedule)
+			ScheduleRedraw();
+		}
+	else if(aRegion.Count()) // often called despite window not being visible
+		{
+		if (aDepth == ERedrawAll)
+			{
+			// red lines for an invalid region which is ready to be drawn
+			DEBUG_REGION(TRgb(0xFF, 0x00, 0x00),TRgb(0xFF, 0x00, 0x00, 0x20),&aRegion);
+			iInvalid.Union(aRegion);
+			
+			if (aSchedule)
+				ScheduleRedraw();
+			}
+		else
+			{
+			// yellow lines for a valid region which we will draw on top of
+			DEBUG_REGION(TRgb(0xFF, 0xFF, 0x00),TRgb(0xFF, 0xFF, 0x00, 0x20),&aRegion);
+				
+			iTopElement.Union(aRegion);
+			
+			if (aSchedule)
+				ScheduleRedraw();			
+			}
+		}
+	}
+
+// This causes any asynchronously scheduled redraw to happen immediately
+// It should be avoided where possible for performance reasons, but is 
+// needed whenever the redraw store is discarded for a window which still
+// has a redraw region pending.
+// @note This method always attempts to DoRedrawNow(), even if currently
+//		 animating. In this context, animation is a frame update. With NGA
+// 		 that update completes at some point in the future. If you get a request
+//		 to DoRedrawNow() the expectation is that all updates scheduled on or 
+//		 before that point in time will have completed by the time the call 
+//		 returns. An update may have been scheduled during a current (asynchronous)
+//		 Animate call...the animation scheduler has this knowledge, so let it
+//		 decide what to do.
+
+void CScreenRedraw::DoRedrawNow()
+	{
+	CWsTop::WindowServer()->AnimationScheduler()->DoRedrawNow(iScreen);
+	}
+	
+#ifdef USE_DEBUG_REGIONS
+void CScreenRedraw::DebugRect(TRgb aColor, TRgb aFill, const TRect* aRect)
+	{
+	if (aRect)
+		{
+		CFbsBitGc * gc = iScreen.GetBitGc();
+		gc->SetPenColor(aColor);
+		gc->SetPenStyle(CGraphicsContext::ESolidPen);
+		gc->SetPenSize(TSize(2,2));
+		gc->SetBrushColor(aFill);
+		gc->SetBrushStyle(CGraphicsContext::ESolidBrush);
+		TRect smaller = *aRect;
+		smaller.iBr.iX -= 1;
+		smaller.iBr.iY -= 1;
+		gc->DrawRect(smaller);
+		iScreen.Update();
+		}
+	}
+
+void CScreenRedraw::DebugRegion(TRgb aColor, TRgb aFill, const TRegion * aRegion)
+	{
+	if (aRegion)
+		{
+		CFbsBitGc * gc = iScreen.GetBitGc();
+		gc->SetPenColor(aColor);
+		gc->SetPenStyle(CGraphicsContext::ESolidPen);
+		gc->SetPenSize(TSize(2,2));
+		gc->SetBrushColor(aFill);
+		gc->SetBrushStyle(CGraphicsContext::ESolidBrush);
+		for (const TRect *rect = aRegion->RectangleList(); rect - aRegion->RectangleList() < aRegion->Count(); ++rect)
+			{
+			TRect smaller = *rect;
+			smaller.iBr.iX -= 1;
+			smaller.iBr.iY -= 1;
+			gc->DrawRect(smaller);
+			}
+		iScreen.Update();
+		}
+	}
+#endif 
+
+void CScreenRedraw::OnAnimation(TRequestStatus* aFinished)
+	{
+	LOG_SCREEN_REDRAW_START
+	WS_ASSERT_ALWAYS(!iAnimating,EWsPanicAnimationAlreadyAnimating);
+	WS_ASSERT_ALWAYS(iScheduled,EWsPanicAnimationNotScheduled);
+	iAnimating = ETrue;
+	iScheduled = EFalse;
+
+	const TBool changeTracking = iScreen.ChangeTracking();
+	
+ 	const TRegionFix<1> fallbackRegion(iScreen.RootWindow()->Abs());
+
+	CWsActiveScheduler::Static()->PrepareDraw();
+	
+	// Calculate any updates required by region changes:
+	RegionUpdate();
+
+	// Use the timed rectangles to mark screen, window or floating sprite as dirty
+	RWsRegionBuf<10> floatingSpriteDirtyRegion;
+ 	const TBool futureAnimationRequired = ScheduleTimedRects(floatingSpriteDirtyRegion);
+ 	if (floatingSpriteDirtyRegion.CheckError())
+ 		{
+ 		floatingSpriteDirtyRegion.Reset();
+ 		floatingSpriteDirtyRegion.Copy(fallbackRegion);
+ 		}
+ 	
+ 	TWalkWindowTreeSchedule* scheduler = NULL;
+ 	TWalkWindowListSchedule windowScheduler(iScheduledWindowList, iInvalid); //ChangeTracking
+	if (!changeTracking)
+		{
+		// Animating rectangles could cause iInvalid to overlap iTopElement, in which case iTopElement won't work.
+		iTopElement.SubRegion(iInvalid);
+		iTopElement.Intersect(iScreen.RootWindow()->WindowArea());
+		iTopElement.Tidy();
+		// Anything in the top element is implicitly invalid
+		iInvalid.Union(iTopElement);
+		iInvalid.Intersect(iScreen.RootWindow()->WindowArea());
+		iInvalid.Tidy();
+		
+		if(iInvalid.CheckError())
+			{
+			iTopElement.Reset();
+			iInvalid.Reset();
+			iInvalid.Copy(fallbackRegion);
+			}
+		}
+	else 
+		{
+		// In ChangeTracking mode, iInvalid is only used to tell the render stage
+		// which part of the screen needs updating.
+		iInvalid.Reset();
+		iInvalid.Copy(floatingSpriteDirtyRegion);
+		scheduler = &windowScheduler;
+		windowScheduler.WalkWindowList();
+		CWsTop::TriggerRedraws(iScreen.RootWindow()); //In case WalkWindowList did queue a request for the client to provide draw commands
+		iInvalid.Tidy();
+		if(iInvalid.CheckError())
+			{
+			iInvalid.Reset();
+			iInvalid.Copy(fallbackRegion);
+			}
+		}
+		
+	// At this point, if the DEBUG_REGION is being used:
+	// Red represents invalid regions that need to be redrawn completely.
+	// Yellow represents regions that only need the top window to be drawn.
+	// Blue represents regions which are being animated server side.
+	if (iRenderScheduled || !iInvalid.IsEmpty() || iQuickFadeList.Count() > 0)  
+		{
+		iRenderScheduled = EFalse;
+		
+		TWalkWindowTreeScheduleRegions regionScheduler(iInvalid, iTopElement); //!ChangeTeacking
+		TWalkWindowTreeScheduleFallback fallbackScheduler(iScreen.FallbackMap());//!ChangeTeacking
+
+		RWsRegionBuf<20> animationRegion;
+		
+		if (!changeTracking)
+			{	
+			animationRegion.Copy(iInvalid);
+			AddQuickFadeableRegions(animationRegion);
+			if (animationRegion.CheckError())
+				{
+				animationRegion.Reset();
+				animationRegion.Copy(fallbackRegion);
+				}
+			iAnimationRegion = &animationRegion; //iAnimationRegion must be reset to NULL before the call stack unwinds.
+			
+			scheduler = &regionScheduler;
+			iScreen.RootWindow()->WalkWindowTree(regionScheduler,EWalkChildren);
+			if (!regionScheduler.ScheduledRegionsOk())
+				{
+				// our region calculations for what to draw failed at some point.
+				// From this point on we MUST NOT rely on allocating memory
+				scheduler = &fallbackScheduler;
+				iScreen.FallbackMap()->Prepare();
+				iScreen.RootWindow()->WalkWindowTree(fallbackScheduler,EWalkChildren);
+				iAnimationRegion = iScreen.FallbackMap()->Region();
+				}
+			}
+		else
+			{
+			iAnimationRegion = &iInvalid;
+			}
+		
+		CWsActiveScheduler::Static()->StartDraw();
+		CWsMemoryManager::Static()->EnableReserve();
+		
+		// Redraw debug regions more brightly than before:
+		DEBUG_REGION(TRgb(0xFF, 0x00, 0x00),TRgb(0xFF, 0x00, 0x00, 0x80),iAnimationRegion);
+		DEBUG_REGION(TRgb(0xFF, 0xFF, 0x00),TRgb(0xFF, 0xFF, 0x00, 0x80),&iTopElement);
+		
+		// Pipe the drawing into the first render stage:
+		iRenderStages->Begin(iAnimationRegion);
+		
+		RWsRegionBuf<10> accumulatedDrawing;
+		
+		MWsGraphicsContext * stageGc = static_cast<MWsGraphicsContext*>(iRenderStages->ResolveObjectInterface(KMWsGraphicsContext));
+		for (CWsWindow * win = scheduler->HeadWindow(); (win!=NULL); win = win->NextScheduled())
+			{
+			if(!changeTracking)
+					accumulatedDrawing.Union(scheduler->WindowRegion(*win));
+			
+			Render(*win, *stageGc, *scheduler);
+			}
+
+		if(!changeTracking && !accumulatedDrawing.CheckError())
+			{
+			//Fade any region of the screen scheduled for fading which has not been redrawn,
+			DoQuickFade(stageGc, accumulatedDrawing);
+			}
+		accumulatedDrawing.Reset();
+		
+		//We limit floating sprite drawing to regions already touched
+		iScreen.SpriteManager()->DrawFloatingSprites(stageGc, iScreen.ChangeTracking() ? floatingSpriteDirtyRegion : *iAnimationRegion);
+		
+		iRenderStages->End(aFinished);
+		stageGc = NULL; //we're not allowed to draw outside Begin()/End()
+
+#if defined(__WINS__) && defined(_DEBUG)
+		MWsDebugBar * debugBar = static_cast<MWsDebugBar*>(iRenderStages->ResolveObjectInterface(KMWsDebugBar));
+		if (debugBar) //optional for the licensees
+			{
+			if (CDebugBar* dbg = iScreen.DebugBar())
+				{	
+				RArray<TPtrC> debugText;
+				dbg->DebugBarInfo(debugText);
+				debugBar->DrawDebugBar(debugText.Array());
+				debugText.Close();
+				}
+			}
+#endif
+		
+		iScheduledWindowList = NULL;
+		CWsMemoryManager::Static()->DisableReserve();
+
+#ifdef USE_DEBUG_FRAME_CAPTURE
+		if (iFrameCapture)
+			{
+			CaptureFrame(iAnimationRegion);
+			}
+#endif
+		
+		// DEBUG_REGION does not work with render stages.
+		// These comments refer to what used to happen.
+		
+		// At this point, if the DEBUG_REGION is being used, there should usually be only green regions
+		// displayed.  If we see red or yellow, then something didn't get redrawn that should have been.
+		// If we see purple then a window has disobeyed the const setting on the region to draw.
+		// Red or yellow is valid - a window can decide it isn't ready to draw yet - purple is very bad.
+		
+		//iScreen.Update();
+		
+		// At this point, if the DEBUG_REGION is being used, there should be no regions visible
+		// of any colour.  If we see green, then it means an area of the screen was drawn to which
+		// wasn't invalid, or the screen update call failed.  The former is more likely.
+		// If we still see red or yellow it is a region that is not yet ready to draw.
+
+		const TRect* rect = iAnimationRegion->RectangleList();
+		TInt pixels = 0;
+		for(TInt r = iAnimationRegion->Count(); r>0; r--)
+			{
+			pixels += (rect->Width()*rect->Height());
+			rect++;
+			}
+		CWsActiveScheduler::Static()->StopDraw(pixels);
+		
+		TWindowServerEvent::NotifyScreenDrawingEvent(iAnimationRegion);
+
+		iAnimationRegion = NULL; //iAnimationRegion must be reset to NULL before the call stack unwinds.
+		iInvalid.Reset();
+		animationRegion.Reset();
+		}
+	else
+		{
+		// There was nothing to compose. Signal that composition is complete.
+		if (aFinished) 
+			{
+			*aFinished = KRequestPending;
+			User::RequestComplete(aFinished, KErrNone);
+			}
+		
+		CWsActiveScheduler::Static()->CancelPrepare();
+		}
+	
+	floatingSpriteDirtyRegion.Reset();
+	iInvalid.Reset();
+	iTopElement.Reset();
+	iAnimating = EFalse;
+	
+	if (futureAnimationRequired && iTimedDrawRect.Count() > 0 && !iScheduled)
+		{
+		// If this flag is set then it means there were already some animations scheduled when we ran,
+		// but they themselves didn't run.  We need to make sure we have _something_ scheduled.
+		CWsTop::WindowServer()->AnimationScheduler()->ScheduleAnimation(iScreen, iTimedDrawRect[0].iTime);
+		iScheduled = ETrue;
+		}
+	
+	if(iObserver)
+		{
+		iObserver->ScreenUpdated(iScreen.ScreenNumber());
+		iObserver=NULL; //once signalled we are never going to call it again
+		}
+	LOG_SCREEN_REDRAW_END
+	}
+
+void CScreenRedraw::AddQuickFadeableRegions(TRegion& aRegion)
+	{
+	if (iQuickFadeList.Count() > 0)
+		{
+		for (TInt idx = iQuickFadeList.Count() - 1; idx >= 0; idx --)
+			{
+			CWsWindow* win = iQuickFadeList[ idx ];
+
+			if ( !win->IsDSAHost() )
+				{
+				aRegion.Union(win->QuickFadeRegion() );
+				}
+			}
+		aRegion.Tidy();
+		}
+	}
+
+void CScreenRedraw::DoQuickFade(MWsGraphicsContext* aGc, TRegion& aAccumulatedDrawing)
+	{
+	if ( iQuickFadeList.Count() > 0 )
+		{
+		for ( TInt idx = iQuickFadeList.Count() - 1; idx >= 0 ; idx -- )
+			{
+
+			CWsWindow* win = iQuickFadeList[ idx ];
+			
+			if ( !win->IsDSAHost() )
+				{				
+				STACK_REGION winFadeRgn;				
+				winFadeRgn.Copy( win->QuickFadeRegion() );
+				winFadeRgn.SubRegion( aAccumulatedDrawing );
+				winFadeRgn.Tidy();
+				if ( !winFadeRgn.IsEmpty() && !winFadeRgn.CheckError() )
+					{ // now fade any region that has not been redrawn (via win->Render())
+					win->Fade( aGc, winFadeRgn );
+					aAccumulatedDrawing.Union( winFadeRgn );	      		
+					}		      	
+	  			winFadeRgn.Close();
+				}
+			}
+	
+		iQuickFadeList.Reset();
+		}
+	}
+
+/**
+This function will iterate the timed rectangles and find any animation or sprite 
+that are due to be scheduled for render now. Then schedule them and remove them 
+from iTimedDrawRect.
+@return ETrue if iTimedDrawRect still contains future animations
+*/
+TBool CScreenRedraw::ScheduleTimedRects(TRegion& aScheduledFloatingSpriteRegion)
+ 	{
+	iNow.UniversalTime();
+	TInt count(iTimedDrawRect.Count());
+	TBool futureAnimationRequired = EFalse;
+	while (0 < count)
+		{
+		if(iTimedDrawRect[0].iTime.Int64() <= iNow.Int64())
+			{
+			if (iScreen.ChangeTracking())
+				{
+				switch(iTimedDrawRect[0].iType)
+					{
+					case ECrpAnim:
+						{
+						CWsWindow* win = iTimedDrawRect[0].iWindow;
+						WS_ASSERT_DEBUG(win,EWsPanicWindowNull);
+						if(!win->IsTrackingVisibleRegion() || (win->IsTrackingVisibleRegion() && !win->VisibleRegion().IsEmpty()))
+							{
+							TRegionFix<1> region;
+							region.AddRect(iTimedDrawRect[0].iRect);
+							win->AddDirtyWindowRegion(region);
+							ScheduleWindow(win);
+							}
+						break;
+						}
+					case EWindowAnim:
+					case ESpriteAnim:
+					case ETextCursor:
+					case EWindowSprite:
+						{
+						CWsWindow* win = iTimedDrawRect[0].iWindow;
+						WS_ASSERT_DEBUG(win,EWsPanicWindowNull);
+						if(!win->IsTrackingVisibleRegion() || (win->IsTrackingVisibleRegion() && !win->VisibleRegion().IsEmpty()))
+							{
+							TRegionFix<1> region;
+							region.AddRect(iTimedDrawRect[0].iRect);
+							win->AddDirtySpriteRegion(region);
+							ScheduleWindow(win);
+							}
+						break;
+						}
+					case EFloatingSprite:
+					case EFloatingSpriteAnim:
+						{
+						aScheduledFloatingSpriteRegion.AddRect(iTimedDrawRect[0].iRect);
+						break;
+						}
+					}
+				
+				TRect screenRect(iTimedDrawRect[0].iRect);
+				if(iTimedDrawRect[0].iWindow)
+					{
+					screenRect.Move(iTimedDrawRect[0].iWindow->Origin()); // convert to screen coordinates
+					}	
+				}
+			else
+				{ 
+				// schedule a screen dirty rect
+				iInvalid.AddRect(iTimedDrawRect[0].iRect);
+				}
+			iTimedDrawRect.Remove(0);
+			count--;
+			}
+		else
+			{
+			futureAnimationRequired = ETrue;	
+			break;
+			}
+		}
+	return futureAnimationRequired;
+ 	}
+
+void CScreenRedraw::Render(CWsWindow& aWin, MWsGraphicsContext& aGc, const TWalkWindowTreeSchedule& aScheduler)
+	{
+	const TRegion* windowRegion = &(aScheduler.WindowRegion(aWin));
+	const TRegion* spriteRegion = &(aScheduler.SpriteRegion(aWin));
+
+	if(windowRegion->IsEmpty() && spriteRegion->IsEmpty()) 
+	    return; //Don't call CWsWindow::Render if there is nothing that can be rendered 
+	
+	//Make sure we don't try to draw outside screen
+	STACK_REGION clippedWindowRegion;
+	if(!windowRegion->IsContainedBy(iScreen.RootWindow()->Abs()))
+		{
+		TRegionFix<1> screen(iScreen.RootWindow()->Abs());
+		clippedWindowRegion.Intersection(*windowRegion, screen);
+		windowRegion = &clippedWindowRegion;
+		}
+	STACK_REGION clippedSpriteRegion;
+	if(!spriteRegion->IsContainedBy(iScreen.RootWindow()->Abs()))
+		{
+		TRegionFix<1> screen(iScreen.RootWindow()->Abs());
+		clippedSpriteRegion.Intersection(*spriteRegion, screen);
+		spriteRegion = &clippedSpriteRegion;
+		}	
+	
+	if(!windowRegion->CheckError() && !spriteRegion->CheckError())
+		{
+		// Purple regions are about to be drawn
+		DEBUG_REGION(TRgb(0x80, 0x00, 0x80),TRgb(0x80, 0x00, 0x80, 0x80), windowRegion);
+		DEBUG_REGION(TRgb(0x80, 0x00, 0x80),TRgb(0x80, 0x00, 0x80, 0x80), spriteRegion);
+		
+		aWin.Render(aGc, *windowRegion, *spriteRegion); //When not in ChangeTracking mode, both windowRegion and spriteRegion points to the same region
+		aWin.ClearScheduledRegion();
+		aWin.ClearScheduledSpriteRegion();
+		
+		// Green regions have been drawn
+		DEBUG_REGION(TRgb(0x00, 0xFF, 0x00),TRgb(0x00, 0xFF, 0x00, 0x80), windowRegion);
+		DEBUG_REGION(TRgb(0x00, 0xFF, 0x00),TRgb(0x00, 0xFF, 0x00, 0x80), spriteRegion);
+		}
+	else
+		{
+		OomRender(aWin, aGc, aScheduler);		
+		}
+	
+	clippedSpriteRegion.Close();
+	clippedWindowRegion.Close();
+	}
+
+void CScreenRedraw::OomRender(CWsWindow& aWin, MWsGraphicsContext& aGc, const TWalkWindowTreeSchedule& aScheduler)
+	{	
+	const TRegion* windowRegion = &(aScheduler.WindowRegion(aWin));
+	const TRegion* spriteRegion = &(aScheduler.SpriteRegion(aWin));
+
+	WS_ASSERT_DEBUG(!(windowRegion->IsEmpty() && spriteRegion->IsEmpty()), EWsPanicRegionNull);
+	
+	TRect validWindow(aWin.Abs());
+	validWindow.Intersection(iScreen.RootWindow()->Abs());
+	
+    TRegionFix<1> fallbackRegion(validWindow);
+    
+    if(windowRegion->CheckError())
+        windowRegion = &fallbackRegion;
+    if(spriteRegion->CheckError())
+        spriteRegion = &fallbackRegion;
+    
+    // Purple regions are about to be drawn
+    DEBUG_REGION(TRgb(0x80, 0x00, 0x80),TRgb(0x80, 0x00, 0x80, 0x80), windowRegion);
+    DEBUG_REGION(TRgb(0x80, 0x00, 0x80),TRgb(0x80, 0x00, 0x80, 0x80), spriteRegion);
+    
+    aWin.Render(aGc, *windowRegion, *spriteRegion); //When not in ChangeTracking mode, both windowRegion and spriteRegion points to the same region
+    aWin.ClearScheduledRegion();
+    aWin.ClearScheduledSpriteRegion();
+    
+    // Green regions have been drawn
+    DEBUG_REGION(TRgb(0x00, 0xFF, 0x00),TRgb(0x00, 0xFF, 0x00, 0x80), windowRegion);
+    DEBUG_REGION(TRgb(0x00, 0xFF, 0x00),TRgb(0x00, 0xFF, 0x00, 0x80), spriteRegion);	
+	}
+
+//
+void CScreenRedraw::DiscardAllSchedules()
+	{
+	ASSERT(!iAnimating);
+	
+	iTimedDrawRect.Reset();
+	iInvalid.Reset();
+	}
+
+/**
+Indicates that a window has moved or changed ordinal position so that the visible regions
+of all windows needs to be recalculated
+*/
+void CScreenRedraw::ScheduleRegionUpdate(const TRegion* aDefinitelyDirty)
+	{
+	if (iScreen.ChangeTracking() && iScreen.WindowVisibilityNotifier())
+		return;
+
+	iRegionUpdateScheduled = ETrue;
+	ScheduleRedraw();
+	if(!iScreen.ChangeTracking() && aDefinitelyDirty)
+		{
+		iInvalid.Union(*aDefinitelyDirty);
+		// Cyan regions for invalidations caused by this:
+		DEBUG_REGION(TRgb(0x00, 0xFF, 0xFF),TRgb(0x00, 0xFF, 0xFF, 0x20),aDefinitelyDirty);
+		}
+	}
+
+/**
+Only used in CHANGETRACKING mode.
+Windows are scheduled to:
+- if required, ask client to validate (from lower loop)
+- render the dirty window region
+*/
+void CScreenRedraw::ScheduleWindow(CWsWindow* aWindow)
+	{
+	// Add a scheduled window to the linked list, ignoring duplicates
+	CWsWindow* win = iScheduledWindowList;
+	while (win && win != aWindow)
+		{
+		win = win->NextScheduled();
+		}
+	if (!win)
+		{
+		aWindow->SetNextScheduled(iScheduledWindowList);
+		iScheduledWindowList = aWindow;
+		}
+	ScheduleRedraw();
+	}
+
+void CScreenRedraw::RemoveFromScheduledList(CWsWindow* aWindow)
+	{
+	// Search for the window
+	CWsWindow* win = iScheduledWindowList;
+	CWsWindow* previous = NULL;
+	while (win && win != aWindow)
+		{
+		previous = win;
+		win = win->NextScheduled();
+		}
+	if (win)
+		{
+		// Found it, remove from list
+		if (win == iScheduledWindowList)
+			{
+			iScheduledWindowList = win->NextScheduled();
+			}
+		else
+			{
+			previous->SetNextScheduled(win->NextScheduled());
+			}
+		}
+	}
+
+void CScreenRedraw::RemoveFromTimedDrawList(CWsWindow* aWindow)
+	{
+	if(aWindow)
+		{	
+		TInt count(iTimedDrawRect.Count());
+		TInt index = 0;
+		while(index < count)
+			{
+			if (iTimedDrawRect[index].iWindow == aWindow)
+				{
+				iTimedDrawRect.Remove(index);
+				--count;
+				}
+			else
+				{
+				++index;
+				}
+			}
+		}
+	}
+
+/**
+Recalculates visible regions and schedules redraws or queues redraw events in response to
+any changes
+*/
+void CScreenRedraw::RegionUpdate()
+	{
+	TBool somethingScheduled = EFalse;
+
+	if (iVisibilityUpdateScheduled)
+		{
+		iVisibilityUpdateScheduled = EFalse;
+		somethingScheduled = ETrue;
+		MWsWindowVisibilityNotifier* const notifier = iScreen.WindowVisibilityNotifier();
+		if(notifier)
+			{
+			notifier->SendVisibilityChanges(); // Should result in one callback to SetWindowVisibility for each window subscribing for visible region updates
+			}
+		}
+
+	if (iRegionUpdateScheduled)
+		{
+		iRegionUpdateScheduled = EFalse;
+		somethingScheduled = ETrue;
+		TWalkWindowTreeUpdateRegions updater(iScreen);
+		updater.Walk();
+		
+		for ( TInt idx = iQuickFadeList.Count() - 1; idx >= 0; idx-- )
+			{
+			CWsWindow* win = iQuickFadeList[ idx ];
+			const TRegion& quickFadeRegion = win->QuickFadeRegion();
+			//If QuickFadeRegion().IsEmpty() we should remove the window from iQuickFadeList.
+			//And if this window has not been drawn to the screen, then it is not possible to quick fade it.
+			if (quickFadeRegion.IsEmpty() || !win->HasBeenDrawnToScreen())
+				{
+				iQuickFadeList.Remove( idx );
+				}
+			}
+		}
+
+	if (somethingScheduled)
+		{
+		TWsPointer::ReLogPointersCurrentWindows();
+		CWsTop::TriggerRedraws(iScreen.RootWindow());
+		}
+	}
+
+/**
+To be called by MWsWindowVisibilityNotifier (when running in ChangeTracking mode) 
+in response to MWsWindowVisibilityNotifier::SendVisibilityChanges()  
+*/
+void CScreenRedraw::SetWindowVisibility(const MWsWindow& aWindow, const TRegion& aVisibleRegion)
+	{
+	CWsWindow& win = static_cast<CWsWindow&>(const_cast<MWsWindow&>(aWindow));
+	WS_ASSERT_DEBUG(win.IsTrackingVisibleRegion(), EWsPanicVisibleRegionTracking);
+	WS_ASSERT_DEBUG(!aVisibleRegion.CheckError(), EWsPanicRegion);
+	
+	if(aVisibleRegion.IsEmpty() && !win.VisibleRegion().IsEmpty())
+		{
+		win.ClearVisibleRegion();
+		}
+	else if(!aVisibleRegion.IsEmpty() && !aVisibleRegion.CheckError())
+		{
+		// Assert that aVisibleRegion is contained by aWin
+		TRect bounds = win.AbsRect();
+		bounds.Resize(1,1);
+		WS_ASSERT_DEBUG(bounds.Contains(aVisibleRegion.BoundingRect().iTl), EWsPanicRegion);
+		WS_ASSERT_DEBUG(bounds.Contains(aVisibleRegion.BoundingRect().iBr), EWsPanicRegion);
+		win.SetVisibleRegion(aVisibleRegion, NULL);
+		}
+	else if(aVisibleRegion.CheckError())
+		{
+		const TRegionFix<1> fallback(win.Abs());
+		win.SetVisibleRegion(fallback, NULL);
+		}
+	}
+
+/**
+To be called by MWsWindowVisibilityNotifier (when running in ChangeTracking mode) when the 
+iVisibleRegion is changed for a window that subscribes for this information. The visible region 
+of the window has not been updated yet at the time of this function call, but the new metrics will be retrieved 
+from MWsWindowVisibilityNotifier (through a call to MWsWindowVisibilityObserver::SetWindowVisibility())
+and set to each window next time OnAnimation is called.   
+*/
+void CScreenRedraw::VisibilityChanged()
+	{
+	iVisibilityUpdateScheduled = ETrue;
+	ScheduleRedraw();
+	}
+
+void CScreenRedraw::SetObserver(MWsScreenRedrawObserver* aObserver)
+	{
+	iObserver = aObserver;
+	}
+
+/**
+Returns ETrue if an update or animation is scheduled.
+
+Note: Now that WSERV surface and composition updates are asynchronous (with NGA)
+it is possible for this method to return EFalse, even if the last update has
+not been completed by the rendering pipeline. This is dependant on the configuration
+and implementation of render stages that make up the rendering pipeline.
+
+If in doubt, it is best to call CScreenRedraw::DoRedrawNow(), which will not return
+untill all updates have been signalled as being complete (note that if signalling
+is not used, then even this cannot guarantee completion).
+*/ 
+TBool CScreenRedraw::IsUpdatePending()
+	{
+	if(iScheduled || iAnimating)
+		return ETrue;
+	else 
+		return EFalse;
+	}
+
+/**
+ Overidding MWsObjectProvider
+*/
+TAny* CScreenRedraw::ResolveObjectInterface(TUint aTypeId)
+	{
+	TAny* interface = NULL;
+
+	switch (aTypeId)
+		{
+		case KWsScreenRedraw:
+			interface = static_cast<MWsScreenRedraw*>(this);
+			break;
+		}
+
+	if (!interface)
+		{
+		interface = iRenderStages->ResolveObjectInterface(aTypeId);
+		}
+	
+	return interface;
+	}
+
+const TRegion * CScreenRedraw::AnimationRegion() const
+	{
+	if (iAnimating)
+		return iAnimationRegion;
+	else
+		return 0;
+	}
+
+void CScreenRedraw::UpdateDevice()
+	{
+	//this used to call iScreen->Update, not needed anymore in NGA
+	}
+
+TBool CScreenRedraw::IsQuickFadeScheduled( CWsWindow* aWin ) const
+	{
+	const TInt idx = iQuickFadeList.Find( aWin );	
+	return (idx > KErrNotFound);
+	}
+
+void CScreenRedraw::ScheduleQuickFade( CWsWindow* aWin )
+	{
+	const TInt idx = iQuickFadeList.Find( aWin );	
+	if ( idx == KErrNotFound )
+		{ // not yet enlisted		
+		if ( KErrNone != iQuickFadeList.Append(aWin) )
+			{  // out of resources?
+			const TRegion& winReg = aWin->VisibleRegion();
+			ScheduleRegionUpdate( &winReg );
+			}		
+		}
+	}
+
+void CScreenRedraw::RemoveFromQuickFadeList( CWsWindow* aWin )
+	{	
+	const TInt idx = iQuickFadeList.Find( aWin );	
+	if ( idx > KErrNotFound )
+		{
+		iQuickFadeList.Remove( idx );
+		}
+	}
+
+void CScreenRedraw::AcceptFadeRequest( CWsWindow* aWin, TBool aFadeOn )
+	{
+	if (aFadeOn)
+		{
+		if (aWin->WinType() != EWinTypeGroup)
+			{
+			ScheduleQuickFade(aWin);
+			ScheduleRegionUpdate(NULL);
+			}
+		}
+	else
+		{ // fade off, just initiate redraw
+		if ( !aWin->IsDSAHost() )
+			{
+			AddRedrawRegion(aWin->VisibleRegion() );
+			ScheduleRegionUpdate(NULL);
+			}
+		}
+	}
+
+
+#ifdef USE_DEBUG_FRAME_CAPTURE
+class TTruncateOverflow : public TDesOverflow
+	{
+public: //from TDesOverflow
+	void Overflow(TDes&) {};
+	};
+
+void CScreenRedraw::CaptureFrame(const TRegion* aRegion)
+	{
+	MWsScreenDevice* screenDevice = static_cast<MWsScreen&>(iScreen).ObjectInterface<MWsScreenDevice>();
+	WS_ASSERT_ALWAYS(screenDevice, EWsPanicScreenDeviceMissing);
+	const TSize screenSize(screenDevice->SizeInPixels());
+	const TDisplayMode screenDisplayMode(screenDevice->DisplayMode());
+
+	//copy the whole screen
+	TRAPD(err, SetupFrameCaptureResourcesL(screenSize, screenDisplayMode);
+	           screenDevice->CopyScreenToBitmapL(iDebugBitmap, TRect(screenSize)));
+
+	if(err)
+		{
+		if(wsDebugLog)
+			wsDebugLog->MiscMessage(CDebugLogBase::ELogEverything, _L("CScreenRedraw::CaptureFrame(): Failed to create resources for screen capture"));
+		return;
+		}
+
+	//remove what's not part of the region
+	iDebugBitmapGc->Activate(iDebugBitmapDevice);
+	
+	if(aRegion && !aRegion->IsEmpty() && !aRegion->CheckError())
+		{
+		RWsRegion inverseRegion;
+		inverseRegion.AddRect(TRect(screenSize));
+		const TRect* rectList = aRegion->RectangleList();
+		for(TInt i = aRegion->Count() - 1; i >= 0; i--)
+			{
+			inverseRegion.SubRect(rectList[i]);
+			}
+		if(!inverseRegion.CheckError())
+			{
+			iDebugBitmapGc->SetDrawMode(CGraphicsContext::EDrawModeWriteAlpha);
+			iDebugBitmapGc->SetClippingRegion(inverseRegion);
+			iDebugBitmapGc->SetBrushColor(TRgb(55, 55, 55, 0));
+			iDebugBitmapGc->Clear();
+			iDebugBitmapGc->CancelClippingRegion();
+			iDebugBitmapGc->SetDrawMode(CGraphicsContext::EDrawModePEN);
+			}
+		inverseRegion.Close();
+		}
+
+	//save to file
+	const TUint timestamp = User::FastCounter();	
+	TFileName filename;
+	TTruncateOverflow overflow;
+	filename.AppendFormat(iFrameCaptureLocation, &overflow);
+	filename.AppendFormat(_L("frame_%010u.mbm"), &overflow, timestamp);
+	iDebugBitmap->Save(filename);
+
+	//log region
+	LogRegion(filename, _L(" CScreenRedraw::CaptureFrame() "), aRegion);	
+	}
+
+void CScreenRedraw::SetupFrameCaptureResourcesL(const TSize& aScreenSize, TDisplayMode aScreenDisplayMode)
+	{
+	//make sure the existing bitmap has the correct display mode
+	if(iDebugBitmap && iDebugBitmap->DisplayMode() != aScreenDisplayMode)
+		{
+		if(iDebugBitmap->SetDisplayMode(aScreenDisplayMode) != KErrNone)
+			{
+			delete iDebugBitmap;
+			iDebugBitmap = NULL;
+			delete iDebugBitmapDevice;
+			iDebugBitmapDevice = NULL;
+			}
+		}
+
+	//make sure the existing bitmap has the correct size
+	if(iDebugBitmap && iDebugBitmap->SizeInPixels() != aScreenSize)
+		{
+		if(iDebugBitmap->Resize(aScreenSize) != KErrNone)
+			{
+			delete iDebugBitmap;
+			iDebugBitmap = NULL;
+			delete iDebugBitmapDevice;
+			iDebugBitmapDevice = NULL;
+			}
+		}
+
+	//make sure the bitmap and bitmap device is created
+	if(!iDebugBitmap)
+		{
+		WS_ASSERT_ALWAYS(!iDebugBitmapDevice, EWsPanicTemp); //this should never occur, they should always be created/destroyed in tandem
+		CFbsBitmap* bitmap = new(ELeave) CFbsBitmap;
+		CleanupStack::PushL(bitmap);
+		User::LeaveIfError(bitmap->Create(aScreenSize, aScreenDisplayMode));
+
+		iDebugBitmapDevice = CFbsBitmapDevice::NewL(bitmap);
+		iDebugBitmap = bitmap;
+		CleanupStack::Pop(bitmap);
+		}
+	
+	//make sure the gc is created
+	if(!iDebugBitmapGc)
+		{
+		User::LeaveIfError(iDebugBitmapDevice->CreateContext(iDebugBitmapGc));
+		}
+	}
+
+void CScreenRedraw::LogRegion(const TDesC& aPrefix, const TDesC& aFunctionName, const TRegion* aRegion)
+	{
+	if(!wsDebugLog)
+		return;
+
+	TBuf<LogTBufSize> log;
+	TTruncateOverflow overflow;
+	TInt rectCount = (aRegion == NULL ? 0 : aRegion->Count());
+	log.AppendFormat(aPrefix, &overflow);
+	log.AppendFormat(aFunctionName, &overflow);
+	log.AppendFormat(_L("region [%d,"), &overflow, rectCount);
+	if (rectCount > 0)
+		{
+		const TRect* rectangles = aRegion->RectangleList();
+		TBuf<1> comma;
+		for (TInt ii = 0; ii < rectCount; ii++)
+			{
+			TRect current = rectangles[ii];
+			log.AppendFormat(_L("%S{{%d,%d},{%d,%d}}"), &overflow, &comma,
+                             current.iTl.iX,current.iTl.iY,current.iBr.iX,current.iBr.iY);
+			comma = _L(",");
+			}
+		}
+	else
+		{
+		log.AppendFormat(_L("NULL"), &overflow);
+		}
+	log.AppendFormat(_L("]"), &overflow);
+	wsDebugLog->MiscMessage(CDebugLogBase::ELogEverything, log);
+	}
+#endif //USE_DEBUG_FRAME_CAPTURE
+