// Copyright (c) 1994-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:
// RWsBuffer class, handles buffering and flushing of window server commands
//
//
#include <e32std.h>
#include "../SERVER/w32cmd.h"
#include "CLIENT.H"
#include "w32comm.h"
// Global functions
GLDEF_C void Assert(TW32Assert aAssert)
{
_LIT(KW32AssertCategory,"W32 Assert");
User::Panic(KW32AssertCategory,aAssert);
}
GLDEF_C void Panic(TW32Panic aPanic)
{
_LIT(KW32PanicCategory,"W32");
User::Panic(KW32PanicCategory,aPanic);
}
// Public functions
RWsBuffer::RWsBuffer(RWsSession *aSession) : iSession(aSession), iManager(NULL),
#if defined(__AUTO_FLUSH)
iAutoFlush(ETrue),
#else
iAutoFlush(EFalse),
#endif
iBuf(NULL,0,0), iNext(NULL), iPreviousHandle(0), iBufSize(0), iMaxBufSize(EMinBufferSize),
iDirectAcessCount(0), iInvalidBitmapArray(EFalse), iWindowSizeCache(NULL)
#ifdef SYMBIAN_GRAPHICS_FIXNATIVEORIENTATION
, iWindowNativeSizeCache(NULL)
#endif // SYMBIAN_GRAPHICS_FIXNATIVEORIENTATION
{
}
TInt WsFbsDestroyCallBack(TAny* aBitmapHandle)
{
TInt* bitmapHandle=static_cast<TInt*>(aBitmapHandle);
RWsBuffer::FlushAllBuffers(aBitmapHandle ? *bitmapHandle : 0);
return(0);
}
void RWsBuffer::SetCallBack()
{
for(RWsBuffer *buffer=(RWsBuffer *)Dll::Tls();buffer;buffer=buffer->iNext)
if (buffer==this)
return; // Already linked
iNext=(RWsBuffer *)Dll::Tls();
Dll::SetTls(this);
if (iNext==NULL) // First connection so set callback
RFbsSession::GetSession()->SetCallBack(TCallBack(WsFbsDestroyCallBack,NULL));
}
void RWsBuffer::CancelCallBack()
{
RWsBuffer *buffer=(RWsBuffer *)Dll::Tls();
if (buffer==this)
{
Dll::SetTls(iNext);
if (iNext==NULL)
RFbsSession::GetSession()->ResetCallBack(); // Last connection closing so cancel the callback
}
else
{
RWsBuffer *prev;
while(buffer)
{
prev=buffer;
buffer=buffer->iNext;
if (buffer==this)
{
prev->iNext=iNext;
break;
}
}
}
}
void RWsBuffer::Close()
{
User::Free((TAny *)iBuf.Ptr());
if (iWindowSizeCache)
{
iWindowSizeCache->Close();
delete iWindowSizeCache;
iWindowSizeCache = NULL;
}
#ifdef SYMBIAN_GRAPHICS_FIXNATIVEORIENTATION
if (iWindowNativeSizeCache)
{
iWindowNativeSizeCache->Close();
delete iWindowNativeSizeCache;
iWindowNativeSizeCache = NULL;
}
#endif // SYMBIAN_GRAPHICS_FIXNATIVEORIENTATION
}
void RWsBuffer::Destroy()
{
Flush();
Close();
delete this;
}
TInt RWsBuffer::Flush(const TIpcArgs* aIpcArgs,TBool aRequestFinish)
{
iBitmapArray.Reset();
iInvalidBitmapArray=EFalse;
if (iBuf.Length()==0)
{
return(KErrNone);
}
TIpcArgs ipcArgs;
if (aIpcArgs!=NULL)
{
ipcArgs=*aIpcArgs;
// check that the caller hasn't used the first slot
ipcArgs.Set(KBufferMessageSlot,TIpcArgs::ENothing);
__ASSERT_ALWAYS(Mem::Compare(REINTERPRET_CAST(const TUint8*, &ipcArgs), sizeof(TIpcArgs), REINTERPRET_CAST(const TUint8*, aIpcArgs), sizeof(TIpcArgs))==0,Panic(EW32PanicUsingReservedIpcSlot));
}
ipcArgs.Set(KBufferMessageSlot,&iBuf);
TInt ret;
if(aRequestFinish)
ret=iSession->DoFlush(ipcArgs);
else
ret=iSession->DoSyncMsgBuf(ipcArgs);
iBuf.Zero();
iPreviousHandle=0;
return(ret);
}
void RWsBuffer::FlushAllBuffers(TInt aBitmapHandle)
{
for(RWsBuffer *buffer=(RWsBuffer *)Dll::Tls();buffer;buffer=buffer->iNext)
{
if(!aBitmapHandle || buffer->iInvalidBitmapArray || (buffer->iBitmapArray.FindInOrder(aBitmapHandle)!=KErrNotFound))
buffer->Flush();
}
}
inline void RWsBuffer::SetAndLimitMaxBufSize(TInt aMaxBufSize)
{ // apply upper & lower limits to input buffer size
if (aMaxBufSize < EMinBufferSize)
{
iMaxBufSize = EMinBufferSize;
}
else if (aMaxBufSize > EMaxBufferSize)
{
iMaxBufSize = EMaxBufferSize;
}
else
{
iMaxBufSize = aMaxBufSize;
}
}
void RWsBuffer::SetBufferSizeL(TInt aBufSize)
{
SetAndLimitMaxBufSize(aBufSize);
ReAllocBufferL(iMaxBufSize);
}
void RWsBuffer::SetMaxBufferSizeL(TInt aMaxBufSize)
{
SetAndLimitMaxBufSize(aMaxBufSize);
if (iMaxBufSize < iBufSize)
{ // shrink to new maximum
ReAllocBufferL(iMaxBufSize);
}
else
{
// initial allocation should be (at least) a quarter of the requested size
TInt minSize = Max( (iMaxBufSize + 3) >> 2, EMinBufferSize);
if (minSize > iBufSize)
{ // new or enlarged buffer
ReAllocBufferL(minSize);
}
}
__ASSERT_DEBUG((iBufSize >= EMinBufferSize) && (iBufSize <= iMaxBufSize), Assert(EW32AssertBufferLogic));
}
// Flush() buffer, try to ReAlloc, Leave if the ReAlloc fails.
void RWsBuffer::ReAllocBufferL(TInt aNewSize)
{
if (aNewSize != iBufSize)
{
Flush();
if (!ReAllocBuffer(aNewSize))
{
User::LeaveNoMemory();
}
}
}
TBool RWsBuffer::ReAllocBuffer(TInt aNewSize)
{
TUint8* ptr = const_cast<TUint8*>(iBuf.Ptr());
__ASSERT_DEBUG((iBufSize == 0) || (ptr != NULL), Assert(EW32AssertBufferLogic));
const TInt len = iBuf.Length();
TUint8* newPtr = static_cast<TUint8*>(User::ReAlloc(ptr, aNewSize));
if (newPtr != NULL)
{ // realloc was successful
iBuf.Set(newPtr, len, aNewSize);
iBufSize = aNewSize;
return ETrue;
}
return EFalse;
}
/* Expand the buffer, to allow new drawing command to fit.
Called either when trying to store additional commands to queue for Wserv, or
the trying to send a command bigger than the current buffer size.
Failure to expand the buffer is a minor problem in the first case but a big
problem in the second.
@param aRequiredSpace Size of buffer increase required.
@param aMsgSize If expanding the buffer fails then needs this value to know whether Flush() is good enough.
@return ETrue if there is enough space, EFalse if not.
*/
void RWsBuffer::GrowBuffer(TInt aRequiredSpace, TInt aMsgSize)
{
__ASSERT_DEBUG(iBufSize < iMaxBufSize, Assert(EW32AssertBufferLogic));
// maximum size will be big enough?
__ASSERT_ALWAYS(aMsgSize <= iMaxBufSize, Panic(EW32PanicDataExceedsBufferLength));
// double or quad the current size, then limit it
TInt newSize = Min((iBufSize >= aRequiredSpace) ? iBufSize << 1 : iBufSize << 2, iMaxBufSize);
if (!ReAllocBuffer(newSize))
{ // OOM error
Flush();
if (aMsgSize > iBufSize)
{ // message is too big for buffer
Panic(EW32PanicDataExceedsBufferLength);
}
}
}
TBool RWsBuffer::SetAutoFlush(TBool aState)
{
TBool old;
old=iAutoFlush;
#if defined(__AUTO_FLUSH)
if (aState)
#else
iAutoFlush=aState;
if (iAutoFlush)
#endif
Flush();
return(old);
}
TInt RWsBuffer::DoWrite(TInt aHandle, TUint aOpcode, TBool aFlush, const TIpcArgs* aIpcArgs, const TAny* aData, TInt aLength, const TAny* aData2, TInt aLength2)
{
__ASSERT_DEBUG(((TUint32) aOpcode) < 0x8000, Assert(EW32AssertIllegalOpcode));
__ASSERT_DEBUG((aLength&0x3) == 0, Assert(EW32AssertOddLengthData));
TInt xtra(0);
if (aLength2 > 0)
{
xtra = 4 - (aLength2&0x3); // Round data upto a multiple of 4
if (xtra==4)
{
xtra=0;
}
}
const TInt msgSize = aLength + aLength2 + xtra + static_cast<TInt>(sizeof(TWsCmdHeader));
TInt available = iBuf.MaxLength() - iBuf.Length();
if (msgSize > available)
{
if (iBufSize >= iMaxBufSize)
{ // buffer is maximum size already
Flush();
}
else
{ // try to grow buffer
if ( (iBuf.Length() + msgSize) > iMaxBufSize)
{ // growing alone will not make enough extra space
Flush();
available = iBufSize;
}
const TInt requiredSpace = msgSize - available;
if (requiredSpace > 0)
{
GrowBuffer(requiredSpace, msgSize);
}
}
}
TWsCmdHeader cmdHeader;
cmdHeader.iBase.iOpcode = (TInt16)aOpcode;
cmdHeader.iBase.iCmdLength = (TInt16)(aLength + aLength2 + xtra);
// For performance reasons we only pass in the handle if it is different
// from the previous command
if (aHandle == iPreviousHandle)
{
iBuf.Append((TUint8 *)&cmdHeader.iBase,sizeof(cmdHeader.iBase));
}
else
{
iPreviousHandle = aHandle;
cmdHeader.iBase.iOpcode|=EWsOpcodeHandle;
cmdHeader.iDestHandle = aHandle;
iBuf.Append((TUint8 *)&cmdHeader,sizeof(cmdHeader));
}
if (aLength)
{
iBuf.Append((TUint8 *)aData, aLength);
}
if (aLength2 > 0)
{
iBuf.Append((TUint8 *)aData2, aLength2);
iBuf.AppendFill(0,xtra);
}
if (aFlush)
{
return Flush(aIpcArgs);
}
return KErrNone;
}
void RWsBuffer::Write(TInt handle,TUint opcode)
{
DoWrite(handle, opcode, iAutoFlush, NULL);
}
void RWsBuffer::Write(TInt handle,TUint opcode,const TAny *pData, TInt length)
{
DoWrite(handle, opcode, iAutoFlush, NULL, pData, length);
}
void RWsBuffer::Write(TInt handle,TUint opcode,const TAny *pData, TInt length,const TAny *pData2, TInt length2)
{
DoWrite(handle, opcode, iAutoFlush, NULL, pData, length, pData2, length2);
}
TInt RWsBuffer::WriteReply(TInt handle,TUint opcode,const TIpcArgs* aIpcArgs)
{
return DoWrite(handle, opcode, ETrue, aIpcArgs);
}
TInt RWsBuffer::WriteReply(TInt handle,TUint opcode,const TAny *pData, TInt length,const TIpcArgs* aIpcArgs)
{
return DoWrite(handle, opcode, ETrue, aIpcArgs, pData, length);
}
TInt RWsBuffer::WriteReply(TInt handle,TUint opcode,const TAny *pData,TInt length,const TAny *pData2,TInt length2,const TIpcArgs* aIpcArgs)
{
return DoWrite(handle, opcode, ETrue, aIpcArgs, pData, length, pData2, length2);
}
TInt RWsBuffer::WriteReplyP(TInt aHandle, TUint aOpcode, const TWriteDescriptorType& aReplyBuffer)
{
TIpcArgs ipcArgs;
aReplyBuffer.SetDescriptorOnIpcArgs(ipcArgs);
return DoWrite(aHandle, aOpcode, ETrue, &ipcArgs);
}
TInt RWsBuffer::WriteReplyP(TInt aHandle,TUint aOpcode,const TAny *aData,TInt aLength,const TWriteDescriptorType& aReplyBuffer)
{
TIpcArgs ipcArgs;
aReplyBuffer.SetDescriptorOnIpcArgs(ipcArgs);
return DoWrite(aHandle, aOpcode, ETrue, &ipcArgs, aData, aLength);
}
TInt RWsBuffer::WriteReplyP(TInt aHandle,TUint aOpcode,const TAny *aData1,TInt aLengthData1,const TAny *aData2,TInt aLengthData2,const TWriteDescriptorType& aReplyBuffer)
{
TIpcArgs ipcArgs;
aReplyBuffer.SetDescriptorOnIpcArgs(ipcArgs);
return DoWrite(aHandle, aOpcode, ETrue, &ipcArgs, aData1, aLengthData1, aData2, aLengthData2);
}
TInt RWsBuffer::WriteReplyWs(TUint opcode)
//
// Do a WriteReply using the sessions handle
//
{
return(iSession->WriteReply(opcode));
}
TInt RWsBuffer::WriteReplyWs(const TAny *pData, TInt aLength, TUint aOpcode)
//
// Do a WriteReply using the sessions handle
//
{
return(iSession->WriteReply(pData,aLength,aOpcode));
}
TInt RWsBuffer::WriteReplyWs(const TAny *pData, TInt aLength, const TAny *pData2, TInt aLength2, TUint aOpcode)
//
// Do a WriteReply using the sessions handle
//
{
return(iSession->WriteReply(pData,aLength,pData2,aLength2,aOpcode));
}
TInt RWsBuffer::WriteReplyByProvidingRemoteReadAccess(TInt aHandle,TUint aOpcode,const TAny *aData, TInt aLength,const TReadDescriptorType& aRemoteReadBuffer)
{
TIpcArgs ipcArgs;
aRemoteReadBuffer.SetDescriptorOnIpcArgs(ipcArgs);
return DoWrite(aHandle, aOpcode, ETrue, &ipcArgs, aData, aLength);
}
void RWsBuffer::AddToBitmapArray(TInt aBitmapHandle)
{
if(aBitmapHandle && !iInvalidBitmapArray)
{
if(iBitmapArray.InsertInOrder(aBitmapHandle)==KErrNoMemory)
iInvalidBitmapArray=ETrue;
}
}
void RWsBuffer::SetWsGraphicManager(CWsGraphic::CManager* aManager)
{
__ASSERT_DEBUG(!WsGraphicManager(),Panic(EW32PanicGraphicInternal));
iManager = aManager;
}
CWsGraphic::CManager* RWsBuffer::WsGraphicManager()
{
for(RWsBuffer *buffer=(RWsBuffer *)Dll::Tls();buffer;buffer=buffer->iNext)
if (buffer->iManager)
return buffer->iManager;
return NULL; // does not yet exist
}
void RWsBuffer::AsyncRequest(TInt aHandle, TUint aOpcode, TRequestStatus& aStatus)
{
aStatus = KRequestPending;
__ASSERT_DEBUG((aOpcode&EWservMessAsynchronousService)==0, Assert(EW32AssertIllegalOpcode));
Flush();
const TInt function = EWservMessAsynchronousService | aOpcode;
TIpcArgs ipcArgs(aHandle);
iSession->SendReceive(function, ipcArgs, aStatus);
}
void RWsBuffer::EnableWindowSizeCacheL()
{
if (iWindowSizeCache == NULL)
{
iWindowSizeCache = new (ELeave) RHashMap<TInt, TWindowSizeCacheEntry>();
}
}
#ifdef SYMBIAN_GRAPHICS_FIXNATIVEORIENTATION
void RWsBuffer::EnableWindowNativeSizeCacheL()
{
if (iWindowNativeSizeCache == NULL)
{
iWindowNativeSizeCache = new (ELeave) RHashMap<TInt, TWindowSizeCacheEntry>();
}
}
#endif // SYMBIAN_GRAPHICS_FIXNATIVEORIENTATION