diff -r 000000000000 -r 5d03bc08d59c windowing/windowserver/nonnga/SERVER/SPRITE.CPP --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/windowing/windowserver/nonnga/SERVER/SPRITE.CPP Tue Feb 02 01:47:50 2010 +0200 @@ -0,0 +1,838 @@ +// Copyright (c) 1999-2009 Nokia Corporation and/or its subsidiary(-ies). +// All rights reserved. +// This component and the accompanying materials are made available +// under the terms of "Eclipse Public License v1.0" +// which accompanies this distribution, and is available +// at the URL "http://www.eclipse.org/legal/epl-v10.html". +// +// Initial Contributors: +// Nokia Corporation - initial contribution. +// +// Contributors: +// +// Description: +// Sprite +// +// + +#include +#include "sprite.h" +#include "rootwin.h" +#include "windowgroup.h" +#include "ScrDev.H" +#include "wstop.h" +#include "ANIM.H" +#include "offscreenbitmap.h" +#include "panics.h" +#include "EVENT.H" + +static const TInt64 KFlashHalfSecond(500000); //interval for flashing sprites + +GLDEF_D CWsDeltaTimer *CWsSpriteBase::iDeltaTimer=NULL; + +TInt TimerCallBack(TAny *aPtr) + { + ((CWsSpriteBase *)aPtr)->TimerExpired(); + return(KErrNone); + } + +// +// CWsSpriteMember +// + +CWsSpriteMember::CWsSpriteMember() + { + } + +CWsSpriteMember::~CWsSpriteMember() + { + delete iBitmap; + delete iMaskBitmap; + } + +TBool CWsSpriteMember::SetL(const TCmdSpriteMember &aCmdSpriteMember) + { + if (aCmdSpriteMember.iBitmap!=0) // Null member of sequence, time is only valid field in member data + { + iBitmap=new(ELeave) CFbsBitmap(); + TInt ret = iBitmap->Duplicate(aCmdSpriteMember.iBitmap); + if (ret == KErrNoMemory) + { + User::Leave(ret); + } + if (ret != KErrNone) + return(ETrue); + + if (aCmdSpriteMember.iMaskBitmap) + { + iMaskBitmap=new(ELeave) CFbsBitmap(); + TInt ret = iMaskBitmap->Duplicate(aCmdSpriteMember.iMaskBitmap); + if (ret == KErrNoMemory) + { + User::Leave(ret); + } + if (ret != KErrNone) + return(ETrue); + } + iInvertMask=aCmdSpriteMember.iInvertMask; + iDrawMode=aCmdSpriteMember.iDrawMode; + iOffset=aCmdSpriteMember.iOffset; + } + iInterval=aCmdSpriteMember.iInterval; + return(EFalse); + } + +// +// CWsSpriteBase +// + +TBool CWsSpriteBase::UpdateMemberL(CWsSpriteMember *aMember, const TCmdSpriteMember &aCmdSpriteMember) + { + CFbsBitmap *old=aMember->iBitmap; + CFbsBitmap *oldMask=aMember->iMaskBitmap; + aMember->iBitmap=NULL; + aMember->iMaskBitmap=NULL; + TBool ret=EFalse; + TRAPD(err,ret=aMember->SetL(aCmdSpriteMember)); + if (err!=KErrNone) + { +um_error: + delete aMember->iBitmap; + delete aMember->iMaskBitmap; + aMember->iBitmap=old; + aMember->iMaskBitmap=oldMask; + User::Leave(err); + } + TRAP(err,CheckSizesL()); + if (err!=KErrNone) + goto um_error; + SetMember(0); + delete old; + delete oldMask; + return(ret); + } + +void CWsSpriteBase::InitStaticsL() + { + iDeltaTimer=CWsDeltaTimer::NewL(ESpriteAnimatePriority); + } + +void CWsSpriteBase::DeleteStatics() + { + delete iDeltaTimer; + } + +CWsSpriteBase::CWsSpriteBase(CWsClient *owner, WH_HANDLES aType) : CWsScreenObject(owner,aType,owner->Screen()), iClipSprite(EFalse) + { + } + +CWsSpriteBase::~CWsSpriteBase() + { + Deactivate(); + + //iDeltaTimer->Remove(iDeltaTimerEntry); + if (iMembers) + { + iMembers->ResetAndDestroy(); + delete iMembers; + } + } + +void CWsSpriteBase::ForceRedraw() + { + TRegionFix<1> region; + region.AddRect(Rect()); + Screen()->AddRedrawRegion(region); + } + +void CWsSpriteBase::Deactivate() + { + //Disconnect from the sprite list and hide the sprite + if (iFlags & ESpriteActive) + { + if(iWin) + iWin->RemoveSprite(this); + if (iMembers && iMembers->Count()>1) + iDeltaTimer->Remove(iDeltaTimerEntry); + iFlags&=~ESpriteActive; + if (iFloating) + { + Screen()->SpriteManager()->RemoveFloatingSprite(this); + ForceRedraw(); + } + } + } + +void CWsSpriteBase::CheckSizesL() + { + iMaxSize.SetSize(0,0); + for(TInt index=0;indexCount();index++) + { + CWsSpriteMember *wsm=(*iMembers)[index]; + if (wsm->iBitmap) + { + TSize size=wsm->iBitmap->SizeInPixels(); + if (wsm->iMaskBitmap) + { + TSize maskSize=wsm->iMaskBitmap->SizeInPixels(); + if (maskSize.iWidthiMaxSize.iWidth) + iMaxSize.iWidth=size.iWidth; + if (size.iHeight>iMaxSize.iHeight) + iMaxSize.iHeight=size.iHeight; + } + } + } + +void CWsSpriteBase::ConstructL(TUint aFlags, CWsWindow *aWindow) + { + // Common part of construct for both sprites and pointer cursors + iFlags=aFlags; +/* + if (iFlags&ESpriteNoChildClip) + iLink.iPriority+=ENoChildPriorityBoost; + if (iFlags&ESpritePointer) + iLink.iPriority+=EPointerPriorityBoost; +*/ + iWin=aWindow; + TCallBack callback(TimerCallBack,this); + iDeltaTimerEntry.Set(callback); + iMembers=new(ELeave) CArrayPtrFlat(10); + } + +void CWsSpriteBase::AppendMemberL(const TCmdSpriteMember &aCmdSpriteMember) + { + CWsSpriteMember *&pwsm=iMembers->ExtendL(); + pwsm=NULL; // In case new leaves + pwsm=new(ELeave) CWsSpriteMember(); + if (pwsm->SetL(aCmdSpriteMember)) + OwnerPanic(EWservPanicBitmap); + } + +void CWsSpriteBase::CompleteL() + { + if (iMembers->Count() <= 0) + { + if(iWin) + iWin->OwnerPanic(EWservPanicNoSpriteMember); + //Not sure if this is a neccessary fall back if iWin is NULL. + else if(iWin==NULL && iGroupWin) + iGroupWin->OwnerPanic(EWservPanicNoSpriteMember); + } + else + { + iMembers->Compress(); + CheckSizesL(); + SetMember(0); + } + } + +void CWsSpriteBase::Activate() + { + if (iFlags&ESpriteDisabled) + { + iFlags&=~ESpriteDisabled; + } + if (iMembers->Count()>1) + { + QueueDeltaTimer(); + iDeltaTimer->Activate(); + } + iFlags|=ESpriteActive; + if(iWin) + iWin->AddSprite(this); + Screen()->SpriteManager()->Schedule(this); + if(iFloating) + { + Screen()->SpriteManager()->AddFloatingSprite(this); + ForceRedraw(); + } + } + +void CWsSpriteBase::SetMember(TInt aIndex) + { + iCurIndex=aIndex; + iPos=iBasePos+(*iMembers)[iCurIndex]->iOffset; + if ((*iMembers)[iCurIndex]->iBitmap) + iSize=(*iMembers)[iCurIndex]->iBitmap->SizeInPixels(); + else + iSize.SetSize(0,0); + + if (iSize.iWidth > iMaxSize.iWidth || iSize.iHeight > iMaxSize.iHeight) + { + WS_ASSERT_DEBUG(EFalse, EWsPanicSpriteBitmapSizeChange); + CheckSizesL(); + } + } + +void CWsSpriteBase::SetPos(const TPoint &aPos) + { + //Non-floating anim whose window is destroyed + if (!iFloating && iWin==NULL) + { + OwnerPanic(EWservPanicWindowDestroyed); + } + + //Floating anim whose group window is destroyed + if (iFloating && iGroupWin==NULL) + { + OwnerPanic(EWservPanicWindowDestroyed); + } + + iBasePos=aPos; + TPoint newPos(iBasePos+(*iMembers)[iCurIndex]->iOffset); + if (iPos!=newPos) + { + //Ensure the region covered by the sprite before as well as after the move gets scheduled for redraw + Screen()->SpriteManager()->Schedule(this); + iPos=newPos; + Screen()->SpriteManager()->Schedule(this); + } + } + +// Andy - replace delta timer with animation scheduling via screen +void CWsSpriteBase::QueueDeltaTimer() + { + iDeltaTimer->Queue((*iMembers)[iCurIndex]->iInterval,iDeltaTimerEntry); + } + +void CWsSpriteBase::TimerExpired() + { + Screen()->SpriteManager()->Schedule(this); + SetMember((iCurIndex+1)==iMembers->Count() ? 0 : iCurIndex+1); + Screen()->SpriteManager()->Schedule(this); + QueueDeltaTimer(); + iScreen->Update(); + } + +TPoint CWsSpriteBase::Pos() const + { + if (iGroupWin || iWin==NULL) + { + return(iPos); + } + return(iPos+iWin->Origin()); + } + +TRect CWsSpriteBase::Rect() const + { + TRect rect; + rect.iTl=Pos(); + rect.iBr=rect.iTl+iSize; + return(rect); + } + +void CWsSpriteBase::CommandL(TInt aOpcode, const TAny *aCmdData) + { + TWsSpriteCmdUnion pData; + + pData.any=aCmdData; + switch(aOpcode) + { + case EWsSpriteOpAppendMember: + AppendMemberL(*pData.SpriteMember); + break; + case EWsSpriteOpActivate: + if(!(iFlags&ESpriteActive)) + { + CompleteL(); + } + break; + case EWsSpriteOpUpdateMember: + if (pData.UpdateMember->index==iCurIndex) + { + TRect rect(Pos(), iMaxSize); + Screen()->SpriteManager()->Schedule(this,&rect); + } + break; + case EWsSpriteOpUpdateMember2: + { + Screen()->SpriteManager()->Schedule(this); + if (pData.UpdateMember->index<0 || pData.UpdateMember->index>=iMembers->Count()) + User::Leave(KErrArgument); + CWsSpriteMember *member=(*iMembers)[pData.UpdateMember->index]; + TBool ret=EFalse; + TRAPD(err,ret=UpdateMemberL(member,pData.UpdateMember->data)); + if (err==KErrNone) + { + TRAP(err,CheckSizesL()); + SetMember(0); + } + Screen()->SpriteManager()->Schedule(this); + User::LeaveIfError(err); + if (ret) + OwnerPanic(EWservPanicBitmap); + } + break; + default: + OwnerPanic(EWservPanicOpcode); + break; + } + } + +TBool CWsSpriteBase::CanBeSeen() const + { + if(iWin) + return (!(iFlags&ESpriteDisabled)) && (!iWin->VisibleRegion().IsEmpty()); + else + return (!(iFlags&ESpriteDisabled)); + } + +void CWsSpriteBase::Redraw(CFbsBitGc * aGc, const TRegion& aRegion) + { + TFlashState currentState=EFlashOn; + if(IsFlashingEnabled()) + currentState=Screen()->SpriteManager()->CurrentSpriteFlashState(this); + + if(currentState==EFlashOn) + { + STACK_REGION region; + region.Copy(aRegion); + const TRegion * pr = ®ion; + if (iClipSprite) + { + //PeterI iWin shouldn't be null as iClipSprite is currently only set by the text cursor (which is never floating) + //but just in case make sure we don't derefernce if it is null. + TPoint origin(0,0); + if(iWin) + origin = iWin->Origin(); + TRect rect(iBasePos + origin + iClipOffset, iClipSize); + region.ClipRect(rect); + } + region.ClipRect(RootWindow()->Abs()); + + // Only need to draw if the region being redrawn overlaps the sprite + const TRect spriteRect(Pos(), iSize); + STACK_REGION spriteRegion; + spriteRegion.AddRect(spriteRect); + region.Intersect(spriteRegion); + spriteRegion.Close(); + + if (pr->CheckError()) + { + if(iWin) + pr = &iWin->VisibleRegion(); + else + pr = &RootWindow()->WindowArea(); + } + + if (!pr->IsEmpty()) + { + CWsSpriteMember *member=(*iMembers)[iCurIndex]; + if (member->iBitmap) + { + aGc->SetClippingRegion(pr); + + // Calculate which piece (rect) of the bitmap needs to be drawn + const TRect redrawRect = pr->BoundingRect(); + TRect bitmapRect(spriteRect); // sprite rect relative to screen + bitmapRect.Intersection(redrawRect); + bitmapRect.Move(-Pos()); // adjust relative to bitmap origin + + if (member->iMaskBitmap) + aGc->BitBltMasked(Pos() + bitmapRect.iTl, member->iBitmap, bitmapRect, member->iMaskBitmap, member->iInvertMask); + else + { + aGc->SetDrawMode(member->iDrawMode); + aGc->BitBlt(Pos() + bitmapRect.iTl, member->iBitmap, bitmapRect); + aGc->SetDrawMode(CGraphicsContext::EDrawModePEN); + } + aGc->SetClippingRegion(NULL); + } + } + region.Close(); + } + //flashing sprites need to reschedule themselves after drawing + if(IsFlashingEnabled()) + Screen()->SpriteManager()->Schedule(this); + } + +TBool CWsSpriteBase::IsActivated() const + { + return (iFlags&ESpriteActive); + } + +// +// CWsSprite +// + +CWsSprite::CWsSprite(CWsClient *owner) : CWsSpriteBase(owner,WS_HANDLE_SPRITE) + { + } + +CWsSprite::~CWsSprite() + { + if (!iFloating && IsActivated() && iWin && iWin->IsVisible()) + ForceRedraw(); + + if (iAnim) + CWsAnim::CloseAnim(iAnim); + } + +void CWsSprite::ConstructL(const TWsClCmdCreateSprite &aParams) + { + NewObjL(); + CWsWindowBase *win; + WsOwner()->HandleToWindow(aParams.window,&win); + if (win->WinType()==EWinTypeGroup) + { + //If a sprite is attached to a group window it is floating. + //Floating sprite drawing is performed by the sprite manager. + iGroupWin=(CWsWindowGroup *)win; + win=NULL; //Floating sprites aren't associated with any particular window. + iFloating=ETrue; + } + CWsSpriteBase::ConstructL(aParams.flags&ESpriteNonSystemFlags,(CWsWindow *)win); + iBasePos=aParams.pos; + } + +void CWsSprite::CompleteL() + { + CWsSpriteBase::CompleteL(); + Activate(); + } + +void CWsSprite::CommandL(TInt aOpcode, const TAny *aCmdData) + { + TWsSpriteCmdUnion pData; + pData.any=aCmdData; + switch(aOpcode) + { + case EWsSpriteOpSetPosition: + SetPos(*pData.Point); + break; + case EWsSpriteOpFree: + delete this; + break; + default: + CWsSpriteBase::CommandL(aOpcode, aCmdData); + break; + } + } + +/** +@see MAnimSpriteFunctions::UpdateMember +@param aFullUpdate Not used. Wserv2 always do full back to front rendering, so there is no distinction between changes needing aFullUpdate or not + */ +void CWsSprite::Update(TInt aMember,TRect aRect,TBool /*aFullUpdate*/) + { + if (iCurIndex!=aMember) + return; + aRect.Move(Pos()); + aRect.Intersection(iScreen->CurrentScreenSize()); + Screen()->SpriteManager()->Schedule(this, &aRect); + } + +// +// CWsPointerCursor +// + +CWsPointerCursor::CWsPointerCursor(CWsClient *owner) : CWsSpriteBase(owner,WS_HANDLE_POINTER_CURSOR) + { + } + +void CWsPointerCursor::CloseObject() + { + RemoveFromIndex(); + Close(); + } + +void CWsPointerCursor::Close() + { + WS_ASSERT_DEBUG(iAccessCount>0, EWsPanicPointerCursorAccessCount); + if (--iAccessCount==0) + delete this; + } + +void CWsPointerCursor::Open() + { + iAccessCount++; + } + +CWsPointerCursor::~CWsPointerCursor() + { + WS_ASSERT_DEBUG(iAccessCount==0, EWsPanicPointerCursorAccessCount); + } + +void CWsPointerCursor::ConstructL(const TWsClCmdCreatePointerCursor &aParams) + { + NewObjL(); + CWsSpriteBase::ConstructL(ESpriteNoShadows|ESpriteNoChildClip|ESpritePointer|(aParams.flags&ESpriteNonSystemFlags),RootWindow()); + Open(); + } + +void CWsPointerCursor::CommandL(TInt aOpcode, const TAny *aCmdData) + { + switch(aOpcode) + { + case EWsSpriteOpFree: + CloseObject(); + break; + default: + CWsSpriteBase::CommandL(aOpcode, aCmdData); + break; + } + } + +// +// CWsCustomTextCursor +// + +CWsCustomTextCursor::CWsCustomTextCursor (CWsClient *aOwner, RWsSession::TCustomTextCursorAlignment aAlignment) +: CWsSpriteBase(aOwner, WS_HANDLE_TEXT_CURSOR), iAlignment(aAlignment) + { + } + +CWsCustomTextCursor::~CWsCustomTextCursor() + { + } + +void CWsCustomTextCursor::ConstructL(TInt aFlags) + { + NewObjL(); + CWsSpriteBase::ConstructL(ESpriteNoShadows|ESpriteNoChildClip|ESpritePointer|(aFlags&ESpriteNonSystemFlags), NULL); + } + +void CWsCustomTextCursor::CompleteL(CWsWindow *aWin, TBool aFlash, TBool aClipSprite, const TPoint& aClipOffset, const TSize& aClipSize) + { + iWin = aWin; + iFlags = aFlash ? iFlags | ESpriteFlash : iFlags & ~ESpriteFlash; + iClipSprite = aClipSprite; + iClipOffset = aClipOffset; + iClipSize = aClipSize; + CWsSpriteBase::CompleteL(); + } + +// Use SetPositionNoRedraw instead of SetPos when you just want to update +// the custom text cursor position without redrawing it +void CWsCustomTextCursor::SetPositionNoRedraw(const TPoint& aPos) + { + iBasePos = aPos; + TPoint newPos(iBasePos+(*iMembers)[iCurIndex]->iOffset); + iPos=newPos; + } + +void CWsCustomTextCursor::CommandL(TInt aOpcode, const TAny *aCmdData) + { + switch(aOpcode) + { + case EWsSpriteOpFree: + // CWsCustomTextCursor objects are owned by the text cursor list. + // They are not deleted when the client closes it's R class. + RemoveFromIndex(); + break; + default: + CWsSpriteBase::CommandL(aOpcode, aCmdData); + break; + } + } + +// +// CWsDeltaTimer, nicked from CDeltaTimer and tweaked so it doesn't re-activate // +// the timers until RunL has finished running all ready timers. // +// // +// This is to stop a problem in Wserv where sprites could hog 100% CPU if the time // +// it took to process them was longer than the time of the timer queued when the first // +// sprite was updated // +// + +CWsDeltaTimer* CWsDeltaTimer::NewL(TInt aPriority) + { + CWsDeltaTimer* wsdt=new(ELeave) CWsDeltaTimer(aPriority); + CleanupStack::PushL(wsdt); + User::LeaveIfError(wsdt->iTimer.CreateLocal()); + CActiveScheduler::Add(wsdt); + CleanupStack::Pop(wsdt); + return(wsdt); + } + +CWsDeltaTimer::CWsDeltaTimer(TInt aPriority) : CActive(aPriority),iQueue(_FOFF(TWsDeltaTimerEntry,iLink)) + { + } + +CWsDeltaTimer::~CWsDeltaTimer() + { + Cancel(); + while(iQueue.RemoveFirst()!=NULL) + {} + } + +void CWsDeltaTimer::Queue(TTimeIntervalMicroSeconds32 aTimeInMicroSeconds,TWsDeltaTimerEntry& anEntry) + { + TInt intervals=aTimeInMicroSeconds.Int()/CWsDeltaTimerGranularity; + if (intervals==0) + intervals=1; + iQueue.Add(anEntry,intervals); + } + +void CWsDeltaTimer::Activate() + { + // Queue a request on the timer. + // The timer runs every tenth of a second and decremented the delta of the head of the queue. + if (IsActive()) + return; + if (!iQueue.IsEmpty()) + { + SetActive(); + iTimer.After(iStatus,CWsDeltaTimerGranularity-1); // -1 to compensate for +1 in kernel! + } + } + +void CWsDeltaTimer::RunL() + { + // Call all zero delta callbacks + iQueue.CountDown(); + TWsDeltaTimerEntry* ent=iQueue.RemoveFirst(); + while (ent) + { + ent->iCallBack.CallBack(); + ent=iQueue.RemoveFirst(); + } + Activate(); + } + +void CWsDeltaTimer::DoCancel() + { + iTimer.Cancel(); + } + +void CWsDeltaTimer::Remove(TWsDeltaTimerEntry& anEntry) + { + if (anEntry.IsPending()) + { + iQueue.Remove(anEntry); + Activate(); + } + } + + +// +// CWsSpriteManager -handles floating and flashing sprites including flashing custom text cursors. i.e. cursors +// that have an associated sprite. + + +CWsSpriteManager::CWsSpriteManager() + { + } + +CWsSpriteManager::~CWsSpriteManager() + { + iFloatingSprites.ResetAndDestroy(); + } + +CWsSpriteManager* CWsSpriteManager::NewL() + { + CWsSpriteManager* self = new (ELeave) CWsSpriteManager(); + CleanupStack::PushL(self); + self->ConstructL(); + CleanupStack::Pop(self); + return self; + } + +void CWsSpriteManager::ConstructL() + { + } + +void CWsSpriteManager::AddFloatingSprite(CWsSpriteBase* aSprite) + { + iFloatingSprites.Append(aSprite); + } + +void CWsSpriteManager::RemoveFloatingSprite(CWsSpriteBase* aSprite) + { + for (TInt i=0 ; iReset(); + iFloatingSprites[i]->Redraw(aGc, aRegion); + } + } + +void CWsSpriteManager::Schedule(CWsSpriteBase* aSprite, TRect* aRect) + { + if (aRect != NULL && aRect->IsEmpty()) + return; + + TRect rect = aSprite->Rect(); + if (aRect) + rect.Intersection(*aRect); + + if(aSprite->IsFlashingEnabled()) + { + aSprite->Screen()->ScheduleAnimation(rect,NextSpriteFlashStateChange(aSprite),0,0); + } + else + { + //Scheduling an animation "now" means it will take place at next animation which might + //be the full animation grace period into the future (see KAnimationGrace in server.cpp) + aSprite->Screen()->ScheduleAnimation(rect,0,0,0); + } + } + +// Sprite flashing is clamped to half second intervals in relation to the global time. +// For the first half of each second all sprites have the EFlashOn state (visible) +// For the second half of each second all sprites have the EFlashOff state (not visible) +TTimeIntervalMicroSeconds CWsSpriteManager::NextSpriteFlashStateChange(const CWsSpriteBase* aSprite) const + { + const TTimeIntervalMicroSeconds remainder = aSprite->Screen()->Now().DateTime().MicroSecond(); + return CalculateTimeToNextFlash(remainder); + } + +TTimeIntervalMicroSeconds CWsSpriteManager::NextCursorFlashStateChange() const + { + const TTimeIntervalMicroSeconds remainder = CWsTop::CurrentFocusScreen()->Now().DateTime().MicroSecond(); + return CalculateTimeToNextFlash(remainder); + } + +TTimeIntervalMicroSeconds CWsSpriteManager::CalculateTimeToNextFlash(TTimeIntervalMicroSeconds aTime) const + { + TInt64 nextStateChange; + if(aTimeScreen()->Now().DateTime().MicroSecond()Now().DateTime().MicroSecond()CanBeSeen() && ( sprite->IsActive() || sprite->IsActivated() ) ) + { + aResultRgn.AddRect( sprite->Rect() ); + } + } + aResultRgn.Tidy(); + if ( aResultRgn.CheckError() && iFloatingSprites.Count() > 0 ) + { + aResultRgn.Clear(); + aResultRgn.AddRect( aDefaultRect ); + } + }