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