// Copyright (c) 1996-2009 Nokia Corporation and/or its subsidiary(-ies).
// All rights reserved.
// This component and the accompanying materials are made available
// under the terms of "Eclipse Public License v1.0"
// which accompanies this distribution, and is available
// at the URL "http://www.eclipse.org/legal/epl-v10.html".
//
// Initial Contributors:
// Nokia Corporation - initial contribution.
//
// Contributors:
//
// Description:
//
// Window server redraw queue handling
//
#include <e32std.h>
#include "server.h"
#include "rootwin.h"
#include "windowgroup.h"
#include "walkwindowtree.h"
#include "EVQUEUE.H"
#include "panics.h"
#include "wstop.h"
#include "wstraces.h"
const TInt KGranularity = 10;
void TWsRedrawEvent::SetHandle(TUint aHandle)
{
iHandle=aHandle;
}
void TWsRedrawEvent::SetRect(TRect aRect)
{
iRect=aRect;
}
TWsRedrawEvent CRedrawQueue::iNullRedrawEvent;
CRedrawQueue::CRedrawQueue(CWsClient *aOwner) : CEventBase(aOwner)
{
__DECLARE_NAME(_S("CRedrawQueue"));
}
void CRedrawQueue::ConstructL()
{
iRedrawTrigger=EFalse;
iRedraws=new(ELeave) CArrayFixSeg<TRedraw>(KGranularity);
iKeyPriority=new(ELeave) TKeyArrayFix(_FOFF(TRedraw,iPriority),ECmpTUint32);
iKeyWindow=new(ELeave) TKeyArrayFix(_FOFF(TRedraw,iRedraw),ECmpTUint32);
Mem::FillZ(&iNullRedrawEvent,sizeof(iNullRedrawEvent));
}
CRedrawQueue::~CRedrawQueue()
{
delete iRedraws;
delete iKeyPriority;
delete iKeyWindow;
}
void CRedrawQueue::ReCalcOrder()
{
const TInt redrawsCount=iRedraws->Count();
for(TInt index=0;index<redrawsCount;index++)
{
TRedraw *redraw=&iRedraws->At(index);
redraw->iPriority=static_cast<CWsClientWindow*>(redraw->iRedraw->WsWin())->RedrawPriority();
}
iRedraws->Sort(*iKeyPriority);
}
void CRedrawQueue::AddInvalid(CWsWindowRedraw *aRedrawWin)
//
// Add a window to the update list.
//
{
TInt index;
TRedraw redraw;
redraw.iRedraw=aRedrawWin;
if (iRedraws->Find(redraw,*iKeyWindow,index)!=0)
{
redraw.iPriority=static_cast<CWsClientWindow*>(aRedrawWin->WsWin())->RedrawPriority();
TRAPD(err,iRedraws->InsertIsqAllowDuplicatesL(redraw,*iKeyPriority));
if (err!=KErrNone)
{
WS_ASSERT_DEBUG(err==KErrNoMemory,EWsPanicRedrawQueueError);
iAllocError=ETrue;
}
iRedrawTrigger=ETrue;
}
}
void CRedrawQueue::DeleteFromQueue(TInt aIndex)
{
iRedraws->Delete(aIndex,1);
//We are certain we will need iRedraws again, so it would be silly to compress away the last KGranularity slots.
const TInt count = iRedraws->Count();
if((count >= KGranularity) && (count % KGranularity == 0))
{
iRedraws->Compress();
}
}
void CRedrawQueue::RemoveInvalid(CWsWindowRedraw *aRedrawWin)
//
// remove the window from the invalid window list.
// harmless to call if the window is not in the list.
//
{
TInt index;
TRedraw redraw;
redraw.iRedraw=aRedrawWin;
redraw.iPriority=0;
if ((iRedraws->Find(redraw,*iKeyWindow,index))==KErrNone)
DeleteFromQueue(index);
}
TBool CRedrawQueue::TriggerRedraw()
//
// Trigger any pending redraw messages in the queue
// Returns ETrue if a redraw was sent, EFalse if not.
//
{
WS_TRACE_SERVER_TRIGGERREDRAW();
TBool ret=EFalse;
if (iRedrawTrigger)
{
iRedrawTrigger=EFalse;
if (!iEventMsg.IsNull() && (iRedraws->Count()>0 || iAllocError))
{
SignalEvent();
ret=ETrue;
}
}
return(ret);
}
void CRedrawQueue::EventReady(const RMessagePtr2& aEventMsg)
//
// Queue a read of an event from the queue
//
{
CEventBase::EventReady(aEventMsg);
iRedrawTrigger=ETrue;
TriggerRedraw();
}
TBool CRedrawQueue::FindOutstandingRedrawEvent(CWsWindowRedraw& aRedraw, TWsRedrawEvent& aEvent)
{
TRect rect;
if (aRedraw.GetRedrawRect(rect))
{
aEvent.SetRect(rect);
aEvent.SetHandle(aRedraw.WsWin()->ClientHandle());
CEventBase::GetData(&aEvent,sizeof(aEvent));
return ETrue;
}
return EFalse;
}
TBool CRedrawQueue::FindWindowNeedingRedrawEvent(TWsRedrawEvent& aEvent)
{
#if defined(_DEBUG)
CWsWindowRedraw* previousRedraw = NULL;
#endif
// search all screens
TInt invalidWindows = 0;
for (TInt screenNo = 0; screenNo < CWsTop::NumberOfScreens(); ++screenNo)
{
CWsRootWindow* rootWindow = CWsTop::Screen(screenNo)->RootWindow();
for (CWsWindowGroup *groupWin = rootWindow->Child(); groupWin; groupWin = groupWin->NextSibling())
{
if (groupWin->WsOwner() == iWsOwner)
{
CWsWindowRedraw* redrawWin = NULL;
// use a window tree walk that can be resumed to find windows with an invalid region
TResumableWalkWindowTreeFindInvalid wwt(&redrawWin);
groupWin->WalkWindowTree(wwt, EWalkChildren, EFalse);
while (redrawWin != NULL)
{
WS_ASSERT_DEBUG(redrawWin != previousRedraw, EWsPanicRedrawQueueError);
// (the window may not actually need the client to redraw it, e.g. a CWsBlankWindow can redraw itself)
if (FindOutstandingRedrawEvent(*redrawWin, aEvent))
{
return ETrue;
}
else
{ // continue the Tree Walk
#if defined(_DEBUG)
previousRedraw = redrawWin;
#endif
if (redrawWin->NeedsRedraw())
{ // needs to be redrawn later?
++invalidWindows;
}
redrawWin = NULL;
groupWin->WalkWindowTree(wwt, EWalkChildren, ETrue);
}
}
}
}
}
if (invalidWindows == 0)
{ // error recovery is complete
iAllocError = 0;
}
return EFalse;
}
void CRedrawQueue::GetData()
//
// If there is an outstanding redraw event in the queue, reply with it's data
//
{
CWsWindowRedraw *redraw;
TWsRedrawEvent event;
while (iRedraws->Count()>0)
{
redraw=(*iRedraws)[0].iRedraw;
if (FindOutstandingRedrawEvent(*redraw,event))
{
return;
}
TInt toDelete=0;
if (redraw!=(*iRedraws)[0].iRedraw)
{ //In low memory conditions calls to FindOutstandingRedrawEvent can cause extra entries to be added to the array
TRedraw redrawFind;
redrawFind.iRedraw=redraw;
iRedraws->Find(redrawFind,*iKeyWindow,toDelete);
}
DeleteFromQueue(toDelete);
}
if (iAllocError && FindWindowNeedingRedrawEvent(event))
{
return;
}
CEventBase::GetData(&iNullRedrawEvent,sizeof(iNullRedrawEvent));
}
TUint CRedrawQueue::RedrawPriority(CWsWindowRedraw *aRedrawWin)
{
TInt index;
TRedraw redraw;
TUint priority=0;
redraw.iRedraw=aRedrawWin;
if ((iRedraws->Find(redraw,*iKeyWindow,index))==KErrNone)
{
priority=iRedraws->At(index).iPriority;
}
return priority;
}