diff -r 000000000000 -r bde4ae8d615e os/graphics/windowing/windowserver/nga/SERVER/openwfc/ScreenRedraw.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/os/graphics/windowing/windowserver/nga/SERVER/openwfc/ScreenRedraw.cpp Fri Jun 15 03:10:57 2012 +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 + +#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 +#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(&iScreen), + this + ); + + LEAVE_LOG_UNINSTALL_C; + + WS_ASSERT_ALWAYS(iRenderStages, EWsPanicNoRenderStagePipeline); + iRenderStageTextCursor = static_cast(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 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; iAnimationScheduler()->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(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(iRenderStages->ResolveObjectInterface(KMWsDebugBar)); + if (debugBar) //optional for the licensees + { + if (CDebugBar* dbg = iScreen.DebugBar()) + { + RArray 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(const_cast(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(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(iScreen).ObjectInterface(); + 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 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 +