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 sl@0: #include sl@0: #include "panics.h" sl@0: #include "rootwin.h" sl@0: #include "EVENT.H" sl@0: #include "wsfont.h" sl@0: #include 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: /** Max number of non-redraw segments allowed before starting to throw away the oldest */ sl@0: const TInt KNonRedrawSegMaxLimit = 16; sl@0: /** The number of non-redraw segments to spare from deletion once their number sl@0: have grown beyond KNonRedrawSegMaxLimit */ sl@0: const TInt KNonRedrawSegThreshold = 8; sl@0: sl@0: TInt CWsRedrawMsgWindow::iNonRedrawAgeLimit = 0; sl@0: CWsRedrawMsgWindow::TAtomicityType CWsRedrawMsgWindow::iAtomicity = ENoAtomicity; sl@0: sl@0: #if defined(__WINS__) && defined(_DEBUG) sl@0: # include "offscreenbitmap.h" sl@0: # define DEBUGOSB { CWsOffScreenBitmap * ofb = Screen()->OffScreenBitmap(); if (ofb) ofb->Update(); } sl@0: #else sl@0: # define DEBUGOSB sl@0: #endif sl@0: sl@0: #ifndef _DEBUG sl@0: sl@0: #define LOG_WINDOW_REDRAW_START(wswin,region) sl@0: #define LOG_WINDOW_REDRAW_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_REDRAW_START(wswin,region) LogDrawCommandsStart(wswin,region) sl@0: #define LOG_WINDOW_REDRAW_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: extern CDebugLogBase *wsDebugLog; sl@0: sl@0: class TTruncateOverflow : public TDesOverflow sl@0: { sl@0: public: sl@0: virtual void Overflow(TDes&) {}; sl@0: }; sl@0: sl@0: LOCAL_C void LogRedrawSegment(TUint aSegmentIndex, CWsRedrawMsgWindow::TRedrawSegmentType aSegmentType) sl@0: { sl@0: if (wsDebugLog) sl@0: { sl@0: TBuf 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: _LIT(KLogRedrawSegmentNonRedraw, "NonRedraw"); 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: case CWsRedrawMsgWindow::ESegmentTypeNonRedraw : sl@0: log.AppendFormat(KLogRedrawSegmentNonRedraw, &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 LogRegion(const TRegion* aRegion) sl@0: { sl@0: TBuf log; sl@0: TTruncateOverflow overflow; sl@0: TInt rectCount = (aRegion == NULL ? 0 : aRegion->Count()); sl@0: log.AppendFormat(_L("region [%d,"), &overflow, rectCount); sl@0: if (rectCount > 0) sl@0: { sl@0: const TRect* rectangles = aRegion->RectangleList(); sl@0: TBuf<1> comma; sl@0: for (TInt ii = 0; ii < rectCount; ii++) sl@0: { sl@0: TRect current = rectangles[ii]; sl@0: log.AppendFormat(_L("%S{{%d,%d},{%d,%d}}"), &overflow, &comma, sl@0: current.iTl.iX,current.iTl.iY,current.iBr.iX,current.iBr.iY); sl@0: comma = _L(","); sl@0: } sl@0: } sl@0: else sl@0: { sl@0: log.AppendFormat(_L("NULL"), &overflow); sl@0: } sl@0: log.AppendFormat(_L("]"), &overflow); sl@0: wsDebugLog->MiscMessage(CDebugLogBase::ELogEverything, log); 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 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 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() sl@0: { sl@0: } sl@0: sl@0: CWsRedrawMsgWindow::CRedrawSegment* CWsRedrawMsgWindow::CRedrawSegment::NewLC(const TRect& aRect, TRedrawSegmentType aNewRegionType) sl@0: { sl@0: CRedrawSegment* self = new (ELeave) CRedrawSegment(); 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: _LIT(KNonRedrawAgeLimit, "NONREDRAWAGELIMIT"); sl@0: const TInt KDefaultNonRedrawAgeLimit = 1000000; sl@0: if (!WsIniFile->FindVar(KNonRedrawAgeLimit, iNonRedrawAgeLimit)) sl@0: iNonRedrawAgeLimit = KDefaultNonRedrawAgeLimit; sl@0: sl@0: _LIT(KAtomicRedraws,"ATOMICREDRAWS"); sl@0: _LIT(KAtomicSegment,"SEGMENT"); sl@0: _LIT(KAtomicWindow,"WINDOW"); sl@0: sl@0: TPtrC atomicityTypeString; sl@0: if (WsIniFile->FindVar(KAtomicRedraws,atomicityTypeString)) sl@0: { sl@0: if(atomicityTypeString.CompareF(KAtomicSegment)==0 || atomicityTypeString.Length()==0) sl@0: iAtomicity = ESegment; sl@0: else if(atomicityTypeString.CompareF(KAtomicWindow)==0) sl@0: iAtomicity = EWindow; sl@0: } sl@0: } sl@0: sl@0: void CWsRedrawMsgWindow::CRedrawSegment::ReleaseFontsAndBitmaps() sl@0: { sl@0: // release Bitmap and Font 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: count = iFbsBitmapArray.Count(); sl@0: for(ii = count - 1 ; ii >= 0; ii--) sl@0: { sl@0: delete iFbsBitmapArray[ii]; sl@0: iFbsBitmapArray.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: delete iDrawCommands; sl@0: sl@0: iRegion.Close(); sl@0: sl@0: // Release Font and Bitmap handles, close arrays sl@0: ReleaseFontsAndBitmaps(); sl@0: iFbsBitmapArray.Close(); sl@0: iWsBitmapArray.Close(); sl@0: iWsFontArray.Close(); sl@0: iDrawerArray.Close(); sl@0: } sl@0: sl@0: void CWsRedrawMsgWindow::WindowClosing() sl@0: { sl@0: iWsWin->WsOwner()->RedrawQueue()->RemoveInvalid(this); 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 += iFbsBitmapArray.Count() * sizeof(CFbsBitmap); sl@0: size += iWsBitmapArray.Count() * sizeof(DWsBitmap); sl@0: size += iWsFontArray.Count() * sizeof(CWsFbsFont); sl@0: size += iDrawerArray.Count() * sizeof(TGraphicDrawerId); sl@0: return size; 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: iCurrentSegment=NULL; 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: 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 (iAtomicity==ENoAtomicity) sl@0: PromoteLastPendingSegment(); 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: If a draw command is received outside a begin/end redraw pair, then it is stored in a non-redraw sl@0: segment. This function creates such a segment if it isn't already available. sl@0: */ sl@0: void CWsRedrawMsgWindow::HandleNonRedrawCommand(TWsGcOpcodes aOpcode) sl@0: { sl@0: // calling code should check the Window State sl@0: WS_ASSERT_DEBUG(!InRedraw(), EWsPanicDrawCommandsInvalidState); sl@0: sl@0: // Attempting to draw part of a polygon in a new segment - only a very bad client can do this: sl@0: TBool canCreate = !(aOpcode == EWsGcOpSegmentedDrawPolygonData || aOpcode == EWsGcOpDrawSegmentedPolygon); sl@0: if ((!iCurrentSegment) && (!canCreate)) sl@0: { sl@0: OwnerPanic(EWservPanicBadPolyData); sl@0: } sl@0: sl@0: if (canCreate) sl@0: AgeNonRedrawSegments(); sl@0: sl@0: // If current redraw region is not for Non-Redraw drawing, add a region corresponding to the sl@0: // full window to the redraw store. Need to make sure that this is done sl@0: // before the AppendCommand, or bitmap/font handles are recorded, in StoreDrawCommandL sl@0: TInt err = KErrNone; sl@0: if ((!iCurrentSegment) || iCurrentSegment->iRedrawSegmentType != ESegmentTypeNonRedraw) sl@0: { sl@0: TRAP(err,CreateNewSegmentL(TRect(WsWin()->Size()), ESegmentTypeNonRedraw)); sl@0: } sl@0: sl@0: if (err != KErrNone) sl@0: { sl@0: Invalidate(); sl@0: } sl@0: sl@0: if(iWsWin->VisibleRegion().CheckError()) sl@0: iWsWin->Screen()->AddRedrawRegion(iWsWin->WindowArea()); sl@0: else if(iWsWin->VisibleRegion().Count()) sl@0: iWsWin->Screen()->AddRedrawRegion(iWsWin->VisibleRegion()); sl@0: else if(!iWsWin->HasBeenDrawnToScreen()) sl@0: CliWin()->ScheduleRegionUpdate(&iWsWin->VisibleRegion()); sl@0: } sl@0: sl@0: /** sl@0: This function attempts to prevent non-redraw segments from growing indefinitely by requesting redraws sl@0: and throwing away old ones. sl@0: */ sl@0: void CWsRedrawMsgWindow::AgeNonRedrawSegments() sl@0: { sl@0: if (!iRedrawSegments.Count()) sl@0: return; sl@0: sl@0: //Count the number of non-redraw segs sl@0: TInt nonRedrawSegCount = 0; sl@0: for (TInt i = iRedrawSegments.Count()-1; i >= 0; --i) sl@0: { sl@0: CRedrawSegment* segment = iRedrawSegments[i]; sl@0: if (segment->iRedrawSegmentType == ESegmentTypeNonRedraw) sl@0: { sl@0: ++nonRedrawSegCount; sl@0: } sl@0: } sl@0: sl@0: TBool callInvalidate = EFalse; sl@0: sl@0: //To prevent the number of non redraw segements to grow indefinitely, sl@0: //delete the oldest if their number exceeds KNonRedrawSegMaxLimit sl@0: if (nonRedrawSegCount > KNonRedrawSegMaxLimit) sl@0: { sl@0: TInt keep = KNonRedrawSegThreshold; // keep this many, the most recent ones sl@0: for (TInt i = iRedrawSegments.Count()-1; i >= 0; --i) sl@0: { sl@0: CRedrawSegment* segment = iRedrawSegments[i]; sl@0: if (segment->iRedrawSegmentType == ESegmentTypeNonRedraw) sl@0: { sl@0: if (keep-- > 0) sl@0: { sl@0: continue; sl@0: } sl@0: else if (segment!=iCurrentSegment) //never delete the current segment sl@0: { sl@0: callInvalidate = ETrue; sl@0: iRedrawSegments.Remove(i); sl@0: delete segment; sl@0: } sl@0: } sl@0: } sl@0: } sl@0: sl@0: if(iCurrentSegment && iCurrentSegment->iRedrawSegmentType == ESegmentTypeNonRedraw) sl@0: { sl@0: // If the current segment is an old non-redraw segment, try to get rid of it sl@0: TTime now; sl@0: now.UniversalTime(); sl@0: TTimeIntervalMicroSeconds age = now.MicroSecondsFrom(iCurrentSegment->iCreationTime); sl@0: if (age > iNonRedrawAgeLimit) sl@0: { sl@0: // First, find any even older non redraw segments and discard them. sl@0: for (TInt seg = iRedrawSegments.Count() - 2; seg >= 0; --seg) sl@0: { sl@0: CRedrawSegment * segment = iRedrawSegments[seg]; sl@0: if (segment->iRedrawSegmentType == ESegmentTypeNonRedraw) sl@0: { sl@0: age = now.MicroSecondsFrom(segment->iCreationTime); sl@0: if ((age > iNonRedrawAgeLimit * 2) && (segment!=iCurrentSegment)) sl@0: { sl@0: iRedrawSegments.Remove(seg); sl@0: delete segment; sl@0: } sl@0: } sl@0: } sl@0: sl@0: // Then force the creation of a new segment, so that the current one can be allowed to age and eventually vanish. sl@0: iCurrentSegment->iCreationTime = now; sl@0: iCurrentSegment = NULL; sl@0: callInvalidate = ETrue; sl@0: } sl@0: } sl@0: sl@0: if(callInvalidate) sl@0: Invalidate(); // Invalidate the window so that a complete redraw should occur 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: // Catch anyone calling this without first deciding where they want to draw sl@0: WS_ASSERT_DEBUG(iWsWin->ScheduledRegion(), EWsPanicScheduledRedraw); 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: // Andy - this allocates memory during drawing, which is bad in OOM conditions. sl@0: iLocalRedrawRegion.Copy(iRedrawSegments[aRegionNum]->iRegion); sl@0: iLocalRedrawRegion.Offset(WsWin()->Origin()); sl@0: iLocalRedrawRegion.Intersect(*iGlobalRedrawRegion); 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: TInt CWsRedrawMsgWindow::SubtractRectFromSegmentArray(const TRect& aRect) sl@0: { sl@0: TInt numOfRegionsRemoved =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: // Andy - This is an error condition and needs to check for infinite loops sl@0: delete iRedrawSegments[regionNum]; sl@0: iRedrawSegments.Remove(regionNum); sl@0: numOfRegionsRemoved++; 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: numOfRegionsRemoved++; 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: return numOfRegionsRemoved; 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&ENoRepeatRedraw)) sl@0: Invalidate(); sl@0: iFlags |= ENoRepeatRedraw; 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: TRAP(err,AppendCommandL(aCmdData)); 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: 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: } 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 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(CWsClient::iCurrentCommand.iOpcode); sl@0: sl@0: // If we get an extra command after the redraw has finished then redraw strategy needs to know sl@0: if (!InRedraw()) sl@0: { sl@0: #ifdef __WINS__ sl@0: TBool isDrawingCommand = (currentOpcode != EWsGcOpSegmentedDrawPolygonData) && (currentOpcode != EWsGcOpDrawSegmentedPolygon); sl@0: sl@0: if( CWsClient::DebugEnforceRedrawCallingConvention() && isDrawingCommand) sl@0: CWsClient::PanicCurrentClient(EWservPanicWindowBeginRedrawNotCalled); sl@0: #endif sl@0: HandleNonRedrawCommand(currentOpcode); 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: 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: 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: /*------------------------------------------------------------------------------ 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_REDRAW_START(WsWin(), iGlobalRedrawRegion); sl@0: WS_ASSERT_DEBUG(iMemoryLock > 0, EWsPanicMemoryLock); sl@0: static TBuf8 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: 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(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: DEBUGOSB // per-redraw-segment debug osb updates sl@0: } sl@0: } sl@0: LOG_WINDOW_REDRAW_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: { sl@0: Screen()->DoRedrawNow(); sl@0: } 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); 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: static TInt FindBitmapByHandle(const TInt* aKey, const CFbsBitmap& aBitmap) sl@0: { // compare handles sl@0: return *aKey - aBitmap.Handle(); sl@0: } sl@0: sl@0: static TInt InsertBitmapByHandle(const CFbsBitmap& aFirst, const CFbsBitmap& aSecond) sl@0: { sl@0: return aFirst.Handle() - aSecond.Handle(); sl@0: } sl@0: sl@0: void CWsRedrawMsgWindow::AddFbsBitmapsL(TInt aHandle, TInt aMaskHandle) sl@0: { sl@0: iCurrentSegment->AddFbsBitmapL(aHandle, this); sl@0: if (aMaskHandle) sl@0: { sl@0: iCurrentSegment->AddFbsBitmapL(aMaskHandle, this); sl@0: } sl@0: } sl@0: sl@0: void CWsRedrawMsgWindow::CRedrawSegment::AddFbsBitmapL(TInt aHandle, CWsRedrawMsgWindow* aWindow) sl@0: { sl@0: if (iFbsBitmapArray.FindInOrder(aHandle, &FindBitmapByHandle) >= 0) sl@0: { sl@0: // Bitmap already in the store sl@0: return; sl@0: } sl@0: sl@0: CFbsBitmap* bitmap = new(ELeave) CFbsBitmap; sl@0: CleanupStack::PushL(bitmap); sl@0: if (bitmap->Duplicate(aHandle)!=KErrNone) sl@0: aWindow->OwnerPanic(EWservPanicBitmap); sl@0: iFbsBitmapArray.InsertInOrderL(bitmap, TLinearOrder(InsertBitmapByHandle)); sl@0: CleanupStack::Pop(bitmap); 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(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(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 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::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& 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::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: 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: if(iFlags&EBackgroundClear) sl@0: { sl@0: DrawBackgroundColor(iGlobalRedrawRegion); 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: } 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: 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 (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: 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: region.Intersect(iWsWin->VisibleRegion()); 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: 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: ++iCount; sl@0: if(!InRedraw()) sl@0: OwnerPanic(EWservPanicDrawCommandsInvalidState); sl@0: if (iCurrentSegment) sl@0: { sl@0: iCurrentSegment->iDrawCommands->Compress(); sl@0: if (iAtomicity==ENoAtomicity) sl@0: { sl@0: ScheduleUpdateOfSegment(iCurrentSegment); sl@0: } sl@0: else if(iAtomicity==ESegment) sl@0: { sl@0: PromoteLastPendingSegment(); sl@0: ScheduleUpdateOfSegment(iCurrentSegment); sl@0: } sl@0: else if(iAtomicity==EWindow) sl@0: { sl@0: //only promote all pending segments when there are no invalid regions left in the window. sl@0: STACK_REGION regionAwaitingRedraws; sl@0: regionAwaitingRedraws.Copy(WsWin()->VisibleRegion()); sl@0: regionAwaitingRedraws.Offset(-WsWin()->Origin()); sl@0: regionAwaitingRedraws.Intersect(iInvalid); sl@0: if(regionAwaitingRedraws.IsEmpty()) sl@0: PromoteAndUpdateAllPendingSegments(); sl@0: regionAwaitingRedraws.Close(); sl@0: } sl@0: } sl@0: sl@0: iCurrentSegment = NULL; sl@0: iFlags&=~(ENoRepeatRedraw|EBeginEndRedraw); sl@0: } sl@0: sl@0: void CWsRedrawMsgWindow::ScheduleUpdateOfSegment(CRedrawSegment* aSegment) sl@0: { sl@0: // Schedule an update of the area of the screen we just drew to: sl@0: iFlags |= EPendingScheduledDraw; sl@0: if(iWsWin->VisibleRegion().Count() || iWsWin->VisibleRegion().CheckError()) 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(aSegment->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: 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& 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: // 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, aRegion); sl@0: } 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: 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()->SetClippedBaseArea(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::PromoteLastPendingSegment() 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: void CWsRedrawMsgWindow::PromoteAndUpdateAllPendingSegments() sl@0: { sl@0: for(TInt i =0; iiRedrawSegmentType == ESegmentTypePendingRedraw) sl@0: { sl@0: const TRect * rect = segment->iRegion.RectangleList(); sl@0: TInt totalRemovedSegments = 0; sl@0: for (TInt r = 0; r < segment->iRegion.Count(); ++r) sl@0: { sl@0: totalRemovedSegments += SubtractRectFromSegmentArray(*rect); sl@0: ++rect; sl@0: } sl@0: //we need to decrement the loop count to take into account any removed segments so we sl@0: //make sure we iterate over every segment in the array. sl@0: i-=totalRemovedSegments; sl@0: segment->iRedrawSegmentType = ESegmentTypeRedraw; sl@0: ScheduleUpdateOfSegment(segment); sl@0: } sl@0: } sl@0: }