// 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