--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/windowing/windowserver/nga/SERVER/openwfc/redrawmsgwindow.cpp Tue Feb 02 01:47:50 2010 +0200
@@ -0,0 +1,1753 @@
+// Copyright (c) 1995-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:
+// Window redraw code, three sorts of redrawing are supported
+// CRedrawMsgWindow handles it via sending a redraw message to the client
+//
+//
+
+#include "redrawmsgwindow.h"
+#include "gc.h"
+#include "playbackgc.h"
+#include "inifile.h"
+#include "rootwin.h"
+#include "wstop.h"
+#include "ANIM.H"
+#include "EVQUEUE.H"
+#include <s32mem.h>
+#include <gdi.h>
+#include "panics.h"
+#include "rootwin.h"
+#include "EVENT.H"
+#include "wsfont.h"
+#include <Graphics/WSGRAPHICDRAWERINTERFACE.H>
+#include <graphics/surface.h>
+#include "windowelementset.h"
+
+#include "drawresource.h"
+#include "../debuglog/DEBUGLOG.H"
+
+const TUint KDrawBufferGranularity = 240;
+const TInt KRedrawRegionGranularity = 8;
+const TInt KReadBufferMaxLen=0x100;
+const TInt KRegionCompressThreshold = 6; // number of rectangles in region at which we try to compress it
+
+TBool CWsRedrawMsgWindow::iAtomic = EFalse;
+
+extern CDebugLogBase *wsDebugLog;
+
+#ifndef _DEBUG
+
+#define LOG_WINDOW_DRAW_COMMANDS_START(wswin,region)
+#define LOG_WINDOW_DRAW_COMMANDS_END(wswin)
+#define LOG_REDRAW_SEGMENT(segmentIndex,segmentType)
+#define LOG_REDRAW_SEGMENT_REGION(region)
+#define LOG_PLAYBACK_GC_COMMAND(opcode,data)
+
+#else
+
+#define LOG_WINDOW_DRAW_COMMANDS_START(wswin,region) LogDrawCommandsStart(wswin,region)
+#define LOG_WINDOW_DRAW_COMMANDS_END(wswin) LogDrawCommandsEnd(wswin)
+#define LOG_REDRAW_SEGMENT(segmentIndex,segmentType) LogRedrawSegment(segmentIndex, segmentType)
+#define LOG_REDRAW_SEGMENT_REGION(region) {if(wsDebugLog){ LogRegion(region);}}
+#define LOG_PLAYBACK_GC_COMMAND(opcode,data) {if (wsDebugLog) {wsDebugLog->Command(WS_HANDLE_GC, opcode, data, NULL);}}
+
+LOCAL_C void LogRedrawSegment(TUint aSegmentIndex, CWsRedrawMsgWindow::TRedrawSegmentType aSegmentType)
+ {
+ if (wsDebugLog)
+ {
+ TBuf<LogTBufSize> log;
+ TTruncateOverflow overflow;
+ _LIT(KLogRedrawSegment, ">> CRedrawSegment[%d] ");
+ log.AppendFormat(KLogRedrawSegment, &overflow, aSegmentIndex);
+ _LIT(KLogRedrawSegmentPending, "Pending");
+ _LIT(KLogRedrawSegmentRedraw, "Redraw");
+ switch(aSegmentType)
+ {
+ case CWsRedrawMsgWindow::ESegmentTypePendingRedraw :
+ log.AppendFormat(KLogRedrawSegmentPending, &overflow);
+ break;
+ case CWsRedrawMsgWindow::ESegmentTypeRedraw :
+ log.AppendFormat(KLogRedrawSegmentRedraw, &overflow);
+ break;
+ default :
+ {
+ }
+ }
+ wsDebugLog->MiscMessage(CDebugLogBase::ELogEverything, log);
+ }
+ }
+
+LOCAL_C void LogDrawCommandsStart(const CWsWindow* aWsWin, const TRegion* aRegion)
+ {
+ if (wsDebugLog)
+ {
+ _LIT(KLogDrawCommandsStart, ">> CWsRedrawMsgWindow::DrawCommandsL() [%S][app %d] RWindow[%d]");
+ const TDesC& clientName = aWsWin->WsOwner()->Client().FullName();
+ TBuf<LogTBufSize> log;
+ TTruncateOverflow overflow;
+ log.AppendFormat(KLogDrawCommandsStart, &overflow, &clientName, aWsWin->WsOwner()->ConnectionHandle(), aWsWin->LogHandle());
+ wsDebugLog->MiscMessage(CDebugLogBase::ELogEverything, log);
+ LogRegion(aRegion);
+ }
+ }
+
+LOCAL_C void LogDrawCommandsEnd(const CWsWindow* aWsWin)
+ {
+ if (wsDebugLog)
+ {
+ _LIT(KLogDrawCommandsEnd, "<< CWsRedrawMsgWindow::DrawCommandsL() [%S][app %d] RWindow[%d]");
+ const TDesC& clientName = aWsWin->WsOwner()->Client().FullName();
+ TBuf<LogTBufSize> log;
+ TTruncateOverflow overflow;
+ log.AppendFormat(KLogDrawCommandsEnd, &overflow, &clientName, aWsWin->WsOwner()->ConnectionHandle(), aWsWin->LogHandle());
+ wsDebugLog->MiscMessage(CDebugLogBase::ELogEverything, log);
+ }
+ }
+
+#endif
+
+//
+// Redraw windows //
+//
+
+CWsRedrawMsgWindow::CRedrawSegment::CRedrawSegment(CWsRedrawMsgWindow& aRedraw) : iRedraw(aRedraw)
+ {
+ }
+
+CWsRedrawMsgWindow::CRedrawSegment* CWsRedrawMsgWindow::CRedrawSegment::NewLC(const TRect& aRect, TRedrawSegmentType aNewRegionType, CWsRedrawMsgWindow& aRedraw)
+ {
+ CRedrawSegment* self = new (ELeave) CRedrawSegment(aRedraw);
+ CleanupStack::PushL(self);
+ self->ConstructL(aRect, aNewRegionType);
+ return self;
+ }
+
+void CWsRedrawMsgWindow::StaticInitL()
+ {
+ // Note that the wsini.ini setting NONREDRAWAGELIMIT is obselete in
+ // Wserv2 NGA. The effective behaviour of the system is as if the
+ // value was set to zero.
+
+ _LIT(KAtomicRedraws, "ATOMICREDRAWS");
+ if (WsIniFile->FindVar(KAtomicRedraws) ||
+ WsIniFile->FindVar(KWSERVIniFileVarChangeTracking)) //ChangeTracking requires atomic redraws to be enabled to
+ iAtomic = ETrue; //prevent playing redraw-segemts before they are completed.
+ else
+ iAtomic = EFalse;
+ }
+
+static TInt FindBitmapRefByHandle(const TInt* aKey, const CFbsBitmapRef& aBitmapRef)
+ { // compare handles
+ return *aKey - aBitmapRef.Handle();
+ }
+
+static TInt InsertBitmapRefByHandle(const CFbsBitmapRef& aFirst, const CFbsBitmapRef& aSecond)
+ {
+ return aFirst.Handle() - aSecond.Handle();
+ }
+
+void CWsRedrawMsgWindow::CRedrawSegment::ReleaseFontsBitmapsDrawableSources()
+ {
+ // release Bitmap, Font and Drawable Source handles
+ TInt count = iWsBitmapArray.Count();
+ TInt ii;
+ for (ii = count - 1; ii >= 0; ii--)
+ {
+ iWsBitmapArray[ii]->DecRefCount();
+ iWsBitmapArray.Remove(ii);
+ }
+
+ count = iWsFontArray.Count();
+ for (ii = count - 1; ii >= 0; --ii)
+ {
+ CWsFontCache::Instance()->ReleaseFont(iWsFontArray[ii]);
+ iWsFontArray.Remove(ii);
+ }
+
+ iRedraw.CleanupBitmapRefArray(iBitmapHandleArray);
+ iBitmapHandleArray.Close();
+
+ count = iWsDrawableSourceArray.Count();
+ for(ii = count - 1 ; ii >= 0; ii--)
+ {
+ iWsDrawableSourceArray[ii]->DecRefCount();
+ iWsDrawableSourceArray.Remove(ii);
+ }
+ }
+
+/* Set new rectangle and region type for initial or reset redraw region
+ @leave KErrNoMemory no memory to update region details
+ */
+void CWsRedrawMsgWindow::CRedrawSegment::ConstructL(const TRect& aRect, TRedrawSegmentType aNewRegionType)
+ {
+ iDrawCommands = CBufSeg::NewL(KDrawBufferGranularity);
+ iCreationTime.UniversalTime();
+
+ iRegion.AddRect(aRect);
+ if (iRegion.CheckError())
+ {
+ User::Leave(KErrNoMemory);
+ }
+ iRedrawSegmentType = aNewRegionType;
+ }
+
+CWsRedrawMsgWindow::CRedrawSegment::~CRedrawSegment()
+ {
+
+ if (iDrawCommands)
+ {
+ delete iDrawCommands;
+ }
+ iRegion.Close();
+ // Release Font, Bitmap and Drawable Source handles, close arrays
+ ReleaseFontsBitmapsDrawableSources();
+ iWsBitmapArray.Close();
+ iWsFontArray.Close();
+ iDrawerArray.Close();
+ iWsDrawableSourceArray.Close();
+ }
+
+
+TInt CWsRedrawMsgWindow::CRedrawSegment::SizeInBytes() const
+ {
+ TInt size = sizeof(CWsRedrawMsgWindow::CRedrawSegment);
+ size += iDrawCommands->Size();
+ size += iBitmapHandleArray.Count() * sizeof(TInt);
+ size += iWsBitmapArray.Count() * sizeof(DWsBitmap);
+ size += iWsFontArray.Count() * sizeof(CWsFbsFont);
+ size += iDrawerArray.Count() * sizeof(TGraphicDrawerId);
+ size += iWsDrawableSourceArray.Count() * sizeof(CWsDrawableSource);
+ return size;
+ }
+
+TBool CWsRedrawMsgWindow::CRedrawSegment::AppendNonDuplicateBitmapHandleL(TInt aHandle)
+ {
+ TBool handleAppend = EFalse;
+ TInt indexBitmapHandle = iBitmapHandleArray.Find(aHandle);
+ if (indexBitmapHandle<0)
+ {
+ iBitmapHandleArray.AppendL(aHandle);
+ handleAppend = ETrue;
+ }
+ return handleAppend;
+ }
+
+CWsRedrawMsgWindow::CWsRedrawMsgWindow(CWsWindow *aWin)
+ :CWsWindowRedraw(aWin), iBackColor(aWin->RootWindow()->DefaultBackgroundColor()), iFlags(EBackgroundClear|EStoringEntireWindow),
+ iRedrawSegments(KRedrawRegionGranularity),
+ iCurrentSegment(0),
+ iOSBStatus(ETrue)
+ {
+ }
+
+void CWsRedrawMsgWindow::ConstructL()
+ {
+ CWsWindowRedraw::ConstructL();
+ Invalidate(&WsWin()->Rel());
+ iWsWin->WsOwner()->RedrawQueue()->ReCalcOrder();
+ }
+
+CWsRedrawMsgWindow::~CWsRedrawMsgWindow()
+ {
+ WS_ASSERT_DEBUG(iMemoryLock == 0, EWsPanicMemoryLock);
+ RemoveFromRedrawQueueIfEmpty();
+ iInvalid.Close();
+ iLocalRedrawRegion.Close();
+ iRedrawSegments.ResetAndDestroy();
+ TInt count = iFbsBitmapRefArray.Count();
+ WS_ASSERT_DEBUG(count==0,EWsPanicBitmapArrayNotEmpty);
+ iFbsBitmapRefArray.ResetAndDestroy();
+ }
+
+/**
+These three functions actually check for a value they have already asserted on. This is intentional.
+*/
+void CWsRedrawMsgWindow::ExpandCommandBufferL(TInt aLength)
+ {
+ WS_ASSERT_DEBUG(iCurrentSegment != NULL, EWsPanicRedrawSegmentsInvalidState);
+
+ if (iCurrentSegment)
+ {
+ // need more space?
+ if (iCurrentSegment->iCurrentCommandBufferWritePos + aLength > iCurrentSegment->iDrawCommands->Size())
+ {
+ iCurrentSegment->iDrawCommands->ResizeL(iCurrentSegment->iCurrentCommandBufferWritePos + aLength);
+ }
+ }
+ }
+
+void CWsRedrawMsgWindow::CommandBufferWrite(const TDesC8& aDes, TInt aLength)
+ {
+ WS_ASSERT_DEBUG(iCurrentSegment != NULL, EWsPanicRedrawSegmentsInvalidState);
+ WS_ASSERT_DEBUG(iCurrentSegment->iCurrentCommandBufferWritePos + aLength <= iCurrentSegment->iDrawCommands->Size(), EWsPanicDrawCommandsInvalidState);
+ if (iCurrentSegment)
+ {
+ iCurrentSegment->iDrawCommands->Write(iCurrentSegment->iCurrentCommandBufferWritePos, aDes, aLength);
+ iCurrentSegment->iCurrentCommandBufferWritePos += aLength;
+ }
+ }
+
+void CWsRedrawMsgWindow::CommandBufferWrite(const TAny* aPtr,TInt aLength)
+ {
+ WS_ASSERT_DEBUG(iCurrentSegment != NULL, EWsPanicRedrawSegmentsInvalidState);
+ WS_ASSERT_DEBUG(iCurrentSegment->iCurrentCommandBufferWritePos + aLength <= iCurrentSegment->iDrawCommands->Size(), EWsPanicDrawCommandsInvalidState);
+ if (iCurrentSegment)
+ {
+ iCurrentSegment->iDrawCommands->Write(iCurrentSegment->iCurrentCommandBufferWritePos, aPtr, aLength);
+ iCurrentSegment->iCurrentCommandBufferWritePos += aLength;
+ }
+ }
+
+/*------------------------------------------------------------------------------
+ Description: Processes draw commands. These are received as opcodes.
+ -----------------------------------------------------------------------------*/
+TBool CWsRedrawMsgWindow::CommandL(TInt aOpcode, TWsWinCmdUnion &aCmd)
+ {
+ switch(aOpcode)
+ {
+ case EWsWinOpEnableOSB:
+ iOSBStatus = ETrue;
+ break;
+ case EWsWinOpDisableOSB:
+ iOSBStatus = EFalse;
+ break;
+ case EWsWinOpSetBackgroundColor:
+ iBackColor = *aCmd.rgb;
+ iFlags |= EBackgroundClear;
+ break;
+ case EWsWinOpSetNoBackgroundColor:
+ iFlags &= ~EBackgroundClear;
+ break;
+ case EWsWinOpInvalidate:
+ Invalidate(aCmd.rect);
+ break;
+ case EWsWinOpInvalidateFull:
+ Invalidate();
+ break;
+ case EWsWinOpBeginRedraw:
+ BeginRedraw(aCmd.rect);
+ ValidateRect(aCmd.rect);
+ break;
+ case EWsWinOpBeginRedrawFull:
+ BeginRedraw(NULL);
+ ValidateRect(NULL);
+ break;
+ case EWsWinOpEndRedraw:
+ EndRedraw();
+ break;
+ case EWsWinOpGetInvalidRegionCount:
+ {
+ SetReply(iInvalid.Count());
+ }
+ break;
+ case EWsWinOpGetInvalidRegion:
+ {
+ if ((*aCmd.Int) <= 0)
+ OwnerPanic(EWservPanicInvalidRegionCount);
+ if ((!iInvalid.CheckError()) && iInvalid.Count() == (*aCmd.Int))
+ {
+ CWsClient::ReplyBuf(iInvalid.RectangleList(),(*aCmd.Int) * sizeof(TRect));
+ SetReply(EFalse);
+ }
+ else
+ SetReply(ETrue);
+ }
+ break;
+ case EWsWinOpStoreDrawCommands:
+ /* If the client asks us not to store commands, we still store the commands
+ for the region of the window which can be seen through the parent, but
+ won't attempt to obtain the entire window.
+ */
+ if (*aCmd.Bool)
+ {
+ SetScope(EStoreEntireWindow);
+ }
+ else
+ {
+ // Clients that turn their redraw store off will still get one,
+ // but it will only attempt to store the current viewport.
+ SetScope(EStoreViewport);
+ }
+ break;
+ case EWsWinOpHandleTransparencyUpdate: // deprecated
+ case EWsWinOpSetTransparencyBitmap: // deprecated
+ case EWsWinOpSetTransparencyFactor: // deprecated
+ case EWsWinOpSetTransparencyBitmapCWs: // deprecated
+ break; // do nothing.
+ case EWsWinOpIsRedrawStoreEnabled:
+ SetReply(ETrue);
+ break;
+ case EWsWinOpClearRedrawStore:
+ DiscardStoredCommands();
+ break;
+ case EWsWinOpSetBackgroundSurface:
+ SetBackgroundSurfaceL(*aCmd.Surface);
+ break;
+ case EWsWinOpSetBackgroundSurfaceConfig:
+ SetBackgroundSurfaceL(aCmd.SurfaceConfigurationAndTrigger->surfaceConfig, aCmd.SurfaceConfigurationAndTrigger->triggerRedraw, EFalse);
+ break;
+ case EWsWinOpRemoveBackgroundSurface:
+ RemoveBackgroundSurface(*aCmd.Bool);
+ break;
+ case EWsWinOpGetBackgroundSurfaceConfig:
+ {
+ TSurfaceConfiguration tempConfiguration = *aCmd.SurfaceConfiguration;
+ GetBackgroundSurfaceL(tempConfiguration);
+ TInt tempSize = aCmd.SurfaceConfiguration->Size();
+ if (sizeof(TSurfaceConfiguration)<tempSize)
+ tempSize = sizeof(TSurfaceConfiguration);
+ CWsClient::ReplyBuf(&tempConfiguration,tempSize);
+ }
+ break;
+ default:
+ return(EFalse);
+ }
+ return(ETrue);
+ }
+
+/**
+*/
+void CWsRedrawMsgWindow::BeginRedraw(const TRect* aRect)
+ {
+ if(InRedraw())
+ OwnerPanic(EWservPanicDrawCommandsInvalidState);
+ iFlags|=EBeginEndRedraw;
+ TRAPD(err,DoBeginRedrawL(aRect));
+ DiscardStoredCommandsIfError(err);
+ }
+
+void CWsRedrawMsgWindow::DoBeginRedrawL(const TRect* aRect)
+ {
+ const TRect redrawRect = (aRect ? *aRect : TRect(WsWin()->Size()));
+ if (redrawRect.IsEmpty())
+ {
+ //Skip empty rects since they are not added to the region
+ iCurrentSegment = NULL;
+ }
+ else
+ {
+ CreateNewSegmentL(redrawRect, CWsRedrawMsgWindow::ESegmentTypePendingRedraw);
+ if (!iAtomic)
+ PromotePendingSegment();
+ }
+ }
+
+void CWsRedrawMsgWindow::Invalidate(const TRect * aRect)
+ {
+ //The memory allocation in this function can trigger a call to ReleaseMemory(), which would
+ //recursively call this function again. This would cause a memory leak. To avoid this
+ //we call Lock() to block ReleaseMemory() from this object while executing this function.
+ Lock();
+ if (!aRect)
+ {
+ iInvalid.Clear();
+ iInvalid.Copy(iWsWin->WindowArea());
+ iInvalid.Offset(-iWsWin->Origin());
+ }
+ else if((!aRect->IsEmpty()) && aRect->IsNormalized())
+ {
+ iInvalid.AddRect(*aRect);
+ iInvalid.Tidy();
+ }
+ if (iWsWin->IsVisible())
+ {
+ QueueRedraw();
+ iWsWin->WsOwner()->TriggerRedraw(); //wtf isn't the redrawq already scheduling itself?
+ }
+ Unlock();
+ }
+
+/**
+Obtains a region from the redraw store, intersects it with the global redraw region which
+we have been asked to draw to, and returns the intersection.
+*/
+const TRegion * CWsRedrawMsgWindow::ReadRegion(const TInt aRegionNum)
+ {
+ // We are drawing to the global region, and we have a region in the redraw store to clip to.
+ // We want the intersection of these to actually draw to.
+ iLocalRedrawRegion.Copy(iRedrawSegments[aRegionNum]->iRegion);
+ iLocalRedrawRegion.Offset(WsWin()->Origin());
+ iLocalRedrawRegion.Intersect(*iRedrawRegion);
+ iLocalRedrawRegion.Tidy();
+
+ // If the resulting region is empty there is no point drawing its corresponding commands
+ if (iLocalRedrawRegion.IsEmpty())
+ return NULL;
+ else
+ return &iLocalRedrawRegion;
+ }
+
+void CWsRedrawMsgWindow::SubtractRectFromSegmentArray(const TRect& aRect)
+ {
+ for (TInt regionNum = iRedrawSegments.Count() - 1; regionNum >= 0; --regionNum)
+ {
+ if (iRedrawSegments[regionNum]->iRedrawSegmentType != ESegmentTypePendingRedraw)
+ {
+ RWsRegion& region = iRedrawSegments[regionNum]->iRegion;
+ region.SubRect(aRect);
+ if (region.CheckError())
+ {
+ // Ouch. Drop the now broken segment and ask for a full redraw
+ delete iRedrawSegments[regionNum];
+ iRedrawSegments.Remove(regionNum);
+ Invalidate();
+ }
+ else
+ {
+ // check if region has zero uncovered rectangles left
+ if (region.IsEmpty())
+ { // delete draw commands, release bitmaps and fonts
+ delete iRedrawSegments[regionNum];
+ iRedrawSegments.Remove(regionNum);
+ }
+ else
+ {
+ if (region.Count() > KRegionCompressThreshold)
+ { // tidy up the rectangles
+ region.Tidy();
+ }
+ }
+ }
+ }
+ }
+ // coverity[extend_simple_error]
+ }
+
+/*------------------------------------------------------------------------------
+ Description: Clears out the command buffer if aError indicates an
+ error has occurred whilst storing commands.
+ -----------------------------------------------------------------------------*/
+void CWsRedrawMsgWindow::DiscardStoredCommandsIfError(TInt aError)
+ {
+ if (aError != KErrNone)
+ {
+ // Discard stored commands by clearing out the command buffer
+ DiscardStoredCommands();
+
+ if(!(iFlags&ECurrentRedrawFailedStorage)) //first time we fail during the current redraw
+ {
+ iFlags |= ECurrentRedrawFailedStorage;
+
+ if(!(iFlags&EPreviousRedrawFailedStorage)) //unless the previous redraw failed as well (to avoid infinite loop)
+ {
+ Invalidate(); //request new redraw from the client
+ }
+ }
+ }
+ }
+
+/*------------------------------------------------------------------------------
+ Description: If the graphics context has changed and we are currently storing
+ commands, store the data given by aCmdData.
+
+ -----------------------------------------------------------------------------*/
+TBool CWsRedrawMsgWindow::DrawCommand(CWsGc* aGc,const TAny *aCmdData)
+ {
+ // Store commands, clearing out buffer if error occurs.
+ TRAPD(err,StoreDrawCommandL(aGc,aCmdData));
+ DiscardStoredCommandsIfError(err);
+ return EFalse;
+ }
+
+void CWsRedrawMsgWindow::GcAttributeChange(CWsGc* aGc,const TAny *aCmdData)
+ {
+ if (iLastDrawGc == aGc && iCurrentSegment)
+ {
+ TInt err = KErrNone;
+ if (IsWsFontOperation(CWsClient::iCurrentCommand.iOpcode))
+ {
+ TWsGcCmdUnion pData;
+ pData.any=aCmdData;
+ TRAP(err,AddWsFontL(*pData.UInt));
+ }
+ if (KErrNone == err)
+ {
+ TRAP(err,AppendCommandL(aCmdData));
+ }
+ DiscardStoredCommandsIfError(err);
+ }
+
+ // INC135845:
+ // Retain the bitmap handle for the lifetime of the redraw store
+ // If the client destroys it, we will still have a reference to it
+ if (iCurrentSegment && CWsClient::iCurrentCommand.iOpcode == EWsGcOpUseBrushPattern)
+ {
+ TInt err = KErrNone;
+ TWsGcCmdUnion pData;
+ pData.any=aCmdData;
+ TRAP(err, AddFbsBitmapsL(*pData.handle, 0));
+ DiscardStoredCommandsIfError(err);
+ }
+ }
+
+void CWsRedrawMsgWindow::GcDeactivate(CWsGc* aGc)
+ {
+ if (iLastDrawGc==aGc)
+ iLastDrawGc=NULL;
+ }
+
+inline TBool CWsRedrawMsgWindow::IsFbsBitmapOperation(TInt aOpCode) const
+ {
+ WS_ASSERT_DEBUG((EWsGcOpGdiBlt3==EWsGcOpGdiBlt2+1)&&(EWsGcOpGdiBltMasked==EWsGcOpGdiBlt3+1)
+ &&(EWsGcOpDrawBitmap2==EWsGcOpDrawBitmap+1)&&(EWsGcOpDrawBitmap3==EWsGcOpDrawBitmap2+1)&&(EWsGcOpDrawBitmapMasked==EWsGcOpDrawBitmap3+1),EWsPanicBitmapOpcodeInvalid);
+ return (aOpCode>=EWsGcOpGdiBlt2&&aOpCode<=EWsGcOpGdiBltMasked)||(aOpCode>=EWsGcOpDrawBitmap&&aOpCode<=EWsGcOpDrawBitmapMasked)||(aOpCode==EWsGcOpGdiAlphaBlendBitmaps);
+ }
+
+inline TBool CWsRedrawMsgWindow::IsWsBitmapOperation(TInt aOpCode) const
+ {
+ WS_ASSERT_DEBUG((EWsGcOpGdiBlt3==EWsGcOpGdiBlt2+1)&&(EWsGcOpGdiBltMasked==EWsGcOpGdiBlt3+1)
+ &&(EWsGcOpDrawBitmap2==EWsGcOpDrawBitmap+1)&&(EWsGcOpDrawBitmap3==EWsGcOpDrawBitmap2+1)&&(EWsGcOpDrawBitmapMasked==EWsGcOpDrawBitmap3+1),EWsPanicBitmapOpcodeInvalid);
+ return (aOpCode>=EWsGcOpGdiWsBlt2&&aOpCode<=EWsGcOpGdiWsBltMasked)||(aOpCode==EWsGcOpGdiWsAlphaBlendBitmaps)||(aOpCode==EWsGcOpWsDrawBitmapMasked);
+ }
+
+inline TBool CWsRedrawMsgWindow::IsWsFontOperation(TInt aOpCode) const
+ {
+ return aOpCode==EWsGcOpUseFont;
+ }
+
+inline TBool CWsRedrawMsgWindow::IsDrawWsGraphicOperation(TInt aOpCode) const
+ {
+ return (aOpCode == EWsGcOpDrawWsGraphic) || (aOpCode == EWsGcOpDrawWsGraphicPtr);
+ }
+
+inline TBool CWsRedrawMsgWindow::IsWsDrawableSourceOperation(TInt aOpCode) const
+ {
+ return (aOpCode == EWsGcOpDrawResourceToPos) || (aOpCode == EWsGcOpDrawResourceToRect) || (aOpCode == EWsGcOpDrawResourceFromRectToRect) || (aOpCode == EWsGcOpDrawResourceWithData);
+ }
+
+void CWsRedrawMsgWindow::ReplaceAndAppendCommandL(TInt aOpcode,const TAny* aCmdData)
+ {
+ const TInt KCharWidthInBytes = 2; // # of bytes for a Unicode character
+ CWsClient* owner=iWsWin->WsOwner();
+ WS_ASSERT_DEBUG(owner,EWsPanicDrawCommandsNullSession);
+
+ // aCmdData doesn't contain data, it should be retrieved from client space using remote read
+ TWsGcCmdUnion cmd;
+ cmd.any=aCmdData;
+ TUint16 newOpcode=EWsGcOpDrawText;
+ TInt strLen=0;
+ switch (aOpcode)
+ {
+ case EWsGcOpDrawTextPtr:
+ newOpcode=EWsGcOpDrawText;
+ strLen=cmd.DrawText->length;
+ break;
+ case EWsGcOpDrawTextVerticalPtr:
+ newOpcode=EWsGcOpDrawTextVertical;
+ strLen=cmd.DrawTextVertical->length;
+ break;
+ case EWsGcOpDrawBoxTextPtr:
+ newOpcode=EWsGcOpDrawBoxText;
+ strLen=cmd.BoxText->length;
+ break;
+ case EWsGcOpDrawBoxTextVerticalPtr:
+ newOpcode=EWsGcOpDrawBoxTextVertical;
+ strLen=cmd.DrawBoxTextVertical->length;
+ break;
+ case EWsGcOpDrawTextInContextPtr:
+ newOpcode=EWsGcOpDrawTextInContextPtr1;
+ strLen=cmd.DrawTextInContext->length;
+ break;
+ case EWsGcOpDrawTextInContextVerticalPtr:
+ newOpcode=EWsGcOpDrawTextInContextVerticalPtr1;
+ strLen=cmd.DrawTextInContextVertical->length;
+ break;
+ case EWsGcOpDrawBoxTextInContextPtr:
+ newOpcode=EWsGcOpDrawBoxTextInContextPtr1;
+ strLen=cmd.BoxTextInContext->length;
+ break;
+ case EWsGcOpDrawBoxTextInContextVerticalPtr:
+ newOpcode=EWsGcOpDrawBoxTextInContextVerticalPtr1;
+ strLen=cmd.DrawBoxTextInContextVertical->length;
+ break;
+ }
+ TInt strSize = strLen * KCharWidthInBytes;
+ TInt oldCmdLen=CWsClient::iCurrentCommand.iCmdLength;
+ TInt newCmdLen=sizeof(TWsCmdHeaderBase)+oldCmdLen+strSize;
+ // resize buffer
+ ExpandCommandBufferL(newCmdLen);
+ // update current command to reflect the new command and data
+ CWsClient::iCurrentCommand.iOpcode=newOpcode;
+ CWsClient::iCurrentCommand.iOpcode|=EWsGcOpFlagDrawOp;
+ CWsClient::iCurrentCommand.iCmdLength=(TInt16)(oldCmdLen+strSize);
+ // write command header
+ CommandBufferWrite(&CWsClient::iCurrentCommand, sizeof(TWsCmdHeaderBase));
+ // write command
+ CommandBufferWrite(aCmdData, oldCmdLen);
+
+ // remote read
+ TBuf<KReadBufferMaxLen> buf;
+ TInt len=KReadBufferMaxLen;
+ TInt bufOffset=0;
+ TInt toGo=strLen;
+ while(toGo>0)
+ {
+ if (len>toGo)
+ len=toGo;
+ owner->RemoteRead(buf,bufOffset);
+ CommandBufferWrite(buf.Ptr(), len * KCharWidthInBytes);
+ bufOffset+=len;
+ toGo-=len;
+ }
+ }
+
+/*------------------------------------------------------------------------------
+ Description: Stores drawing related commands into the command buffer
+ -----------------------------------------------------------------------------*/
+void CWsRedrawMsgWindow::StoreDrawCommandL(CWsGc* aGc,const TAny *aCmdData)
+ {
+ TWsGcOpcodes currentOpcode = static_cast<TWsGcOpcodes>(CWsClient::iCurrentCommand.iOpcode);
+
+ if (!InRedraw())
+ {
+ // We've received a drawing operation outside of a BeginRedraw()/EndRedraw()
+ // bracketed block. This is known as a "non redraw drawing operation".
+
+#ifdef __WINS__
+ // If configured, panic on non redraw drawing operations.
+ if( CWsClient::DebugEnforceRedrawCallingConvention())
+ CWsClient::PanicCurrentClient(EWservPanicWindowBeginRedrawNotCalled);
+#endif
+
+ // We get here if we are not configured to panic when a non-redraw drawing operation
+ // is issued.
+ // We ignore this DrawOp as WServ2 is not supporting non-redraw drawing because
+ // storing such non-redraw commands in the redraw store consumes significant
+ // amounts of memory, and reduces rendering performance.
+ //
+ // Issuing non-redraw drawing from a client Redrawer is a special case. Here
+ // an infinite loop would occur if we mark the area as invalid. Therefore
+ // we must always panic in this case.
+ if (iWsWin->WsOwner()->ClientProcessingRedrawEvent())
+ {
+ // Non-redraw drawing in the Redrawer!!
+ CWsClient::PanicCurrentClient(EWservPanicWindowBeginRedrawNotCalled);
+ }
+ else
+ {
+ // The Redrawer will eventually fix up the screen
+ Invalidate();
+ }
+ return;
+ }
+
+ // If there is no current segment then we have discarded it at some point
+ // since beginning this redraw.
+ if (iCurrentSegment)
+ {
+ TWsGcCmdUnion pData;
+ pData.any = aCmdData;
+ if (IsFbsBitmapOperation(currentOpcode))
+ {
+ TInt maskHandle = 0;
+ TInt handle = aGc->FbsBitmapHandle(currentOpcode, pData, maskHandle);
+ AddFbsBitmapsL(handle, maskHandle);
+ }
+ else if (IsWsBitmapOperation(currentOpcode))
+ {
+ TInt maskHandle = 0;
+ TInt handle = aGc->WsBitmapHandle(currentOpcode, pData, maskHandle);
+ AddWsBitmapsL(handle, maskHandle);
+ }
+ else if (IsDrawWsGraphicOperation(currentOpcode))
+ {
+ TGraphicDrawerId drawerId;
+ drawerId.iId = pData.WsGraphic->iId;
+ drawerId.iIsUid = (pData.WsGraphic->iFlags & EWsGraphicIdUid);
+ iCurrentSegment->AddDrawerL(drawerId);
+ }
+ else if (IsWsDrawableSourceOperation(currentOpcode))
+ {
+ TInt handle = aGc->WsDrawableSourceHandle(currentOpcode, pData);
+ AddWsDrawableSourceL(handle);
+ }
+
+ // If the graphics context has changed since last time store the new graphics
+ // context attributes.
+ if (aGc != iLastDrawGc)
+ {
+ StoreAllGcAttributesL(aGc);
+ iLastDrawGc = aGc;
+ }
+
+ // For operation which requires remote read from client space, we must retrieve that data and store
+ // it in command buffer at server side and change opcode if necessary e.g EWsGcOpDrawTextPtr to EWsGcOpDrawText
+ // to avoid remote read during DoDrawing operation
+ if (IsRemoteReadRequired(currentOpcode))
+ ReplaceAndAppendCommandL(currentOpcode,aCmdData);
+ else
+ // Append the command data to the command buffer
+ AppendCommandL(aCmdData, EWsGcOpFlagDrawOp);
+ }
+ }
+
+/*------------------------------------------------------------------------------
+ Description: Stores given drawing command data into the command buffer.
+ -----------------------------------------------------------------------------*/
+void CWsRedrawMsgWindow::AppendCommandL(const TAny* aCmdData, const TUint16 aOpcodeFlags)
+ {
+ if (CWsClient::iCurrentCommand.iOpcode == EWsGcOpSetClippingRegion)
+ {
+ // The client is defining a clipping region
+
+ // make room for the header
+ ExpandCommandBufferL(sizeof(TWsCmdHeaderBase));
+
+ // Externalize the clipping region data from position after the header
+ RBufWriteStream bufWriteStream;
+ bufWriteStream.Open(*CurrentDrawCommandBuffer(), CurrentCommandBufferWritePos() + sizeof(TWsCmdHeaderBase));
+ CleanupClosePushL(bufWriteStream);
+ TInt dataLen = iLastDrawGc->ExternalizeClippingRegionL(bufWriteStream);
+
+ // Setup the clipping region data header
+ CWsClient::iCurrentCommand.iOpcode = EWsStoreClippingRegion;
+ CWsClient::iCurrentCommand.iCmdLength = REINTERPRET_CAST(TInt16&,dataLen);
+
+ // Store command header for clipping region data at current write position
+ CommandBufferWrite(&CWsClient::iCurrentCommand,sizeof(TWsCmdHeaderBase));
+
+ // Update write position for command data
+ iCurrentSegment->iCurrentCommandBufferWritePos += dataLen;
+ CleanupStack::PopAndDestroy(&bufWriteStream);
+ }
+ else
+ {
+ TUint16 opcode = CWsClient::iCurrentCommand.iOpcode;
+ CWsClient::iCurrentCommand.iOpcode |= aOpcodeFlags;
+
+ // ensure room in command buffer
+ ExpandCommandBufferL(sizeof(TWsCmdHeaderBase) + CWsClient::iCurrentCommand.iCmdLength);
+
+ // Store command header to current position
+ CommandBufferWrite(&CWsClient::iCurrentCommand, sizeof(TWsCmdHeaderBase));
+
+ // If there's command data (other than header), store it
+ if (CWsClient::iCurrentCommand.iCmdLength > 0)
+ {
+ CommandBufferWrite(aCmdData, CWsClient::iCurrentCommand.iCmdLength);
+ }
+
+ CWsClient::iCurrentCommand.iOpcode = opcode;
+ }
+ }
+
+
+/*------------------------------------------------------------------------------
+ Description: Stores graphics context information into the command buffer
+ from the current write position.
+ -----------------------------------------------------------------------------*/
+void CWsRedrawMsgWindow::StoreAllGcAttributesL(CWsGc* aGc)
+ {
+ // In order for the externalize below to work correctly from
+ // a non-zero position we have to create the header placeholder
+ ExpandCommandBufferL(sizeof(TWsCmdHeaderBase));
+
+ // Externalise GC attribute data. We do this before writing the
+ // header as we do not know the size of the data yet and it is
+ // part of the header.
+ TInt numOfBytesAdded = aGc->ExternalizeL(*CurrentDrawCommandBuffer(),
+ CurrentCommandBufferWritePos() + sizeof(TWsCmdHeaderBase));
+
+ // Setup the header
+ TWsCmdHeaderBase cmdHeader;
+ cmdHeader.iCmdLength = (TInt16) numOfBytesAdded; // as calculated above
+ cmdHeader.iOpcode = (TInt16) EWsStoreAllGcAttributes;
+
+ // Store the header for the GC data into the space we created
+ CommandBufferWrite(&cmdHeader, sizeof(TWsCmdHeaderBase));
+
+ // Update write position for command data
+ iCurrentSegment->iCurrentCommandBufferWritePos += numOfBytesAdded;
+ }
+
+LOCAL_C void CallSegmentEnd(TAny* aAnnotationObserver)
+ {
+ static_cast<MWsDrawAnnotationObserver*>(aAnnotationObserver)->SegmentRedrawEnd();
+ }
+
+/*------------------------------------------------------------------------------
+ Description: Loops through the whole of the current command buffer, processing
+ each in turn.
+ -----------------------------------------------------------------------------*/
+void CWsRedrawMsgWindow::DrawCommandsL()
+ {
+ LOG_WINDOW_DRAW_COMMANDS_START(WsWin(), iRedrawRegion);
+ WS_ASSERT_DEBUG(iMemoryLock > 0, EWsPanicMemoryLock);
+ static TBuf8<EClientBufferMaxSize> buf;
+ TInt regionCount = iRedrawSegments.Count();
+
+ for (TInt regionNum = 0; regionNum < regionCount; ++regionNum)
+ {
+ CRedrawSegment* segment = iRedrawSegments[regionNum];
+ LOG_REDRAW_SEGMENT(regionNum, segment->iRedrawSegmentType);
+ if (segment->iRedrawSegmentType == ESegmentTypePendingRedraw)
+ continue;
+
+ // The amount of commands we process is given by the value of the
+ // current write position rather than the size of the command buffer.
+ // Note: the write position is incremented as each command is stored and
+ // will typically be less than the buffer size.
+ const TInt length = segment->iCurrentCommandBufferWritePos;
+
+ // need to draw this region?
+ const TRegion * localDrawRegion = 0;
+ if (length)
+ localDrawRegion = ReadRegion(regionNum);
+ if (localDrawRegion)
+ {
+ MWsDrawAnnotationObserver* const annoObs = Screen()->DrawAnnotationObserver();
+ if(annoObs)
+ {
+ annoObs->SegmentRedrawStart(*localDrawRegion);
+ CleanupStack::PushL(TCleanupItem(CallSegmentEnd, annoObs));
+ }
+ CPlaybackGc::Instance()->SetTargetRegion(localDrawRegion);
+ LOG_REDRAW_SEGMENT_REGION(localDrawRegion)
+
+ TWsCmdHeaderBase header;
+ TInt pos = 0; // Set to first command position in buffer
+ CBufSeg* drawCmdBuffer = segment->iDrawCommands;
+
+#ifdef _DEBUG
+ // Read the first command header. The associated opcode must always be
+ // EWsStoreAllGcAttributes as this is always the first stored item.
+ drawCmdBuffer->Read(pos,&header,sizeof(TWsCmdHeaderBase));
+ WS_ASSERT_DEBUG(header.iOpcode == EWsStoreAllGcAttributes, EWsPanicDrawCommandsBufferCorrupt);
+#endif
+
+ // Read through remaining commands
+ while (pos < length)
+ {
+ // Get header of command
+ drawCmdBuffer->Read(pos, &header, sizeof(TWsCmdHeaderBase));
+ pos += sizeof(TWsCmdHeaderBase);
+
+ switch(header.iOpcode)
+ {
+ case EWsStoreAllGcAttributes:
+ {
+ // Header indicates command encapsulates gc data
+ CPlaybackGc::Instance()->Reset();
+
+ // Read gc data
+ CPlaybackGc::Instance()->InternalizeL(*drawCmdBuffer,pos);
+
+ }
+ break;
+ case EWsStoreClippingRegion:
+ {
+ // Clipping region data read in from current position via stream
+ RBufReadStream bufReadStream;
+ bufReadStream.Open(*drawCmdBuffer,pos);
+ CleanupClosePushL(bufReadStream);
+ CPlaybackGc::Instance()->InternalizeClippingRegionL(bufReadStream);
+ CleanupStack::PopAndDestroy(&bufReadStream);
+ }
+ break;
+ default:
+ {
+ // Another type of command. Read it.
+ CWsClient::iCurrentCommand.iCmdLength = header.iCmdLength;
+ drawCmdBuffer->Read(pos,buf,header.iCmdLength);
+
+ TInt opcode = header.iOpcode;
+
+ // Drawing command?
+ if (opcode & EWsGcOpFlagDrawOp)
+ {
+ opcode &= ~EWsGcOpFlagDrawOp;
+ }
+ if (opcode > -1)
+ {
+ LOG_PLAYBACK_GC_COMMAND(opcode, buf.Ptr())
+ CPlaybackGc::Instance()->CommandL(static_cast<TWsGcOpcodes>(opcode),buf);
+ }
+ }
+ break;
+ }
+ pos += header.iCmdLength; // Move on, header indicates length
+ }
+ if (annoObs)
+ CleanupStack::PopAndDestroy(annoObs);
+ }
+ }
+ LOG_WINDOW_DRAW_COMMANDS_END(WsWin());
+ }
+
+/*------------------------------------------------------------------------------
+ Description: Called when the currently stored graphics commands
+ are no longer required.
+ -----------------------------------------------------------------------------*/
+void CWsRedrawMsgWindow::DiscardStoredCommands()
+ {
+ iCurrentSegment = NULL;
+ if (iRedrawSegments.Count() > 0)
+ {
+ // First of all, if we have any redraws pending, update the screen with
+ // whatever commands we have before we throw them away:
+ if (iFlags & EPendingScheduledDraw)
+ Screen()->DoRedrawNow();
+
+ // for all regions or just Partial Redraw regions > index 0: delete bitmaps and draw commands
+ iRedrawSegments.ResetAndDestroy();
+
+ iLastDrawGc = NULL;
+ }
+ }
+
+void CWsRedrawMsgWindow::CreateNewSegmentL(const TRect& aRect, TRedrawSegmentType aNewRedrawRegionType)
+ {
+ CWsRedrawMsgWindow::CRedrawSegment* newRegion = CWsRedrawMsgWindow::CRedrawSegment::NewLC(aRect, aNewRedrawRegionType, *this);
+
+ iRedrawSegments.AppendL(newRegion);
+ iCurrentSegment = newRegion;
+ CleanupStack::Pop(newRegion);
+
+ // Set iLastDrawGc to NULL. This will cause all GC attributes to be stored
+ // in redraw store when the window receives the next command
+ iLastDrawGc = NULL;
+ }
+
+void CWsRedrawMsgWindow::AddFbsBitmapsL(TInt aHandle, TInt aMaskHandle)
+ {
+ AddFbsBitmapRefL(aHandle);
+ if (aMaskHandle)
+ {
+ AddFbsBitmapRefL(aMaskHandle);
+ }
+ }
+
+void CWsRedrawMsgWindow::AddFbsBitmapRefL(TInt aHandle)
+ {
+ TInt indexBitmapRef = iFbsBitmapRefArray.FindInOrder(aHandle, &FindBitmapRefByHandle);
+
+ // check whether the window already has a reference for the bitmap through a segment other than the current one
+ if (indexBitmapRef >=0)
+ {
+ TBool handleAppend = iCurrentSegment->AppendNonDuplicateBitmapHandleL(aHandle);
+ if (handleAppend)
+ {
+ iFbsBitmapRefArray[indexBitmapRef]->IncRefCount();
+ }
+ }
+ else
+ {
+ CFbsBitmapRef* bitmapRef = new(ELeave) CFbsBitmapRef;
+ CleanupStack::PushL(bitmapRef);
+ if (bitmapRef->Duplicate(aHandle)!=KErrNone)
+ OwnerPanic(EWservPanicBitmap);
+ iFbsBitmapRefArray.InsertInOrderL(bitmapRef, TLinearOrder<CFbsBitmapRef>(InsertBitmapRefByHandle));
+ CleanupStack::Pop(bitmapRef);
+ bitmapRef->IncRefCount();
+#ifdef _DEBUG
+ TBool bitmapHandleAppended = EFalse;
+ TRAPD(err, bitmapHandleAppended = iCurrentSegment->AppendNonDuplicateBitmapHandleL(aHandle);
+ WS_ASSERT_DEBUG(bitmapHandleAppended,EWsPanicUnexpectedBitmapHandleInArray););
+#else
+ TRAPD(err, iCurrentSegment->AppendNonDuplicateBitmapHandleL(aHandle););
+#endif
+ if (err != KErrNone)
+ {
+ // Cleanup the array, then propagate the error
+ indexBitmapRef = iFbsBitmapRefArray.FindInOrder(aHandle, &FindBitmapRefByHandle);
+ if (indexBitmapRef >= 0)
+ {
+ iFbsBitmapRefArray[indexBitmapRef]->DecRefCount();
+ delete iFbsBitmapRefArray[indexBitmapRef];
+ iFbsBitmapRefArray.Remove(indexBitmapRef);
+ }
+ User::Leave(err);
+ }
+ }
+ }
+
+// Only called during playback of a redraw store segment
+CFbsBitmap* CWsRedrawMsgWindow::BitmapFromHandle(TInt aHandle) const
+ {
+ const TInt index = iFbsBitmapRefArray.FindInOrder(aHandle, &FindBitmapRefByHandle);
+ if (index != KErrNotFound)
+ {
+ CFbsBitmap* bitmap = iFbsBitmapRefArray[index];
+ return bitmap;
+ }
+ return NULL;
+ }
+
+
+void CWsRedrawMsgWindow::AddWsBitmapsL(TInt aHandle, TInt aMaskHandle)
+ {
+ if (iWsWin->WsOwner() == NULL)
+ Panic(EWsPanicDrawCommandsInvalidState);
+ DWsBitmap * bmp = static_cast<DWsBitmap*>(iWsWin->WsOwner()->HandleToObj(aHandle, WS_HANDLE_BITMAP));
+ if (!bmp)
+ OwnerPanic(EWservPanicBitmap);
+ iCurrentSegment->AddWsBitmapL(bmp);
+ if (aMaskHandle)
+ {
+ bmp = static_cast<DWsBitmap*>(iWsWin->WsOwner()->HandleToObj(aMaskHandle, WS_HANDLE_BITMAP));
+ if (!bmp)
+ OwnerPanic(EWservPanicBitmap);
+ iCurrentSegment->AddWsBitmapL(bmp);
+ }
+ }
+
+void CWsRedrawMsgWindow::CRedrawSegment::AddWsBitmapL(DWsBitmap* bitmap)
+ {
+ iWsBitmapArray.AppendL(bitmap);
+ bitmap->IncRefCount();
+ }
+
+void CWsRedrawMsgWindow::AddWsFontL(TInt aHandle)
+ {
+ if (iWsWin->WsOwner()==NULL)
+ Panic(EWsPanicDrawCommandsInvalidState);
+ TDblQueIter<CWsFbsFont> iter(CWsFontCache::List());
+ CWsFbsFont* font=NULL;
+ while((font=iter++)!=NULL)
+ {
+ if (font->Handle()==aHandle)
+ break;
+ }
+ if (font)
+ {
+ iCurrentSegment->iWsFontArray.AppendL(font);
+ ++(font->iCount);
+ }
+ }
+
+void CWsRedrawMsgWindow::CRedrawSegment::AddDrawerL(TGraphicDrawerId aDrawerId)
+ {
+ TInt error = iDrawerArray.InsertInOrder(aDrawerId, TLinearOrder<TGraphicDrawerId>(TGraphicDrawerId::Compare));
+ if (error != KErrAlreadyExists && error != KErrNone)
+ {
+ User::Leave(error);
+ }
+ }
+
+TBool CWsRedrawMsgWindow::CRedrawSegment::ContainsDrawers(const TArray<TGraphicDrawerId>& aDrawers,const TRegion& aRegion) const
+ {
+ TBool result = EFalse;
+ if (iDrawerArray.Count() > 0)
+ {
+ STACK_REGION tempRegion;
+ tempRegion.Intersection(iRegion, aRegion);
+ if (tempRegion.CheckError() || (tempRegion.Count() > 0) )
+ { // regions do intersect, (presumed if region had an error); so check for a matching Id
+ const TInt drawersCount = aDrawers.Count();
+ for (TInt idx = 0; idx < drawersCount; ++idx)
+ { // (iDrawerArray is kept sorted)
+ if (KErrNotFound != iDrawerArray.FindInOrder(aDrawers[idx], TLinearOrder<TGraphicDrawerId>(TGraphicDrawerId::Compare)))
+ {
+ result = ETrue;
+ break;
+ }
+
+ const TInt count = iDrawerArray.Count();
+ for(TInt i = 0; i < count; i++)
+ {
+ const CWsGraphicDrawer* drawer = CWsTop::WindowServer()->ResolveGraphic(iDrawerArray[i]);
+ if(drawer && drawer->Contains(aDrawers))
+ {
+ result = ETrue;
+ break;
+ }
+ }
+ }
+ }
+ tempRegion.Close();
+ }
+ return result;
+ }
+
+void CWsRedrawMsgWindow::AddWsDrawableSourceL(TInt aHandle)
+ {
+ if (iWsWin->WsOwner() == NULL)
+ Panic(EWsPanicDrawCommandsInvalidState);
+ CWsDrawableSource* drawableSource = static_cast<CWsDrawableSource*>(iWsWin->WsOwner()->HandleToObj(aHandle, WS_HANDLE_DRAWABLE_SOURCE));
+ if (!drawableSource)
+ OwnerPanic(EWservPanicDrawableSource);
+ iCurrentSegment->AddWsDrawableSourceL(drawableSource);
+ }
+
+void CWsRedrawMsgWindow::CRedrawSegment::AddWsDrawableSourceL(CWsDrawableSource* aDrawableSource)
+ {
+ iWsDrawableSourceArray.AppendL(aDrawableSource);
+ aDrawableSource->IncRefCount();
+ }
+
+inline TBool CWsRedrawMsgWindow::NoBuffer() const
+ {
+ return (iRedrawSegments.Count() == 0);
+ }
+
+void CWsRedrawMsgWindow::ClientExposing()
+ {
+ Invalidate();
+ }
+
+/*------------------------------------------------------------------------------
+ Description: If a complete set of drawing commands have been stored
+ this method attempts to draw ALL the commands via DrawCommandsL().
+ It also draws the window in the background colour if the window is
+ opaque.
+ -----------------------------------------------------------------------------*/
+void CWsRedrawMsgWindow::DrawWindow()
+ {
+ iFlags &= ~EPendingScheduledDraw;
+ // This is a happy window - it can draw itself whenever we ask.
+ CScreen* screen = Screen();
+ if ((screen->AutoClear() && (iFlags & EBackgroundClear)) || HasElement())
+ {
+ DrawBackgroundColor(*iRedrawRegion,(iFlags&EBackgroundClear)!=0);
+ }
+ if (HasElement())
+ {
+ screen->WindowElements().UnassignPlacedElements(*iRedrawRegion,*CliWin(),CPlaybackGc::Instance()->GcDrawingCount());
+ }
+ // If valid commands have been stored, draw them.
+ if (iRedrawSegments.Count() > 0)
+ {
+ Lock();
+ TRAP_IGNORE(DrawCommandsL());
+ Unlock();
+ }
+ if (HasElement()) //HasElement won't be cleared following first call above. May get set...
+ {
+ TInt state=screen->WindowElements().CleanUpPlacedElements(*CliWin(),CPlaybackGc::Instance()->GcDrawingCount());
+ if (state&CWindowElement::EFastPath)
+ {
+ if (HasElement())
+ {
+ screen->ElementAdded();
+ }
+ else
+ {
+ screen->ElementRemoved();
+ }
+ }
+ }
+ }
+
+void CWsRedrawMsgWindow::RemoveFromRedrawQueueIfEmpty()
+ {
+ if (iInvalid.Count()==0)
+ {
+ iInvalid.Clear(); // Ensures heap cell is freed, otherwise may be left as an empty cell
+ iWsWin->WsOwner()->RedrawQueue()->RemoveInvalid(this);
+ }
+ }
+
+const TRegion& CWsRedrawMsgWindow::InvalidArea() const
+ {
+ return(iInvalid);
+ }
+
+TBool CWsRedrawMsgWindow::NeedsRedraw() const
+// If iInvalid has an persistant error it will not be reported as needing a redraw,
+// this is needed as otherwise cases where validation of a window results
+// in iInvalid having an error will get into an endless cycle of redraws.
+// The down side of this is that sometimes a window will not be sent a redraw
+// message when it needs it, some things can't be perfect!
+//
+ {
+ if ((!iWsWin->IsVisible()) || iInvalid.IsEmpty())
+ return EFalse;
+
+ TRect nextRedrawRect;
+ return GetRedrawRect(nextRedrawRect);
+ }
+
+TBool CWsRedrawMsgWindow::GetRedrawRect(TRect &aRect) const
+ {
+ if (iWsWin->ClientSetInvisible())
+ return EFalse;
+
+ if(InRedraw())
+ {
+ aRect = iRedrawRect;
+ return (!aRect.IsEmpty());
+ }
+ else if(iInvalid.CheckError())
+ {
+ if (Screen()->ChangeTracking())
+ {
+ aRect = iWsWin->AbsRect();
+ }
+ else
+ {
+ if (iFlags & EStoringEntireWindow || iWsWin->VisibleRegion().CheckError())
+ {
+ aRect = iWsWin->AbsRect();
+ }
+ else
+ {
+ aRect = iWsWin->VisibleRegion().BoundingRect();
+ }
+ }
+ if (!(iFlags & EStoringEntireWindow))
+ iWsWin->ClipRectToViewport(aRect);
+ aRect.Move(-iWsWin->Origin());
+ return (!aRect.IsEmpty());
+ }
+ else if(iInvalid.Count())
+ {
+ if (iFlags & EStoringEntireWindow)
+ {
+ aRect = iInvalid.BoundingRect();
+ }
+ else
+ {
+ RWsRegion region;
+ region.Copy(iInvalid);
+ region.Offset(iWsWin->Origin());
+ if (!Screen()->ChangeTracking())
+ {
+ region.Intersect(iWsWin->VisibleRegion());
+ }
+ if (region.CheckError())
+ {
+ aRect = iInvalid.BoundingRect();
+ aRect.Move(iWsWin->Origin());
+ }
+ else
+ {
+ aRect = region.BoundingRect();
+ }
+ region.Close();
+ iWsWin->ClipRectToViewport(aRect);
+ aRect.Move(-iWsWin->Origin());
+ }
+ return (!aRect.IsEmpty());
+ }
+ else
+ {
+ return EFalse;
+ }
+ }
+
+const TRegion &CWsRedrawMsgWindow::BaseDrawRegion() const
+ {
+ return(iWsWin->VisibleRegion());
+ }
+
+void CWsRedrawMsgWindow::ClipInvalidRegion(const TRect &aRect)
+ {
+ if (iInvalid.Count()>0)
+ {
+ iInvalid.ClipRect(aRect);
+ RemoveFromRedrawQueueIfEmpty();
+ }
+ }
+
+void CWsRedrawMsgWindow::EndRedraw()
+ {
+ if(!InRedraw())
+ OwnerPanic(EWservPanicDrawCommandsInvalidState);
+ if (iCurrentSegment)
+ {
+ iCurrentSegment->iDrawCommands->Compress();
+ if (iAtomic)
+ PromotePendingSegment();
+
+ // Schedule an update of the area of the screen we just drew to:
+ iFlags |= EPendingScheduledDraw;
+ if (Screen()->ChangeTracking())
+ {
+ iWsWin->AddDirtyWindowRegion(iCurrentSegment->iRegion); // stored in window coordinates
+ if (iWsWin->IsVisible())
+ {
+ // Window is visible, (we're ignoring whether it's obscured or not)
+ // we need to send the new draw commands to the render stage.
+ Screen()->ScheduleWindow(iWsWin);
+ }
+ }
+ if (!iWsWin->HasBeenDrawnToScreen())
+ {
+ CliWin()->ScheduleRegionUpdate(NULL);
+ }
+ else if(!Screen()->ChangeTracking() && (iWsWin->VisibleRegion().Count() || iWsWin->VisibleRegion().CheckError())) // on screen? good.
+ {
+ STACK_REGION draw; //### in low memory where VisibleRegion() is intact we can degrade much better than this!
+ draw.Copy(iCurrentSegment->iRegion);
+ draw.Offset(iWsWin->Origin());
+ draw.Intersect(iWsWin->VisibleRegion());
+ if(!draw.CheckError())
+ Screen()->AddRedrawRegion(draw);
+ else
+ Screen()->AddRedrawRegion(iWsWin->VisibleRegion());
+ draw.Close();
+ }
+ }
+
+ iCurrentSegment = NULL;
+
+ //store the result of the current redraw
+ if(iFlags&ECurrentRedrawFailedStorage)
+ iFlags |= EPreviousRedrawFailedStorage; //set
+ else
+ iFlags &= ~EPreviousRedrawFailedStorage; //unset
+ iFlags &= ~ECurrentRedrawFailedStorage; //unset the flag for the next redraw
+ //
+
+ iFlags&=~EBeginEndRedraw;
+ }
+
+void CWsRedrawMsgWindow::ValidateRect(const TRect *aRect)
+ {
+ if (!WsWin()->BaseParent())
+ OwnerPanic(EWservPanicParentDeleted);
+ if (aRect)
+ iRedrawRect = *aRect;
+ if (!iInvalid.IsEmpty())
+ {
+ STACK_REGION validated;
+ validated.Copy(iInvalid);
+ if (aRect)
+ validated.ClipRect(iRedrawRect);
+
+ if (iInvalid.CheckError())
+ {
+ iInvalid.Copy(iWsWin->VisibleRegion());
+ iInvalid.Offset(-iWsWin->Origin());
+ }
+ iInvalid.SubRegion(validated);
+ validated.Close();
+ }
+ RemoveFromRedrawQueueIfEmpty();
+ }
+
+TRgb CWsRedrawMsgWindow::BackColor() const
+ {
+ return(iBackColor);
+ }
+
+/**
+This function used to be quite clever about what it invalidated and what it redrew by copying
+rectangles of the screen around. This is a lot less subtle, and makes calling Scroll pretty much
+pointless, but it IS functionally correct.
+*/
+void CWsRedrawMsgWindow::Scroll(const TRect &aClipRect, const TPoint &aOffset,const TRect &aRect)
+ {
+ TRect rect = aRect;
+ rect.Intersection(aClipRect);
+ Invalidate(&rect);
+ rect = aRect;
+ rect.Move(aOffset);
+ rect.Intersection(aClipRect);
+ Invalidate(&rect);
+ }
+
+void CWsRedrawMsgWindow::ClearRedrawStore(TBool aClearPendingRedraw)
+ {
+ if(aClearPendingRedraw && (iFlags & EPendingScheduledDraw))
+ iFlags &= ~EPendingScheduledDraw;
+
+ DiscardStoredCommands();
+ Invalidate();
+ }
+
+
+void CWsRedrawMsgWindow::PrepareForResizeL(const TSize& aSize, TSize& /*aOldSize*/)
+ {
+ TBool anyIncreases(EFalse);
+ if (aSize.iWidth>iWsWin->Size().iWidth||aSize.iHeight>iWsWin->Size().iHeight)
+ {
+ anyIncreases = ETrue;
+ }
+
+ TRect newWinRect(TPoint(0,0),aSize);
+ iInvalid.ClipRect(newWinRect);
+ if (anyIncreases)
+ {
+ // add new invalid region to iInvalid
+ iInvalid.AddRect(newWinRect);
+ QueueRedraw();
+ iWsWin->WsOwner()->TriggerRedraw();
+ }
+ }
+
+void CWsRedrawMsgWindow::Moved()
+ {
+ if (!(iFlags & EStoringEntireWindow))
+ {
+ DiscardSegmentsOutsideViewport();
+ }
+ if (iInvalid.Count())
+ {
+ QueueRedraw();
+ iWsWin->WsOwner()->TriggerRedraw();
+ }
+ }
+
+TBool CWsRedrawMsgWindow::Contains(const TArray<TGraphicDrawerId>& aDrawers,const TRegion& aRegion) const
+ {
+ if (iRedrawSegments.Count() > 0)
+ {
+ // scan redraw store: calls Contains() on every region drawing commands are stored for,
+ // looking for a DrawWsGraphic command that intersects the aRegion
+ TBool contains = EFalse;
+ const TInt regionCount = iRedrawSegments.Count();
+
+ // Apply an origin correction. The input aRegion is screen-absolute, while the redraw regions are window-relative.
+ STACK_REGION relRegion;
+ relRegion.Copy(aRegion);
+ relRegion.Offset(-(CliWin()->Origin().iX), -(CliWin()->Origin().iY));
+
+ // loop through regions, stops when a match is found
+ for (TInt regionNum = 0; (regionNum < regionCount) && !contains; ++regionNum)
+ {
+ contains = iRedrawSegments[regionNum]->ContainsDrawers(aDrawers, relRegion);
+ }
+ relRegion.Close();
+ return contains;
+ }
+ else
+ {
+ return CWsWindowRedraw::Contains(aDrawers,aRegion);
+ }
+ }
+
+TBool CWsRedrawMsgWindow::RedrawingInProgress() const
+ {
+ return (iFlags & EBeginEndRedraw);
+ }
+
+void CWsRedrawMsgWindow::WindowClosing()
+ {
+ iWsWin->WsOwner()->RedrawQueue()->RemoveInvalid(this);
+ CWsWindowRedraw::WindowClosing();
+ }
+
+TBool CWsRedrawMsgWindow::IsRedrawStoreEmpty() const
+ {
+ return (iRedrawSegments.Count() <= 0); // Begin and End redraw are in the store too.
+ }
+
+TBool CWsRedrawMsgWindow::IsBackgroundClearEnabled() const
+ {
+ return ((iFlags & EBackgroundClear) != 0);
+ }
+
+void CWsRedrawMsgWindow::CleanupBitmapRefArray(const RArray<TInt>& aHandleArray)
+ {
+ TInt count = aHandleArray.Count();
+ for(TInt ii = count - 1 ; ii >= 0; ii--)
+ {
+ const TInt indexBitmapRef = iFbsBitmapRefArray.FindInOrder(aHandleArray[ii], &FindBitmapRefByHandle);
+ WS_ASSERT_DEBUG(indexBitmapRef >= 0,EWsPanicUnexpectedBitmapHandleInArray);
+ if (indexBitmapRef>=0)
+ {
+ iFbsBitmapRefArray[indexBitmapRef]->DecRefCount();
+ if (iFbsBitmapRefArray[indexBitmapRef]->RefCount()==0)
+ {
+ delete iFbsBitmapRefArray[indexBitmapRef];
+ iFbsBitmapRefArray.Remove(indexBitmapRef);
+ }
+ }
+ }
+ }
+
+void CWsRedrawMsgWindow::SetScope(TScope aScope)
+ {
+ if (aScope == EStoreEntireWindow)
+ {
+ if (!(iFlags & EStoringEntireWindow))
+ {
+ iFlags |= EStoringEntireWindow;
+ Invalidate();
+ }
+ }
+ else
+ {
+ if (iFlags & EStoringEntireWindow)
+ {
+ iFlags &= ~ EStoringEntireWindow;
+ DiscardSegmentsOutsideViewport();
+ }
+ }
+ }
+
+/**
+Removes all segments from the redraw store which are outside the viewport onto the window.
+Note that this doesn't clip the regions of those segments which are partly outside, since
+this wouldn't actually achieve anything useful.
+
+This function allocates memory so it is not suitable to run as part of ReleaseMemory.
+*/
+TBool CWsRedrawMsgWindow::DiscardSegmentsOutsideViewport()
+ {
+ TBool discarded = EFalse;
+ TInt count = iRedrawSegments.Count();
+ STACK_REGION viewport;
+ CliWin()->GetClippedBaseArea(viewport);
+ viewport.Offset(-iWsWin->Origin());
+ STACK_REGION intersect;
+ for (TInt idx = count - 1; idx >= 0; --idx)
+ {
+ CRedrawSegment * segment = iRedrawSegments[idx];
+ intersect.Intersection(segment->iRegion, viewport);
+ if (!intersect.CheckError() && intersect.IsEmpty())
+ {
+ iInvalid.Union(segment->iRegion);
+ delete segment;
+ iRedrawSegments.Remove(idx);
+ if (iCurrentSegment == segment)
+ iCurrentSegment = NULL;
+ discarded = ETrue;
+ }
+ }
+ intersect.Close();
+ viewport.Close();
+ return discarded;
+ }
+
+/**
+Statements encapsulated in between Lock() and Unlock() is guaranteed to execute in an
+atomic way without being interupted by a call to ReleaseMemory from CWsMemoryManager.
+Locking will prevent memory belonging to this object to be freed during a
+memory alloc/realloc originating from self.
+*/
+void CWsRedrawMsgWindow::Lock()
+ {
+ ++iMemoryLock;
+ }
+
+void CWsRedrawMsgWindow::Unlock()
+ {
+ --iMemoryLock;
+ WS_ASSERT_DEBUG(iMemoryLock >= 0, EWsPanicMemoryLock);
+ }
+
+TBool CWsRedrawMsgWindow::ReleaseMemory(MWsMemoryRelease::TMemoryReleaseLevel aLevel)
+ {
+ //When this function is called, wserv is in the middle of executing something.
+ //Therefore we can not safely do anything that alters the state of any shared
+ //resouces (like e.g. CScreenRedraw::iInvalid).
+ //In addition, we should refrain from anything that might try to allocate memory.
+ TBool released = EFalse;
+ //Don't release iRedrawSegments from this win if its currently being rendered,
+ //is releasing memory or is receiving drawcommands.
+ if (iMemoryLock == 0 && !iCurrentSegment)
+ {
+ Lock();
+ switch (aLevel)
+ {
+ case MWsMemoryRelease::ELow:
+ break;
+ case MWsMemoryRelease::EMedium:
+ break;
+ case MWsMemoryRelease::EHigh:
+ //Only release memory from background windows.
+ if (iRedrawSegments.Count() > 0 && iWsWin->VisibleRegion().IsEmpty())
+ {
+ ReleaseRedrawSegments();
+ released = ETrue;
+ }
+ break;
+ }
+ Unlock();
+ }
+ return released;
+ }
+
+void CWsRedrawMsgWindow::ReleaseRedrawSegments()
+ {
+ iLastDrawGc = NULL;
+ iCurrentSegment = NULL;
+ iRedrawSegments.ResetAndDestroy();
+
+ //The call to ResetAndDestroy just freed some memory so it should be
+ //possible to call Invalidate() now.
+ Invalidate();
+
+ //Releasing the same window over and over again could quickly end up in
+ //a never ending loop with a high-prio client before we find the window
+ //that has nicked all memory. So call accessed now to prevent that.
+ iWsWin->Accessed();
+ }
+
+void CWsRedrawMsgWindow::VisibleRegionChange()
+ {
+ if (!iFlags & EStoringEntireWindow)
+ {
+ DiscardSegmentsOutsideViewport();
+ }
+ if ((!iInvalid.IsEmpty()) && (!iWsWin->VisibleRegion().IsEmpty()))
+ {
+ STACK_REGION exposed;
+ exposed.Copy(iInvalid);
+ exposed.Offset(iWsWin->Origin());
+ exposed.Intersect(iWsWin->VisibleRegion());
+ if (!exposed.IsEmpty())
+ {
+ QueueRedraw();
+ }
+ exposed.Close();
+ }
+ }
+
+TBool CWsRedrawMsgWindow::ReadyToDraw() const
+ {
+ //We are only ready to draw when we have a complete segment.
+ if (iWsWin->HasBeenDrawnToScreen())
+ return ETrue;
+
+ if (iRedrawSegments.Count() == 0)
+ return EFalse;
+
+ if (iRedrawSegments.Count() > 1)
+ return ETrue;
+
+ if (iRedrawSegments[0]->iRedrawSegmentType == ESegmentTypePendingRedraw)
+ return EFalse;
+
+ return ETrue;
+ }
+
+TInt CWsRedrawMsgWindow::SizeInBytes() const
+ {
+ TInt size = sizeof(CWsRedrawMsgWindow);
+ for(TInt i = iRedrawSegments.Count()-1; i >= 0; i--)
+ {
+ size += iRedrawSegments[i]->SizeInBytes();
+ }
+ size += iInvalid.Count() * sizeof(TRect);
+ size += iLocalRedrawRegion.Count() * sizeof(TRect);
+ return size;
+ }
+
+void CWsRedrawMsgWindow::PromotePendingSegment()
+ {
+ if (iRedrawSegments.Count() > 0 && iRedrawSegments[iRedrawSegments.Count() - 1]->iRedrawSegmentType == ESegmentTypePendingRedraw)
+ {
+ CRedrawSegment * segment = iRedrawSegments[iRedrawSegments.Count() - 1];
+ const TRect * rect = segment->iRegion.RectangleList();
+ // when we get here there should only ever be one rectangle in the region, but we are playing safe
+ for (TInt r = 0; r < segment->iRegion.Count(); ++r)
+ {
+ SubtractRectFromSegmentArray(*rect);
+ ++rect;
+ }
+ segment->iRedrawSegmentType = ESegmentTypeRedraw;
+ }
+ }
+
+CFbsBitmapRef::CFbsBitmapRef()
+ {
+ }
+
+CFbsBitmapRef::~CFbsBitmapRef()
+ {
+ WS_ASSERT_DEBUG(iRefCount==0,EWsPanicCounterValue);
+ }