// Copyright (c) 1995-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:
// Pointer functions
// 
//

#include <e32std.h>
#include <e32hal.h>
#include "W32CLICK.H"
#include "pointer.h"
#include "rootwin.h"
#include "windowgroup.h"
#include "KEYCLICK.H"
#include "ScrDev.H"
#include "EVENT.H"
#include "panics.h"
#include "wstop.h"
#include "inifile.h"
#include "graphics/pointereventdata.h"

TPoint WsPointer::iCurrentPos;
TBool WsPointer::iPointerDown;
const CWsWindow *WsPointer::iCurrentWindow;
const CWsWindow *WsPointer::iActualWinPointerIsOver;
const CWsWindow *WsPointer::iGrabWindow;
const CWsWindow *WsPointer::iPrevClickWindow;
TPoint WsPointer::iPrevClickPos;
TTime WsPointer::iPrevClickTime;
TPointerEvent::TType WsPointer::iPrevClickEventType;
TTimeIntervalMicroSeconds32 WsPointer::iDoubleClickMaxInterval;
TInt WsPointer::iDoubleClickMaxDistance;
CWsPointerCursor *WsPointer::iCursorSprite;
CWsPointerTimer *WsPointer::iRepeatTimer=NULL;
TPointerCursorMode WsPointer::iPointerCursorMode=EPointerCursorNormal;
CWsWindow *WsPointer::iRepeatWindow=NULL;
TRect WsPointer::iRepeatRect;
TXYInputType WsPointer::iXyInputType;
TUint WsPointer::iLastUnmatchedDown1;
TUint WsPointer::iLastUnmatchedDown2;
TUint WsPointer::iLastUnmatchedDown3;
TBool WsPointer::iTimerQueued;
TBool WsPointer::iUpdateRequired;
CPeriodic *WsPointer::iPeriodicTimer;
CWsRootWindow* WsPointer::iRootWindow;

TBool CWsPointerBuffer::iSignalled=EFalse;
CWsPointerBuffer *CWsPointerBuffer::iCurrentBuffer=NULL;
CCirBuf<TPoint> *CWsPointerBuffer::iPointerBuffer=NULL;
TSglQue<CWsPointerBuffer> CWsPointerBuffer::iList(_FOFF(CWsPointerBuffer,iQue));

void WsPointer::InitStaticsL()
	{
	iRepeatTimer=new(ELeave) CWsPointerTimer();
	iRepeatTimer->ConstructL();
	TMachineInfoV1Buf machineInfo;
	UserHal::MachineInfo(machineInfo);
	iXyInputType=machineInfo().iXYInputType;

	iRootWindow = CWsTop::Screen()->RootWindow();

	iCurrentWindow=MovesAvailable() ? iRootWindow : NULL;
	iPeriodicTimer=CPeriodic::NewL(EPointerCursorPriority);
	}

void WsPointer::DeleteStatics()
	{
	SetPointerCursorMode(EPointerCursorNone);
	UpdatePointerCursor();
	delete iRepeatTimer;
	delete iPeriodicTimer;
	}

void WsPointer::SetPointerCursorPos(TPoint aPos)
	{
	RestrictPos(aPos,EFalse);
	iCurrentPos=aPos;
	ReLogCurrentWindow();
	UpdatePointerCursor();
	}

void WsPointer::SendEnterExitEvent(TEventCode aType)
	{
	if (iCurrentWindow
		&& !(iCurrentWindow->PointerFilter()&EPointerFilterEnterExit)
		&& !iCurrentWindow->ShutDownInProgress())
		iCurrentWindow->QueueEvent(aType);
	}

void WsPointer::SetCurrentWindow(const CWsWindow *aWin)
	{
	if (aWin!=iCurrentWindow)
		{
		SendEnterExitEvent(EEventPointerExit);
		iCurrentWindow=aWin;
		SendEnterExitEvent(EEventPointerEnter);
		}
	}

void WsPointer::ReLogCurrentWindow(TPoint &aPos, TPoint &aParentPos, const CWsWindowGroup *aForceInGroup)
//
// Relog the current pointer window, can be used to set a new iCurrentPos or when the window layout has changed.
// Sets iCurrentPos to aPos and modifys aPos to the relative position within the new current window
//
	{
	if (iRootWindow)
		SetCurrentWindow(iRootWindow->PointerWindow(iCurrentPos,&aPos,&aParentPos,iGrabWindow,iActualWinPointerIsOver
																										,aForceInGroup));
	else
		iCurrentWindow=NULL;
	}

void WsPointer::ReLogCurrentWindow()
//
// Relog the current pointer window when the window layout has changed.
//
	{
	if (iCurrentWindow)	// NULL iCurrentWindow means pointer is up so don't relog it
		SetCurrentWindow(iRootWindow->PointerWindow(iCurrentPos,NULL,NULL,iGrabWindow,iActualWinPointerIsOver,NULL));
	}

void WsPointer::ReLogWindow(const CWsWindow *aWin)
//
// Called when a window has changed it's filter state, will trigger a 'Enter' message if the window
// is the current window
//
	{
	if (aWin==iCurrentWindow)
		SendEnterExitEvent(EEventPointerEnter);
	}

void WsPointer::UnmatchedDownPurged(TPointerEvent::TType aPointerType, TUint aHandle)
	{
	if (aPointerType==TPointerEvent::EButton1Up && iGrabWindow && iGrabWindow->ClientHandle()==aHandle)
		{
		if (iGrabWindow && iGrabWindow->ClientHandle()==aHandle)
			iGrabWindow=NULL;
		if (iRepeatWindow && iRepeatWindow->ClientHandle()==aHandle)
			CancelPointerRepeatEventRequest();
		}
	switch(aPointerType)
		{
		case TPointerEvent::EButton1Up:
			iLastUnmatchedDown1=aHandle;
			break;
		case TPointerEvent::EButton2Up:
			iLastUnmatchedDown2=aHandle;
			break;
		case TPointerEvent::EButton3Up:
			iLastUnmatchedDown3=aHandle;
			break;
		default:;
		}
	}

void WsPointer::WindowDisconected(const CWsWindow *deletedWindow)
	{
	if (iRepeatWindow==deletedWindow)
		CancelPointerRepeatEventRequest();
	if (iGrabWindow==deletedWindow)
		iGrabWindow=NULL;
	if (iCurrentWindow==deletedWindow)
		{
		ReLogCurrentWindow();
		UpdatePointerCursor();
		}
	}

TEventQueueWalkRet RemovePointerUpFunc(TAny *aHandle, TWsEvent *aEvent)
//
// Callback function pointer for up event remove event queue walk
//
	{
	if (aEvent->Type()==EEventPointer && aEvent->Pointer()->iType==TPointerEvent::EButton1Up && (*(TUint *)aHandle)==aEvent->Handle())
		return(EEventQueueWalkDeleteEvent);
	return(EEventQueueWalkOk);
	}

void WsPointer::ClaimGrab(const CWsWindow *aWindow,TBool aSendUpEvent)
//
// If the pointer is down claim grab in aWindow as though the down event had gone to this window
// also send an up event to the window (if any) that would receive it the pointer was released now
//
	{
	TInt modState=TWindowServerEvent::GetModifierState();
	TWsEvent event;
	TPointerEvent& pointerEvent=*event.Pointer();
	pointerEvent.iModifiers=modState;
	pointerEvent.iPosition=iCurrentPos;
	if (iPointerDown)
		{
		if (iCurrentWindow!=aWindow)
			{
			if (aSendUpEvent)
				ProcessEvent(TPointerEvent::EButton1Up,iCurrentPos,modState,NULL,EFalse);
			else // If up event already in queue purge it
				{
				TUint handle=iCurrentWindow->ClientHandle();
				iCurrentWindow->EventQueue()->WalkEventQueue(&RemovePointerUpFunc,&handle);
				}
			iPointerDown=ETrue;
			if (aWindow->HasPointerGrab())
				iGrabWindow=aWindow;
			ReLogCurrentWindow(pointerEvent.iPosition,pointerEvent.iParentPosition,NULL);
			pointerEvent.iType=TPointerEvent::EDrag;
			ProcessPointerEvent(event);
			}
		}
	else
		{
		const CWsWindow *current=iCurrentWindow;
		iCurrentWindow=aWindow;
		WS_ASSERT_DEBUG(iGrabWindow==NULL, EWsPanicPointerClaimGrab);
		iGrabWindow=aWindow;	// Force the up event to be sent to aWindow
		ReLogCurrentWindow(pointerEvent.iPosition,pointerEvent.iParentPosition,NULL);
		pointerEvent.iType=TPointerEvent::EButton1Up;
		ProcessPointerEvent(event);
		iGrabWindow=NULL;
		iCurrentWindow=current;
		}
	}

TBool WsPointer::CheckDownEventPurged(TPointerEvent::TType aType)
	{
	switch(aType)
		{
		TUint lastUnmatchedDown;
		case TPointerEvent::EButton1Up:
			lastUnmatchedDown=iLastUnmatchedDown1;
			iLastUnmatchedDown1=0;
			goto lastUnmatchedDownCheck;
		case TPointerEvent::EButton2Up:
			lastUnmatchedDown=iLastUnmatchedDown2;
			iLastUnmatchedDown2=0;
			goto lastUnmatchedDownCheck;
		case TPointerEvent::EButton3Up:
			lastUnmatchedDown=iLastUnmatchedDown3;
			iLastUnmatchedDown3=0;
lastUnmatchedDownCheck:
		if (lastUnmatchedDown==iCurrentWindow->ClientHandle())
			return ETrue; // Don't deliver the event as we've already thrown away the down
		default:		//Should never get to default
			break;
		}
	return EFalse;
	}

TBool WsPointer::QueuePointerEvent(const CWsWindow *aWindow, TWsEvent &aEvent)
	{
	CWsClient *client=aWindow->WsOwner();
	if (client)
		{
		CEventQueue *queue=aWindow->EventQueue();
		aEvent.SetHandle(aWindow->ClientHandle());
		if (aEvent.Handle()!=0)
			{
			if (aEvent.Pointer()->iType==TPointerEvent::EMove || aEvent.Pointer()->iType==TPointerEvent::EDrag)
				{
				queue->Wait();
				const TWsEvent *prev=queue->PeekLastEvent();
				if (prev!=NULL && prev->Type()==EEventPointer && prev->Handle()==aEvent.Handle() && prev->Pointer()->iType==aEvent.Pointer()->iType)
					{
					queue->UpdateLastEvent(aEvent);
					return EFalse;
					}
				queue->Signal();
				}
			TWservEventPriorities priority=EEventPriorityLow;
			switch (aEvent.Pointer()->iType)
				{
				case TPointerEvent::EButton1Up:
				case TPointerEvent::EButton2Up:
				case TPointerEvent::EButton3Up:
					if (CheckDownEventPurged(aEvent.Pointer()->iType))
						return ETrue;
					if (queue->CheckRoom())
						{
						if (CheckDownEventPurged(aEvent.Pointer()->iType))
							return ETrue;
						} 
					/*Fall Through if an event was not purged*/
				case TPointerEvent::EButton1Down:
				case TPointerEvent::EButton2Down:
				case TPointerEvent::EButton3Down:
					priority=EEventPriorityHigh;
					break;
				default:;
				}
			queue->QueueEvent(aEvent,priority);
			}
		}
	return EFalse;
	}

void WsPointer::ProcessForegroundCheck()
	{
	CWsWindowGroup *group=iCurrentWindow->WinGroup();
	if (group->iFlags&CWsWindowGroup::EGroupFlagAutoForeground)
		group->SetOrdinalPosition(0);
	}

void WsPointer::ProcessPointerEvent(TWsEvent& aEvent)
	{
	if (iCurrentWindow && iCurrentWindow!=iRootWindow)
		{
		aEvent.SetType(EEventPointer);
		aEvent.SetTimeNow();
		TPointerEvent::TType type=aEvent.Pointer()->iType;
		switch(type)
			{
			//TUint lastUnmatchedDown;
			case TPointerEvent::EButton1Down:
				ProcessForegroundCheck(); 
				/*Fall Through*/
			case TPointerEvent::EButton2Down:
			case TPointerEvent::EButton3Down:
				{
				TPoint& pos=aEvent.Pointer()->iPosition;
				if (iCurrentWindow==iPrevClickWindow &&
					type==iPrevClickEventType &&
					(Abs(pos.iX-iPrevClickPos.iX)+Abs(pos.iY-iPrevClickPos.iY))<iDoubleClickMaxDistance &&
					aEvent.Time()<(iPrevClickTime+iDoubleClickMaxInterval))
					{
					aEvent.Pointer()->iModifiers|=EModifierDoubleClick;
					iPrevClickWindow=NULL;	// Set to NULL to block a double double click
					}
				else
					iPrevClickWindow=iCurrentWindow;
				iPrevClickEventType=type;
				iPrevClickPos=pos;
				iPrevClickTime=aEvent.Time();
				}
				break;
			default:
				break;
			}
		if (iRepeatWindow)
			{
			if (PointerEventRepeatCheck(&aEvent, iCurrentWindow->ClientHandle()))
				return;
			CancelPointerRepeatEventRequest();
			}
		if (QueuePointerEvent(iCurrentWindow, aEvent))
			return;
		if (iCurrentWindow->DragDropCapture())
			{
			aEvent.SetType(EEventDragDrop);
			QueuePointerEvent(iActualWinPointerIsOver, aEvent);
			}
		}
	}

TInt PointerTimerCallBack(TAny *)
	{
	WsPointer::TimerExpired();
	return(KErrNone);
	}

void WsPointer::RestrictPos(TPoint& aPos,TBool aWithinDrawableArea/*=ETrue*/)
	{
	CScreen* screen = iRootWindow->Screen();
	WS_ASSERT_DEBUG(screen->IsValidScreenSizeMode(screen->ScreenSizeMode()), EWsPanicInvalidScreenSizeMode);
#if defined(__WINS__)
	if (aWithinDrawableArea)
		{
		if (!DeltaMouse() && !TRect(screen->DrawableArea()).Contains(aPos))
			{
			return;			//Not in the drawable area so user may be trying to click on facia button.
			}
		}
#endif
	TRect validRect=screen->GetPointerCursorArea(screen->ScreenSizeMode());
	if (aPos.iX<validRect.iTl.iX)
		aPos.iX=validRect.iTl.iX;
	else if (aPos.iX>=validRect.iBr.iX)
		aPos.iX=validRect.iBr.iX-1;
	if (aPos.iY<validRect.iTl.iY)
		aPos.iY=validRect.iTl.iY;
	else if (aPos.iY>=validRect.iBr.iY)
		aPos.iY=validRect.iBr.iY-1;
	}

#if defined(__WINS__)
TBool WsPointer::PreProcessEvent(TRawEvent &aRawEvent,TBool aFromHardware/*=EFlase*/)
#else
TBool WsPointer::PreProcessEvent(TRawEvent &aRawEvent)
#endif
	{
#if defined(__WINS__)
	WS_ASSERT_DEBUG(TRawEvent::EPointerMove==1, EWsPanicRawEventsTypeChanged);
	WS_ASSERT_DEBUG(TRawEvent::EPointerMove+1==TRawEvent::EPointerSwitchOn, EWsPanicRawEventsTypeChanged);
	WS_ASSERT_DEBUG(TRawEvent::EPointerSwitchOn+8==TRawEvent::EButton1Down, EWsPanicRawEventsTypeChanged);
	WS_ASSERT_DEBUG(TRawEvent::EButton1Down+5==TRawEvent::EButton3Up, EWsPanicRawEventsTypeChanged);
#endif
	TRawEvent::TType type=aRawEvent.Type();
	if (type<TRawEvent::EPointerMove || (type>TRawEvent::EPointerSwitchOn && type<TRawEvent::EButton1Down)
																						|| type>TRawEvent::EButton3Up)
		return ETrue;
	if (!XyInput())
		return EFalse;
	TPoint xy=aRawEvent.Pos();
	if (DeltaMouse())
		{
	#if defined(__WINS__)
		if (aFromHardware)
			return EFalse;
	#endif
		if (type==TRawEvent::EPointerMove)
			{
			xy+=iCurrentPos;
			RestrictPos(xy);
			}
		else
			xy=iCurrentPos;
		}
	else
		{
		CScreen* screen=iRootWindow->Screen();
	#if !defined(__WINS__)
		TSize screenSize=screen->ScreenDevice()->SizeInPixels()-TSize(1,1);             //This is in the current rotation
		switch (screen->Orientation())
			{
			case CFbsBitGc::EGraphicsOrientationRotated90:
				xy.SetXY(xy.iY,screenSize.iHeight-xy.iX);
				break;
			case CFbsBitGc::EGraphicsOrientationRotated180:
				xy=-(xy-screenSize);
				break;
			case CFbsBitGc::EGraphicsOrientationRotated270:
				xy.SetXY(screenSize.iWidth-xy.iY,xy.iX);
				break;
			default:;		//To stop warning
			}
	#endif
		// Move the raw event position by shifting it by Origin and scale
		xy=screen->PhysicalToLogical(xy);
		RestrictPos(xy);
		}
	aRawEvent.Set(type,xy.iX,xy.iY);
	return ETrue;
	}

void WsPointer::ProcessEvent(TPointerEvent::TType aType, const TPoint &aPos, TUint aModifiers
																				,const CWsWindowGroup *aForceInGroup,TBool aNatural)
	{
	iCurrentPos=aPos;
	if (aType==TPointerEvent::EMove && !MovesAvailable() && !iPointerDown)
		return;
	TPoint pos(iCurrentPos);	// We need a non-const TPoint for 'ReLogCurrentWindow'
	TPoint parPos;
	ReLogCurrentWindow(pos,parPos,aForceInGroup);
	TWsEvent event;
	TPointerEvent& pointerEvent=*event.Pointer();
	pointerEvent.iModifiers=aModifiers;
	pointerEvent.iPosition=pos;
	pointerEvent.iParentPosition=parPos;
	switch(aType)
		{
		case TPointerEvent::EButton1Down:
			iPointerDown=ETrue;
			if (iGrabWindow==NULL && iCurrentWindow->HasPointerGrab())
				iGrabWindow=iCurrentWindow;
			if (!MovesAvailable() && iCurrentWindow->PointerFilter()&EPointerGenerateSimulatedMove)
				{
				pointerEvent.iType=TPointerEvent::EMove;
				ProcessEvent(event);
				}
			break;
		case TPointerEvent::EButton1Up:
			iPointerDown=EFalse;
			iGrabWindow=NULL;
			break;
		case TPointerEvent::EMove:
			if (iPointerDown)
				aType=TPointerEvent::EDrag;
			break;
		default:;
		}
	pointerEvent.iType=aType;
	if (aNatural && CClick::IsHandler())
		{
		CClick::PointerEvent(iCurrentPos,pointerEvent);
		TPointerEventData params;
		params.iVersion=0;
		params.iCurrentPos=iCurrentPos;
		params.iPointerEvent.iType 		= pointerEvent.iType;
		params.iPointerEvent.iModifiers = pointerEvent.iModifiers;
		params.iPointerEvent.iPosition  = pointerEvent.iPosition;
		params.iPointerEvent.iParentPosition = pointerEvent.iParentPosition;
		params.iClientHandle=iCurrentWindow->ClientHandle();
		params.iWindowOrigin=iCurrentWindow->Origin();
		CWsWindowGroup* groupWin=iCurrentWindow->WinGroup();
		params.iWindowGroupId=groupWin ? groupWin->Identifier() : 0;
		params.iSource=TPointerEventData::EUnspecified;
		CClick::OtherEvent(EEventPointer,&params);
		}
	ProcessEvent(event);
	}

void WsPointer::ProcessEvent(TWsEvent& aEvent)
	{
	TUint filter=iCurrentWindow->PointerFilter();
	TPointerEvent::TType type=aEvent.Pointer()->iType;
	if ((type!=TPointerEvent::EMove || !(filter&EPointerFilterMove)) &&
		 (type!=TPointerEvent::EDrag || !(filter&EPointerFilterDrag)))
		{
		TPoint pos=aEvent.Pointer()->iPosition;
		if ((type==TPointerEvent::EMove || type==TPointerEvent::EDrag) && iCurrentWindow->UsingPointerBuffer())
			CWsPointerBuffer::PointerEvent((CWsClientWindow *)iCurrentWindow,pos);
		else if (!WsKeyboardEmulator::PointerEvent(type,pos,iCurrentWindow->PointerKeyList()))
			ProcessPointerEvent(aEvent);
		}
	if (!MovesAvailable() && (type==TPointerEvent::EButton1Up || type==TPointerEvent::ESwitchOn))
		iCurrentWindow=NULL;
	PointerCursorUpdateCheck();
	}

void WsPointer::TimerExpired()
	{
	WS_ASSERT_DEBUG(iTimerQueued, EWsPanicPointerTimer);
	if (iUpdateRequired)
		{
		UpdatePointerCursor();
		iUpdateRequired=EFalse;
		}
	else
		{
		iTimerQueued=EFalse;
		iPeriodicTimer->Cancel();
		}
	}

void WsPointer::GetDoubleClickSettings(TTimeIntervalMicroSeconds32 &aTime, TInt &aDistance)
	{
	aTime=iDoubleClickMaxInterval;
	aDistance=iDoubleClickMaxDistance;
	}

void WsPointer::SetDoubleClick(const TTimeIntervalMicroSeconds32 &aTime, TInt aDistance)
	{
	iDoubleClickMaxInterval=aTime;
	iDoubleClickMaxDistance=aDistance;
	}

void WsPointer::PointerCursorUpdateCheck()
	{
	CWsPointerCursor *sprite=CalculatePointerCursor();
	if (iCursorSprite || sprite)	// If there either was, or is a pointer cursor we need an update
		{
		if (!iTimerQueued)
			{
			UpdatePointerCursorTo(sprite);
			iPeriodicTimer->Start(TTimeIntervalMicroSeconds32(EPointerUpdateGapInMicroSeconds),
									TTimeIntervalMicroSeconds32(EPointerUpdateGapInMicroSeconds),
									  TCallBack(PointerTimerCallBack,NULL));
			iTimerQueued=ETrue;
			}
		else
			iUpdateRequired=ETrue;
		}
	}

void WsPointer::UpdatePointerCursor()
	{
//__PROFILE_START(3);
	CWsPointerCursor *sprite=CalculatePointerCursor();
	UpdatePointerCursorTo(sprite);
//__PROFILE_END(3);
	}

void WsPointer::UpdatePointerCursorTo(CWsPointerCursor* aNewCursor)
	{
	CScreen* screen=NULL;
	if (iCursorSprite!=aNewCursor)
		{
		if (iCursorSprite)
			{
			iCursorSprite->Deactivate();
			screen=iCursorSprite->Screen();		//This will need changing ##
			}
		iCursorSprite=aNewCursor;
		if (iCursorSprite)
			{
			iCursorSprite->SetPos(iCurrentPos);
			iCursorSprite->Activate();
			}
		goto Update;
		}
	else if (iCursorSprite)
		{
		iCursorSprite->SetPos(iCurrentPos);
	Update:
		if (!screen)
			screen=iCursorSprite->Screen();
		screen->Update();
		}
	}

CWsPointerCursor* WsPointer::CalculatePointerCursor()
	{
	CWsPointerCursor *sprite=NULL;
	if (iCurrentWindow && (iPointerCursorMode==EPointerCursorNormal || iPointerCursorMode==EPointerCursorWindow))
		{
		const CWsWindowBase* window=iCurrentWindow;
		do	{
			sprite=window->PointerCursor();
			if (window->WinType()!=EWinTypeClient)
				break;
			window=window->BaseParent();
			} while (!sprite);
		}
	if (!sprite && iCurrentWindow && (iPointerCursorMode==EPointerCursorFixed || iPointerCursorMode==EPointerCursorNormal))
		sprite=CWsClient::DefaultSystemPointerCursor();
	return sprite;
	}

TEventQueueWalkRet PointerRepeatPurgeFunc(TAny *, TWsEvent *aEvent)
//
// Callback function for event queue walk
//
	{
	return(WsPointer::PointerRepeatPurgeCheck(aEvent));
	}

TBool WsPointer::PointerEventRepeatCheck(const TWsEvent *aEvent, TUint32 aHandle)
//
// Return ETrue if this pointer event is consumed by the pointer repeat
//
	{
	TPointerEvent *pntEvent=aEvent->Pointer();
	if (aHandle==iRepeatWindow->ClientHandle() && 
		 (pntEvent->iType==TPointerEvent::EDrag || pntEvent->iType==TPointerEvent::EMove) &&
		  iRepeatRect.Contains(pntEvent->iPosition))
		return(ETrue);
	return(EFalse);
	}

TEventQueueWalkRet WsPointer::PointerRepeatPurgeCheck(const TWsEvent *aEvent)
	{
	if (iRepeatWindow && aEvent->Type()==EEventPointer)
		{
		if (PointerEventRepeatCheck(aEvent,aEvent->Handle()))
			return(EEventQueueWalkDeleteEvent);	// Purge the event as it is a move/drag within the repeat rect
		CancelPointerRepeatEventRequest();
		}
	return(EEventQueueWalkOk);
	}

void WsPointer::RequestPointerRepeatEvent(CWsWindow *aWindow, TTimeIntervalMicroSeconds32 aTime,const TRect &aRect)
	{
	CancelPointerRepeatEventRequest();
	iRepeatWindow=aWindow;
	iRepeatRect=aRect;
	iRepeatTimer->After(aTime);
	aWindow->EventQueue()->WalkEventQueue(&PointerRepeatPurgeFunc,NULL);
	if (iRepeatWindow && !iRepeatRect.Contains(iCurrentPos-iRepeatWindow->Origin()))
		CancelPointerRepeatEventRequest();
	}

void WsPointer::CancelPointerRepeatEventRequest()
	{
	if (iRepeatWindow)
		{
		iRepeatWindow=NULL;
		iRepeatTimer->Cancel();
		}
	}

void WsPointer::RepeatTimerCompleted()
	{
	TWsEvent event;
	event.SetType(EEventPointer);
	event.SetTimeNow();
	event.Pointer()->iModifiers=TWindowServerEvent::GetModifierState();
	event.Pointer()->iPosition=iCurrentPos-iRepeatWindow->Origin();
	event.Pointer()->iParentPosition=iCurrentPos-iRepeatWindow->BaseParent()->Origin();
	event.Pointer()->iType=TPointerEvent::EButtonRepeat;
	QueuePointerEvent(iRepeatWindow, event);
	iRepeatWindow=NULL;
	}

#if defined(__WINS__)
void WsPointer::SetXyInputType(TXYInputType aXyInputType)
	{
	if (iXyInputType>EXYInputPointer && aXyInputType<EXYInputMouse && !iPointerDown)
		{
		iCurrentWindow=NULL;
		UpdatePointerCursor();
		}
	else if (iXyInputType<EXYInputMouse && aXyInputType>EXYInputPointer && !iPointerDown)
		{
		TPoint pos(iCurrentPos);
		TPoint parPos;
		ReLogCurrentWindow(pos,parPos,NULL);
		UpdatePointerCursor();
		}
	iXyInputType=aXyInputType;
	}
#endif

//

CWsPointerTimer::CWsPointerTimer() : CTimer(EPointerRepeatPriority)
	{}

void CWsPointerTimer::ConstructL()
	{
	CTimer::ConstructL();
	CActiveScheduler::Add(this);
	}

void CWsPointerTimer::RunL()
	{
	User::ResetInactivityTime();
	WS_ASSERT_DEBUG(iStatus.Int()==KErrNone, EWsPanicPointerRepeatTimerStatus);
	WsPointer::RepeatTimerCompleted();
	}

//

CWsPointerBuffer::~CWsPointerBuffer()
	{
	if (this == iCurrentBuffer)
		{
		// We're about to be destroyed - don't want to be pointed at any more.
		iCurrentBuffer = NULL;
		}
	iList.Remove(*this);
	}

void CWsPointerBuffer::ConnectL(CWsClientWindow *aWindow, TInt aMaxPoints, TUint aFlags)
	{
	CWsPointerBuffer *pb=NULL;
	for(TSglQueIter<CWsPointerBuffer> iter(iList);(pb=iter++)!=NULL;)
		if (pb->iWindow==aWindow)
			User::Leave(KErrInUse);
	CWsPointerBuffer *pbuf=new(ELeave) CWsPointerBuffer;
	pbuf->iWindow=aWindow;
	pbuf->iMaxPoints=aMaxPoints;
	pbuf->iFlags=aFlags;
	iList.AddFirst(*pbuf);
	CleanupStack::PushL(pbuf);
	AdjustMaxSizeL();
	CleanupStack::Pop();
	}

void CWsPointerBuffer::Disconnect(CWsClientWindow *aWindow)
	{
	CWsPointerBuffer *pb=NULL;
	for(TSglQueIter<CWsPointerBuffer> iter(iList);(pb=iter++)!=NULL;)
		{
		if (pb->iWindow==aWindow)
			{
			delete pb; // Note that the destructor also sets iCurrentBuffer to NULL if it is pointing at pb
			TRAP_IGNORE(AdjustMaxSizeL());	// Shouldn't fail, but doesn't matter if it does as we simply have a larger buffer than needed
			break; // from for loop
			}
		}
	}

void CWsPointerBuffer::Reset()
	{
	iSignalled=EFalse;
	iPointerBuffer->Reset();
	}

void CWsPointerBuffer::SignalBufferReady()
	{
	if (!iSignalled)
		if (iCurrentBuffer && iCurrentBuffer->iWindow->QueueEvent(EEventPointerBufferReady))
			iSignalled=ETrue;
	}

void CWsPointerBuffer::PointerEvent(CWsClientWindow *aWindow,const TPoint &aPoint)
	{
	if (iCurrentBuffer==NULL || aWindow!=iCurrentBuffer->iWindow)
		{
		Reset();
		CWsPointerBuffer *pb=NULL;
		for(TSglQueIter<CWsPointerBuffer> iter(iList);(pb=iter++)!=NULL;)
			{
			if (pb->iWindow==aWindow)
				{
				iCurrentBuffer=pb;
				break; // from for loop
				}
			}
		}
	iPointerBuffer->Add(&aPoint);
	SignalBufferReady();
	}

void CWsPointerBuffer::RetrievePointerMoveBuffer(CWsClientWindow *aWindow,TInt aMaxPoints)
	{
	enum {KPointerMoveBufferSize=32};	// Puts 256 bytes on the stack
	if (iCurrentBuffer && aWindow==iCurrentBuffer->iWindow)
		{
		iSignalled=EFalse;
		TInt max=Min(aMaxPoints,iPointerBuffer->Count());
		TInt buflen=0;
		aWindow->WsOwner()->SetReply(max);
		TPoint point;
		TBuf8<KPointerMoveBufferSize*sizeof(TPoint)> pnts;
		for(TInt index=0;index<max;index++)
			{
			iPointerBuffer->Remove(&point);
			pnts.Append((TUint8 *)&point,sizeof(TPoint));
			buflen++;
			if (buflen==KPointerMoveBufferSize)
				{
				CWsClient::ReplyBuf(pnts);
				pnts.Zero();
				buflen=0;
				}
			}
		if (buflen>0)
			CWsClient::ReplyBuf(pnts);
		if (iPointerBuffer->Count())
			SignalBufferReady();
		}
	}

void CWsPointerBuffer::DiscardPointerMoveBuffer(TUint aHandle)
	{
	if (iCurrentBuffer && aHandle==iCurrentBuffer->iWindow->ClientHandle())
		Reset();
	}

void CWsPointerBuffer::DiscardPointerMoveBuffer(CWsClientWindow *aWindow)
	{
	if (iCurrentBuffer && aWindow==iCurrentBuffer->iWindow)
		Reset();
	}

void CWsPointerBuffer::AdjustMaxSizeL()
	{
	TInt max=0;
	CWsPointerBuffer *pb=NULL;
	for(TSglQueIter<CWsPointerBuffer> iter(iList);(pb=iter++)!=NULL;)
		if (pb->iMaxPoints>max)
			max=pb->iMaxPoints;
	if (max==0)
		{
		delete iPointerBuffer;
		iPointerBuffer=NULL;
		}
	else if (!iPointerBuffer)
		{
		CCirBuf<TPoint> *pointerBuffer=new(ELeave) CCirBuf<TPoint>;
		CleanupStack::PushL(pointerBuffer);
		pointerBuffer->SetLengthL(max);
		CleanupStack::Pop();
		iPointerBuffer=pointerBuffer;
		}
	else
		iPointerBuffer->SetLengthL(max);
	}
