--- /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 = ®ionScheduler;
+ 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
+