// 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:
// wins\specific\gui.cpp
// 
//
#define WINVER 0x0500

#include "gui.h"
#include <emulator.h>
#include <assp.h>
#include <kernel/kern_priv.h>
#include <kernel/kpower.h>
#include "variant.h"
#include "resource.h"
#include "winsgui.h"
#include "display_chan.h"
#include "pixelformats.h"
#include "multitouch.h"

#include "monitors.h"

//Define these so that emulator generates varying values for gce stride and offset.
//By default in emulator, stride is exactly right for display resolution and offset is zero
//Setting these will identify code which incorrectly calculates these factors instead of requesting them
//Note that multiples of 4 bytes are preferred for various reasons.
//[3/5/07 The Secure presentation burffer ignores stride extra because it uses a windows bitmap header to render.]
// #define TEST_GCE_VARIABLE_STRIDE_EXTRA	16		 	//This constant is added to each mode's scanline length in bytes. It may cause a break if enabled because the iDisplayBufferOffset is not being set
// #define TEST_GCE_VARIABLE_START_EXTRA	16			//A multiple of this is added to each mode's start address in bytes
// #define ASSYMETRIC_SQUARE_STRIDE						//If this is defined and the width==height the the stride will not be the same!

enum  
	{
		KMaskModeNum=0x0FFFFFFF,
		KMaskModeFlag8=0x80000000,
		KMaskModeFlag4=0x40000000,
		KMaskModeFlag2=0x20000000,
		KMaskModeFlag1=0x10000000,
		
		KModeFlagFlipped=KMaskModeFlag8,
		
	};
enum  
	{
		KMaskScreenNum=0x0FFF,
		KMaskScreenFlag8=0x8000,
		KMaskScreenFlag4=0x4000,
		KMaskScreenFlag2=0x2000,
		KMaskScreenFlag1=0x1000,
		
		KScreenFlagSecure=KMaskScreenFlag8,
		
	};
const TInt KMaxDisplayColors=16777216;
const TInt KMaxDisplayContrast=1;

static TEmulatorFlip* CurrentFlipState=NULL;
static TInt CurrentConfiguration = 0;
static TInt SavedFlipMessage = 0;

DWinsUi *systemIni=NULL;
DMasterIni* masterIni;

DMultiTouch* TheMultiTouch;
static HWND TheControlWin;
static HWND* TheChildWin=NULL;
static HWND* TheWin=NULL;
static HWND hwndStatus; // To display the X,Y,Z information of each mouse
static TInt VirtualKeyPressed = EStdKeyNull;
static HBITMAP* TheScreenBitmap=NULL;
static TUint LedMask;
static TBool WsSwitchOnScreen;

const char * DefaultWindowTitle = "Symbian OS Emulator";

#ifdef __VC32__

#ifdef _DEBUG
const char * VersionText = " - wins udeb";
#else
const char * VersionText = " - wins urel";
#endif

#else
#ifdef __CW32__

#ifdef _DEBUG
const char * VersionText = " - winscw udeb";
#else
const char * VersionText = " - winscw urel";
#endif

#else
//not winscw or wins!
#ifdef _DEBUG
const char * VersionText = " - unknown udeb";
#else
const char * VersionText = " - unknown urel");
#endif

#endif
#endif

void Inactive();
void Active();
void DrawLeds();
void UpdateModifiers(); 
TInt DisplayHalFunction(TAny*, TInt aFunction, TAny* a1, TAny* a2);
LOCAL_C TBool PaintWindowFromBuffer(HWND hWnd);

GLDEF_C const char* skipws(const char* aPtr)
	{
	while (isspace(*aPtr))
		++aPtr;
	return aPtr;
	}

GLDEF_C const char* skiptok(const char* aPtr)
	{
	while (isalnum(*aPtr))
		++aPtr;
	return aPtr;
	}

GLDEF_C TInt CompareI(const TDesC8& aLhs, const TDesC8& aRhs)
//
// Case insensitive comparison of descriptors
// (TDesC::CompareF not available to kernel side code)
//
	{
	TInt ll = aLhs.Length();
	TInt rl = aRhs.Length();
	TInt len = Min(ll, rl);
	TInt k = _strnicmp((const char*)aLhs.Ptr(), (const char*)aRhs.Ptr(), len);
	return k != 0 ? k : ll - rl;
	}

GLDEF_C TInt MultiProperty(TInt (*aHandler)(TAny* aObj, const char*), TAny* aPtr, const char* aProperty)
	{
	const char* value = Property::GetString(aProperty);
	if (!value)
		return KErrNone;
	for (;;)
		{
		TInt r = aHandler(aPtr, value);
		if (r != KErrNone)
			return r;
		const char* ev = strchr(value, ';');
		if (!ev)
			break;
		value = ev + 1;
		}
	return KErrNone;
	}

class DWinsGuiPowerHandler : public DPowerHandler
	{
public: // from DPowerHandler
	void PowerDown(TPowerState);
	void PowerUp();
public:
	static DWinsGuiPowerHandler* New();
	void ScreenOn();
	void ScreenOff();
	void ScreenOn(TInt aScreen);
	void ScreenOff(TInt aScreen);
public:
	DWinsGuiPowerHandler();
	TBool ProcessEvent(const TRawEvent* aEvent);
	TBool ProcessEventDfc(const TRawEvent* aEvent);
	TBool	iStandby;
	};

static DWinsGuiPowerHandler* WinsGuiPowerHandler;

_LIT(KWinsGuiName, "WinsGui");

DWinsGuiPowerHandler* DWinsGuiPowerHandler::New()
	{
	DWinsGuiPowerHandler* self = new DWinsGuiPowerHandler();
	if (!self)
		return NULL;
	self->Add();

	return self;
	}

DWinsGuiPowerHandler::DWinsGuiPowerHandler() : DPowerHandler(KWinsGuiName)
	{
	}

void DWinsGuiPowerHandler::ScreenOff()
	{
	for(TInt ix=0;ix<systemIni->iScreens.Count();ix++)
		ScreenOff(ix);
	}

void DWinsGuiPowerHandler::ScreenOn()
	{
	for(TInt ix=0;ix<systemIni->iScreens.Count();ix++)
		ScreenOn(ix);
	}

void DWinsGuiPowerHandler::ScreenOff(TInt aScreen)
	{
	PostMessageA(TheWin[aScreen], WM_EMUL_POWER_ON, FALSE, NULL);
	systemIni->iScreens[aScreen]->iScreenOff = ETrue;
	}

void DWinsGuiPowerHandler::ScreenOn(TInt aScreen)
	{
	PostMessageA(TheWin[aScreen], WM_EMUL_POWER_ON, TRUE, NULL);
	systemIni->iScreens[aScreen]->iScreenOff = EFalse;
	}

void DWinsGuiPowerHandler::PowerDown(TPowerState aState)
	{
	if (aState == EPwStandby)
		iStandby = ETrue;
	ScreenOff();
	PowerDownDone();
	}


void DWinsGuiPowerHandler::PowerUp()
	{
	iStandby = EFalse;
	ScreenOn();
	PowerUpDone();
	}

// called in the interrupt context
TBool DWinsGuiPowerHandler::ProcessEvent(const TRawEvent* aEvent)
	{
	if (!iStandby)
		// Pass through 
		return EFalse;

	if ((aEvent->Type() == TRawEvent::EKeyDown))
			{
			Wins::Self() -> AssertWakeupSignal();
			}
	
	// Ignore
	return ETrue;
	}

// called in DFC
TBool DWinsGuiPowerHandler::ProcessEventDfc(const TRawEvent* aEvent)
	{
	if (aEvent->Type() == TRawEvent::EKeyDown)
		{
		Wins::Self() -> WakeupEvent();
		if (aEvent->ScanCode() == EStdKeyF5)
			{
			// Simulate a media change interrupt (media removed)
			Wins::MediaChangeCallBack();
			*Wins::MediaDoorOpenPtr()=ETrue;
			// Ignore
			return ETrue;
			}
		if (aEvent->ScanCode() == EStdKeyF8)
			{
			TRawEvent v;
			v.Set(TRawEvent::ECaseClose);
			Kern::AddEvent(v);
			// Ignore
			return ETrue;
			}
		if (aEvent->ScanCode() == EStdKeyF8)
			{
			TRawEvent v;
			v.Set(TRawEvent::ECaseClose);
			Kern::AddEvent(v);
			// Ignore
			return ETrue;
			}
		if (aEvent->ScanCode() == EStdKeyOff)
			{
			// Pass through
			return EFalse;
			}
		if (aEvent->ScanCode() == EStdKeyF10)
			{
			TRawEvent v;
			v.Set(TRawEvent::ESwitchOff);
			Kern::AddEvent(v);
			// Ignore
			return ETrue;
			}
		if (aEvent->ScanCode() == EStdKeyF11)
			{
			TRawEvent v;
			v.Set(TRawEvent::ECaseOpen);
			Kern::AddEvent(v);
			// Ignore
			return ETrue;
			}
		}
	else if (aEvent->Type() == TRawEvent::EKeyUp)
		{
		if (aEvent->ScanCode() == EStdKeyF10)
			// Ignore
			return ETrue;

		if (aEvent->ScanCode() == EStdKeyF5)
			{
			// Simulate a media change interrupt (media Present)
			*Wins::MediaDoorOpenPtr()=EFalse;
			return ETrue;
			}
		}

	// Path through
	return EFalse;
	}

class EventQ
	{
	enum {ESize = 16};
public:
	EventQ();
	void Add(const TRawEvent& aEvent);
private:
	static void Dfc(TAny* aPtr);
	void Empty();
private:
	TDfc iDfc;
	TRawEvent* iTail;
	TRawEvent iQ[ESize];
	};

EventQ::EventQ()
	:iDfc(&EventQ::Dfc, this, Kern::DfcQue0(), 6), iTail(iQ)
	{}


void EventQ::Add(const TRawEvent& aEvent)
	{
	StartOfInterrupt();	
	if (WinsGuiPowerHandler->ProcessEvent(&aEvent)) 
		{
		EndOfInterrupt();
		return;
		}

	TRawEvent* pE = iTail;
	if (pE != &iQ[ESize])
		{
		*pE = aEvent;
		iTail = pE + 1;
		if (pE == iQ)
			iDfc.Add();
		}
	EndOfInterrupt();
	}

void EventQ::Dfc(TAny* aPtr)
	{
	static_cast<EventQ*>(aPtr)->Empty();
	}

void EventQ::Empty()
//
// Called in the DFC
//
	{
	TInt irq;
	TRawEvent* pE = iQ;
	for (;;)
		{
		if (!WinsGuiPowerHandler->ProcessEventDfc(pE)) 
			Kern::AddEvent(*pE);
		++pE;
		irq = NKern::DisableAllInterrupts();
		if (pE == iTail)
			break;
		NKern::RestoreInterrupts(irq);
		}
	iTail = iQ;
	NKern::RestoreInterrupts(irq);
	}

LOCAL_D EventQ TheEventQ;

// Virtual keys


VirtualKey::VirtualKey(const TInt aCommandData, const TEmulCommand aCommand) : iCommand(aCommand), iData(aCommandData)
	{
	}

TBool VKRect::Contains(TInt aX, TInt aY) const
	{
	return (aX >= iLeft && aX < iRight && aY >= iTop && aY < iBottom);
	}

VKRect::VKRect(const TInt aCommandData, const TEmulCommand aCommand, TInt aX, TInt aY, TInt aWidth, TInt aHeight) :
	VirtualKey(aCommandData, aCommand)
	{
	iLeft = aX;
	iTop = aY;
	iRight = aX + aWidth;
	iBottom = aY + aHeight;
	}



void VKRect::Draw(HDC aHdc,COLORREF aColor) const
	{
	HPEN pen;
	pen=CreatePen(PS_SOLID, 2, aColor);
	SelectObject(aHdc, pen);
	POINT point;

	MoveToEx(aHdc, (int)iLeft, (int)iTop, &point);
	LineTo(aHdc, (int)iLeft, (int)iBottom);
	LineTo(aHdc, (int)iRight, (int)iBottom);
	LineTo(aHdc, (int)iRight, (int)iTop);
	LineTo(aHdc, (int)iLeft, (int)iTop);
	}


KeyCombination::KeyCombination(const TInt aCommandData, TEmulCommand aCommand):
	iData(aCommandData),
	iCommand(aCommand)
{
	for (TInt i=0;i<KMaxHotKeyCombinationLength;i++) 
		{
		iCombination[i]=EStdKeyNull;
		}
}

TBool KeyCombination::CheckCombinationPressed()
{
	for (TInt j=0;(j<KMaxHotKeyCombinationLength && iCombination[j]!=0);j++)
		{
		if (GetAsyncKeyState(MapVirtualKey(iCombination[j],1))>=0)//if at least one key is not pressed, we return false
			return EFalse;				
		}
	return ETrue;	
}

TBool KeyCombination::AddKey(TStdScanCode aKey)
{
	TInt i;
	for (i=0;i<KMaxHotKeyCombinationLength;i++) 
		{
		if (iCombination[i]==EStdKeyNull) 
			break;
		}
	if (KMaxHotKeyCombinationLength==i)
		return EFalse;
	else		
		iCombination[i]=aKey;	

	return ETrue;
}


DScreenProperties::DScreenProperties()
	{
	memset(this,0,sizeof(DScreenProperties));
	iColorDepth=KDefaultColorDepth;
	
	iViewport = TViewport(this);
	}


LOCAL_C TInt MaskGceOnly(TInt aModeBits)
	{	   //All HAL modes are now reported. The GCE may refuse to register the surfaces.
	return aModeBits&KEmulModes;	//previous useful settings: //(KEmulPixPerLong2|KEmulPixPerLong1);	//|KEmulPixPerLong4;
	}
	
LOCAL_C TInt BitsForSingleMode(TInt aModeColor)
	{	//only 1 bit should be set in aModeColor
	switch (aModeColor)
		{
		case KEmulGray2:	return 1;	
		case KEmulGray4:	return 2;	
		case KEmulGray16:	return 4;
		case KEmulGray256:	return 8;
		case KEmulColor16:	return 4;
		case KEmulColor256:	return 8;
		case KEmulColor4K:	return 12;
		case KEmulColor64K:	return 16;
		case KEmulColor16M:	return 24;
		default:	return 32;
		}
	
	}

DScreenProperties::~DScreenProperties() 
	{
	}

TWindowState DScreenProperties::GetWindowState()
	{
	TWindowState state;
	state.iWinPlace = iWinPlace;
	state.iFlipstate = iScreenRotation;
	state.iXoffset = iViewport.GetViewportOffsetX();
	state.iYoffset = iViewport.GetViewportOffsetY();
	return state;
	}
	
TInt DScreenProperties::SetupProperties(TInt aConf, TInt aScreen)
	{
	char property[50];

	// Calculate maximum dimensions
	TInt configurations = Property::GetInt("ConfigCount", 0);
	if (configurations == 0)
		return KErrGeneral;

	TInt count, screenWidth, screenHeight, physicalScreenWidth, physicalScreenHeight;
	for (count = 0; count < configurations; ++count)
		{
		wsprintfA(property, "Configuration[%d][%d]ScreenWidth", count, aScreen);
		screenWidth = Property::GetInt(property, KScreenWidth);
		screenWidth = (screenWidth + 3) & ~3;
		if (screenWidth > iMaxScreenWidth)
			iMaxScreenWidth = screenWidth;
		wsprintfA(property, "Configuration[%d][%d]ScreenHeight", count, aScreen);
		screenHeight = Property::GetInt(property, KScreenHeight);
		screenHeight = (screenHeight + 3) & ~3;
		if (screenHeight > iMaxScreenHeight)
			iMaxScreenHeight = screenHeight;
//
		wsprintfA(property, "Configuration[%d][%d]PhysicalScreenWidth", count, aScreen);
		physicalScreenWidth = Property::GetInt(property);
		if (physicalScreenWidth > iMaxPhysicalScreenWidth)
			iMaxPhysicalScreenWidth = physicalScreenWidth;
		wsprintfA(property, "Configuration[%d][%d]PhysicalScreenHeight", count, aScreen);
		physicalScreenHeight = Property::GetInt(property);
		if (physicalScreenHeight > iMaxPhysicalScreenHeight)
			iMaxPhysicalScreenHeight = physicalScreenHeight;
		}

	// Read figures for current configuration
	TInt givenWidth, givenHeight;
	wsprintfA(property, "Configuration[%d][%d]ScreenWidth",aConf,aScreen);
	givenWidth = Property::GetInt(property, KScreenWidth);
	iScreenWidth = (givenWidth + 3) & ~3;
	wsprintfA(property, "Configuration[%d][%d]ScreenHeight",aConf,aScreen);
	givenHeight = Property::GetInt(property, KScreenHeight);
	iScreenHeight = (givenHeight + 3) & ~3;
	// Width of screen should be multiple number of 4 pixels.
	if (givenWidth & 3 || givenHeight & 3)
		{
		Kern::Printf("Width and Height of Screen should be multiple number of 4 pixels.\n"
				"\tWidth of screen[%d] set to: %d\n\tHeight of screen[%d] set to: %d", 
				aScreen, iScreenWidth, aScreen, iScreenHeight);
		}

//
	wsprintfA(property, "Configuration[%d][%d]PhysicalScreenWidth",aConf,aScreen);
	iPhysicalScreenWidth = Property::GetInt(property);
	wsprintfA(property, "Configuration[%d][%d]PhysicalScreenHeight",aConf,aScreen);
	iPhysicalScreenHeight = Property::GetInt(property);
//
	wsprintfA(property, "Configuration[%d][%d]ScreenOffsetX",aConf,aScreen);
	iScreenOffsetX = Property::GetInt(property, KScreenOffsetX);
	wsprintfA(property, "Configuration[%d][%d]ScreenOffsetY",aConf,aScreen);
	iScreenOffsetY = Property::GetInt(property, KScreenOffsetY);
	
	wsprintfA(property, "Configuration[%d][%d]CompositionBuffers",aConf,aScreen);
	iCompositionBuffers = Property::GetInt(property, KCompositionBuffers);

	wsprintfA(property, "Configuration[%d][%d]RefreshRateHz",aConf,aScreen);
	iRefreshRateHz = Property::GetInt(property, KRefreshRateHz);


	wsprintfA(property, "Configuration[%d][%d]ColorDepth",aConf,aScreen);
	const char* colors = Property::GetString(property);
	if (colors)
		{
		TUint colorDepth=0;
		const char* end = colors;
		for (;;)
			{
			const char* beg = skipws(end);
			if(*beg==';')
				break;
			if (!*beg)
				break;
			end	= skiptok(beg);
			if (_strnicmp("Gray2",beg,end-beg) == 0)
				{
				colorDepth|=KEmulGray2|KEmulIsBitMask;
				}
			else if (_strnicmp("Gray4",beg,end-beg) == 0)
				{
				colorDepth|=KEmulGray4|KEmulIsBitMask;
				}
			else if (_strnicmp("Gray16",beg,end-beg) == 0)
				{
				colorDepth|=KEmulGray16|KEmulIsBitMask;
				}
			else if (_strnicmp("Gray256",beg,end-beg) == 0)
				{
				colorDepth|=KEmulGray256|KEmulIsBitMask;
				}
			else if (_strnicmp("Color16",beg,end-beg) == 0)
				{
				colorDepth|=KEmulColor16|KEmulIsBitMask;
				}
			else if (_strnicmp("Color256",beg,end-beg) == 0)
				{
				colorDepth|=KEmulColor256|KEmulIsBitMask;
				}
			else if (_strnicmp("Color4K",beg,end-beg) == 0)
				{
				colorDepth|=KEmulColor4K|KEmulIsBitMask;
				}
			else if (_strnicmp("Color64K",beg,end-beg) == 0)
				{
				colorDepth|=KEmulColor64K|KEmulIsBitMask;
				}
			else if (_strnicmp("Color16M",beg,end-beg) == 0)
				{
				colorDepth|=KEmulColor16M|KEmulIsBitMask;
				}
			else
				return KErrArgument;
			}
		iColorDepth = colorDepth;
		
		}
	//multiple mode support is currently only for GCE. 
	//I fill this array in before knowing if GCE will be instanced.
	if (iColorDepth&KEmulIsBitMask)
		{
		//iModeDepths is only used by GCE
		TInt colorDepth=MaskGceOnly(iColorDepth);
		TInt setMode=0;
		for (TInt i=1;i!=KEmulIsBitMask;i+=i)
			if (colorDepth&i)
				iModeDepths[setMode++]=BitsForSingleMode(i);
		iMaxModes= setMode;	
		iModeDepths[setMode++]=0;	//a bit width of 0 is illegal	
		}
	else
		{
		iModeDepths[0]=iColorDepth;
		iMaxModes=1;
		iModeDepths[1]=0;	//a bit width of 0 is illegal
		}

	wsprintfA(property, "Configuration[%d][%d]FasciaBitmap",aConf,aScreen);
	const char* fascia = Property::GetString(property);
	if (fascia)
		{
		TInt len = strlen(fascia);
		//the path may have quotes at the start and end
		//need to work out if this is an absolute or relative path
		if (fascia[0] == '\"')
			{
			++fascia;
			--len;
			if (--len > 0 && fascia[len-1] == '\"')
				--len;
			}
		char* p = iFasciaFileName;
		if (fascia[0] != '\\' && (len < 3 || fascia[1] != ':'))
			{
			//relative path
			strcpy(p, Property::GetString("EmulatorDataPath"));
			p += strlen(p);
			}
		memcpy(p, fascia, len);
		p[len] = '\0';
		}
	else
		{
		// default to machine name
		strcpy(iFasciaFileName, Property::GetString("EmulatorDataPath"));
		strcat(iFasciaFileName, Property::GetString("MachineName"));
		strcat(iFasciaFileName, ".bmp");
		}
	return KErrNone;
	}

TViewport::TViewport() 
	:iScreenProps(NULL),iViewportWidth(0), iViewportHeight(0), iViewportOffsetX(0), iViewportOffsetY(0)
	{
	}

TViewport::TViewport(DScreenProperties* aScreenProps)
	:iScreenProps(aScreenProps),iViewportWidth(0), iViewportHeight(0), iViewportOffsetX(0), iViewportOffsetY(0)
	{	
	}	
TViewport::~TViewport()
	{
	}
	

/**
Changes the logical position of the viewport within the input area
of the emulator screen. The method may adjust the position so that
the viewport stays within the input area.
@param aPosition The new Y position of the top left hand corner of the viewport.
@param aHwnd The window associated with the viewport
*/
void TViewport::ScrollToY(TInt aPosition, HWND aHwnd)
	{

	SCROLLINFO scrollinfo;
	scrollinfo.cbSize=sizeof(scrollinfo);

	//save for later
	scrollinfo.fMask=SIF_POS;
	GetScrollInfo(aHwnd, SB_VERT, &scrollinfo);
	TInt oldY=scrollinfo.nPos;

	if(aPosition<0)
	{
		scrollinfo.nPos = 0;
	}
	else if( (aPosition+GetViewportHeight())>GetMaxHeight())
	{
		scrollinfo.nPos = max(0,GetMaxHeight() - GetViewportHeight() );
	}
	else
	{
		scrollinfo.nPos=aPosition;
	}

	SetViewportOffsetY(scrollinfo.nPos);
	scrollinfo.fMask=SIF_POS;
	SetScrollInfo(aHwnd,SB_VERT, &scrollinfo, TRUE );
	ScrollWindowEx(aHwnd, 0, oldY-scrollinfo.nPos, 0, 0, NULL, NULL, SW_INVALIDATE);
	
	UpdateChildPos(aHwnd);
	}
	
/**
As for ScrollToY but for the X direction
*/
void TViewport::ScrollToX(TInt aPosition, HWND aHwnd)
	{
	SCROLLINFO scrollinfo;
	scrollinfo.cbSize=sizeof(scrollinfo);
	
	//save for later
	scrollinfo.fMask=SIF_POS;
	GetScrollInfo(aHwnd, SB_HORZ, &scrollinfo);
	TInt oldX=scrollinfo.nPos;

	if(aPosition<0)
	{
		scrollinfo.nPos = 0;
	}
	else if( (aPosition+GetViewportWidth())>GetMaxWidth())
	{
		scrollinfo.nPos = max(0,GetMaxWidth() - GetViewportWidth() );
	}
	else
	{
		scrollinfo.nPos=aPosition;
	}

	SetViewportOffsetX(scrollinfo.nPos);
	scrollinfo.fMask=SIF_POS;
	SetScrollInfo(aHwnd,SB_HORZ, &scrollinfo, TRUE );
	ScrollWindowEx(aHwnd, oldX-scrollinfo.nPos, 0, 0, 0, NULL, NULL, SW_INVALIDATE);

	UpdateChildPos(aHwnd);
	}
	
//Forward declaration
LOCAL_C TInt ScreenFromHWND(HWND aHwnd,HWND* pWin);

/**
Move the child window to it's correct position.

@param aHwnd The HWND of the parent window
*/
void TViewport::UpdateChildPos(HWND aHwnd)
	{
	TInt screenNumber = ::ScreenFromHWND(aHwnd,TheWin);
	HWND childWin = TheChildWin[screenNumber];

	switch (iScreenProps->iScreenRotation)
		{
		case EEmulatorFlipRestore:
			MoveWindow(
				childWin,
				iScreenProps->iScreenOffsetX - GetViewportOffsetX(),
				iScreenProps->iScreenOffsetY - GetViewportOffsetY(),
				iScreenProps->iScreenWidth,
				iScreenProps->iScreenHeight,
				TRUE
				);
			break;
		case EEmulatorFlipInvert:
			MoveWindow(
				childWin,
				iScreenProps->iXYInputWidth-(iScreenProps->iScreenOffsetX+iScreenProps->iScreenWidth) - GetViewportOffsetX(),
				iScreenProps->iXYInputHeight-(iScreenProps->iScreenOffsetY+iScreenProps->iScreenHeight) - GetViewportOffsetY(),
				iScreenProps->iScreenWidth,
				iScreenProps->iScreenHeight,
				TRUE
				);
			break;
		case EEmulatorFlipLeft:
			MoveWindow(
				childWin,
				iScreenProps->iScreenOffsetY - GetViewportOffsetX(),
				iScreenProps->iXYInputWidth-(iScreenProps->iScreenOffsetX+iScreenProps->iScreenWidth)- GetViewportOffsetY(),
				iScreenProps->iScreenHeight,
				iScreenProps->iScreenWidth,
				TRUE
				);
			break;
		case EEmulatorFlipRight:
			MoveWindow(
				childWin,
				iScreenProps->iXYInputHeight-(iScreenProps->iScreenOffsetY+iScreenProps->iScreenHeight) - GetViewportOffsetX(),
				iScreenProps->iScreenOffsetX - GetViewportOffsetY(),
				iScreenProps->iScreenHeight,
				iScreenProps->iScreenWidth,
				TRUE
				);
			break;
		}

	}
	
/**
Update the range of the horizontal scrollbar,
to take account of the current viewport width.

@param aHwnd The window to be updated
*/
void TViewport::UpdateScrollBarH(HWND aHwnd)
	{

	SCROLLINFO scrollinfoHor;
	scrollinfoHor.cbSize=sizeof(scrollinfoHor);
	scrollinfoHor.fMask=SIF_RANGE|SIF_PAGE;
	scrollinfoHor.nMin=0;
	scrollinfoHor.nMax= GetMaxWidth()-1;
	
	
	TInt newPage = GetViewportWidth() ;
	TBool redraw=EFalse; //redraw window if a resize has caused a scrollbar to disappear and reveal image.
	if ( newPage>= scrollinfoHor.nMax -GetSystemMetrics(SM_CXVSCROLL)
		&& newPage < scrollinfoHor.nMax+1)
		{
		redraw=ETrue;
		newPage=GetMaxWidth();

		}
	scrollinfoHor.nPage= newPage;

	SetScrollInfo(aHwnd,SB_HORZ, &scrollinfoHor, TRUE );
	if(redraw)
		{
		ScrollToX(0, aHwnd); //in case egde of fascia was against edge of vertical scrollbar
		InvalidateRect(aHwnd, NULL, TRUE);
		}
	}
	
/**
Update the range of the vertical scrollbar,
to take account of the current viewport width.

@param aHwnd The window to be updated
*/	
void TViewport::UpdateScrollBarV(HWND aHwnd)
	{
	SCROLLINFO scrollinfoVer;
	scrollinfoVer.cbSize=sizeof(scrollinfoVer);
	scrollinfoVer.fMask=SIF_RANGE|SIF_PAGE;
	scrollinfoVer.nMin=0;
	scrollinfoVer.nMax= GetMaxHeight()-1;
	
	TInt newPage = GetViewportHeight() ;
	TBool redraw=EFalse; //redraw window if a resize has caused a scrollbar to disappear and reveal image.
	if ( newPage>= scrollinfoVer.nMax -GetSystemMetrics(SM_CYHSCROLL)
		&& newPage < scrollinfoVer.nMax+1)
		{
		redraw=ETrue;
		newPage=GetMaxHeight();
		}
	scrollinfoVer.nPage= newPage;

	SetScrollInfo(aHwnd,SB_VERT, &scrollinfoVer, TRUE );
	if(redraw)
		{
		ScrollToY(0, aHwnd); //in case egde of fascia was against edge of vertical scrollbar
		InvalidateRect(aHwnd, NULL, TRUE);
		}
	}

/**
Returns the max width for the viewport window (non-client area) so that it
may be bounded. Takes account of scrollbar.

@return Max width
*/	
TInt TViewport::GetMaxWindowWidth() const
	{
	
	RECT rect = {0,0,0,0};
	
	switch(iScreenProps->iScreenRotation)
		{
		case EEmulatorFlipRestore:
		case EEmulatorFlipInvert:
			{
			rect.right=iScreenProps->iXYInputWidth;
			rect.bottom=iScreenProps->iXYInputHeight;
			break;
			}
		case EEmulatorFlipLeft:
		case EEmulatorFlipRight:
			{
			rect.right=iScreenProps->iXYInputHeight;
			rect.bottom=iScreenProps->iXYInputWidth;
			break;
			}
		}
	AdjustWindowRect(//take account of window decorations
		&rect,
		KWinStyle,
		FALSE
		);
	
	
	return (rect.right-rect.left);
	}
	
/**
Returns the max height for the viewport window (non-client area) so that it
may be bounded. Takes account of scrollbar.

@return Max height
*/
TInt TViewport::GetMaxWindowHeight() const
	{
	
	RECT rect ={0,0,0,0};

	switch(iScreenProps->iScreenRotation)
		{
		case EEmulatorFlipRestore:
		case EEmulatorFlipInvert:
			{
			rect.right=iScreenProps->iXYInputWidth;
			rect.bottom=iScreenProps->iXYInputHeight;
			break;
			}
		case EEmulatorFlipLeft:
		case EEmulatorFlipRight:
			{
			rect.right=iScreenProps->iXYInputHeight;
			rect.bottom=iScreenProps->iXYInputWidth;
			break;
			}
		}
	AdjustWindowRect(//take account of window decorations
		&rect,
		KWinStyle,
		FALSE
		);
	return (rect.bottom-rect.top);
	}

/**
Returns the maximum width for the viewport (client area only).
Allowing for the orientation of the emulator.

@return Max width
*/
TInt TViewport::GetMaxWidth() const
	{
	TInt width=0;
	switch(iScreenProps->iScreenRotation)
		{
		case EEmulatorFlipRestore:
		case EEmulatorFlipInvert:
			{
			width = iScreenProps->iXYInputWidth;
			break;
			}
		case EEmulatorFlipLeft:
		case EEmulatorFlipRight:
			{
			width = iScreenProps->iXYInputHeight;
			break;
			}
		}
	
	return width;
	}

/**
Returns the maximum height for the viewport (client area only).
Allowing for the orientation of the emulator.

@return Max height
*/
TInt TViewport::GetMaxHeight() const
	{
	TInt height=0;
	switch(iScreenProps->iScreenRotation)
		{
		case EEmulatorFlipRestore:
		case EEmulatorFlipInvert:
			{
			height = iScreenProps->iXYInputHeight;
			break;
			}
		case EEmulatorFlipLeft:
		case EEmulatorFlipRight:
			{
			height =  iScreenProps->iXYInputWidth;
			break;
			}
		}
	
	return height;
	
	}

/**
Sets the X offset of the viewport from the edge of the input area
@param aOffset The X offset
*/
void TViewport::SetViewportOffsetX(TInt aOffset)
	{
	iViewportOffsetX = aOffset;
	}

/**
Sets the Y offset of the viewport from the edge of the input area
@param aOffset The Y offset
*/
void TViewport::SetViewportOffsetY(TInt aOffset)
	{
	iViewportOffsetY = aOffset;
	}

TInt TViewport::GetViewportOffsetX() const
	{
	return iViewportOffsetX;	
	}
TInt TViewport::GetViewportOffsetY() const
	{
	return iViewportOffsetY;
	}
	
/**
Sets the viewport width, this is equal to the width
of the window's client area
@param aWidth The width
*/
void TViewport::SetViewportWidth(TInt aWidth)
	{
	iViewportWidth=aWidth;
	}

/**
Sets the viewport height, this is equal to the height
of the window's client area
@param aHeight The height
*/
void TViewport::SetViewportHeight(TInt aHeight)
	{
	iViewportHeight=aHeight;
	}

TInt TViewport::GetViewportWidth() const
	{
	return iViewportWidth;
	}
TInt TViewport::GetViewportHeight() const
	{
	return iViewportHeight;
	}

// the UI class

DWinsUi::DWinsUi()
	:iVirtualKeys(10),
	iControlHotKeys(10)
	{}

/// Returns the current mode's depth. Remember current mode is never set!
TUint DWinsUi::ColorDepth(TInt aScreenNumber)
	{
	TVideoInfoV01 info;
	VideoInfo(aScreenNumber, info);
	return info.iBitsPerPixel;
	}

TInt DWinsUi::SetFlip(TEmulatorFlip aFlip, TInt aScreenNumber)
	{
	if(TUint(aScreenNumber)>=TUint(systemIni->iScreens.Count()))
		return KErrArgument;
	int r1 = PostMessageA(TheChildWin[aScreenNumber],WM_FLIP_MESSAGE,(TUint)aFlip,NULL);
	return r1 ? KErrNone : KErrGeneral;
	}

void DWinsUi::Info(TVariantInfoV01& aInfo)
	{
	aInfo.iLedCapabilities=0x3;
	}

HWND DWinsUi::HWnd()
	{
	return TheControlWin;
	}

TInt DWinsUi::SetupProperties(TInt aId)
	
//
// load UI settings from the emulator properties
//
	{
	//setup the screens
	TInt screens = Property::GetInt("[screens]", 1);
 
 	for (TInt x = 0; x < screens; ++x)
 		{
 		DScreenProperties * pScr = new DScreenProperties();
 		if (!pScr)
 			return KErrNoMemory;
 		
 		TInt ret = pScr->SetupProperties(aId,x);
 		if (KErrNone == ret)
 			ret = iScreens.Append(pScr);
 
 		if (KErrNone != ret)
 			{
 			delete pScr;
 			return ret;
 			}
 		}
//
	char property[50];
	wsprintfA(property, "Configuration[%d]LedSize",aId);
	iLedSize = Property::GetInt(property, KLedSize);
	wsprintfA(property, "Configuration[%d]LedArrangeVertically",aId);
	iLedVertical = Property::GetBool(property, KLedVertical);
	wsprintfA(property, "Configuration[%d]LedArrangeHorizontally",aId);
	if (Property::GetBool(property))
		iLedVertical = EFalse;
	wsprintfA(property, "Configuration[%d]LedOffsetX",aId);
	iLedOffsetX = Property::GetInt(property, KLedLeft);
	wsprintfA(property, "Configuration[%d]LedOffsetY",aId);
	iLedOffsetY = Property::GetInt(property, KLedTop);
	wsprintfA(property, "Configuration[%d]LedGap",aId);
	iLedGap = Property::GetInt(property, KLedGap);
//
	wsprintfA(property, "Configuration[%d]PointerType",aId);
	const char* pointer = Property::GetString(property, "Pen");
	if (_stricmp(pointer, "None") == 0)
		{
		iPointerType=_S8("NONE");
		iXYInputType=EXYInputNone;
		}
	else if (_stricmp(pointer,"Pen") == 0)
		{
		iPointerType=_S8("PEN");
		iXYInputType=EXYInputPointer;
		}
	else if (_stricmp(pointer,"Mouse") == 0)
		{
		iPointerType=_S8("MOUSE");
		iXYInputType=EXYInputMouse;
		}
	else if (_stricmp(pointer,"Delta-Mouse") == 0)
		{
		iPointerType=_S8("MOUSE");
		iXYInputType=EXYInputDeltaMouse;
		}
	else
		return KErrArgument;
//
	wsprintfA(property, "Configuration[%d]DigitizerOffsetX",aId);
	iDigitizerOffsetX = Property::GetInt(property, -iScreens[0]->iScreenOffsetX);
	wsprintfA(property, "Configuration[%d]DigitizerOffsetY",aId);
	iDigitizerOffsetY = Property::GetInt(property, -iScreens[0]->iScreenOffsetY);
	wsprintfA(property, "Configuration[%d]DigitizerWidth",aId);
	iDigitizerWidth = Property::GetInt(property,-1);
	wsprintfA(property, "Configuration[%d]DigitizerHeight",aId);
	iDigitizerHeight = Property::GetInt(property,-1);
	wsprintfA(property, "Configuration[%d]DisableDigitizer",aId);
	iDigitizerEnabled = !Property::GetBool(property);
//	To enable the multitouch 
	wsprintfA(property, "EnableMultiTouch");
	iMultiTouchEnabled = Property::GetBool(property,EFalse);
	wsprintfA(property, "SYMBIAN_BASE_USE_GCE");
	iGCEEnabled = Property::GetBool(property,EFalse);
	wsprintfA(property, "MultiTouchProximityStep");
	iMultiTouchProximityStep = Property::GetInt(property,-1);
	wsprintfA(property, "MultiTouchPressureStep");
	iMultiTouchPressureStep = Property::GetInt(property,-1);
//
	strcpy(iSysIniFileName, Property::GetString("EmulatorDataPath"));
	strcat(iSysIniFileName, "emulator\\");
	if (!Emulator::CreateAllDirectories(iSysIniFileName))
		return Emulator::LastError();
	strcat(iSysIniFileName, Property::GetString("MachineName"));
	strcat(iSysIniFileName, ".sys.ini");
//
	TInt r = iKeyboard.Init(aId);
	if (r != KErrNone)
		return r;

	wsprintfA(property, "Configuration[%d]VirtualKey",aId);
	r = MultiProperty(&DWinsUi::DoDefineVirtualKey, this, property);
	if (r != KErrNone)
		return r;
//

	wsprintfA(property, "Configuration[%d]NoVersionInfo",aId);
	iDisplayVersionInfo = !Property::GetBool(property);
	
	wsprintfA(property, "Configuration[%d]WindowTitle",aId);
	const char * p = Property::GetString(property);
	if (p && (strlen(p) <= KMaxNameSize))
		strcpy(iWindowTitle, p);
	else
		strcpy(iWindowTitle, DefaultWindowTitle);

	if (iDisplayVersionInfo)
		{
		TInt wtLen = strlen(iWindowTitle);
		TInt vtLen = strlen(VersionText);
		if ((wtLen + vtLen) > KMaxNameSize)
			iWindowTitle[KMaxNameSize-vtLen] = '\0';
		strcat(iWindowTitle, VersionText);
		}

	wsprintfA(property, "Configuration[%d]OnActivation",aId);
	pointer = Property::GetString(property);	
	//example	OnActivation 270 EKeyScreenDimension1
	//params are rotation(int) and key(string)
	if (pointer)
		{
		char * next;
	
		//skip any white space
		const char* beg = skipws(pointer);
		
		//get the number
		long rotation = strtol(beg, &next, 0);
		if (next == beg)
			return KErrArgument;

		switch (rotation)
			{
			case 0:
				iScreens[0]->iScreenRotation = EEmulatorFlipRestore;
				break;
			case 90:
				iScreens[0]->iScreenRotation = EEmulatorFlipRight;
				break;
			case 180:
				iScreens[0]->iScreenRotation = EEmulatorFlipInvert;
				break;
			case 270:
				iScreens[0]->iScreenRotation = EEmulatorFlipLeft;
				break;
			default:
				r = KErrArgument;
			}
		if (r != KErrNone)
			return r;
		
		beg = skipws(next);
		
		//beg should now point to the keycode
		TInt key = iKeyboard.GetEPOCKeyCode(TPtrC8((const TUint8*)beg, strlen(beg)));
		if (key == KErrNotFound)
			return key;
		iInitialFlipMsg = key;
		}

	//EmulatorControl messages are a bit like virtual keys
	wsprintfA(property, "Configuration[%d]EmulatorControl",aId);
	r = MultiProperty(&DWinsUi::DoDefineEmulatorControl, this, property);
	if (r != KErrNone)
		return r;

	wsprintfA(property, "Configuration[%d]EmulatorControlHotKey",aId);
	r = MultiProperty(&DWinsUi::DoDefineEmulatorControlHotKey, this, property);
	if (r != KErrNone)
		return r;
	
	return KErrNone;
	}

TInt DWinsUi::NumberOfScreens()
	{
	return iScreens.Count();
	}

/**
Return the highest bit depth from an emulator mode mask.
@param aModeMask	A bitwise combination of KEmul... display mode mask values.
@return A color depth in bits per pixel.
*/
LOCAL_C TInt MaximumBitDepthFromMask(TInt aModeMask)
	{
	// Choose the highest bits per pixel based on the display mode mask.
	if (aModeMask & KEmulColor16M)
		{
		return 24;
		}
	if (aModeMask & KEmulColor64K)
		{
		return 16;
		}
	if (aModeMask & KEmulColor4K)
		{
		return 12;
		}

	// Lower bit depths are not supported, so use the default
	return 24;
	}


/**
Return the TDisplayRotation corresponding to the given TEmulatorFlip.
@param aFlip	A screen rotation as a TEmulatorFlip.
@return The screen rotation as a TDisplayRotation.
*/
LOCAL_C RDisplayChannel::TDisplayRotation FlipToDisplayRotation(TEmulatorFlip aFlip)
	{
	switch (aFlip)
		{
		case EEmulatorFlipLeft:
			return RDisplayChannel::ERotation90CW;
		case EEmulatorFlipInvert:
			return RDisplayChannel::ERotation180;
		case EEmulatorFlipRight:
			return RDisplayChannel::ERotation270CW;
		}
	return RDisplayChannel::ERotationNormal;
	}


TInt DWinsUi::SetDisplayChannel(TInt aScreenNumber, DDisplayChannel* aDisplay)
	{
	return systemIni->SetDisplayChannelImpl(aScreenNumber,aDisplay);
	}


TInt DWinsUi::SetDisplayChannelImpl(TInt aScreenNumber, DDisplayChannel* aDisplay)
	{
	if (TUint(aScreenNumber) >= TUint(NumberOfScreens()))
		{
		// Screen number is either negative or too big.
		return KErrArgument;
		}

	TInt r = KErrNone;
	HWND hWnd = TheChildWin[aScreenNumber];
	TBufferSet& buffer = masterIni->iBufferSet[aScreenNumber];
	
	if (aDisplay)
		{
		// Display driver connecting
		DScreenProperties* screen = iScreens[aScreenNumber];
		RDisplayChannel::TDisplayInfo info;

		TInt pixelBytes = 2;
		info.iBitsPerPixel = MaximumBitDepthFromMask(screen->iColorDepth);

		switch (info.iBitsPerPixel)
			{
			case 12:	// XRGB4444
				info.iPixelFormat = EUidPixelFormatXRGB_4444;
				break;
			case 16:	// RGB565
				info.iPixelFormat = EUidPixelFormatRGB_565;
				break;
			default:
				// Force anything else to packed RGB888
				pixelBytes = 4;
				info.iBitsPerPixel = 24;
				info.iPixelFormat = EUidPixelFormatXRGB_8888;
				break;
			}

		TInt width = screen->iMaxScreenWidth;
		TInt height = screen->iMaxScreenHeight;

		info.iRefreshRateHz = screen->iRefreshRateHz;
		info.iAvailableRotations = RDisplayChannel::ERotationNormal | RDisplayChannel::ERotation90CW |
									RDisplayChannel::ERotation180 | RDisplayChannel::ERotation270CW;
		info.iNormal.iWidth = width;
		info.iNormal.iHeight = height;
		// Windows requires rounding up to 4-bytes words
		info.iNormal.iOffsetBetweenLines = _ALIGN_UP(width * pixelBytes, 4);
		info.iFlipped.iWidth = height;
		info.iFlipped.iHeight = width;
		// Windows requires rounding up to 4-bytes words
		info.iFlipped.iOffsetBetweenLines = _ALIGN_UP(height * pixelBytes, 4);
	
		TInt maxSize=0;	 
		//ensure legacy buffer is large enough for all supported modes.
		//It would be a very strange setup for the max size to not be the max bpp,
		//but we don't know which mode is max bpp anyway!
		TVideoInfoV01 videoInfo;
		for (TInt mode=0,maxMode=screen->iMaxModes;mode<maxMode;mode++)
			{
			if (systemIni->VideoInfoForDisplayDriver(aScreenNumber,mode, videoInfo))	//can't actually fail currently
				{
				TInt dwSize=videoInfo.iOffsetToFirstPixel+videoInfo.iOffsetBetweenLines*videoInfo.iSizeInPixels.iHeight;
				if (dwSize>maxSize)
					maxSize=dwSize;
				}
			else
				{
				Fault(EGuiVideoInfoUnavailable);
				}
			//rotated mode may use more RAM?? Height may be >Width or may not be a multiple of stride quantum
			if (systemIni->VideoInfoForDisplayDriver(aScreenNumber,mode|KModeFlagFlipped, videoInfo))	//can't actually fail currently
				{
				TInt dwSize=videoInfo.iOffsetToFirstPixel+videoInfo.iOffsetBetweenLines*videoInfo.iSizeInPixels.iWidth;
				if (dwSize>maxSize)
					{
					maxSize=dwSize;
					}
				}
			else
				{
				Fault(EGuiVideoInfoUnavailable);
				}
			}

		masterIni->iMaxSizeInBytes = maxSize;
		if (__e32_atomic_add_ord32(&buffer.iDisplayDriverCount, 1) == 0)
			{
			// First driver to connect, allocate frame buffers.
			// +1 frame buffer is ui legacy buffer at [0], so does need to take account of stride and offset
			r = masterIni->AllocateFrameBuffers(aScreenNumber, screen->iCompositionBuffers + 1, maxSize);
			}

		if (r == KErrNone)
			{
			buffer.iScreenBuffer.iDisplayBufferOffset = 0;
			masterIni->iBufferSet[aScreenNumber].iDisplayChannel = aDisplay;
			masterIni->InitBitmapHeader(*screen, &buffer.iInfo);
			masterIni->InitBufferFormat(*screen, buffer.iBufferFormat);
			if(systemIni->VideoInfoForDisplayDriver(aScreenNumber,screen->iCurrentMode, videoInfo, ETrue))
				{
					r = aDisplay->Initialize(info,
											 FlipToDisplayRotation(screen->iScreenRotation),
											 hWnd, buffer.iScreenBuffer.iFrameBuffers,
											 buffer.iScreenBuffer.iMemChunks,
											 buffer.iDsaBuffer,
						                     videoInfo.iSizeInPixels,videoInfo.iSizeInTwips,
						                     masterIni->iSupportedPixelFormatTable,
						                     masterIni->iSupportedPixelFormatTableSize,
						                     buffer.iBufferFormat);
				}
			else
				{
				Fault(EGuiVideoInfoUnavailable);
				}
			}

		if (r != KErrNone && __e32_atomic_tas_ord32(&buffer.iDisplayDriverCount, 1, -1, 0) == 1)
			{
			// Release any that were allocated
			masterIni->ReleaseFrameBuffers(aScreenNumber);
			}
		}
	else
		{
		// Display driver disconnected
		if (__e32_atomic_tas_ord32(&buffer.iDisplayDriverCount, 1, -1, 0) == 1)
			{
			// All drivers disconnected, deallocate memory.
			masterIni->ReleaseFrameBuffers(aScreenNumber);
			}
		}

	return r;
	}


void DWinsUi::SetVirtualKey(const TBool aProcessing, const TInt aCommandData, const TEmulCommand aCommand)
	{
	iProcessingVirtualKey = aProcessing;
	iFakedVirtualKey = aCommandData;
	iVirtualKeyCommand = aCommand;
	}

TBool DWinsUi::WasVirtualKey(TInt& aCommandData, TEmulCommand& aCommand)
	{
	if (iProcessingVirtualKey)
		{

		aCommandData = iFakedVirtualKey;
		aCommand = iVirtualKeyCommand;
		}
	return iProcessingVirtualKey;
	}


TInt DWinsUi::DoDefineEmulatorControl(TAny* aPtr, const char* aValue)
	{
	return static_cast<DWinsUi*>(aPtr)->DefineEmulatorControl(aValue);
	}


TInt DWinsUi::DefineEmulatorControl(const char* aValue)
	{

	//example EmulatorControl SelectConfig 2 rect 223,640 29,22
	//example EmulatorControl NextConfig rect 223,640 29,22
	const char* beg = skipws(aValue);
	const char* end = skiptok(beg);
	TInt err = KErrNone;
	
	TEmulCommand command = ENoCommand;
	TInt data = 0;
	if (_strnicmp(beg, "SelectConfig", end-beg) == 0)
		{
		//get the int param which is the config to switch to
		beg = end;
		char * e;
		data = strtol(beg, &e,0);
		if (beg == e)
			err = KErrArgument;
		end = e;
		command = ESelectConfig;
		}
	else if(_strnicmp(beg, "NextConfig", end-beg) == 0)

		{
		command = ENextConfig;
		}
	else
		err = KErrArgument;

	if (err != KErrNone)
		return err;
	
	//get the shape
	beg = skipws(end);
	end = skiptok(beg);
	if (end - beg != 4 || _strnicmp(beg, "rect", 4) != 0)
		return KErrArgument;
		
	// get the parameters
	beg = skipws(end);
	char* end2;
	TInt x = strtol(beg, &end2, 10);
	if (beg == end2 || *end2++ != ',')
		return KErrArgument;
	beg = end2;
	TInt y = strtol(beg, &end2, 10);
	if (beg == end2)
		return KErrArgument;
	beg = skipws(end2);
	TInt w = strtol(beg, &end2, 10);
	if (beg == end2 || *end2++ != ',')
		return KErrArgument;
	beg = end2;
	TInt h = strtol(beg, &end2, 10);
	if (beg == end2)
		return KErrArgument;
	
	VKRect* pRect = new VKRect(data, command, x, y, w, h);
	if (!pRect)
		return KErrNoMemory;
	return iVirtualKeys.Append(pRect);

	}


TInt DWinsUi::DoDefineVirtualKey(TAny* aPtr, const char* aValue)
	{
	return static_cast<DWinsUi*>(aPtr)->DefineVirtualKey(aValue);
	}

TInt DWinsUi::DefineVirtualKey(const char* aValue)
	{
	// Get the key to map
	const char* beg = skipws(aValue);
	const char* end = skiptok(beg);
	TInt key = iKeyboard.GetEPOCKeyCode(TPtrC8((const TUint8*)beg, end-beg));
	if (key == KErrNotFound)
		return key;

	//get the shape
	beg = skipws(end);
	end = skiptok(beg);
	if (end - beg != 4 || _strnicmp(beg, "rect", 4) != 0)
		return KErrArgument;
		
	// get the parameters
	beg = skipws(end);
	char* end2;
	TInt x = strtol(beg, &end2, 10);
	if (beg == end2 || *end2++ != ',')
		return KErrArgument;
	beg = end2;
	TInt y = strtol(beg, &end2, 10);
	if (beg == end2)
		return KErrArgument;
	beg = skipws(end2);
	TInt w = strtol(beg, &end2, 10);
	if (beg == end2 || *end2++ != ',')
		return KErrArgument;
	beg = end2;
	TInt h = strtol(beg, &end2, 10);
	if (beg == end2)
		return KErrArgument;
	
	VKRect* pRect = new VKRect(key, EKey, x, y, w, h);
	if (!pRect)
		return KErrNoMemory;
	return iVirtualKeys.Append(pRect);
	}


LOCAL_C TInt readBitmapInfo(PBITMAPINFOHEADER aHeader, const char* aFileName)
	{
	PBITMAPFILEHEADER pbmfh=NULL;
	PBITMAPINFOHEADER pbmih=NULL;
	TInt bfOffBits;

	HANDLE fh=CreateFileA(aFileName,GENERIC_READ,NULL,NULL,OPEN_EXISTING,NULL,NULL);
	if (!fh || fh==INVALID_HANDLE_VALUE)
		return KErrNotFound;

	TInt r=KErrNone;

	// read in the bitmap file header.  save the offset to bits.
	pbmfh = (PBITMAPFILEHEADER)LocalAlloc(LPTR, sizeof(BITMAPFILEHEADER));
	if (pbmfh==NULL)
		{
		r=KErrNotFound;
        goto exit;
		}
	DWORD bytesRead;
	ReadFile(fh, (LPVOID)pbmfh, sizeof(BITMAPFILEHEADER), &bytesRead, NULL);
	bfOffBits=pbmfh->bfOffBits;

	// read in the bitmap info header and the color table right after it.
	pbmih = (PBITMAPINFOHEADER)LocalAlloc(LPTR, bfOffBits- sizeof(BITMAPFILEHEADER));
	if (pbmih==NULL)
		{
		r=KErrNotFound;
        goto exit;
		}
	ReadFile(fh, (LPVOID)pbmih, bfOffBits-sizeof(BITMAPFILEHEADER),&bytesRead,NULL);
	*aHeader=*pbmih;
exit:
	LocalFree(LocalHandle ((LPSTR)pbmih));
	LocalFree(LocalHandle ((LPSTR)pbmfh));
	CloseHandle(fh);
	return r;
	}

HBITMAP readBitmap(HDC aHdc, const char* aFileName)
//
// reads a BMP file from disk and returns a HBITMAP
//
	{
	HBITMAP hbm=NULL;
	PBITMAPFILEHEADER pbmfh=NULL;
	PBITMAPINFOHEADER pbmih=NULL;
	TUint8 *pBits=NULL;
	TInt bfOffBits;
	TInt nbytes;

	HANDLE fh=CreateFileA(aFileName, GENERIC_READ, NULL, NULL, OPEN_EXISTING, NULL, NULL);
	if (!fh || fh==INVALID_HANDLE_VALUE)
		return NULL;

	nbytes=GetFileSize((HANDLE)fh, NULL);
	// read in the bitmap file header.  save the offset to bits.
	pbmfh = (PBITMAPFILEHEADER)LocalAlloc(LPTR, sizeof(BITMAPFILEHEADER));
	if (pbmfh==NULL)
        goto exit;
	DWORD bytesRead;
	ReadFile(fh, (LPVOID)pbmfh, sizeof(BITMAPFILEHEADER),&bytesRead,NULL);
	bfOffBits=pbmfh->bfOffBits;

	// read in the bitmap info header and the color table right after it.
	pbmih = (PBITMAPINFOHEADER)LocalAlloc(LPTR, bfOffBits- sizeof(BITMAPFILEHEADER));
	if (pbmih==NULL)
        goto exit;
	ReadFile(fh, (LPVOID)pbmih, bfOffBits-sizeof(BITMAPFILEHEADER),&bytesRead,NULL);

	// finally read in the bit data.
	pBits = (PBYTE)LocalAlloc (LPTR, (nbytes - bfOffBits));
	if (pBits==NULL)
        goto exit;
	ReadFile(fh, (LPVOID)pBits, nbytes-bfOffBits,&bytesRead,NULL);
		
	hbm=CreateDIBitmap(aHdc, pbmih, CBM_INIT, pBits,(PBITMAPINFO) pbmih, DIB_RGB_COLORS);
exit:
	LocalFree(LocalHandle ((LPSTR)pBits));
	LocalFree(LocalHandle ((LPSTR)pbmih));
	LocalFree(LocalHandle ((LPSTR)pbmfh));
	CloseHandle(fh);
	return hbm;
	}

void LoadFasciaBitmap(TInt aScreen)
	{
	HDC hdc=GetDC(TheWin[aScreen]);
	RECT windowRect = {0};
	windowRect.right=systemIni->iScreens[aScreen]->iXYInputWidth;
	windowRect.bottom=systemIni->iScreens[aScreen]->iXYInputHeight;
	HBITMAP screenBitmap=readBitmap(hdc, systemIni->iScreens[aScreen]->iFasciaFileName);
	if (screenBitmap==NULL)
		{
		screenBitmap=CreateCompatibleBitmap(hdc, windowRect.right-windowRect.left, windowRect.bottom-windowRect.top);
		HDC hdcMem=CreateCompatibleDC(hdc);
		SelectObject(hdcMem, screenBitmap);
		PatBlt(hdcMem, 0, 0, windowRect.right-windowRect.left, windowRect.bottom-windowRect.top, BLACKNESS);
		DeleteDC(hdcMem);
		}
	__ASSERT_ALWAYS(screenBitmap!=NULL,Fault(EGuiCreateBitmap));
	TheScreenBitmap[aScreen]=screenBitmap;

	DrawLeds();

	ReleaseDC(TheWin[aScreen], hdc);
	}
TBool DWinsUi::MultiTouchEnabled() const
	{
	return iMultiTouchEnabled;
	}

TBool DWinsUi::GCEEnabled() const
	{
	return iGCEEnabled;
	}

TInt DWinsUi::MultiTouchProximityStep() const
	{
	return iMultiTouchProximityStep;
	}

TInt DWinsUi::MultiTouchPressureStep() const
	{
	return iMultiTouchPressureStep;
	}

TBool DWinsUi::OnDigitizer(TInt aX, TInt aY) const
	{
	if (!iDigitizerEnabled)
		return EFalse;
	switch(CurrentFlipState[0])
		{
		case EEmulatorFlipRestore:
			{
			aX -= iDigitizerOffsetX;
			aY -= iDigitizerOffsetY;
			break;
			}
		case EEmulatorFlipInvert:
			{
			aX -= systemIni->iScreens[0]->iScreenWidth - iDigitizerOffsetX - iDigitizerWidth;
			aY -= systemIni->iScreens[0]->iScreenHeight - iDigitizerOffsetY - iDigitizerHeight;
			break;
			}
		case EEmulatorFlipRight:
			{
			TInt oldY = aY;
			aY = aX - (systemIni->iScreens[0]->iScreenHeight - iDigitizerOffsetY - iDigitizerHeight);
			aX = oldY - iDigitizerOffsetX;
			break;
			}
		case EEmulatorFlipLeft:
			{
			TInt oldY = aY;
			aY = aX - iDigitizerOffsetY;
			aX = oldY - (systemIni->iScreens[0]->iScreenWidth - iDigitizerOffsetX - iDigitizerWidth);
			break;
			}
		}
	return (TUint(aX) < TUint(iDigitizerWidth) && TUint(aY) < TUint(iDigitizerHeight));
	}

LOCAL_C void addMouseEvent(TRawEvent::TType aType,TInt aXpos,TInt aYpos)
//
// Add a mouse event.
//
	{
	if (systemIni->OnDigitizer(aXpos, aYpos))
		{
		TRawEvent v;
		v.Set(aType,aXpos,aYpos);
		TheEventQ.Add(v);
		}
	}

LOCAL_C void addMouseEvent(TRawEvent::TType aType,TInt aXpos,TInt aYpos,TInt aZpos, TInt aPointerId=0)
//
// Add a multitouch mouse event.
//
	{
	if (systemIni->OnDigitizer(aXpos, aYpos))
		{
		TRawEvent v;
		v.Set(aType,aXpos,aYpos, aZpos);
		v.SetPointerNumber(static_cast<const TUint8>(aPointerId));
		TheEventQ.Add(v);
		}
	}
LOCAL_C void addKeyEvent(TRawEvent::TType aType,TInt aKey)
	{
	TRawEvent v;
	v.Set(aType, aKey);
	TheEventQ.Add(v);
	}


LOCAL_C void SwitchConfiguration(TInt aData, TBool aSendFlipKey = ETrue)
	{
	if (aData < 0 || aData >= masterIni->iSystemInis.Count())
		return;

	CurrentConfiguration = aData;
	systemIni = masterIni->iSystemInis[aData];
	
	//get the correct fascia bitmaps
	TInt screens=systemIni->iScreens.Count();
	TInt i;
	TUint disabledWinType=ENormalResolution;
	for(i=0;i<screens;i++)
		{
		DeleteObject(TheScreenBitmap[i]);
		LoadFasciaBitmap(i);
		if (masterIni->iBufferSet[i].iDisplayState!=ENormalResolution)
			{
			disabledWinType=masterIni->iBufferSet[i].iDisplayState;
			}
		}
	
	//update the window title
	if (disabledWinType!=ENormalResolution && disabledWinType < 4)	//hardwired 4 because the code below is hardwired
		{	//string may be multi-part indexed by disable type, or it may not
		CHAR* firstsemi=strchr(systemIni->iWindowTitle,';');
		CHAR* secondsemi=NULL;
		if (firstsemi)
			{
			secondsemi=strchr(firstsemi+1,';');
			}
		if (firstsemi&&secondsemi)
			{
			*firstsemi='\0';
			*secondsemi='\0';
			char* ptr[4]={0,systemIni->iWindowTitle,firstsemi+1,secondsemi+1};
			SetWindowTextA(TheControlWin, ptr[disabledWinType]);
			*firstsemi=';';
			*secondsemi=';';
			}
		else
			{
			SetWindowTextA(TheControlWin, systemIni->iWindowTitle);
			}
		
		}
	else
		{
		SetWindowTextA(TheControlWin, systemIni->iWindowTitle);
		}
	//resize and repaint the current window anyway.
	//the text window server doesn't respond to orientation messages
	for(i=0;i<screens;i++)
		{
		InvalidateRect(TheWin[i], NULL, false);
		SendMessage(TheWin[i], WM_FLIP_MESSAGE, systemIni->iScreens[i]->iScreenRotation,0);
		}

	//pass on the orientation key to the windows server
	if (aSendFlipKey)
		{
		if (!WinsGuiPowerHandler->iStandby)
			{
			addKeyEvent(TRawEvent::EKeyDown, systemIni->iInitialFlipMsg);
			addKeyEvent(TRawEvent::EKeyUp, systemIni->iInitialFlipMsg);
			}
		else
			{
			//remember the flip message so we can send it to the window server when we come out of standby
			SavedFlipMessage = systemIni->iInitialFlipMsg;
			}
		}
	}
/**
Sets the specified screen to the given width and height, if available.

The configurations are searched to find a match, taking the display state into
account. If no configuration is available, the request is ignored.

@param aScreenNumber	the screen index
@param aWidth 			the desired width
@param aHeight			the desired height
**/
void DMasterIni::SetDisplaySize(TInt aDisplayNumber, TInt aWidth, TInt aHeight)
	{
	TInt displayCount = iBufferSet.Count();

	if (aDisplayNumber < 0 || aDisplayNumber >= displayCount)
		{
		// Invalid screen number, discard.
		return;
		}

	if (iBufferSet[aDisplayNumber].iDisplayState != ENormalResolution)
		{
		// No (non-zero) resolutions available, discard.
		return;
		}

	TInt count = iSystemInis.Count();
	TInt index;
	for (index = 0; index < count; index++)
		{
		DWinsUi* newIni = masterIni->iSystemInis[index];
		DScreenProperties* newProps = newIni->iScreens[aDisplayNumber];

		if (newProps->iScreenWidth == aWidth && newProps->iScreenHeight == aHeight)
			{
			// Found a potential match. Check other screens match their current size.
			if (newIni == systemIni)
				{
				// Current configuration, already in use. Nothing to do.
				break;
				}
			
			TInt display;
			for (display = 0; display < displayCount; display++)
				{
				if (display == aDisplayNumber)
					{
					// No need to check the display we are changing
					continue;
					}

				DScreenProperties* currentPropsN = systemIni->iScreens[display];
				DScreenProperties* newPropsN = newIni->iScreens[display];
				
				if (newPropsN->iScreenWidth != currentPropsN->iScreenWidth ||
						newPropsN->iScreenHeight != currentPropsN->iScreenHeight)
					{
					// Resolution mismatch, try next configuration.
					break;
					}
				}
			
			if (display == displayCount)
				{
				// Match found, switch to this configuration and stop. Force
				// rotation to the same as the current rotation.
				newProps->iScreenRotation = systemIni->iScreens[aDisplayNumber]->iScreenRotation;
				SwitchConfiguration(index);
				break;
				}
			}
		}
	}


void DMasterIni::SetBufferFormat(TInt aDisplayNumber, TUint aAggregateSize, RDisplayChannel::TPixelFormat aPixelFormat)
	{
	TInt displayCount = iBufferSet.Count();

	if (aDisplayNumber < 0 || aDisplayNumber >= displayCount)
		{
		// Invalid screen number, discard.
		return;
		}
	
	LPBITMAPV4HEADER info = &iBufferSet[aDisplayNumber].iInfo;
	
	// update the bitmap header taking in consideration the new pixel format
	switch (aPixelFormat)
		{
		case EUidPixelFormatXRGB_4444:
		case EUidPixelFormatARGB_4444:
			info->bV4BitCount=16;
			info->bV4V4Compression = BI_BITFIELDS;
			info->bV4RedMask   = 0x0F00;
			info->bV4GreenMask = 0x00F0;
			info->bV4BlueMask  = 0x000F;
			break;
		case EUidPixelFormatRGB_565:
			info->bV4BitCount=16;
			info->bV4V4Compression = BI_BITFIELDS;
			info->bV4RedMask   = 0xF800;
			info->bV4GreenMask = 0x07E0;
			info->bV4BlueMask  = 0x001F;
			break;
		case EUidPixelFormatXRGB_8888:	// Really 32bpp, but top 8 unused
		case EUidPixelFormatARGB_8888:
		case EUidPixelFormatARGB_8888_PRE:
			info->bV4BitCount=32;
			info->bV4V4Compression = BI_RGB;
			// Mask is implicit for BI_RGB compression
			break;
		default:
			// We got an error, it seems. Let's ignore the message
			return;
		}
	iBufferSet[aDisplayNumber].iBufferFormat.iPixelFormat = aPixelFormat;
	
	// taking advantage of limiting the width and size to KMaxTInt16
	TInt width = aAggregateSize & 0x0000ffff;
	TInt height = (aAggregateSize >> 16) & 0x0000ffff;

	// let's deal with the new size just received
	iBufferSet[aDisplayNumber].iBufferFormat.iSize.iWidth = width;
	iBufferSet[aDisplayNumber].iBufferFormat.iSize.iHeight = height;
	
	// update the bitmap header, taking in consideration the rotation
	switch (CurrentFlipState[aDisplayNumber])
		{
		case EEmulatorFlipRestore:
		case EEmulatorFlipInvert:
			info->bV4Width = width;
			info->bV4Height = -height;
			break;
		case EEmulatorFlipLeft:
		case EEmulatorFlipRight:
			info->bV4Width = height;
			info->bV4Height = -width;
			break;
		}
	// finally, update the image size
	SetImageSize(aDisplayNumber);
	}

void DMasterIni::SetImageSize(TInt aScreenNumber)
	{
	TInt displayCount = iBufferSet.Count();

	if (aScreenNumber >= 0 && aScreenNumber < displayCount)
		{
		LPBITMAPV4HEADER info = &iBufferSet[aScreenNumber].iInfo;
		TInt bpp = _ALIGN_UP(info->bV4BitCount, 16); //12 & 16 --> 16 ; 24 & 32 --> 32
		TInt widthInBpp = info->bV4Width * bpp;
		//rounding to 32 bits (4 octets) and converting, then, bits to octets;
		TInt scanLineInBytes = _ALIGN_UP(widthInBpp, 32) >> 3;
		// info->bV4Height is negative or zero
		info->bV4SizeImage = -info->bV4Height * scanLineInBytes;
		}
	}

LOCAL_C void NextConfiguration()
	{
	TInt config = CurrentConfiguration;
	if (++config == masterIni->iSystemInis.Count())
		config = 0;
	SwitchConfiguration(config);
	}



LOCAL_C TBool ProcessedByEmulatorKey(TInt aScanCode, HWND hWnd,TUint message,TUint wParam,TUint lParam)
	{

	TBool rVal = EFalse;
	rVal = ETrue;
	for (TInt i=0;i<systemIni->iControlHotKeys.Count();i++)//check key combinations
		{
		if (systemIni->iControlHotKeys[i]->CheckCombinationPressed()) 
			{				
			switch (systemIni->iControlHotKeys[i]->iCommand)
				{
				
				case ENextConfig:
					NextConfiguration();
					break;
		
				case ESelectConfig:
					SwitchConfiguration(systemIni->iControlHotKeys[i]->iData);
					break;
							
				}
			return ETrue;
			}
		}
	switch (aScanCode)
	{
	
	case EStdKeyF4:
		{
		// Simulate a change of media card
		TInt irq = NKern::DisableAllInterrupts();
		if (*Wins::MediaDoorOpenPtr())
			{
			*Wins::CurrentPBusDevicePtr() += 1;
			if (*Wins::CurrentPBusDevicePtr() == 2)
				{
				*Wins::CurrentPBusDevicePtr() = -1;
				}
			}
		NKern::RestoreInterrupts(irq);
		
		// pass on to the windows system so that if
		// Alt-F4 is pressed the window will close
		if (hWnd)
			DefWindowProcA(hWnd,message,wParam,lParam);
		break;
		}

	default:
		rVal = EFalse;
		break;
	}
	return rVal;
	}

LOCAL_C void MultiChildWndPointer(TUint aMessage,TInt aXpos,TInt aYpos, TInt aZ, TInt aPointerId)
//
// Handle a multi-touch pointer event in the Symbian OS screen window 
//
	{
	TRawEvent::TType eventType=TRawEvent::ENone;
	CHAR buf[50];
	
	if (aZ <= TheMultiTouch->iZMaxRange) //negative
		{
		eventType = TRawEvent::EPointer3DOutOfRange;
		wsprintf((LPTSTR)buf, (LPCTSTR)TEXT("Out Of Range"));
		SendMessage(hwndStatus, SB_SETTEXT, aPointerId , (LPARAM)(buf));					
		}
	else 
		{
		wsprintf((LPTSTR)buf, (LPCTSTR)TEXT("%d: %d,%d,%d"), aPointerId, aXpos,aYpos,aZ);
		SendMessage(hwndStatus, SB_SETTEXT, aPointerId , (LPARAM)(buf));					
		switch (aMessage)
	    	{
			case WM_MOUSEMOVE:
				{
				eventType=TRawEvent::EPointerMove;
				break;
				}
			case WM_LBUTTONDOWN:
				{
				SetCapture(TheChildWin[0]);
				eventType = TRawEvent::EButton1Down;
				}
				break;
			case WM_LBUTTONUP:
				{
				ReleaseCapture();
				eventType = TRawEvent::EButton1Up;
				break;
				}
			case WM_RBUTTONDOWN:
				{
				eventType = TRawEvent::EButton3Down;
				break;
				}
			case WM_RBUTTONUP:
				{
				eventType = TRawEvent::EButton3Up;
				break;
				}
			case WM_MOUSEWHEEL:
				{
				eventType = TRawEvent::EPointerMove;
				break;
				}
			default:
				return;
			}
		}

	if (!WinsGuiPowerHandler->iStandby)
		{
		addMouseEvent(eventType, aXpos, aYpos, aZ, aPointerId);
		}
	}

LOCAL_C void ChildWndPointer(TUint message,TInt aXpos,TInt aYpos)
//
// Handle a pointer event in the Symbian OS screen window
//
	{
	// Enable the multitouch if the cursor is inside the main client window
	if (DMultiTouch::iMultiTouchCreated)
		{
		RECT client;
		WINDOWINFO info;
		GetWindowInfo(TheChildWin[0], &info);
		POINT pt = {aXpos+(TInt)info.rcClient.left, aYpos+(TInt)info.rcClient.top};
		if (GetWindowRect(TheChildWin[0], &client) &&
				(PtInRect(&client,pt)!=NULL) && !DMultiTouch::iMultiTouchTempEnabled)	// within the window
			{
			if (systemIni->MultiTouchEnabled() && systemIni->GCEEnabled())
				{
				if(TheMultiTouch->Register())	// Register successfully
					{
					DMultiTouch::iMultiTouchTempEnabled = TRUE;
					//Show the status bars at the bottom of the emulator
					SetWindowPos(hwndStatus,0,0,0,0,0,SWP_SHOWWINDOW);
					SetFocus(TheWin[0]);
					SetCursor(LoadCursorA(NULL,MAKEINTRESOURCEA(32512)));
					}
				}
			}
		}
	TRawEvent::TType eventType=TRawEvent::ENone;
	switch (message)
    	{
	case WM_MOUSEMOVE:
		eventType=TRawEvent::EPointerMove;
		break;
	case WM_LBUTTONDOWN:
		{
		SetCapture(TheChildWin[0]);
		eventType=TRawEvent::EButton1Down;
		}
		break;
	case WM_LBUTTONUP:
		ReleaseCapture();
		eventType=TRawEvent::EButton1Up;
		break;
	case WM_RBUTTONDOWN:
		eventType=TRawEvent::EButton3Down;
		break;
	case WM_RBUTTONUP:
		eventType=TRawEvent::EButton3Up;
		break;
	case WM_MBUTTONDOWN:
		eventType=TRawEvent::EButton2Down;
		break;
	case WM_MBUTTONUP:
		eventType=TRawEvent::EButton2Up;
		break;
		}
	if (!WinsGuiPowerHandler->iStandby)
		{
		addMouseEvent(eventType, aXpos, aYpos);
		}
	}

LOCAL_C void FrameWndPointer(TUint message,TInt aXpos,TInt aYpos, TInt aScreenNumber, TInt aPointerId = 0)
//
// Handle a frame wnd pointer event.
//
	{
	static bool	processingScreenOn=FALSE;
	TEmulCommand command = ENoCommand;
	TInt commandData = 0;
	TBool mouseEvent = ETrue;
	
	TRawEvent::TType eventType=TRawEvent::ENone;
	
	TViewport& viewport = systemIni->iScreens[aScreenNumber]->iViewport;
	aXpos += viewport.GetViewportOffsetX(); // make mouse-coords relative to fascia edge even if window is scrolled
	aYpos += viewport.GetViewportOffsetY();
	
	switch (message)
    	{
	case WM_MOUSEMOVE:
		{
		TInt newX, newY;
		systemIni->TranslateMouseCoords(CurrentFlipState[0], aXpos, aYpos, systemIni->iScreens[0]->iXYInputWidth, systemIni->iScreens[0]->iXYInputHeight, newX, newY);

		if (aPointerId == 0)
			{ // only system pointer changes shape
		if (systemIni->GetVirtualKey(command, newX, newY) >= 0)
			{
			HMODULE hmodule = GetModuleHandleA("winsgui.dll");
			SetCursor(LoadCursorA((HINSTANCE)hmodule,MAKEINTRESOURCEA(OVERKEY)));		//hand cursor
			}
		else
			SetCursor(LoadCursorA(NULL,MAKEINTRESOURCEA(32512))); //ICD_ARROW
				}

		eventType=TRawEvent::EPointerMove;
		
		}
		break;
	case WM_LBUTTONDOWN:
		{
		SetCapture(TheWin[0]);
		//check the configuration
		TInt newX, newY;
		
		//if the emulator screen is rotated, rotate/flip the current mouse cursor position
		//so it can be checked to see if it is in a key region.
		systemIni->TranslateMouseCoords(CurrentFlipState[0], aXpos, aYpos, systemIni->iScreens[0]->iXYInputWidth, systemIni->iScreens[0]->iXYInputHeight, newX, newY);
		commandData = systemIni->GetVirtualKey(command, newX, newY);
		
		if (commandData >= 0)
			{
			eventType=TRawEvent::EKeyDown;
			mouseEvent = EFalse;
			systemIni->SetVirtualKey(ETrue, commandData, command);
			}
		else
			eventType=TRawEvent::EButton1Down;
		}
		break;
	case WM_LBUTTONUP:
		ReleaseCapture();
		if (processingScreenOn)
			{
			// ignore button up - button down was switching things on
			processingScreenOn=FALSE;
			return;
			}
		if (systemIni->WasVirtualKey(commandData, command))
			{
			eventType=TRawEvent::EKeyUp;
			mouseEvent = EFalse;
			systemIni->SetVirtualKey(EFalse, EStdKeyNull, ENoCommand);
			}
		else
			eventType=TRawEvent::EButton1Up;
		break;
	case WM_RBUTTONDOWN:
		eventType=TRawEvent::EButton3Down;
		break;
	case WM_RBUTTONUP:
		eventType=TRawEvent::EButton3Up;
		break;
	case WM_MBUTTONDOWN:
		eventType=TRawEvent::EButton2Down;
		break;
	case WM_MBUTTONUP:
		eventType=TRawEvent::EButton2Up;
		break;
		}
	if (mouseEvent)
		{
		
		if (!WinsGuiPowerHandler->iStandby)
			{
			/*
			mouse events are relative to the child window position
			and are clipped to the digitzer region in addMouseEvent
			so all the mouse clicks are passed on here after being translated 
			to the child window coordinate system (under the current rotation)
			*/
			TInt newX, newY;
			switch (CurrentFlipState[0])
				{
				case EEmulatorFlipRestore:
				default:
					newX = aXpos - systemIni->iScreens[0]->iScreenOffsetX;
					newY = aYpos - systemIni->iScreens[0]->iScreenOffsetY;
					break;
				case EEmulatorFlipInvert:
					newX = aXpos - (systemIni->iScreens[0]->iXYInputWidth - systemIni->iScreens[0]->iScreenWidth - systemIni->iScreens[0]->iScreenOffsetX);
					newY = aYpos - (systemIni->iScreens[0]->iXYInputHeight - systemIni->iScreens[0]->iScreenHeight - systemIni->iScreens[0]->iScreenOffsetY);
					break;
				case EEmulatorFlipLeft:
					newX = aXpos - systemIni->iScreens[0]->iScreenOffsetY;
					newY = aYpos - (systemIni->iScreens[0]->iXYInputWidth - systemIni->iScreens[0]->iScreenWidth - systemIni->iScreens[0]->iScreenOffsetX);
					break;	
				case EEmulatorFlipRight:
					newX = aXpos - (systemIni->iScreens[0]->iXYInputHeight - systemIni->iScreens[0]->iScreenHeight - systemIni->iScreens[0]->iScreenOffsetY);
					newY = aYpos - systemIni->iScreens[0]->iScreenOffsetX;
					break;
				}
			addMouseEvent(eventType, newX, newY);
			}
		}
	else if ((((message == WM_LBUTTONDOWN && command == EKey) && !ProcessedByEmulatorKey((TUint8)commandData,0,0,0,0)))
			|| (message == WM_LBUTTONUP))
		{
			switch (command)
			{
			case EKey:
				if (!WinsGuiPowerHandler->iStandby)
					addKeyEvent(eventType, (TUint8)commandData);
				break;

			case ENextConfig:
				NextConfiguration();
				break;
			
			case ESelectConfig:
				SwitchConfiguration(commandData);
				break;
			}
		}
	}

LOCAL_C TInt ScreenFromHWND(HWND aHwnd,HWND* pWin)
	{
	TInt screens=systemIni->iScreens.Count();
	TInt r=-1;
	for(TInt i=0;i<screens;i++)
		{
		if(pWin[i]==aHwnd)
			{
			r=i;
			break;
			}
		}
	return r;
	}
void MultiTouchWndPointer(TUint message,TInt aXpos,TInt aYpos, TInt aZ, TInt aPointerId)
	{
	WINDOWINFO info;
	info.cbSize = sizeof(WINDOWINFO);
	if (GetWindowInfo(TheWin[0], &info))
		{
		POINT pt = {aXpos,aYpos};
		if (PtInRect(&info.rcWindow,pt))
			{
			RECT client;
			if (GetWindowRect(TheChildWin[0], &client) && PtInRect(&client,pt))	// within the window
				{
				MultiChildWndPointer(message, aXpos-client.left, aYpos-client.top, aZ, aPointerId);
				}
			else  
				{		
				//	Disable the multitouch if the cursor is outside the application window
				if (DMultiTouch::iMultiTouchTempEnabled)	// within the window
					{
					DMultiTouch::iMultiTouchTempEnabled = FALSE;
					if(TheMultiTouch->UnRegister())
						{
						SetWindowPos(hwndStatus,0,0,0,0,0,SWP_HIDEWINDOW);
						}
					}
				FrameWndPointer(message, aXpos-info.rcClient.left, aYpos-info.rcClient.top, 0, aPointerId);	
				}
			}
		}
	}

LOCAL_C DScreenProperties* ScreenPropsFromHWND(HWND aHwnd, HWND* pWin)
	{
	TInt screenNumber =  ScreenFromHWND(aHwnd, pWin);
	
	if(screenNumber >=0)
		{
		return systemIni->iScreens[screenNumber];
		}
	return NULL;

	}


TInt APIENTRY childWinProc(HWND hWnd,TUint message,TUint wParam,TUint lParam)
//
// The child window function.
//
	{
	TInt screenNumber = 0;
	TRawEvent v;
    switch (message)
    	{
	case WM_FLIP_MESSAGE: // pass the flip message onto the parent window
		{
		screenNumber =ScreenFromHWND(hWnd,TheChildWin);
		if(TUint(screenNumber) < TUint(systemIni->iScreens.Count()))
			PostMessageA(TheWin[screenNumber],WM_FLIP_MESSAGE,wParam,NULL);
		break;
		}
	case WM_LBUTTONDOWN:
	case WM_LBUTTONUP:
	case WM_MOUSEMOVE:
	case WM_RBUTTONDOWN:
	case WM_RBUTTONUP:
	case WM_MBUTTONDOWN:
	case WM_MBUTTONUP:
		{
		if (DMultiTouch::iMultiTouchTempEnabled)
			{
			DMultiTouch::iMultiTouchTempEnabled = FALSE;
			}
		screenNumber=ScreenFromHWND(hWnd,TheChildWin);
		if(screenNumber==0)
			{
			ChildWndPointer(message,(TInt16)(lParam&0xFFFF),(TInt16)((lParam>>16)&0xFFFF));
			}
		break;
		}
    case WM_PAINT:
		if (!PaintWindowFromBuffer(hWnd))
			{
			// Original behaviour
			ValidateRect(hWnd,NULL);
			v.Set(TRawEvent::ERedraw);
			TheEventQ.Add(v);
			}
        break;
    case WM_ACTIVATE:
	case WM_SYSKEYDOWN:
	case WM_KEYDOWN:
	case WM_SYSKEYUP:
	case WM_KEYUP:
		Fault(EGuiChildWinProc);
		break;
    case WM_DESTROY:
		break;

	case WM_CHAR:
	case WM_SYSCHAR:
	case WM_DEADCHAR:
	case WM_SYSDEADCHAR:
        break;

	case WMU_SET_DISPLAY_BUFFER:
		screenNumber = ScreenFromHWND(hWnd, TheChildWin);
		if (TUint(screenNumber) < TUint(systemIni->iScreens.Count()))
			{
			masterIni->iBufferSet[screenNumber].iDisplayBuffer = (LPVOID)lParam;
			}
		break;
	case WMU_SET_DISPLAY_SIZE:
		screenNumber = ScreenFromHWND(hWnd, TheChildWin);
		masterIni->SetDisplaySize(screenNumber, wParam, lParam);
		break;
		
	case WMU_SET_BUFFER_FORMAT:
		screenNumber = ScreenFromHWND(hWnd, TheChildWin);
		masterIni->SetBufferFormat(screenNumber, wParam, (RDisplayChannel::TPixelFormat) lParam);
		break;
		
 	default:
        return(DefWindowProcA(hWnd,message,wParam,lParam));
	    }
    return(FALSE);
	}


LOCAL_C TBool PaintWindowFromBuffer(HWND hWnd)
	{
	TInt screenNumber = ScreenFromHWND(hWnd,TheChildWin);
	if (TUint(screenNumber) >= TUint(masterIni->iBufferSet.Count()))
		{
		return EFalse;
		}

	LPVOID displayBuffer = masterIni->iBufferSet[screenNumber].iDisplayBuffer;
	if (!displayBuffer)
		{
		return EFalse;
		}

	TInt   frameOffset = masterIni->iBufferSet[screenNumber].iScreenBuffer.iDisplayBufferOffset;
	displayBuffer=LPVOID(frameOffset+(char*)displayBuffer);

	PAINTSTRUCT ps;
	BeginPaint(hWnd, &ps);

	// Paint directly from the buffer to the window
	LPBITMAPINFO info = (LPBITMAPINFO)&masterIni->iBufferSet[screenNumber].iInfo;
	WORD width = (WORD)info->bmiHeader.biWidth;
	WORD height = (WORD)-info->bmiHeader.biHeight;	// stored -ve in info
	SetDIBitsToDevice(ps.hdc,
						0, 0, 	// Dst X, Y
						width, height,	// Src W, H
						0, 0,	// Src X, Y
						0,		// Src offset to first line
						height,	// Src lines available
						displayBuffer,	// Src pointer to pixels
						info,			// Src info
						DIB_RGB_COLORS);

	EndPaint(hWnd, &ps);

	return TRUE;
	}


LOCAL_C void CalcTextPos(TInt aScreen, TInt& aX, TInt& aY)
	{
	switch (CurrentFlipState[aScreen])
		{
	case EEmulatorFlipInvert:
		aX = systemIni->iScreens[aScreen]->iXYInputWidth-(systemIni->iScreens[aScreen]->iScreenOffsetX+systemIni->iScreens[aScreen]->iScreenWidth);
		aY = systemIni->iScreens[aScreen]->iXYInputHeight-(systemIni->iScreens[aScreen]->iScreenOffsetY+systemIni->iScreens[aScreen]->iScreenHeight);
		break;
	case EEmulatorFlipLeft:
		aX = systemIni->iScreens[aScreen]->iScreenOffsetY;
		aY = systemIni->iScreens[aScreen]->iXYInputWidth-(systemIni->iScreens[aScreen]->iScreenOffsetX+systemIni->iScreens[aScreen]->iScreenWidth);
		break;
	case EEmulatorFlipRight:
		aX = systemIni->iScreens[aScreen]->iXYInputHeight-(systemIni->iScreens[aScreen]->iScreenOffsetY+systemIni->iScreens[aScreen]->iScreenHeight);
		aY = systemIni->iScreens[aScreen]->iScreenOffsetX;
		break;
	case EEmulatorFlipRestore:
	default:
		aX = systemIni->iScreens[aScreen]->iScreenOffsetX;
		aY = systemIni->iScreens[aScreen]->iScreenOffsetY;
	break;
		}
	//subtract viewport offset here
	aX -= systemIni->iScreens[aScreen]->iViewport.GetViewportOffsetX();
	aY -= systemIni->iScreens[aScreen]->iViewport.GetViewportOffsetY();
	}

TInt APIENTRY ctrlwinProc(HWND hWnd,TUint message,TUint wParam,TUint lParam)
//
// The control window function
//
	{
	switch(message)
	{
		case WM_SYSCOMMAND:
			{
				switch(wParam)
				{
					case 1:
						{
						NextConfiguration();
						return 0;
						}
					case SC_MINIMIZE:
					case SC_RESTORE:
						{
						if (wParam == SC_RESTORE) 
							Active();
						for(TInt ix=0;ix<systemIni->iScreens.Count();ix++)
							{
							SendMessage(TheWin[ix],message,wParam,lParam);
							}
						if (wParam == SC_MINIMIZE) 
							Inactive();
						}			
				}
				return(DefWindowProcA(hWnd,message,wParam,lParam));
			}
		case WM_CLOSE: // tell all emulator screen windows to close
			{
			for(TInt i=0;i<systemIni->iScreens.Count();i++)
				{
				DestroyWindow(TheWin[i]);
				}
			DestroyWindow(hWnd);
			break;
			}
		case WM_DESTROY:
			{
			// save the main window position information
			HANDLE hSysIni;
			hSysIni = CreateFileA(systemIni->iSysIniFileName, GENERIC_WRITE, 0, 0, CREATE_ALWAYS, 0, 0);
			DScreenProperties* screenProps;
			if (hSysIni != INVALID_HANDLE_VALUE)
				{
				DWORD numWritten;
				//write an identifier into file so that program can avoid loading old version
				WriteFile(hSysIni, &KDatFileVersion, sizeof(TInt), &numWritten, 0);
				
				//record current configuration at start of file.
				WriteFile(hSysIni, &CurrentConfiguration, sizeof(TInt), &numWritten, 0);
				
				//Write out the state for each window.
				for(TInt i=0;i<systemIni->iScreens.Count();i++)
					{
					screenProps= systemIni->iScreens[i];
					
					TWindowState winState= screenProps->GetWindowState();
					WriteFile(hSysIni, &winState, sizeof(TWindowState), &numWritten, 0);
					
					
					}
				}
			CloseHandle(hSysIni);

			PostQuitMessage(KErrNone);
			break;
			}
		case WM_INPUT:
			{
			if (!DMultiTouch::iMultiTouchTempEnabled)
				{
				for(TInt ix=0;ix<systemIni->iScreens.Count();ix++)
					{
					DMultiTouch::iMultiTouchTempEnabled = TRUE;

					SendMessage(TheWin[ix],message,wParam,lParam);
					}
				}
			else if (systemIni->MultiTouchEnabled() && DMultiTouch::iMultiTouchSupported && systemIni->GCEEnabled())
				{
			   	TheMultiTouch->OnWmInput(hWnd, message, wParam, lParam,TheChildWin[0]);	
				}
			else
				{
				Fault(EGuiInvalidMultiTouch);
				}
		   	break;
			}	
		default:
			return(DefWindowProcA(hWnd,message,wParam,lParam));
	}
	return(FALSE);
	}

TInt APIENTRY winProc(HWND hWnd,TUint message,TUint wParam,TUint lParam)
//
// The border window function.
//
	{

	TRawEvent v;
	
	switch (message)
    	{
		case WM_GETMINMAXINFO:
			{
			DScreenProperties* screenProps = ScreenPropsFromHWND(hWnd, TheWin);
			if(screenProps == NULL)
				{
				return DefWindowProcA(hWnd, message, wParam, lParam);
				}
						
			MINMAXINFO* minMaxInfo = reinterpret_cast<MINMAXINFO*>(lParam);
			minMaxInfo->ptMaxTrackSize.x = screenProps->iViewport.GetMaxWindowWidth();
			minMaxInfo->ptMaxTrackSize.y = screenProps->iViewport.GetMaxWindowHeight();

			minMaxInfo->ptMaxSize.x = minMaxInfo->ptMaxTrackSize.x;
			minMaxInfo->ptMaxSize.y = minMaxInfo->ptMaxTrackSize.y;
			DefWindowProcA(hWnd, message, wParam, lParam);
			
			break;
			}
	   
    	    		
 	    	
    	case WM_SIZE:
    		{
    		DScreenProperties* screenProps = ScreenPropsFromHWND(hWnd, TheWin);
			if(screenProps == NULL)
				{
				return DefWindowProcA(hWnd, message, wParam, lParam);
				}
    		TViewport& viewport = screenProps->iViewport;
    		//update size of viewport
    		viewport.SetViewportWidth(LOWORD(lParam));
    		viewport.SetViewportHeight(HIWORD(lParam));
    		
    		
			//If resize goes beyond boundary of emulator then scroll to compensate
			TInt ox = viewport.GetViewportOffsetX();
    		TInt xs = ox + LOWORD(lParam) - viewport.GetMaxWidth();
    		if (xs>0) 
    			{    			
    			viewport.ScrollToX(ox-xs, hWnd);
    			}
    		
    		TInt oy = viewport.GetViewportOffsetY();
    		TInt ys = oy + HIWORD(lParam) - viewport.GetMaxHeight();
    		if (ys>0) 
    			{    			
    			viewport.ScrollToY(oy-ys, hWnd);
    			}
    	
    		//Adjust ranges of scroll bars
    	   	viewport.UpdateScrollBarH(hWnd);
    		viewport.UpdateScrollBarV(hWnd);
    		    		
    		
    		
    		break;    		
    		}
    	case WM_HSCROLL:
    		{
    		DScreenProperties* screenProps = ScreenPropsFromHWND(hWnd, TheWin);
			if(screenProps == NULL)
				{
				return DefWindowProcA(hWnd, message, wParam, lParam);
				}
    		TViewport& viewport = screenProps->iViewport;
    		
    		switch (LOWORD(wParam)) 
    			{
    			case SB_THUMBTRACK:
    				{
    				viewport.ScrollToX(HIWORD(wParam),hWnd);
    				break;
    				}
				case SB_PAGELEFT:
					{
					viewport.ScrollToX(viewport.GetViewportOffsetX() - viewport.GetViewportWidth(), hWnd );
					break;
					}
				case SB_PAGERIGHT:
					{
					viewport.ScrollToX(viewport.GetViewportOffsetX() + viewport.GetViewportWidth() , hWnd);
					break;
					}
				case SB_LINEUP:
					{
					viewport.ScrollToX(viewport.GetViewportOffsetX() - 1, hWnd);
					break;
					}
				case SB_LINEDOWN:
					{
					viewport.ScrollToX(viewport.GetViewportOffsetX() + 1, hWnd);
					break;
					}
    			
    			}
    		   		
   
    		break;
    		}
    	
    	case WM_VSCROLL:
    		{
    		DScreenProperties* screenProps = ScreenPropsFromHWND(hWnd, TheWin);
			if(screenProps == NULL)
				{
				return DefWindowProcA(hWnd, message, wParam, lParam);
				}
    		TViewport& viewport = screenProps->iViewport;
    		
			
			switch (LOWORD(wParam)) 
    			{
    			case SB_THUMBTRACK:
    				{
    				viewport.ScrollToY(HIWORD(wParam), hWnd);
    				break;
    				}
				case SB_PAGELEFT:
					{
					viewport.ScrollToY(viewport.GetViewportOffsetY() - viewport.GetViewportHeight() , hWnd);
					break;
					}
				case SB_PAGERIGHT:
					{
					viewport.ScrollToY(viewport.GetViewportOffsetY() + viewport.GetViewportHeight(), hWnd );
					break;
					}
				case SB_LINEUP:
					{
					viewport.ScrollToY(viewport.GetViewportOffsetY() - 1, hWnd);
					break;
					}
				case SB_LINEDOWN:
					{
					viewport.ScrollToY(viewport.GetViewportOffsetY() + 1, hWnd);
					break;
					}
    			
    			}
    		
    		break;
    		
    		}
    	
    	 		
		case WM_FLIP_MESSAGE:
			{
			DScreenProperties* screenProps = ScreenPropsFromHWND(hWnd, TheWin);
			if(screenProps == NULL)
				{
				return DefWindowProcA(hWnd, message, wParam, lParam);

				}
			
			TViewport& viewport = screenProps->iViewport;
			RECT windowRect={0,0,0,0};
			GetClientRect(hWnd, &windowRect);
			
			TInt screenNumber=ScreenFromHWND(hWnd,TheWin);
			if(TUint(screenNumber) >= TUint(systemIni->iScreens.Count()))
				break;
			PBITMAPV4HEADER info = &masterIni->iBufferSet[screenNumber].iInfo;
			CurrentFlipState[screenNumber]=(TEmulatorFlip)wParam;
			TBufferSet* bufferSet = &masterIni->iBufferSet[screenNumber];
			switch (CurrentFlipState[screenNumber])
				{
				case EEmulatorFlipRestore:
				case EEmulatorFlipInvert:
					windowRect.right=systemIni->iScreens[screenNumber]->iXYInputWidth;
					windowRect.bottom=systemIni->iScreens[screenNumber]->iXYInputHeight;
					info->bV4Width = bufferSet->iBufferFormat.iSize.iWidth;
					info->bV4Height = -bufferSet->iBufferFormat.iSize.iHeight;
					break;
				case EEmulatorFlipLeft:
				case EEmulatorFlipRight:
					windowRect.right=systemIni->iScreens[screenNumber]->iXYInputHeight;
					windowRect.bottom=systemIni->iScreens[screenNumber]->iXYInputWidth;
					info->bV4Width = bufferSet->iBufferFormat.iSize.iHeight;
					info->bV4Height = -bufferSet->iBufferFormat.iSize.iWidth;
					break;
				}
			AdjustWindowRect(&windowRect,KWinStyle,FALSE);
    		
    		
			viewport.ScrollToX(0, hWnd);
			viewport.ScrollToY(0, hWnd);

			
			screenProps->iScreenRotation = (TEmulatorFlip)wParam; 
			
						
			RECT currentWindowRect;
			GetWindowRect(hWnd,&currentWindowRect);
			InvalidateRect(hWnd,NULL,FALSE);
			MoveWindow(
				TheWin[screenNumber],
				max(currentWindowRect.left,0), // so the window doesn't disappear off the screen
				max(currentWindowRect.top,0),
				windowRect.right-windowRect.left,
				windowRect.bottom-windowRect.top,
				TRUE
				);
			// move the child window
			screenProps->iViewport.UpdateChildPos(hWnd);
			
			viewport.UpdateScrollBarH(hWnd);
    		viewport.UpdateScrollBarV(hWnd);

			InvalidateRect(hWnd,NULL,TRUE);
			UpdateWindow(hWnd);
			
			break;
			}
		case WM_SYSKEYDOWN:
		case WM_KEYDOWN:
			if (!(HIWORD(lParam)&KF_REPEAT))
				{
				
				
				TUint scanCode=DWinsKeyboard::ScanCodeToStandardKey(HIWORD(lParam));
				TUint newScanCode = systemIni->iKeyboard.ScanCodeToRemappedKey(HIWORD(lParam));
				MSG msg={hWnd,message,wParam,lParam,GetMessageTime(),GetMessagePos()};
				TranslateMessage(&msg);
				TUint charCode=0;
				// look in the message queue to get character associated with keypress
				// so long as the control, shift and alt keys aren't depressed
				if (!(HIBYTE(GetKeyState(VK_CONTROL)) && HIBYTE(GetKeyState(VK_MENU)) && HIBYTE(GetKeyState(VK_SHIFT))))
					if (PeekMessageA(&msg,hWnd,WM_CHAR,WM_CHAR,PM_NOREMOVE) &&
						scanCode == newScanCode) //no remapping
						charCode=msg.wParam;
				// Pass the character as the HIWORD of the Epoc scan code
				
				scanCode = newScanCode;
				v.Set(TRawEvent::EKeyDown,(charCode<<16)|scanCode);
				if (!ProcessedByEmulatorKey(scanCode,hWnd,message,wParam,lParam))
   					TheEventQ.Add(v);
				
				}
			break;
		case WM_TIMER:
			break;
		case WM_EMUL_POWER_ON:
			{
			TInt screenNumber=ScreenFromHWND(hWnd,TheWin);
			if(TUint(screenNumber) >= TUint(systemIni->iScreens.Count()))
				break;
//			HWND win = systemIni->iSecureDisplay ? TheSecureChildWin : TheChildWin;
			HWND win = TheChildWin[screenNumber];
			if (wParam==TRUE)
				{
				ShowWindow(win, SW_HIDE);
				ShowWindow(win, SW_SHOWNORMAL);
				if (SavedFlipMessage)
					{
					addKeyEvent(TRawEvent::EKeyDown, SavedFlipMessage);
					addKeyEvent(TRawEvent::EKeyUp, SavedFlipMessage);
					SavedFlipMessage = 0;
					}
				}
			else
				{
				ShowWindow(win, SW_SHOWNORMAL);
				ShowWindow(win, SW_HIDE);
				}
			}
			break;
		case WM_SYSKEYUP:
		case WM_KEYUP:
			{
			//get the key code, this will pick up if it has been remapped or not.
			TUint scanCode = systemIni->iKeyboard.ScanCodeToRemappedKey(HIWORD(lParam));
	   /*
		* We could do this to support generation of special characters using Alt+KeyPadNum
		* combinations, but we would need to find a way to suppress the generation of
		* home/end scancodes etc., so leave it for the moment.
					MSG msg={hWnd,message,wParam,lParam,GetMessageTime(),GetMessagePos()};
					TranslateMessage(&msg);
					TUint charCode=0;
					// look in the message queue to get character associated with keypress
					if (PeekMessageU()(&msg,hWnd,WM_CHAR,WM_CHAR,PM_NOREMOVE))
						charCode=msg.wParam;
					// Pass the character as the HIWORD of the Epoc scan code
					v.Set(TRawEvent::EKeyUp,(charCode<<16)|scanCode);
		*/
					v.Set(TRawEvent::EKeyUp,scanCode);
	    			TheEventQ.Add(v);
			break;
			}
		case WM_MOUSEMOVE:
		case WM_LBUTTONDOWN:
		case WM_LBUTTONUP:
		case WM_RBUTTONDOWN:
		case WM_RBUTTONUP:
		case WM_MBUTTONDOWN:
		case WM_MBUTTONUP:
				{
				//only handle mouse clicks on screen 0
				TInt xpos=((TInt16)(lParam&0xffff));
				TInt ypos = (TInt16)((lParam>>16)&0xFFFF);
				if (DMultiTouch::iMultiTouchTempEnabled)
					{
					MultiChildWndPointer(message,xpos,ypos,0,0);
					DMultiTouch::iMultiTouchTempEnabled = FALSE;				
					}
				else
					{
					TInt screenNumber=ScreenFromHWND(hWnd,TheWin);
					if(screenNumber!=0)
						break;
					FrameWndPointer(message,xpos,ypos,screenNumber);
					}
				break;
				}
		case WM_PAINT:
			{
			DScreenProperties* screenProps = ScreenPropsFromHWND(hWnd, TheWin);
			if(screenProps == NULL)
				{
				return DefWindowProcA(hWnd, message, wParam, lParam);
				}
    		TViewport& viewport = screenProps->iViewport;
    		TInt screenNumber=ScreenFromHWND(hWnd,TheWin);

			PAINTSTRUCT p;

			BeginPaint(hWnd,&p);
	   		HDC hdcBits;
			BITMAP bm;
    		hdcBits=CreateCompatibleDC(p.hdc);
			GetObjectA(TheScreenBitmap[screenNumber],sizeof(BITMAP),&bm);
    		SelectObject(hdcBits,TheScreenBitmap[screenNumber]);
		
			RECT windowRect;
			GetClientRect(TheWin[screenNumber],&windowRect);
			
			viewport.SetViewportHeight(windowRect.bottom);
			viewport.SetViewportWidth(windowRect.right);
			
			
			switch (CurrentFlipState[screenNumber])
				{
				case EEmulatorFlipRestore:
					{
					BitBlt(p.hdc,0,0,windowRect.right,windowRect.bottom,hdcBits,
						viewport.GetViewportOffsetX(),viewport.GetViewportOffsetY(),SRCCOPY);
					break;
					}
				case EEmulatorFlipInvert:
					{
					TInt sourceX = screenProps->iXYInputWidth - viewport.GetViewportWidth() - viewport.GetViewportOffsetX();
					if(sourceX<0)
						sourceX=0;
					TInt sourceY = screenProps->iXYInputHeight - viewport.GetViewportHeight() - viewport.GetViewportOffsetY();
					if(sourceY<0)
						sourceY=0;
					TInt sourceWidth = viewport.GetMaxWidth()-sourceX - viewport.GetViewportOffsetX();
					TInt sourceHeight = viewport.GetMaxHeight()-sourceY - viewport.GetViewportOffsetY();
					
					//when inverted it is necessary to translate the image by 1 pixel up and to the left,
					//to avoid a glitch when scrolling using ScrollWindowEx()
					POINT arrayPoints[3]={
						{sourceWidth-1,sourceHeight-1},
						{-1,sourceHeight-1},
						{sourceWidth-1,-1}
						};
					PlgBlt(p.hdc,arrayPoints,hdcBits,sourceX,sourceY,sourceWidth,sourceHeight,NULL,NULL,NULL);
					break;
					}
				case EEmulatorFlipLeft:
					{
					TInt offsetX = screenProps->iXYInputWidth- viewport.GetViewportHeight()  - viewport.GetViewportOffsetY(); 	
					TInt offsetY = viewport.GetViewportOffsetX(); 	
		
					POINT arrayPoints[3]={{0,windowRect.bottom},{0,0},{windowRect.right,windowRect.bottom}};
					PlgBlt(p.hdc,arrayPoints,hdcBits,offsetX,offsetY,viewport.GetViewportHeight(),viewport.GetViewportWidth(),NULL,NULL,NULL);
					break;
					}
				case EEmulatorFlipRight:
					{
					TInt offsetX = viewport.GetViewportOffsetY(); 
					TInt offsetY = screenProps->iXYInputHeight - viewport.GetViewportWidth() - viewport.GetViewportOffsetX(); 	
									
					POINT arrayPoints[3]={{windowRect.right,0},{windowRect.right,windowRect.bottom},{0,0}};
					PlgBlt(p.hdc,arrayPoints,hdcBits,offsetX,offsetY,viewport.GetViewportHeight(),viewport.GetViewportWidth(),NULL,NULL,NULL);
					break;
					}
				}

			
			DeleteDC(hdcBits);
			if (WinsGuiPowerHandler->iStandby)
				{
				TInt x,y;
				CalcTextPos(screenNumber, x, y);
				TextOutA(p.hdc, x, y, "Standby", 7);
				}
			else if (systemIni->iScreens[screenNumber]->iScreenOff)
				{
				TInt x,y;
				CalcTextPos(screenNumber, x, y);
				TextOutA(p.hdc, x, y, "Screen Off", 10);
				}
 			EndPaint(hWnd,&p);
			break;
			}
		case WM_ACTIVATE:
			//Added so that change in modifier keys can be detected without sending
			//EActive/EInActive to wserv as it results in switching the timers
   			if((wParam & 0xffff)!= WA_INACTIVE)
   				UpdateModifiers();
			break;
		case WM_CHAR:
		case WM_SYSCHAR:
		case WM_DEADCHAR:
		case WM_SYSDEADCHAR:
			break;
		case WM_CLOSE: //pass on message to control window, it will then destroy all e,ulator windows
			SendMessage(TheControlWin,WM_CLOSE, NULL, NULL);
			break;
		case WM_DESTROY:
			{
			DScreenProperties* screenProps = ScreenPropsFromHWND(hWnd, TheWin);
			if(screenProps == NULL)
				{
				return DefWindowProcA(hWnd, message, wParam, lParam);
				}
    					
			// save window's position information
			screenProps->iWinPlace.length = sizeof(WINDOWPLACEMENT);
			GetWindowPlacement(hWnd, &screenProps->iWinPlace);
			
			break;
			}
		case WM_INPUT:
			{
			if (systemIni->MultiTouchEnabled() && DMultiTouch::iMultiTouchSupported && systemIni->GCEEnabled())
				{
				TInt screenNumber=ScreenFromHWND(hWnd,TheWin);
				if(screenNumber==0)
					{
					TheMultiTouch->OnWmInput(hWnd, message, wParam, lParam,TheChildWin[screenNumber]);	
					}
				}
			else
				{
				Fault(EGuiInvalidMultiTouch);
				}
		   	break;
			}
		default:
	        return(DefWindowProcA(hWnd,message,wParam,lParam));
	    }
    return(FALSE);
	
	}

void SetStatusBarFont(HWND& aStatusBar)
	{
	int statwidths[] = {100,200,300,400,500,600,700,800};
	SendMessage(aStatusBar, SB_SETPARTS, sizeof(statwidths)/sizeof(int), (LPARAM)statwidths);
	HFONT hOrigFont = (HFONT) SendMessage(aStatusBar, WM_GETFONT, 0, 0);
	SetProp(aStatusBar, TEXT("PROP_ORIGINAL_FONT"), (HANDLE) hOrigFont);
	LOGFONT lf;
	GetObject(hOrigFont, sizeof(lf), &lf);
	lf.lfHeight = (long)(lf.lfHeight / 1.5);
	lf.lfWeight = FW_NORMAL;
	HFONT hFont = CreateFontIndirect(&lf);
	SetProp(aStatusBar, TEXT("PROP_ITALIC_FONT"), (HANDLE) hFont);
	hFont = (HFONT) GetProp(hwndStatus, TEXT("PROP_ITALIC_FONT"));
	SendMessage(aStatusBar, WM_SETFONT, (WPARAM) hFont, FALSE);
	}

DWORD WINAPI KernelWindowThread(LPVOID aArg)
//
// The kernel window thread.
//
	{
	HMODULE hmodule = GetModuleHandleA("winsgui.dll");
	__ASSERT_ALWAYS(hmodule!=NULL,Fault(EGuiGetModuleHandle));

	WNDCLASSA w;
	memclr(&w, sizeof(WNDCLASSA));
   	w.style=CS_OWNDC|CS_VREDRAW|CS_HREDRAW;
   	w.lpfnWndProc=(WNDPROC)ctrlwinProc;
   	w.hInstance=(HINSTANCE)aArg;
   	w.hIcon=LoadIconA((HINSTANCE)hmodule,MAKEINTRESOURCEA(EPOC_ICON));
   	w.hbrBackground=(HBRUSH)GetStockObject(WHITE_BRUSH);
   	w.lpszClassName="E32KernelControlWindow";
	
	ATOM a=RegisterClassA(&w);
	__ASSERT_ALWAYS(a!=0,Fault(EGuiRegisterWindow));

	RECT ctlrwindowRect={0,0,270,0};
	AdjustWindowRect(&ctlrwindowRect,KControlWinStyle,FALSE);
	TInt ctrlwindowWidth=ctlrwindowRect.right-ctlrwindowRect.left;
	TInt ctrlwindowHeight=ctlrwindowRect.bottom-ctlrwindowRect.top;

    TheControlWin=CreateWindowA(
		"E32KernelControlWindow",
		systemIni->iWindowTitle,
		KInvisibleControlWinStyle,
		KWinPosX,
		KWinPosY,
		ctrlwindowWidth,
		ctrlwindowHeight,
		(HWND)NULL,
		NULL,
		(HINSTANCE)aArg,
		(LPSTR)NULL
		);
	__ASSERT_ALWAYS(TheControlWin!=NULL,Fault(EGuiKernelWindowCreate));

	memclr(&w, sizeof(WNDCLASSA));
   	w.style=CS_OWNDC;
   	w.lpfnWndProc=(WNDPROC)winProc;
   	w.hInstance=(HINSTANCE)aArg;
   	w.hIcon=LoadIconA((HINSTANCE)hmodule,MAKEINTRESOURCEA(EPOC_ICON));
   	w.hbrBackground=(HBRUSH)GetStockObject(WHITE_BRUSH);
   	w.lpszClassName="E32KernelWindow";

	a=RegisterClassA(&w);
	__ASSERT_ALWAYS(a!=0,Fault(EGuiRegisterWindow));

	memclr(&w, sizeof(WNDCLASSA));
	w.style=CS_OWNDC;
	w.lpfnWndProc=(WNDPROC)childWinProc;
	w.hInstance=(HINSTANCE)aArg;
	w.hCursor=LoadCursorA(NULL,MAKEINTRESOURCEA(32512)); //ICD_ARROW
	w.hbrBackground=(HBRUSH)GetStockObject(WHITE_BRUSH);
	w.lpszMenuName=NULL;
	w.lpszClassName="E32KernelChildWindow";
	a=RegisterClassA(&w);
	__ASSERT_ALWAYS(a!=0,Fault(EGuiRegisterChildWindow));

	if (masterIni && masterIni->iSystemInis.Count() > 1)	
		{
		//add Configuration Items to the system menu if there's > 1 config
		HMENU hMenu = GetSystemMenu(TheControlWin, FALSE);
		InsertMenu(hMenu,5, MF_BYPOSITION|MF_SEPARATOR,0,NULL);
		InsertMenuA(hMenu,6, MF_BYPOSITION|MF_STRING, 1, "Next Config...");
		}
	
	ShowWindow(TheControlWin,SW_SHOWDEFAULT);
	UpdateWindow(TheControlWin);

	//Create frame windows and child windows
	for(TInt screen=0;screen<systemIni->iScreens.Count();screen++)
		{
	
		RECT windowRect={0,0,systemIni->iScreens[screen]->iXYInputWidth,systemIni->iScreens[screen]->iXYInputHeight};
		AdjustWindowRect(&windowRect,KWinStyle,FALSE);
		TInt windowWidth=windowRect.right-windowRect.left;
		TInt windowHeight=windowRect.bottom-windowRect.top;
    
		CHAR title[20];
		wsprintfA(title, "Screen %d", screen);

		TheWin[screen]=CreateWindowA(
			"E32KernelWindow",
			title,
			KInvisibleWinStyle,
			KWinPosX,
			KWinPosY,
			windowWidth,
			windowHeight,
			(HWND)NULL,
			NULL,
			(HINSTANCE)aArg,
			(LPSTR)NULL
			);
		__ASSERT_ALWAYS(TheWin[screen]!=NULL,Fault(EGuiKernelWindowCreate));
		
		LoadFasciaBitmap(screen);

		TheChildWin[screen]=CreateWindowA(
			"E32KernelChildWindow",
			"",
			WS_CHILDWINDOW|WS_VISIBLE|WS_CLIPCHILDREN|WS_CLIPSIBLINGS,
			systemIni->iScreens[screen]->iScreenOffsetX,
			systemIni->iScreens[screen]->iScreenOffsetY,
			systemIni->iScreens[screen]->iScreenWidth,
			systemIni->iScreens[screen]->iScreenHeight,
			TheWin[screen],
			NULL,
			(HINSTANCE)aArg,
			(LPSTR)NULL
			);
		__ASSERT_ALWAYS(TheChildWin[screen]!=NULL,Fault(EGuiKernelChildWindowCreate));
		
		// Create status bars
		if (systemIni->MultiTouchEnabled() && systemIni->GCEEnabled())
			{
			HMODULE hmodComCtl = LoadLibrary(TEXT("comctl32.dll"));
			typedef int (WINAPI* FNINITCC)();
			FNINITCC pfnInitCommonControls = GetProcAddress(hmodComCtl, "InitCommonControls");
			pfnInitCommonControls();
			hwndStatus = CreateWindowExA(0, STATUSCLASSNAMEA, NULL,
								WS_CHILD | WS_VISIBLE | CCS_BOTTOM ,
									0,0,0,0,
									TheWin[0], NULL, GetModuleHandle(NULL), NULL);                 
			SetStatusBarFont(hwndStatus);
			SetWindowPos(hwndStatus,NULL, 0,0,0,0,SWP_HIDEWINDOW);
			}
		}
	
	//Restore window data from ini file if it exists. 
	HANDLE hSysIni = CreateFileA(systemIni->iSysIniFileName, GENERIC_READ, 0, 0, OPEN_EXISTING, 0, 0);
	TBool success=(hSysIni != INVALID_HANDLE_VALUE) ? ETrue : EFalse;
	
	DWORD numRead;
	TInt fileVersion=0;
	if(success) 
		{
		ReadFile(hSysIni, &fileVersion, sizeof(TInt), &numRead, 0);
		}
	
	//Check we are using a dat file created by this version of the program.
	if(success && (fileVersion==KDatFileVersion) )
		{
				
		TInt savedConfiguration=0; //set this to default configuration
				
		if(ReadFile(hSysIni, &savedConfiguration, sizeof(TInt), &numRead, 0) && (numRead>0) )
			{
			//Don't restore the saved configuration, see INC114502.
			//This could be reenabled in future as an optional operation
			//dependent on an entry in the epoc.ini file.

			//SwitchConfiguration(savedConfiguration);
			}
	
		//restore each window to saved state
		for(TInt screen=0;screen<systemIni->iScreens.Count();screen++)
			{
		
			//	If the .ini file was opened, get the saved settings for the windows position the last time
			//	this emulator was run, if any, and move the window accordingly.
			
			TWindowState savedState;
			
			TBool stateLoaded = ReadFile(hSysIni, &savedState, sizeof(TWindowState), &numRead, 0) && (numRead>0);
			 			
			if (stateLoaded)
				{				
				//only allow window to be restored to 
				//maximized or normal mode,
				//this prevents it being restored in minimized mode
				//or others.
				if(savedState.iWinPlace.showCmd != SW_MAXIMIZE)
						savedState.iWinPlace.showCmd= SW_NORMAL;
				
				//if starting in same configuration and/or rotation as last time emulator was run
				//it makes sense to restore scroll offset, window position,
				//and dimensions, if not, only restore position and window (ie. maximized/normal) state.
				if(savedConfiguration == CurrentConfiguration &&
					savedState.iFlipstate == CurrentFlipState[screen])
					{
					//Restore window placement
					SetWindowPlacement(TheWin[screen], &savedState.iWinPlace);
					
					TViewport& viewport = systemIni->iScreens[screen]->iViewport;
								
					viewport.ScrollToX(savedState.iXoffset, TheWin[screen]);
					viewport.ScrollToY(savedState.iYoffset, TheWin[screen]);
					}
				else
					{
					
					RECT oldRect;
					GetWindowRect(TheWin[screen], &oldRect);
					//save default window dimensions
					TInt width=oldRect.right-oldRect.left;
					TInt height=oldRect.bottom - oldRect.top;

					//restore position and window state from file
					SetWindowPlacement(TheWin[screen], &savedState.iWinPlace);
					
					RECT currentRect;
					GetWindowRect(TheWin[screen], &currentRect);
					//keep default size.
					MoveWindow(TheWin[screen],currentRect.left, currentRect.top, width, height, TRUE);
					
					}


				// Check that enough of the recorded window position is visible on the screen

				TBool enoughVisible = false;

				// vague values for ensuring we have enough of the window title bar to grab
				// if the window is partly off screen 
				const TInt KTitleBarGrabX=80;
				const TInt KTitleBarGrabY=50;

				//inspect dimensions of the window to be restored.
				RECT savedRect;
				GetWindowRect(TheWin[screen], &savedRect);

				SystemMonitors monitors;

				if (monitors.Count() == 1)		/* Original algorithm */
					{
					RECT rcIntersect, rcScreen;

					SetRect(&rcScreen, KTitleBarGrabX, savedRect.bottom-savedRect.top,
						GetSystemMetrics(SM_CXSCREEN)-KTitleBarGrabX, GetSystemMetrics(SM_CYSCREEN)-KTitleBarGrabY);

					enoughVisible = IntersectRect(&rcIntersect, &savedRect, &rcScreen);
					}
				else							/* > 1 monitor; do it differently */
					{
					RECT cornerBox1, cornerBox2;

					// The top-left corner box
					SetRect(&cornerBox1, savedRect.left, savedRect.top,
						savedRect.left + KTitleBarGrabX, savedRect.top + KTitleBarGrabY);

					// The top-right corner box
					SetRect(&cornerBox2, savedRect.right - KTitleBarGrabX, savedRect.top,
						savedRect.right, savedRect.top + KTitleBarGrabY);

					// Require one of these rectangles to be all on one monitor
					enoughVisible = monitors.RectAllOnOne(cornerBox1) || monitors.RectAllOnOne(cornerBox2);
					}

				if (!enoughVisible)
					{
					SetWindowPos(TheWin[screen], HWND_TOP, 0,0,0,0, SWP_NOSIZE);
					}

				}
			else //if there was no stored info for this screen
				{
				ShowWindow(TheWin[screen],SW_MAXIMIZE);
				}
			}
		}
	else
		{
		//use default configuration and make windows visible
		SwitchConfiguration(CurrentConfiguration);
		for(TInt screen=0;screen<systemIni->iScreens.Count();screen++)
			{
			ShowWindow(TheWin[screen],SW_MAXIMIZE);
			UpdateWindow(TheWin[screen]);
			}
		}

	//close file if it was opened	
	if(success) 
		{
		CloseHandle(hSysIni);
		}
		
			
	if (systemIni->iInitialFlipMsg != 0)
		{
		addKeyEvent(TRawEvent::EKeyDown,systemIni->iInitialFlipMsg);
		addKeyEvent(TRawEvent::EKeyUp,systemIni->iInitialFlipMsg);
		}

	SetFocus(TheWin[0]);

	MSG m;
	while (GetMessageA(&m,NULL,0,0))
    	{
        DispatchMessageA(&m);
	    }
	
	ExitProcess(m.wParam);
	return 0;
	}

SystemMonitors::SystemMonitors(void)
	{
	TInt n;

	iCount = 1;
	iHaveMultiMonFunctions = false;

	if ((n = GetSystemMetrics(SM_CMONITORS)) <= 1)
		{
		return;
		}

	HMODULE huser32 = GetModuleHandleA("user32.dll");

	// Get pointers to the APIs we want
	if (huser32 == NULL ||
		(ipMonitorFromRect =
		(HMONITOR (WINAPI *)(LPCRECT lprcScreenCoords, UINT uFlags))
		GetProcAddress(huser32, "MonitorFromRect")) == NULL ||
		(ipGetMonitorInfo =
		(BOOL (WINAPI *)(HMONITOR hMonitor, LPMONITORINFO lpMonitorInfo))
		GetProcAddress(huser32, "GetMonitorInfoA")) == NULL)
		{
		return;
		}

	iCount = n;
	iHaveMultiMonFunctions = true;
	}

TBool SystemMonitors::RectAllOnOne(RECT& rect)
	{
	HMONITOR monitor = MonitorFromRect(rect);
	if (monitor == NULL)
		{
		return false;
		}

	MONITORINFO monInfo;
	monInfo.cbSize = sizeof(MONITORINFO);

	if (! GetMonitorInfo(monitor, &monInfo))
		{
		return false;
		}

	RECT overlap;

	if (IntersectRect(&overlap, &rect, &monInfo.rcWork) &&
		EqualRect(&overlap, &rect))
		{
		return true;
		}

	return false;
	}

HMONITOR SystemMonitors::MonitorFromRect(const RECT& rect, UINT flags)
	{
	if (! iHaveMultiMonFunctions)
		{
		return NULL;
		}

	return (*ipMonitorFromRect)(&rect, flags);
	}

TBool SystemMonitors::GetMonitorInfo(HMONITOR monitor, LPMONITORINFO pMonInfo)
	{
	if (! iHaveMultiMonFunctions)
		{
		return false;
		}

	return (*ipGetMonitorInfo)(monitor, pMonInfo);
	}

void DrawLeds()
	{
	HDC winDC = GetDC(TheWin[0]);
	HDC hdcBits;
	hdcBits=CreateCompatibleDC(winDC);
	SelectObject(hdcBits,TheScreenBitmap[0]);
	HPEN pen=CreatePen(PS_SOLID,0,RGB(0,0,0));
	SelectObject(hdcBits,pen);
	HBRUSH brush;
	LOGBRUSH redbrush={BS_SOLID, RGB(0xff,0,0)};
	LOGBRUSH greenbrush={BS_SOLID, RGB(0,0xff,0)};
	LOGBRUSH blackbrush={BS_SOLID, RGB(0,0,0)};
	// red
	if (LedMask & KLedMaskRed1)
		brush=CreateBrushIndirect(&redbrush);
	else
		brush=CreateBrushIndirect(&blackbrush);
	SelectObject(hdcBits,brush);
	DWinsUi *ini=systemIni;
	Ellipse(hdcBits, ini->iLedOffsetX, ini->iLedOffsetY, ini->iLedOffsetX+ini->iLedSize, ini->iLedOffsetY+ini->iLedSize);
	DeleteObject(brush);
	// green
	if (LedMask & KLedMaskGreen1)
		brush=CreateBrushIndirect(&greenbrush);
	else
		brush=CreateBrushIndirect(&blackbrush);
	SelectObject(hdcBits,brush);
	if (ini->iLedVertical)
		Ellipse(hdcBits, ini->iLedOffsetX, ini->iLedOffsetY+ini->iLedSize+ini->iLedGap,
		ini->iLedOffsetX+ini->iLedSize, ini->iLedOffsetY+ini->iLedSize+ini->iLedGap+ini->iLedSize);
	else
		Ellipse(hdcBits, ini->iLedOffsetX+ini->iLedSize+ini->iLedGap, ini->iLedOffsetY,
		ini->iLedOffsetX+ini->iLedSize+ini->iLedGap+ini->iLedSize, ini->iLedOffsetY+ini->iLedSize);
	DeleteObject(brush);
	DeleteObject(pen);
	DeleteDC(hdcBits);
	ReleaseDC(TheWin[0], winDC);
	if (ini->iLedVertical)
		{
		RECT r={ini->iLedOffsetX,
			ini->iLedOffsetY,
			ini->iLedOffsetX+ini->iLedSize,
			ini->iLedOffsetY+ini->iLedSize+ini->iLedGap+ini->iLedSize};
		InvalidateRect(TheWin[0], &r, FALSE);
		}
	else
		{
		RECT r={ini->iLedOffsetX,
			ini->iLedOffsetY,
			ini->iLedOffsetX+ini->iLedSize+ini->iLedGap+ini->iLedSize,
			ini->iLedOffsetY+ini->iLedSize};
		InvalidateRect(TheWin[0], &r, FALSE);
		}
	}

void DWinsUi::ScreenInfo(TScreenInfoV01& aInfo)
//
// Return screen 0 info to the window server.
//
	{
	aInfo.iWindowHandleValid=ETrue;
	aInfo.iWindowHandle=TheChildWin[0];
	aInfo.iScreenAddressValid=EFalse;
	aInfo.iScreenAddress=NULL;
	aInfo.iScreenSize.iWidth = iScreens[0]->iMaxScreenWidth;
	aInfo.iScreenSize.iHeight = iScreens[0]->iMaxScreenHeight;
	}
	
	
TBool DWinsUi::VideoInfo(TInt aScreenNumber, TVideoInfoV01& aInfo)
	{
	return VideoInfo(aScreenNumber,iScreens[aScreenNumber&KMaskScreenNum]->iCurrentMode,aInfo);
	}
	
/// Could fail if flip mode is not supported
TBool DWinsUi::VideoInfo(TInt aScreenNumber,TInt aModeNumber, TVideoInfoV01& aInfo)
	{
	aScreenNumber &= KMaskScreenNum;
	if (aScreenNumber>=iScreens.Count())
		return EFalse;
	if (masterIni->iBufferSet.Count() && masterIni->iBufferSet[aScreenNumber].iDisplayDriverCount > 0)
		{
			return VideoInfoForDisplayDriver(aScreenNumber,aModeNumber,aInfo);
		}
	else
		{
		if ((aModeNumber&KMaskModeNum)>=1)
			return EFalse;	//non-gce emulator doesn't support changing the mode number.
		DScreenProperties* screenProperties=iScreens[aScreenNumber];
		aInfo.iSizeInPixels.iWidth = screenProperties->iMaxScreenWidth;
		aInfo.iSizeInPixels.iHeight = screenProperties->iMaxScreenHeight;
		aInfo.iSizeInTwips.iWidth = screenProperties->iMaxPhysicalScreenWidth ? screenProperties->iMaxPhysicalScreenWidth : TInt(screenProperties->iScreenWidth*KDefaultPixelsToTwipsX);
		aInfo.iSizeInTwips.iHeight = screenProperties->iMaxPhysicalScreenHeight ? screenProperties->iMaxPhysicalScreenHeight : TInt(screenProperties->iScreenHeight*KDefaultPixelsToTwipsY);
		aInfo.iIsMono = EFalse;
		aInfo.iIsPalettized = EFalse;
		aInfo.iDisplayMode=screenProperties->iCurrentMode;
		aInfo.iIsPixelOrderRGB = ETrue;
		aInfo.iIsPixelOrderLandscape=ETrue;

		aInfo.iVideoAddress =  (TInt)TheChildWin[aScreenNumber];
		aInfo.iBitsPerPixel = screenProperties->iColorDepth;
		aInfo.iOffsetToFirstPixel=0;
		aInfo.iOffsetBetweenLines=0;
		}
	return ETrue;	
	}

/** Could fail if flip mode is not supported.
 Note that this method is inteneded to be called directly to parameterise the setting up of the display driver,
 so it must survive absense of the display driver installation!
**/
 
TBool DWinsUi::VideoInfoForDisplayDriver(TInt aScreenNumber,TInt aModeNumber, TVideoInfoV01& aInfo,  TBool aRealWidthAndHeight)
	{
	aScreenNumber &= KMaskScreenNum;
	DScreenProperties* screenProperties = iScreens[aScreenNumber];
	if ((aModeNumber&KMaskModeNum) >= screenProperties->iMaxModes)
		{
		return EFalse;
		}
		
	aInfo.iSizeInPixels.iWidth = aRealWidthAndHeight ? screenProperties->iScreenWidth : screenProperties->iMaxScreenWidth;
	aInfo.iSizeInPixels.iHeight = aRealWidthAndHeight ? screenProperties->iScreenHeight : screenProperties->iMaxScreenHeight;
	
	if (aRealWidthAndHeight==EFalse)
		{
		aInfo.iSizeInTwips.iWidth = screenProperties->iMaxPhysicalScreenWidth ?	screenProperties->iMaxPhysicalScreenWidth : TInt(screenProperties->iScreenWidth*KDefaultPixelsToTwipsX);
		aInfo.iSizeInTwips.iHeight = screenProperties->iMaxPhysicalScreenHeight ? screenProperties->iMaxPhysicalScreenHeight : TInt(screenProperties->iScreenHeight*KDefaultPixelsToTwipsY);
		}
	else
		{
		aInfo.iSizeInTwips.iWidth = screenProperties->iPhysicalScreenWidth ? screenProperties->iPhysicalScreenWidth : TInt(screenProperties->iScreenWidth*KDefaultPixelsToTwipsX);
		aInfo.iSizeInTwips.iHeight = screenProperties->iPhysicalScreenHeight ? screenProperties->iPhysicalScreenHeight : TInt(screenProperties->iScreenHeight*KDefaultPixelsToTwipsY);
		}
	
	aInfo.iIsMono = EFalse;
	aInfo.iIsPalettized = EFalse;
	aInfo.iDisplayMode=screenProperties->iCurrentMode;
	aInfo.iIsPixelOrderRGB = ETrue;
	aInfo.iIsPixelOrderLandscape=ETrue;
	
	// Set memory to iVideoAddress to NULL to trigger the HAL code into querying the video address
	// separately
	aInfo.iVideoAddress = NULL;

	TInt bpp=screenProperties->iModeDepths[aModeNumber&KMaskModeNum];												
	aInfo.iBitsPerPixel=bpp;
	if (bpp>8)
		{
		bpp=(bpp+15)&-16;	//12 & 16 --> 16 ; 24 & 32 --> 32
		}
	
	aInfo.iOffsetToFirstPixel=0;
#ifdef TEST_GCE_VARIABLE_START_EXTRA
	aInfo.iOffsetToFirstPixel+= TEST_GCE_VARIABLE_START_EXTRA*(1+aModeNumber&KMaskScreenModeNum);
	if ((aModeNumber& KModeFlagFlipped)
		{
#ifndef ASSYMETRIC_SQUARE_STRIDE		
		if (aInfo.iSizeInPixels.iWidth!=aInfo.iSizeInPixels.iHeight)
#endif
			aInfo.iOffsetToFirstPixel+= TEST_GCE_VARIABLE_START_EXTRA*KEmulMaxNumModes;	
		}
#endif
	if (aModeNumber& KModeFlagFlipped)
		{
		// we calculate the number of bytes per scanline that MUST be a multiple of 32 bits word (alignment)
		// screenProperties->iMaxScreenHeight * bpp represnts the number of bits per scanline
		// +31 is the ceiling
		// we shift right (>>3) because there are 8 bits/byte
		// we mask with ~3 because we are intrested in the octet value
		aInfo.iOffsetBetweenLines=((screenProperties->iMaxScreenHeight * bpp + 31) >> 3) & ~3;
		}
	else
		{
		// please see the comment above
		aInfo.iOffsetBetweenLines=((screenProperties->iMaxScreenWidth * bpp + 31) >> 3) & ~3;
		}
#ifdef  TEST_GCE_VARIABLE_STRIDE_EXTRA
	aInfo.iOffsetBetweenLines+=TEST_GCE_VARIABLE_STRIDE_EXTRA;
#endif	

	return ETrue;	
	}

TInt DMasterIni::DoHalFunction(TAny* aPtr, TInt aFunction, TAny* a1, TAny* a2)
	{
	return masterIni->HalFunction((TInt)aPtr,aFunction,a1,a2);
	}


TInt DMasterIni::HalFunction(TInt aDeviceNumber, TInt aFunction, TAny* a1, TAny* a2)
	{
	if (TUint(aDeviceNumber) >= TUint(systemIni->iScreens.Count()))
		return KErrArgument;
	
	TInt mode;
	TInt maxMode=1;
	TInt r=KErrNone;
	switch(aFunction)
		{
		case EDisplayHalScreenInfo:
			{
			TPckgBuf<TScreenInfoV01> vPckg;
			systemIni->ScreenInfo(vPckg());
			Kern::InfoCopy(*(TDes8*)a1,vPckg);
			break;
			}
		case EDisplayHalWsRegisterSwitchOnScreenHandling:
			WsSwitchOnScreen=(TBool)a1;
			break;
		case EDisplayHalSetState:
			{
			if(!Kern::CurrentThreadHasCapability(ECapabilityPowerMgmt,__PLATSEC_DIAGNOSTIC_STRING("Checked by Hal function EDisplayHalSetState")))
				return KErrPermissionDenied;
			if ((TBool)a1)
				WinsGuiPowerHandler->ScreenOn(aDeviceNumber);
			else
				WinsGuiPowerHandler->ScreenOff(aDeviceNumber);
			}
			break;

		case EDisplayHalState:
			*(TInt*)a1=!systemIni->iScreens[aDeviceNumber]->iScreenOff;
			break;
		case EDisplayHalWsSwitchOnScreen:
			WinsGuiPowerHandler->ScreenOn();
			break;
		case EDisplayHalMaxDisplayContrast:
			kumemput32(a1,&KMaxDisplayContrast,sizeof(KMaxDisplayContrast));
			break;
		case EDisplayHalDisplayContrast:
			kumemput32(a1,&systemIni->iScreens[aDeviceNumber]->iDisplayContrast,sizeof(systemIni->iScreens[aDeviceNumber]->iDisplayContrast));
			break;
		case EDisplayHalSetDisplayContrast:
			if(!Kern::CurrentThreadHasCapability(ECapabilityWriteDeviceData,__PLATSEC_DIAGNOSTIC_STRING("Checked by Hal function EDisplayHalSetDisplayContrast")))
				return KErrPermissionDenied;
			if (TUint(a1) <= TUint(KMaxDisplayContrast))
				systemIni->iScreens[aDeviceNumber]->iDisplayContrast = TInt(a1);
			else
				r = KErrArgument;
			break;
		case EDisplayHalBacklightOn:
			{
			TBool c = EFalse;
			kumemput32(a1,&c,sizeof(TBool));
			}
			break;
		
		case EDisplayHalCurrentModeInfo:
			{
			//a1 has ptr to buffer for results
			TPckgBuf<TVideoInfoV01> vPckg;
			if (systemIni->VideoInfo(aDeviceNumber, vPckg()))
				Kern::InfoCopy(*(TDes8*)a1,vPckg);
			else
				r=KErrNotSupported;
			}
			break;

		case EDisplayHalSpecifiedModeInfo:
			{
			kumemget32(&mode, a1, sizeof(mode));
			TPckgBuf<TVideoInfoV01> vPckg;
			if (!systemIni->VideoInfo(aDeviceNumber, mode, vPckg()))
				return KErrArgument;
			Kern::InfoCopy(*(TDes8*)a2, vPckg);
			}
			break;

		case EDisplayHalSetMode:
// 			if(!Kern::CurrentThreadHasCapability(ECapabilityMultimediaDD,__PLATSEC_DIAGNOSTIC_STRING("Checked by Hal function EDisplayHalSetMode")))
// 				return KErrPermissionDenied;

			//Note that at present the HAL mode does not apparently get set when the CFbsScreenDevice requires a different mode.
			//At least in the emulator and default h4 implementation...
			
			mode=KMaskModeNum&(TInt) a1;
			maxMode=1;
			//can't avoid this behaviour change test against gce loaded 
			if (masterIni->iBufferSet.Count() &&  masterIni->iBufferSet[aDeviceNumber].iDisplayDriverCount > 0)
				maxMode=systemIni->iScreens[aDeviceNumber]->iMaxModes;
			if (mode >=maxMode || mode<0)
				{
				r = KErrArgument;
				break;
				}
			//Harmless/Pointless in vanilla wins mode.
			systemIni->iScreens[aDeviceNumber]->iCurrentMode=mode;
			
			break;
		
		case EDisplayHalMode:
			{
			//This is always 0 in non-gce emulator
			kumemput32(a1,&systemIni->iScreens[aDeviceNumber]->iCurrentMode,sizeof(systemIni->iScreens[aDeviceNumber]->iCurrentMode));
			}
			break;

		case EDisplayHalModeCount:
			{
			//Need to actually count them here!
			//GCE will ignore modes<=8
			TInt encodedMode=1;
			if (masterIni->iBufferSet.Count() &&  masterIni->iBufferSet[aDeviceNumber].iDisplayDriverCount > 0)
				encodedMode=systemIni->iScreens[aDeviceNumber]->iMaxModes;
			kumemput32(a1,&encodedMode,sizeof(encodedMode));
			}
			break;

		case EDisplayHalColors:
			{
			TInt deepestMode=0;
			if (masterIni->iBufferSet.Count()==0 ||  masterIni->iBufferSet[aDeviceNumber].iDisplayDriverCount <= 0)
				{
				deepestMode = KMaxDisplayColors;  	//I could try and work it out, but this is what used to happen!
				}
			else
				{
				TInt maxBpp=0;
				for (TInt i=0,maxI=systemIni->iScreens[aDeviceNumber]->iMaxModes;i<maxI;i++)	
					if (systemIni->iScreens[aDeviceNumber]->iModeDepths[i]>maxBpp)
						maxBpp=systemIni->iScreens[aDeviceNumber]->iModeDepths[i];
				deepestMode= 1<<maxBpp;
				}

			kumemput32(a1,&deepestMode,sizeof(deepestMode));
			}
			break;
		case EDisplayHalGetDisplayMemoryHandle:
			{
			TInt val = 0;
			TInt passedIn = 0;
			kumemget32(&passedIn, a1, sizeof(TInt));
			if (passedIn != -1)	//not from a getall
				{
				NKern::ThreadEnterCS();
				if (!(masterIni->iBufferSet.Count() == 0 || masterIni->iBufferSet[aDeviceNumber].iDisplayDriverCount <= 0 ))
					{
						r = masterIni->DisplayMemoryHandle(aDeviceNumber, val); 
					}
				else
					{
					r = KErrArgument;
					}
				NKern::ThreadLeaveCS();
				}
			kumemput32(a1, &val, sizeof(TInt));

			}
			break;

		case EDisplayHalGetDisplayMemoryAddress:
			{
			TInt val = 0;
			TInt passedIn = 0;
			kumemget32(&passedIn, a1, sizeof(TInt));
			if (passedIn != -1)	//not from a getall
				{
				if (!(masterIni->iBufferSet.Count() == 0 || masterIni->iBufferSet[aDeviceNumber].iDisplayDriverCount <= 0 ))
					{
					r = masterIni->DisplayMemoryAddress(aDeviceNumber, val);
					}
				else
					{
					r = KErrArgument;
					}
				}
			kumemput32(a1, &val, sizeof(TInt));
			}
			break;

		case EDisplayHalNumberOfResolutions:
			{
			r = NumberOfResolutions(aDeviceNumber, a1, a2);
			}
			break;
		case EDisplayHalSpecificScreenInfo:
			{
			r = SpecificScreenInfo(aDeviceNumber, a1, a2);
			}
			break;
		case EDisplayHalCurrentScreenInfo:
			{
			r = CurrentScreenInfo(aDeviceNumber, a1, a2);			
			}
			break;
		case EDisplayHalSetDisplayState:
			{
			//increase the spinner at both beginning and end of resetting the display state
			NKern::LockedInc(iBufferSet[aDeviceNumber].iStateChangeCount);
			kumemget32(&iBufferSet[aDeviceNumber].iDisplayState, a1, sizeof(TInt));

			switch(iBufferSet[aDeviceNumber].iDisplayState)
				{
				case ENoResolution:
				case EDisconnect:
				case ESingleResolution:
					// the fascia effect of 0x0 resolution
					SetDisplaySize(aDeviceNumber, 0, 0);
					break;
				}

			NKern::LockedInc(iBufferSet[aDeviceNumber].iStateChangeCount);
			}
			break;
		case EDisplayHalGetStateSpinner:
			{
			kumemput32(a1,&iBufferSet[aDeviceNumber].iStateChangeCount,
					sizeof(iBufferSet[aDeviceNumber].iStateChangeCount));
			}
			break;
		default:
			r=KErrNotSupported;
			break;
		}
	return r;
	}

TInt DMasterIni::NumberOfResolutions(TInt aDeviceNumber, TAny* a1, TAny* a2)
	{
	TInt numberOfConfigs;
	switch(iBufferSet[aDeviceNumber].iDisplayState)
		{
		case ENoResolution:
			{
			numberOfConfigs = 0;
			}
			break;
		case EDisconnect:
			{
			return KErrDisconnected;
			}
			break;
		case ESingleResolution:
			{
			numberOfConfigs = 1;
			}
			break;
		case ENormalResolution:
		default:
			{
			numberOfConfigs = iSystemInis.Count();
			if (numberOfConfigs > 1)
				{
				TVideoInfoV01 info1;
				TVideoInfoV01 info2;
				iSystemInis[0]->VideoInfoForDisplayDriver(aDeviceNumber, 0, info1, ETrue);
				iSystemInis[1]->VideoInfoForDisplayDriver(aDeviceNumber, 0, info2, ETrue);
				if (info1.iSizeInPixels.iWidth == info2.iSizeInPixels.iWidth &&
						info1.iSizeInPixels.iHeight == info2.iSizeInPixels.iHeight)
					{
					numberOfConfigs = 1;	//It looks like all resolutions for this device are the same
					}
				}
			}
		}
	kumemput32(a1,&numberOfConfigs,sizeof(numberOfConfigs));
	if(a2)
		{
		kumemput32(a2,&(iBufferSet[aDeviceNumber].iStateChangeCount),sizeof(iBufferSet[aDeviceNumber].iStateChangeCount));
		}
	return KErrNone;
	}

TInt DMasterIni::SpecificScreenInfo(TInt aDeviceNumber, TAny* a1, TAny* a2)
	{
	TInt config;
	switch(iBufferSet[aDeviceNumber].iDisplayState)
		{
		case ENoResolution: //Do Nothing
			break;
		case EDisconnect:
			{
			return KErrDisconnected;
			}
			break;
		case ESingleResolution: //fill (0,0) as the only element in resolution array
			{
			if(*(TInt*)a1 == 0)
				{
				TVideoInfoV01 info;
				info.iSizeInPixels.iHeight = 0;
				info.iSizeInPixels.iWidth = 0;
				info.iSizeInTwips.iHeight = 0;
				info.iSizeInTwips.iWidth = 0;
				TPtr8 infoPtr((TUint8*)&info, sizeof(info), sizeof(info));
				Kern::InfoCopy(*(TDes8*)a2, infoPtr);
				}
			}
			break;
		case ENormalResolution:
		default:
			{
			kumemget32(&config, a1, sizeof(config));
			TPckgBuf<TVideoInfoV01> vPckg;
			iSystemInis[config]->VideoInfoForDisplayDriver(aDeviceNumber, 0, vPckg(), ETrue);
			Kern::InfoCopy(*(TDes8*)a2,vPckg);
			}
		}
	return KErrNone;
	}

TInt DMasterIni::CurrentScreenInfo(TInt aDeviceNumber, TAny* a1, TAny* /*a2*/)
	{
	switch(iBufferSet[aDeviceNumber].iDisplayState)
		{
		case ENoResolution: //Do Nothing
			break;
		case EDisconnect:
			{
			return KErrDisconnected;
			}
			break;
		case ESingleResolution: //fill (0,0)
			{
			TVideoInfoV01 info;
			info.iSizeInPixels.iHeight = 0;
			info.iSizeInPixels.iWidth = 0;
			info.iSizeInTwips.iHeight = 0;
			info.iSizeInTwips.iWidth = 0;
			TPtr8 infoPtr((TUint8*)&info, sizeof(info), sizeof(info));
			Kern::InfoCopy(*(TDes8*)a1, infoPtr);
			}
			break;
		case ENormalResolution:
		default:
			{
			TPckgBuf<TVideoInfoV01> vPckg;
			systemIni->VideoInfoForDisplayDriver(aDeviceNumber, 0, vPckg(), ETrue);
			Kern::InfoCopy(*(TDes8*)a1,vPckg);
			}
		}
	return KErrNone;
	}

TInt DMasterIni::DoXYHalFunction(TAny* aThis, TInt aFunction, TAny* a1, TAny* a2)
	{
	return static_cast<DMasterIni*>(aThis)->XYHalFunction(aFunction,a1,a2);
	}

TInt DMasterIni::XYHalFunction(TInt aFunction, TAny* a1, TAny* /*a2*/)
	{
	TInt r=KErrNone;
	switch(aFunction)
		{
		case EDigitiserHalXYInfo:
			{
			if(systemIni->iXYInputType==EXYInputPointer)
				{
				TPckgBuf<TDigitiserInfoV01> vPckg;
				TDigitiserInfoV01& xyinfo=vPckg();
				xyinfo.iDigitiserSize.iWidth=max(systemIni->iScreens[0]->iXYInputWidth,systemIni->iScreens[0]->iMaxScreenWidth+systemIni->iScreens[0]->iScreenOffsetX);		
				xyinfo.iDigitiserSize.iHeight=max(systemIni->iScreens[0]->iXYInputHeight,systemIni->iScreens[0]->iMaxScreenHeight+systemIni->iScreens[0]->iScreenOffsetY);
				xyinfo.iOffsetToDisplay.iX=systemIni->iScreens[0]->iScreenOffsetX;
				xyinfo.iOffsetToDisplay.iY=systemIni->iScreens[0]->iScreenOffsetY;
				Kern::InfoCopy(*(TDes8*)a1,vPckg);
				}
			else
				r=KErrNotSupported;
			}
			break;
		default:
			r=KErrNotSupported;
			break;
		}
	return r;
	}

TInt DMasterIni::DoMouseHalFunction(TAny* aThis, TInt aFunction, TAny* a1, TAny* a2)
	{
	return static_cast<DMasterIni*>(aThis)->MouseHalFunction(aFunction,a1,a2);
	}

TInt DMasterIni::MouseHalFunction(TInt aFunction, TAny* a1, TAny* /*a2*/)
	{
	TInt r=KErrNone;
	switch(aFunction)
		{
		case EMouseHalMouseInfo:
			{
			if(systemIni->iXYInputType==EXYInputMouse || systemIni->iXYInputType==EXYInputDeltaMouse)
				{
				TPckgBuf<TMouseInfoV01> vPckg;
				TMouseInfoV01& xyinfo=vPckg();
				xyinfo.iMouseButtons=2;
				xyinfo.iMouseAreaSize.iWidth=max(systemIni->iScreens[0]->iXYInputWidth,systemIni->iScreens[0]->iMaxScreenWidth+systemIni->iScreens[0]->iScreenOffsetX);		
				xyinfo.iMouseAreaSize.iHeight=max(systemIni->iScreens[0]->iXYInputHeight,systemIni->iScreens[0]->iMaxScreenHeight+systemIni->iScreens[0]->iScreenOffsetY);
				xyinfo.iOffsetToDisplay.iX=systemIni->iScreens[0]->iScreenOffsetX;
				xyinfo.iOffsetToDisplay.iY=systemIni->iScreens[0]->iScreenOffsetY;
				Kern::InfoCopy(*(TDes8*)a1,vPckg);
				}
			else
				r=KErrNotSupported;
			}
			break;
		default:
			r=KErrNotSupported;
			break;
		}
	return r;
	}

TInt DMasterIni::DoKbdHalFunction(TAny* /*aThis*/, TInt aFunction, TAny* /*a1*/, TAny* /*a2*/)
	{
	// don't actually do anything, just enough to report a Keyboard is present
	TInt r=KErrNone;
	if(aFunction!=EKeyboardHalKeyboardInfo)
		r=KErrNotSupported;
	return r;
	}

void Inactive()
//
// Window has been minimised.
//
    {

	TRawEvent v;
	v.Set(TRawEvent::EInactive);
    TheEventQ.Add(v);
    }

void UpdateModifiers()
//Updates the modifier key states and sends an event to wserv about the 
//change in state
	{
	TRawEvent v;
	TInt modifiers=0;
    if(GetKeyState(VK_SCROLL)&1)
        modifiers|=EModifierScrollLock;
    if(GetKeyState(VK_NUMLOCK)&1)
        modifiers|=EModifierNumLock;
    if(GetKeyState(VK_CAPITAL)&1)
        modifiers|=EModifierCapsLock;
    v.Set(TRawEvent::EUpdateModifiers,modifiers);
    TheEventQ.Add(v);
	}
	
void Active()
//
// Window has been restored.
// Update the toggling modifiers, reset any others
//
    {
	TRawEvent v;
    UpdateModifiers();
    v.Set(TRawEvent::EActive);
    TheEventQ.Add(v);
    }


void ClearScreen()
    {

    }


TInt DWinsUi::GetVirtualKey(TEmulCommand& aCommand, TInt aX, TInt aY)
	{
	//if the point is in the list of shapes, set the key and return true
	for (TInt keyCount = iVirtualKeys.Count(); --keyCount >= 0; )
		{
		const VirtualKey& vk = *iVirtualKeys[keyCount];
		if (vk.Contains(aX, aY))
			{
			aCommand = vk.Command();
			return vk.Value();
			}
		}
	return -1;
	}

void DWinsUi::TranslateMouseCoords(const TEmulatorFlip aFlipState, const TInt aX, const TInt aY, const TInt aRegionWidth, const TInt aRegionHeight, TInt& aNewX, TInt& aNewY)
	{
	switch (aFlipState)
		{
		case EEmulatorFlipRestore:
			aNewX = aX;
			aNewY = aY;
			break;
		
		case EEmulatorFlipInvert:
			aNewX = aRegionWidth - aX;
			aNewY = aRegionHeight - aY;
			break;
		
		case EEmulatorFlipLeft:
			aNewX = aRegionWidth - aY;
			aNewY = aX;
			break;

		case EEmulatorFlipRight:
			aNewX = aY;
			aNewY = aRegionHeight - aX;
			break;
		}
	}


TBool DWinsUi::OnScreen(TInt aScreen, TInt ax, TInt ay) const
//
// Checks if a point within the Emulator window is on the screen
//
	{
	return (TUint(ax) < TUint(systemIni->iScreens[aScreen]->iScreenWidth) && TUint(ay) < TUint(systemIni->iScreens[aScreen]->iScreenHeight));
	}

TInt DWinsUi::Create(TInt aId)
	{
	TInt r = SetupProperties(aId);
	if (r != KErrNone)
		return r;
	TInt screen;
	DScreenProperties* currentScreen = NULL;
	for(screen=0;screen<iScreens.Count();screen++)
		{
		BITMAPINFOHEADER bitmapinfo;
		currentScreen = iScreens[screen];
		if (readBitmapInfo(&bitmapinfo, currentScreen->iFasciaFileName) == KErrNone)
			{
			currentScreen->iXYInputWidth=bitmapinfo.biWidth;
			currentScreen->iXYInputHeight=bitmapinfo.biHeight;
			}
		currentScreen->iXYInputWidth=max(currentScreen->iXYInputWidth,currentScreen->iScreenWidth+currentScreen->iScreenOffsetX);
		currentScreen->iXYInputHeight=max(currentScreen->iXYInputHeight,currentScreen->iScreenHeight+currentScreen->iScreenOffsetY);
		}

	currentScreen = iScreens[0];
	//note digitizer offsets are relative to EPOC screen 0
	if (-1 == iDigitizerWidth)
		iDigitizerWidth = currentScreen->iXYInputWidth - 
			(currentScreen->iScreenOffsetX + iDigitizerOffsetX);
	else
		currentScreen->iXYInputWidth=max(currentScreen->iXYInputWidth,iDigitizerWidth);

	if (-1 == iDigitizerHeight)
		iDigitizerHeight = currentScreen->iXYInputHeight - 
			(currentScreen->iScreenOffsetY + iDigitizerOffsetY);
	else
		currentScreen->iXYInputHeight=max(currentScreen->iXYInputHeight,iDigitizerHeight);

	return r;
	}

const RDisplayChannel::TPixelFormat DMasterIni::iSupportedPixelFormatTable[] =
	{
		EUidPixelFormatXRGB_8888,
		EUidPixelFormatARGB_8888,
		EUidPixelFormatARGB_8888_PRE,
		EUidPixelFormatXRGB_4444,
		EUidPixelFormatARGB_4444,
		EUidPixelFormatRGB_565 
	};

const TInt DMasterIni::iSupportedPixelFormatTableSize = static_cast<TInt>(sizeof(iSupportedPixelFormatTable)/
		                                                                  sizeof(iSupportedPixelFormatTable[0]));

void DMasterIni::InitBufferFormat(DScreenProperties& aScreenProperties, RDisplayChannel::TBufferFormat& aBufferFormat)
	{
	TUint bitsPerPixel = MaximumBitDepthFromMask(aScreenProperties.iColorDepth);
	
	aBufferFormat.iSize.iWidth = aScreenProperties.iMaxScreenWidth;
	aBufferFormat.iSize.iHeight = aScreenProperties.iMaxScreenHeight;
	switch (bitsPerPixel)
		{
	case 12:	// XRGB4444
		aBufferFormat.iPixelFormat = EUidPixelFormatXRGB_4444;
		break;
	case 16:	// RGB565
		aBufferFormat.iPixelFormat = EUidPixelFormatRGB_565;
			break;
	case 24:	// Really 32bpp, but top 8 unused
	case 32:	// While 32bpp, top 8 will not be used
	default:
		aBufferFormat.iPixelFormat = EUidPixelFormatXRGB_8888;
			break;
		}
	}

TInt DMasterIni::Create()
	{
	TInt configurations = Property::GetInt("ConfigCount", 0);
	if (configurations == 0)
		return KErrGeneral;

	// the pixel formats table is, at present, configuration independent
	TInt count;
	TInt r = KErrNone;
	for (count = 0; count < configurations && r == KErrNone; ++count)
		{
		DWinsUi* dwi = new DWinsUi;
		if (dwi)
			r = dwi->Create(count);

		if (r == KErrNone)
			iSystemInis.Append(dwi);
		}
	if (r != KErrNone)
		return r;

	systemIni = masterIni->iSystemInis[0];

	WinsGuiPowerHandler = DWinsGuiPowerHandler::New();
	if (!WinsGuiPowerHandler)
		return KErrNoMemory;

	TheWin=new HWND[systemIni->iScreens.Count()];
	TheChildWin=new HWND[systemIni->iScreens.Count()];
	TheScreenBitmap=new HBITMAP[systemIni->iScreens.Count()];
	CurrentFlipState=new TEmulatorFlip[systemIni->iScreens.Count()];

	if(!TheWin || !TheChildWin || !TheScreenBitmap || !CurrentFlipState)
		return KErrNoMemory;
	memset(CurrentFlipState,EEmulatorFlipRestore,systemIni->iScreens.Count());

	TBufferSet buffer;
	buffer.iDisplayDriverCount = 0;
	buffer.iDisplayState = ENormalResolution;
	buffer.iDisplayBuffer = 0;
	buffer.iDisplayChannel = NULL;


	TInt i;
	for(i=0;i<systemIni->iScreens.Count();i++)
		{
		DScreenProperties *pScr = systemIni->iScreens[i];

		masterIni->InitBitmapHeader(*pScr, &buffer.iInfo);
		masterIni->InitBufferFormat(*pScr, buffer.iBufferFormat);

		r = masterIni->iBufferSet.Append(buffer);
		if (r != KErrNone)
			return r;
		}

	if (CreateWin32Thread(EThreadEvent, &KernelWindowThread, NULL, ETrue) == NULL)
		return KErrGeneral;

	for(i=0;i<systemIni->iScreens.Count();i++)
		{
		r = Kern::AddHalEntry(EHalGroupDisplay,&DoHalFunction,(TAny*)i,i);
		if (r != KErrNone)
			return r;
		}

	// should really come from Keyboard driver, but not doing it now...
	r = Kern::AddHalEntry(EHalGroupKeyboard,&DoKbdHalFunction,this);
	if (r != KErrNone)
		return r;

	if(systemIni->iXYInputType==EXYInputPointer)
		{
		r = Kern::AddHalEntry(EHalGroupDigitiser,&DoXYHalFunction,this);
		if (r != KErrNone)
			return r;
		}
	else if(systemIni->iXYInputType==EXYInputMouse || systemIni->iXYInputType==EXYInputDeltaMouse)
		{
		r = Kern::AddHalEntry(EHalGroupMouse,&DoMouseHalFunction,this);
		if (r != KErrNone)
			return r;
		}

	return r;
	}

void DMasterIni::InitBitmapHeader(DScreenProperties& aScreenProperties, LPBITMAPV4HEADER aInfo)
	{
	TInt width = aScreenProperties.iMaxScreenWidth; 
	TInt height = aScreenProperties.iMaxScreenHeight;
	TUint bitsPerPixel = MaximumBitDepthFromMask(aScreenProperties.iColorDepth);

	memset(aInfo, 0, sizeof(BITMAPV4HEADER));

	switch (bitsPerPixel)
		{
	case 12:	// XRGB4444
			aInfo->bV4BitCount = 16;
			aInfo->bV4V4Compression = BI_BITFIELDS;
			aInfo->bV4RedMask   = 0x0F00;
			aInfo->bV4GreenMask = 0x00F0;
			aInfo->bV4BlueMask  = 0x000F;
			break;
	case 16:	// RGB565
			aInfo->bV4BitCount = 16;
			aInfo->bV4V4Compression = BI_BITFIELDS;
			aInfo->bV4RedMask   = 0xF800;
			aInfo->bV4GreenMask = 0x07E0;
			aInfo->bV4BlueMask  = 0x001F;
			break;
	case 24:	// Really 32bpp, but top 8 unused
	case 32:	// While 32bpp, top 8 will not be used
	default:
			aInfo->bV4BitCount = 32;
			aInfo->bV4V4Compression = BI_RGB;
			// Mask is implicit for BI_RGB compression
			break;
		}
	
	aInfo->bV4Size = sizeof(BITMAPV4HEADER);
	aInfo->bV4Width = width;
	aInfo->bV4Height = -height;	// Bitmap runs top to bottom
	aInfo->bV4Planes = 1;
	
	TInt bpp = _ALIGN_UP(aInfo->bV4BitCount, 16); //12 & 16 --> 16 ; 24 & 32 --> 32
	TInt widthInPixel = aInfo->bV4Width * bpp;
	//rounding to 32 bits (4 octets) and converting, then, bits to octets;
	TInt scanLineInBytes = _ALIGN_UP(widthInPixel, 32) >> 3; 
	aInfo->bV4SizeImage = -aInfo->bV4Height * scanLineInBytes;

	// Set color space as uncalibrated. All other members are then ignored.
#if defined(LCS_DEVICE_RGB)
	aInfo->bV4CSType = LCS_DEVICE_RGB;
#elif defined(LCS_sRGB)
	aInfo->bV4CSType = LCS_sRGB;
#endif
	}

// Helper function that allocates a single frame buffer. 
static TInt AllocateOneFrameBuffer(TInt aSize, TScreenBuffer &aScreenBuffer)
	{
    // Open shared chunk to the composition framebuffer
	DChunk* chunk = 0;
	// round to page size
	if (aSize <= 0)
		return KErrArgument;
	TUint round = Kern::RoundToPageSize(aSize);
	TLinAddr chunkKernelAddr = 0;
	TUint32 physicalAddress = 0;
	TUint32 chunkMapAttr = 0;

	// create shared chunk
	NKern::ThreadEnterCS();

	TChunkCreateInfo info;
	info.iType = TChunkCreateInfo::ESharedKernelMultiple;
	info.iMaxSize = round;
	info.iMapAttr = 0;
	info.iOwnsMemory = ETrue;
	info.iDestroyedDfc = 0;

	TInt r = Kern::ChunkCreate(info, chunk, chunkKernelAddr, chunkMapAttr);
	if (r == KErrNone)
		{
		// map our chunk to specific 
		r = Kern::ChunkCommitContiguous(chunk, 0, aSize, physicalAddress);
		if (r != KErrNone)
			{
			Kern::ChunkClose(chunk);
			}
		}

	if (r == KErrNone)
		{
		TBufferAddressA* bufferAddress = new TBufferAddressA;
		if (!bufferAddress)
			{
			r = KErrNoMemory;
			}
		else 
			{
			bufferAddress->iAddress = (TAny*)chunkKernelAddr;
			bufferAddress->iChunk = chunk;

			if ((r = aScreenBuffer.iFrameBuffers.Append(bufferAddress->iAddress)) == KErrNone)
				{
				r = aScreenBuffer.iMemChunks.Append(bufferAddress);
				}
			}
		}
	if (r != KErrNone)
		{
		Kern::ChunkClose(chunk);
		}
	NKern::ThreadLeaveCS();
	return KErrNone;
	}

TInt DMasterIni::AllocateFrameBuffers(TInt aScreenNumber, TInt aCount, TInt aSize)
	{
	while (aCount--)
		{
		TInt r = AllocateOneFrameBuffer(aSize, masterIni->iBufferSet[aScreenNumber].iScreenBuffer);
		if (r != KErrNone)
			{
			return r;
			}
		}
	return KErrNone;
	}

void DMasterIni::ReleaseFrameBuffers(TInt aScreenNumber)
	{
	RPointerArray<TAny>& frameBuffers = masterIni->iBufferSet[aScreenNumber].iScreenBuffer.iFrameBuffers;
	RPointerArray<TBufferAddressA>& memChunks = masterIni->iBufferSet[aScreenNumber].iScreenBuffer.iMemChunks;
	RPointerArray<TBufferAddressA>& dsaChunks = masterIni->iBufferSet[aScreenNumber].iDsaBuffer.iMemChunks;

   	NKern::ThreadEnterCS();
	TInt index;
	TInt count = memChunks.Count();
	for (index = 0; index < count; index++)
		{
		Kern::ChunkClose(memChunks[index]->iChunk);
		}
	count = dsaChunks.Count();
	for (index = 0; index < count; index++)
		{
		Kern::ChunkClose(dsaChunks[index]->iChunk);
		}
	NKern::ThreadLeaveCS();
	
	frameBuffers.Reset();
	memChunks.Reset();
	dsaChunks.Reset();
	}


TProcessAddrEntry::TProcessAddrEntry(DProcess *aProcess, TUint8* aAddress): 
	iProcess(aProcess), iAddress(aAddress)
	{
	}


/**
Contruct a Shared Chunk cleanup object which will be used to clean up 
after the process/address table entry. 
*/
TChunkCleanup::TChunkCleanup(DProcess* aProcess, TInt aScreenNumber)
    : TDfc((TDfcFn)TChunkCleanup::ChunkDestroyed,this,Kern::SvMsgQue(),0)
    , iProcess(aProcess)
    , iScreenNumber(aScreenNumber)
    , iIndex(-1)
    {}

/**
Cancel the action of the cleanup object.
*/
void TChunkCleanup::Cancel()
    {
    // Clear iProcess which means that when the DFC gets queued on chunk destruction
    // our ChunkDestroyed method will do nothing other than cleanup itself.
    iProcess = NULL;
    }

/**
Callback function called when the DFC runs, i.e. when a chunk is destroyed.
*/
void TChunkCleanup::ChunkDestroyed(TChunkCleanup* aSelf)
    {
    DProcess* process = aSelf->iProcess;
    TInt screenNumber = aSelf->iScreenNumber;
    TUint index = aSelf->iIndex;
    // If we haven't been Cancelled...
    if(process && index != -1)
        {
    	if (masterIni->iBufferSet[screenNumber].iProcAddrTable[index].iProcess == process)
    		{
    		masterIni->iBufferSet[screenNumber].iProcAddrTable[index].iProcess = 0;
    		}
    	else
    		{
    		__KTRACE_OPT(KEXTENSION,Kern::Printf("Oops! Someone has messed up our process index!"));
    		}
        }

    // We've finished so now delete ourself
    delete aSelf;
    }


TInt DMasterIni::DisplayMemoryHandle(TInt aScreenNumber, TInt& aHandle)
	{
	if (iBufferSet[aScreenNumber].iDsaBuffer.iMemChunks.Count() == 0)
		{	
		int r;
		r = AllocateOneFrameBuffer(iMaxSizeInBytes, iBufferSet[aScreenNumber].iDsaBuffer);
		if (KErrNone != r)
			{
			return r;
			}
		__ASSERT_DEBUG(iBufferSet[aScreenNumber].iDisplayChannel, Fault(EGuiNoDisplayChannel));
		iBufferSet[aScreenNumber].iDisplayChannel->SetLegacyBuffer(iBufferSet[aScreenNumber].iDsaBuffer.iFrameBuffers[0]);
		}
	
	aHandle = Kern::MakeHandleAndOpen(&Kern::CurrentThread(), 
			iBufferSet[aScreenNumber].iDsaBuffer.iMemChunks[0]->iChunk);
	
	if (aHandle < 0)
		{
		return aHandle;
		}
	
	return KErrNone;
	}



// Find the address of the display memory. 
TInt DMasterIni::DisplayMemoryAddress(TInt aScreenNumber, TInt& aAddress)
	{
	TBufferSet &bufferSet = iBufferSet[aScreenNumber]; 
	DProcess *process = &Kern::CurrentProcess();
	TInt firstFree = -1;
	NKern::FMWait(&iLock);
	TUint count = bufferSet.iProcAddrTable.Count();
	for(TUint i = 0; i < count; ++i)
		{
		DProcess *curProcess = bufferSet.iProcAddrTable[i].iProcess;
		if (curProcess == process)
			{
			aAddress = reinterpret_cast<TInt>(bufferSet.iProcAddrTable[i].iAddress);
			NKern::FMSignal(&iLock);
			return KErrNone;
			}
		if (curProcess == 0 && firstFree == -1)
			{
			firstFree = i;
			}
		}
	NKern::FMSignal(&iLock);
	// If we get here, we couldn't find the process in the iProcAddrTable. 
	// Create a new Process Address entry. 
	// Step 1
	// Create a dummy chunk so that we can detect when the process dies, 
	// give a handle to the user [but don't actually let the process KNOW what the handle is
	// so the user process can't do anything silly with the chunk]. Close our side of the
	// chunk to make sure there is only one reference to it. 
	DChunk* chunk = 0;
	// find page size for one page. 
	TUint round = Kern::RoundToPageSize(1);
	TLinAddr chunkKernelAddr = 0;
	TUint32 chunkMapAttr = 0;
	
	// create shared chunk
	NKern::ThreadEnterCS();
	// Cleanup object, used to issue a DFC when the chunk is closed (and 
	// as the handle of the chunk is not given to the process, it can only
	// be closed when the process terminates!)
	TChunkCleanup *cleanup = new TChunkCleanup(process, aScreenNumber);
	if (!cleanup)
		{
		NKern::ThreadLeaveCS();
		return KErrNoMemory;
		}

	TChunkCreateInfo info;
	info.iType = TChunkCreateInfo::ESharedKernelMultiple;
	info.iMaxSize = round;
	info.iMapAttr = 0;
	info.iOwnsMemory = ETrue;
	info.iDestroyedDfc = cleanup;

	TInt r = Kern::ChunkCreate(info, chunk, chunkKernelAddr, chunkMapAttr);
	
	if (r != KErrNone)
		{
		delete cleanup;
		NKern::ThreadLeaveCS();
		return r;
		}
	
	
	// Create a new handle for the user thread. 
    r = Kern::MakeHandleAndOpen(&Kern::CurrentThread(), chunk);
	Kern::ChunkClose(chunk);
	if (r <= 0)
		{
		NKern::ThreadLeaveCS();
		return r;
		}

	// Step 2
	// Create a second handle for the chunk to the DSA buffer.
	// First part: Make sure there is a DisplayMemoryHandle;
	TInt handle = 0;
	r = DisplayMemoryHandle(aScreenNumber, handle); 
	if (r != KErrNone)
		{
		Kern::ChunkClose(chunk);
		NKern::ThreadLeaveCS();
		return r;
		}
	
	DChunk *dsaChunk = bufferSet.iDsaBuffer.iMemChunks[0]->iChunk;
	
	// Step 3
	// Get the base addrss and insert into table. 
	TUint8* baseAddress = Kern::ChunkUserBase(dsaChunk, &Kern::CurrentThread());
	NKern::FMWait(&iLock);
	// Optimistically, the place we found earlier in the table is still free.
	if (firstFree != -1 && bufferSet.iProcAddrTable[firstFree].iProcess != 0)
		{
		// If not, we go find another one.
		firstFree = -1;
		TUint count = bufferSet.iProcAddrTable.Count();
		for(TUint i = 0; i < count; ++i)
			{
			if (bufferSet.iProcAddrTable[i].iProcess == 0)
				{
				firstFree = i;
				break;
				}
			}
		}
	// Check if there is a free entry - if so, re-use it. 
	if (firstFree != -1)
		{
		bufferSet.iProcAddrTable[firstFree].iProcess = process;
		bufferSet.iProcAddrTable[firstFree].iAddress = baseAddress;
		cleanup->SetIndex(firstFree);
		NKern::FMSignal(&iLock);
		}
	else
		{
		// No free entry. Append it to the list. 
		NKern::FMSignal(&iLock);
		TProcessAddrEntry entry(process, baseAddress);
		r = bufferSet.iProcAddrTable.Append(entry);
		if (r != KErrNone)
			{
			Kern::ChunkClose(chunk);
			NKern::ThreadLeaveCS();
			return r;
			}
		// We added it at the end - so we start from the back and check for the 
		// process, as some other process COULD have added one after us, so we 
		// can't just use the count for index!
		TUint index;
		for(index = bufferSet.iProcAddrTable.Count()-1; index; --index)
			{
			if (bufferSet.iProcAddrTable[index].iProcess == process 
					&& bufferSet.iProcAddrTable[index].iAddress != baseAddress)
				{
				break;
				}
			}
		cleanup->SetIndex(index);
		}
	aAddress = reinterpret_cast<TInt>(baseAddress);

	NKern::ThreadLeaveCS();
	return KErrNone;
	}

EXPORT_C TInt WinsGui::CurrentConfiguration()
	{
	return ::CurrentConfiguration;
	}

GLDEF_C void Fault(TGuiPanic aPanic)
	{
	Kern::Fault("WINS-UI",aPanic);
	}

DECLARE_STANDARD_EXTENSION()
	{
	__KTRACE_OPT(KEXTENSION,Kern::Printf("Starting Emulator GUI"));

	// if NoGui property == true do nothing
	if (Property::GetBool("NoGui",EFalse))
		return KErrNone;

	// create keyboard driver
	TInt r=KErrNoMemory;
	masterIni = new DMasterIni;
	if (masterIni)
		{
		r = masterIni->Create();
		if (r!= KErrNone)
			{
			return r;
			}
		}
	
	DMultiTouch::iMultiTouchSupported = DMultiTouch::Init();

	// Create multitouch when necessary
	if (systemIni->MultiTouchEnabled() && systemIni->GCEEnabled() && DMultiTouch::iMultiTouchSupported)
		{
		TheMultiTouch = new DMultiTouch(systemIni->MultiTouchProximityStep(),systemIni->MultiTouchPressureStep());
		if(!TheMultiTouch)
			{
			r = KErrNoMemory;
			__KTRACE_OPT(KEXTENSION,Kern::Printf("Returns %d",r));
			return r;
			}
		DMultiTouch::iMultiTouchCreated = TRUE;
		}

	__KTRACE_OPT(KEXTENSION,Kern::Printf("Returns %d",r));
	return r;
	}

TInt DWinsUi::DoDefineEmulatorControlHotKey(TAny* aPtr, const char* aValue)
	{
	return static_cast<DWinsUi*>(aPtr)->DefineEmulatorControlHotKey(aValue);
	}


TInt DWinsUi::DefineEmulatorControlHotKey(const char* aValue)
	{
	const char* beg = skipws(aValue);
	const char* end = skiptok(beg);
	TInt err = KErrNone;
	
	TEmulCommand command = ENoCommand;
	TInt data = 0;
	if (_strnicmp(beg, "SelectConfig", end-beg) == 0)
		{
		//get the int param which is the config to switch to
		beg = end;
		char * e;
		data = strtol(beg, &e,0);
		if (beg == e)
			err = KErrArgument;
		end = e;
		command = ESelectConfig;
		}
	else if(_strnicmp(beg, "NextConfig", end-beg) == 0)
		{
		command = ENextConfig;
		}
	else
		{
		err = KErrArgument;
		}
	if (err != KErrNone)
		return err;
	
	// get the keys	
	KeyCombination* pCombination = new KeyCombination(data, command);
	if (!pCombination)
		return KErrNoMemory;	
	
	beg = skipws(end);
	const char* end2;	
	for (TInt i=0;i<KMaxHotKeyCombinationLength;i++)
		{
		TInt key=KErrNotFound;		
		end2 = skiptok(beg);
		TPtrC8 name((const TUint8*)beg, end2-beg);
		if ((key=iKeyboard.GetScanCode(name))!=KErrNotFound)
			pCombination->AddKey((TStdScanCode)key);
		
		if (beg == end2 || *end2++ != ',') 
			break;		
		beg = end2;
		}
	return iControlHotKeys.Append(pCombination);
	}
