windowing/windowserverplugins/keyeventrouting/src/keyrouter.cpp
author hgs
Fri, 24 Sep 2010 16:44:34 +0300
changeset 188 1b081cb0800b
permissions -rw-r--r--
201026_1

// Copyright (c) 2010 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:
// Reference implementation of Key Event Routing plug-in

/**
@file
@internalTechnology
@prototype
*/

#include <e32cmn.h>
#include <e32debug.h>
#include "keyrouterimpl.h"
#include "keyaliases.h"

#ifdef KEYROUTER_TEST
/**
Privileged application UIDs
*/
const TInt KUidTAutoServer = 0x10205152;
const TInt KUidReservedApp = 0x102872e3;

/**
Key Capture Translation Table

The following scan codes and key codes are translated at capture-request time.

@note	The data in this table is for example/test purposes only.
*/
const TTranslationEntry KTranslations[] =
	{
		// Entry for TEvent test case GRAPHICS-WSERV-0751.
		// Capture requests for psuedo key Device0 are mapped to capture
		// the real key Device1.
		// Req. scancode,	Capture scancode,	Req. keycode,	Capture keycode
		{ EStdKeyDevice0,	EStdKeyDevice1,		EKeyDevice0,	EKeyDevice1 }
	};

/**
Restricted Key Table

The following keys may be captured only by the specified application.

@note	The data in this table is for example/test purposes only.
*/
const TAppKeyEntry KRestrictedKeys[] =
	{
		// Examples:
		// Only the Phone app is allowed to capture long End key events
		// { EKeyPhoneEnd,			ECaptureTypeLongKey,	KUidPhoneApp },
		// Only the Home Screen app is allowed to capture short Apps key events
		// { EStdKeyApplication,	ECaptureTypeKeyUpDown,	KUidHomeScreenApp },
		// { EKeyApplication,		ECaptureTypeKey,		KUidHomeScreenApp },
		// Only SysApp is allowed to capture long Apps key events (for
		// launching the task switcher).
		// { EKeyApplication,		ECaptureTypeLongKey,	KUidSysApp },

		// Test Case GRAPHICS-WSERV-0754. Only a reserved UID is allowed to
		// capture F20 key events.
		{ EKeyF20,				ECaptureTypeKey,		KUidReservedApp },
		{ EKeyF20,				ECaptureTypeLongKey,	KUidReservedApp },
		{ EStdKeyF20,			ECaptureTypeKeyUpDown,	KUidReservedApp },
		// Only TAuto tests are allowed to capture F21 key events
		{ EKeyF21,				ECaptureTypeKey,		KUidTAutoServer },
		{ EKeyF21,				ECaptureTypeLongKey,	KUidTAutoServer },
		{ EStdKeyF21,			ECaptureTypeKeyUpDown,	KUidTAutoServer }
	};

/**
Priority Application Table

The following applications have priority over other applications for capture
of the specified key.

@note	The data in this table is for example/test purposes only.
		If a key should be never be delivered to any other application, then
		it should be placed in KRestrictedKeys[] instead.
*/
const TAppKeyEntry KPriorityAppKeys[] =
	{
		// Example:
		// The Phone app has priority for capture of short Send and End
		// key events
		// { EStdKeyPhoneSend,	ECaptureTypeKeyUpDown,	KUidPhoneApp },
		// { EKeyPhoneSend,		ECaptureTypeKey,		KUidPhoneApp },
		// { EStdKeyPhoneEnd,	ECaptureTypeKeyUpDown,	KUidPhoneApp },
		// { EKeyPhoneEnd,		ECaptureTypeKey,		KUidPhoneApp },

		// Test Case GRAPHICS-WSERV-0757. TAuto tests have priority for
		// capture of F22 key events.
		{ EStdKeyF22,			ECaptureTypeKeyUpDown,	KUidTAutoServer },
		{ EKeyF22,				ECaptureTypeKey,		KUidTAutoServer },
		{ EKeyF22,				ECaptureTypeLongKey,	KUidTAutoServer }
	};

/**
Blocked key table.

The following keys are not routed by default.

@note	The data in this table is for example/test purposes only.
		Since long key events are never routed by default, there is no need
		to list them here.
*/
const TBlockedKeyEntry KBlockedKeys[] =
	{
		// Entries for TEvent test case GRAPHICS-WSERV-0760
		{ EStdKeyDevice3,	ECaptureTypeKeyUpDown },
		{ EKeyDevice3,		ECaptureTypeKey }
	};

const TInt KNumTranslations = TABLE_SIZE(KTranslations);
const TInt KNumRestrictedKeys = TABLE_SIZE(KRestrictedKeys);
const TInt KNumPriorityAppKeys = TABLE_SIZE(KPriorityAppKeys);
const TInt KNumBlockedKeys = TABLE_SIZE(KBlockedKeys);
#endif // KEYROUTER_TEST

const TInt KCaptureKeyArrayGranularity = 5;

/**
Create and return an instance of CKeyEventRouter

@return	Pointer to new router instance
*/
EXPORT_C CKeyEventRouter* CKeyEventRouter::NewL()
	{
	return new(ELeave) CKeyEventRouterImpl;
	}

CKeyEventRouterImpl::CKeyEventRouterImpl()
: iCaptureKeys(KCaptureKeyArrayGranularity, _FOFF(TKeyCaptureRequest, iHandle))
	{
	}

CKeyEventRouterImpl::~CKeyEventRouterImpl()
	{
	iCaptureKeys.Close();
	}

/**
Add a new key capture request

@param	aRequest	Capture request details

@see CKeyEventRouter::AddCaptureKeyL
*/
void CKeyEventRouterImpl::AddCaptureKeyL(const TKeyCaptureRequest& aRequest)
	{
	CheckCaptureKeyL(aRequest);
	iCaptureKeys.InsertL(aRequest, 0);
#ifdef KEYROUTER_TEST
	TranslateCaptureKey(aRequest.iType, iCaptureKeys[0].iInputCode);
#endif
	}
   
/**
Update an existing key capture request

@param	aRequest	Updated capture request details

@see CKeyEventRouter::UpdateCaptureKeyL
*/
void CKeyEventRouterImpl::UpdateCaptureKeyL(const TKeyCaptureRequest& aRequest)
	{
	CheckCaptureKeyL(aRequest);

	TInt index = iCaptureKeys.Find(aRequest);
	ASSERT(index != KErrNotFound);

	if (index != KErrNotFound)
		{
		iCaptureKeys[index] = aRequest;
		}
	}

/**
Cancel a key capture request

@param	aType		Capture type
@param	aHandle		Opaque handle of request to be cancelled

@see CKeyEventRouter::CancelCaptureKey

Note: aType is unused in this implementation, but is present to permit
implementations that use separate lists for each of the three capture types.
*/
void CKeyEventRouterImpl::CancelCaptureKey(TKeyCaptureType /*aType*/, TAny* aHandle)
	{
	TKeyCaptureRequest request;
	request.iHandle = aHandle;

	TInt index = iCaptureKeys.Find(request);
	if (index != KErrNotFound)
		{
		iCaptureKeys.Remove(index);
		}
	}

/**
Route the key event described by aInput and return its destination in iOutput

@param	aInput		Input data with key event to be routed
@param	aOutput		Output key event and routing results

@see CKeyEventRouter::RouteKey
*/
void CKeyEventRouterImpl::RouteKey(const TKeyEventRouterInput& aInput, TKeyEventRouterOutput& aOutput)
	{
	TUint inputCode = (aInput.iType == ECaptureTypeKeyUpDown) ?
						aInput.iKeyEvent.iScanCode : aInput.iKeyEvent.iCode;
#ifdef KEYROUTER_TEST
	TInt priUid = PriorityAppUid(aInput.iType, inputCode);
#endif
	TInt priority = KMinTInt;
	TInt captureCount = iCaptureKeys.Count();
	TKeyCaptureRequest* matchRequest = NULL;

	// Find the highest priority matching capture request. If there are
	// multiple entries with the same priority then use the first one, i.e.
	// the most recent request. Capture priorities are overridden if a
	// particular application has precedence for capture of the key.
	for (TInt index = 0; index < captureCount; index++)
		{
		TKeyCaptureRequest& request = iCaptureKeys[index];
		if (request.iType == aInput.iType &&
			request.iInputCode == inputCode &&
		    (aInput.iKeyEvent.iModifiers & request.iModifierMask) == request.iModifiers &&
			(request.iPriority > priority
#ifdef KEYROUTER_TEST
			 || priUid != 0 && request.iAppUid.iUid == priUid
#endif
			 ))
			{
			matchRequest = &request;
#ifdef KEYROUTER_TEST
			if (priUid != 0 && request.iAppUid.iUid == priUid)
				{
				break;
				}
#endif
			priority = request.iPriority;
			}
		}

	if (matchRequest)
		{
		// Found matching capture request. Route the key event to the window
		// group that made the capture request.
		aOutput.iWindowGroup = matchRequest->iWindowGroup;
		if (aInput.iType == ECaptureTypeKeyUpDown)
			{
			aOutput.iKeyEvent.iScanCode = matchRequest->iOutputCode;
			aOutput.iKeyEvent.iCode = aInput.iKeyEvent.iCode;
			}
		else
			{
			aOutput.iKeyEvent.iScanCode = aInput.iKeyEvent.iScanCode;
			aOutput.iKeyEvent.iCode = matchRequest->iOutputCode;
			}
		aOutput.iKeyEvent.iModifiers = aInput.iKeyEvent.iModifiers;
		aOutput.iCaptureHandle = matchRequest->iHandle;
		aOutput.iResult = ECaptured;
		}
#ifdef KEYROUTER_TEST
	else if (IsKeyBlocked(aInput.iType, inputCode))
		{
		// No matching capture request and key is blocked. Do not route.
		aOutput.iResult = EConsumed;
		}
#endif
	else
		{
		// No matching capture request. Route the key event to the current
		// focussed window group.
		aOutput.iWindowGroup = aInput.iFocusWindowGroup;
		aOutput.iKeyEvent = aInput.iKeyEvent;
		aOutput.iCaptureHandle = NULL;
		aOutput.iResult = ERouted;
		}
	}

/**
Check that capture request arguments are sane and capture is allowed

@param	aRequest	Capture request details

@leave	KErrArgument if modifier state contains bits that are not in the
		modifier mask or modifier mask contains EModifierLongKey or priority
		is KMinTInt.
		KErrPermissionDenied if key is restricted to capture by a specific
		application UID and the UID of the requesting app does not match.

@note	Requests with a priority of KMinTint would never be captured: this was
		also true of the previous implementation in ektran.dll but was ignored.
		Client code must use the appropriate API function to capture short or
		long key events, so including EModifierLongKey in the modifier mask is
		not allowed. (Hotkeys are excepted since a mask of 0xffffffff is used
		to disable them).
*/
void CKeyEventRouterImpl::CheckCaptureKeyL(const TKeyCaptureRequest& aRequest)
	{
	if ((aRequest.iModifiers & ~aRequest.iModifierMask) != 0 ||
		(aRequest.iModifierMask & EModifierLongKey) != 0 && aRequest.iWindowGroup != NULL ||
		aRequest.iPriority == KMinTInt)
		{
		User::Leave(KErrArgument);
		}

#ifdef KEYROUTER_TEST
	if (aRequest.iWindowGroup != NULL && IsRestrictedKey(aRequest))
		{
		User::Leave(KErrPermissionDenied);
		}
#endif
	}

#ifdef KEYROUTER_TEST
/**
Check if the requested key capture is restricted by application UID

@param	aRequest	Capture request details

@return	ETrue if key is restricted and requesting UID does not match,
		otherwise EFalse.
*/
TBool CKeyEventRouterImpl::IsRestrictedKey(const TKeyCaptureRequest& aRequest)
	{
	for (const TAppKeyEntry* entry = KRestrictedKeys; entry < &KRestrictedKeys[KNumRestrictedKeys]; entry++)
		{
		if (entry->iCode == aRequest.iInputCode &&
			entry->iType == aRequest.iType &&
			entry->iAppUidValue != aRequest.iAppUid.iUid)
			{
			return ETrue;
			}
		}

		return EFalse;
	}

/**
Return the UID of the application with priority for capture of the specified
key.

@param	aType	Key capture type
@param	aCode	Key code or scan code

@return	UID3 of the privileged application or zero if none.
*/
TInt CKeyEventRouterImpl::PriorityAppUid(TKeyCaptureType aType, TUint aCode)
	{
	for (const TAppKeyEntry* entry = KPriorityAppKeys; entry < &KPriorityAppKeys[KNumPriorityAppKeys]; entry++)
		{
		if (entry->iCode == aCode && entry->iType == aType)
			{
			return entry->iAppUidValue;
			}
		}

		return 0;
	}

/**
Check if the specified key is blocked from default routing

@param	aType		Key capture type
@param	aCode		Scan code or key code

@return	ETrue if key is blocked, otherwise EFalse.
*/
TBool CKeyEventRouterImpl::IsKeyBlocked(TKeyCaptureType aType, TUint aCode)
	{
	for (const TBlockedKeyEntry* entry = KBlockedKeys; entry < &KBlockedKeys[KNumBlockedKeys]; entry++)
		{
		if (entry->iCode == aCode && entry->iType == aType)
			{
			return ETrue;
			}
		}

		return EFalse;
	}

/**
Translate the scan or key code of a capture request

@param	aType	Key capture type
@param	aCode	Key code or scan code, updated to translated value (if any)

@note	This function is used to translate the input key or scan code of a
		capture request on addition to the capture list. When a key event that
		matches the list entry is routed, the output code will be taken from
		TKeyCaptureRequest.iOutputCode and hence automatically translated again
		(typically back to the original input code of the request, unless the
		input and output codes were different). For example, a request to
		capture key code A will be translated to key code B in the capture list.
		When RouteKey() processes an event with key code B and selects that
		list entry, it will output key code A for delivery to the application.
*/
void CKeyEventRouterImpl::TranslateCaptureKey(TKeyCaptureType aType, TUint& aCode)
{
	for (const TTranslationEntry* entry = KTranslations; entry < &KTranslations[KNumTranslations]; entry++)
		{
		if (aType == ECaptureTypeKeyUpDown)
			{
			if (aCode == entry->iRequestScanCode)
				{
				aCode = entry->iCaptureScanCode;
				}
			}
		else
			{
			if (aCode == entry->iRequestKeyCode)
				{
				aCode = entry->iCaptureKeyCode;
				}
			}
		}
	}
#endif // KEYROUTER_TEST