diff -r 000000000000 -r 2f259fa3e83a uifw/AvKon/animdllsrc/AknKeyEventMap.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/uifw/AvKon/animdllsrc/AknKeyEventMap.cpp Tue Feb 02 01:00:49 2010 +0200 @@ -0,0 +1,1110 @@ +/* +* Copyright (c) 2005 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: An implementation of a configurable key translation map. +* +*/ + + +// INCLUDES +#include "AknKeyEventMap.h" +#include +#include +#include +#include +#include +#include "AknKeyResource.h" +#include +#include "AknAnimLogger.h" +#include "aknanimdllstd.h" + +// CONSTANTS +#define STRIP_MODS &0x0000FFFF +#define GET_MODS &0xFFFF0000 + +// Key event map resource file. +_LIT( KAknKeyTableRsc, "z:\\resource\\AknKeyEventMap.rsc" ); + +// Publish & Subscribe device mode category. +const TInt32 KUidWinservCategoryValue = 0x10003B20; + +// Publish & Subscribe device mode category as TUid. +const TUid KUidWinservCategory = { KUidWinservCategoryValue }; + +// Always pass policy. +_LIT_SECURITY_POLICY_PASS(KAlwaysPassPolicy); + +// Only system application can access. +_LIT_SECURITY_POLICY_S0(KSysapOnlyPolicy, 0x100058F3); + +// ============================ MEMBER FUNCTIONS =============================== + +// ----------------------------------------------------------------------------- +// CAknKeyEventMap::CAknKeyEventMap +// ----------------------------------------------------------------------------- +// +CAknKeyEventMap::CAknKeyEventMap(CAknAsynchTonePlayer& aSoundSession) + : iDownEvents( KAknMaxComboKeys ), iUpEvents( KAknMaxComboKeys ), iSoundSession(aSoundSession) + { + } + +// ----------------------------------------------------------------------------- +// CAknKeyEventMap::ConstructL +// +// Initializes member variables, reads the default keytables from a resource +// file, instantiates P&S subscriber and opens the door for raw events. +// ----------------------------------------------------------------------------- +// +void CAknKeyEventMap::ConstructL( MAnimGeneralFunctions* aFunctions ) + { + iFunctions = aFunctions; + + if ( KAknMaxDeviceMode < 1 || KAknMaxDeviceMode > 15) + { + __AKNANIMLOGSTRING("CAknKeyEventMap::ConstructL ERROR: must have at least 1 and at most 15 device modes"); + User::Leave(KErrGeneral); + } + + iCurrentDeviceMode = KAknModeDefault; + iKeyBeacon.iKeyCode = EKeyNull; + iKeyBeacon.iEventType = 0; + iDownTimer = CPeriodic::New( CActive::EPriorityLow ); + iCombos = new (ELeave) RArray*>( KAknMaxComboKeys ); + + iRepeatRate = KAknRepeatRate; + iKeyPollInterval = static_cast(1000000./iRepeatRate); + + iConsume = EFalse; + iIsCanceled = EFalse; + + iScanCodeIJustGenerated = -1; + + // Initialize keymap. + const TAknKeyEvent nullEvent = { EKeyNull, EKeyNull, 0 }; + for ( TUint j = 0; j < KAknKeyMapRows; j++ ) + { + for ( TUint i = 0; i < KAknKeyMapColumns; i++ ) + { + iKeyMap[i][j] = nullEvent; + } + } + + // Load the default configuration. + TRAPD( ierr, InitializeKeyTableFromResourceL( KAknKeyTableRsc ) ); + if ( ierr != KErrNone ) + { + __AKNANIMLOGSTRING1( "Error initializing the key tables, %d", ierr ); + } + + if ( !IsKeyEventMapEnabled() ) + { + return; + } + + // Define Publish & Subscribe key + TInt err = RProperty::Define( KUidWinservCategory, KAknPSDeviceMode, + RProperty::EInt, KAlwaysPassPolicy, KSysapOnlyPolicy ); + if ( err != KErrNone ) + { + __AKNANIMLOGSTRING1("CAknKeyEventMap::ConstrucL() Problem defining The Key %d", err); + } + + // Subscribe to Publish & Subscribe keys + err = KErrNone; + CAknKefSubscriber *sub = NULL; + + sub = CAknKefSubscriber::NewL( *this, KUidWinservCategory , KAknPSDeviceMode ); + if ( sub ) + { + err = iKefSubscribers.Append( sub ); + if ( err != KErrNone ) + { + delete sub; + __AKNANIMLOGSTRING( "CAknKeyEventMap::ConstructL ERROR: device mode subscription failed." ); + // We could stop the boot here, but we can also continue, just without + // the device modes. + } + } + } + +// ----------------------------------------------------------------------------- +// CAknKeyEventMap::~CAknKeyEventMap +// ----------------------------------------------------------------------------- +// +CAknKeyEventMap::~CAknKeyEventMap() + { + __AKNANIMLOGSTRING("CAknKeyEventMap::Destructor entered."); + + if ( iCombos ) + { + const TInt count = iCombos->Count(); + for ( TInt i = count - 1; i >= 0; i-- ) + { + (*iCombos)[i]->Close(); + delete (*iCombos)[i]; + iCombos->Remove(i); + } + iCombos->Close(); + delete iCombos; + } + + delete iDownTimer; + + iUpEvents.Close(); + iDownEvents.Close(); + iRepeatRates.Close(); + + iKefSubscribers.ResetAndDestroy(); + iKefSubscribers.Close(); + } + +// ----------------------------------------------------------------------------- +// CAknKeyEventMap::IsKeyEventMapEnabled +// ----------------------------------------------------------------------------- +// +TBool CAknKeyEventMap::IsKeyEventMapEnabled() const + { + return iKeyEventMapEnabled; + } + +// ----------------------------------------------------------------------------- +// CAknKeyEventMap::OfferRawEvent +// +// ----------------------------------------------------------------------------- +// +TBool CAknKeyEventMap::OfferRawEvent( const TRawEvent& aRawEvent ) + { + return OfferRawEvent(aRawEvent, EFalse); + } + +// ----------------------------------------------------------------------------- +// CAknKeyEventMap::OfferRawEvent +// +// This is where the raw events end up in, and +// where we have to decide what to do with them. +// ----------------------------------------------------------------------------- +// +TBool CAknKeyEventMap::OfferRawEvent( const TRawEvent& aRawEvent, TBool aSilentEvent ) + { + const TBool downEvent = ( aRawEvent.Type() == TRawEvent::EKeyDown ); + const TBool upEvent = ( aRawEvent.Type() == TRawEvent::EKeyUp ); + + // + // Raw events created by this CAknKeyEventMap itself: pass them on. + // + if ( ( downEvent || upEvent ) && + ( aRawEvent.ScanCode() == iScanCodeIJustGenerated ) ) + { + if ( downEvent ) + { + // Add this scan code to iUpEvents so we can generate + // the appropriate raw UPs when the key press is finished. + __AKNANIMLOGSTRING1("CAknKeyEventMap::OfferRawEvent adding %x to iUpEvents", iScanCodeIJustGenerated); + TInt err = iUpEvents.Append( iScanCodeIJustGenerated ); + if ( err != KErrNone ) // never happened + { + __AKNANIMLOGSTRING("CAknKeyEventMap::OfferRawEvent ERROR appending up event!"); + } + } + + if ( upEvent ) + { + // would be nice to .Remove() elements from iUpEvents here, + // but that'd require another call to ResolveMapping(), + // -- .Remove() in CancelProcessingKeyPress() instead. + } + + iScanCodeIJustGenerated = -1; + return EFalse; // not further processing for scan codes iJustGenerated. + } + + TInt err = KErrNone; + iConsume = EFalse; // very important to set this here. + + // log arrays + for ( TInt i = 0 ; i < iDownEvents.Count() ; i++ ) + { + __AKNANIMLOGSTRING2("CAknKeyEventMap::OfferRawEvent iDownEvents[%d] = %x", i, iDownEvents[i] ); + } + for ( TInt i = 0 ; i < iUpEvents.Count() ; i++ ) + { + __AKNANIMLOGSTRING2("CAknKeyEventMap::OfferRawEvent iUpEvents[%d] = %x", i, iUpEvents[i] ); + } + + // Down events + if ( downEvent && aRawEvent.ScanCode() >= 0 ) + { + iCurrentScanCode = aRawEvent.ScanCode(); + iPostMeOnUp.iKeyCode = EKeyNull; + iPostMeOnUp.iEventType = 0; + __AKNANIMLOGSTRING3( "CAknKeyEventMap::OfferRawEvent KeyDown scancode=%x mods=%x, we're in devicemode=%d", + iCurrentScanCode STRIP_MODS, iCurrentScanCode GET_MODS, iCurrentDeviceMode); + + // Combo support + if ( iDownEvents.Count() < KAknMaxComboKeys ) + { + err = iDownEvents.Append( aRawEvent.ScanCode() STRIP_MODS ); + if ( err != KErrNone ) // never happened + { + __AKNANIMLOGSTRING("CAknKeyEventMap::OfferRawEvent ERROR appending down event!"); + } + } + else + { + // User is trying to press more simultaneous keys than supported. + // We have no problem with that, just ignore it. + } + + // Resolve mapping for this event and set is as the beacon. + iKeyBeacon = ResolveMapping(); + + // Launch a CPeriodic so the duration of the keypress can be known. + // There's only one timer; for combos, it measures the time starting + // from the first DOWN event of the combo. If usability troubles appear, + // try re-starting the timer for each participating key in a combo + // (by removing the test against iDownEvents.Count()) + if ( iDownEvents.Count() < 2 ) + { + iKeyPressDuration = 0.0; + iDownTimer->Cancel(); + iIsCanceled = EFalse; // set this before Start() + iDownTimer->Start( iKeyPollInterval, iKeyPollInterval, + TCallBack( &KeyPressDuration, (TAny*) this ) ); + } + + if ( iKeyBeacon.iKeyCode > KAknKeyDisabled ) + { + if ( ( iKeyBeacon.iEventType & KAknIsLongOnly ) ) + { + // delayed posting of the short event when KAknIsLongOnly + iPostMeOnUp = iKeyBeacon; + return iConsume; // notice! we're finished with this event. + } + else + { + if ( ( iKeyBeacon.iEventType & KAknIsShortKey ) == EFalse ) + { + PostEvent( EAknPostRaw ); + } + else + { + PostEvent( EAknPostKey ); + } + } + } + + // Emit a keyclick. + if ( !aSilentEvent && iKeyBeacon.iKeyCode != KAknKeyDisabled ) + { + if ( iKeyBeacon.iKeyCode > KAknKeyDisabled ) + { + iSoundSession.KeyPressed( iKeyBeacon.iKeyCode STRIP_MODS ); + } + else + { + iSoundSession.KeyPressed( aRawEvent.ScanCode() STRIP_MODS ); + } + } + + return iConsume; // this was set by ResolveMapping + } + + // Up events + if ( upEvent ) + { + iCurrentScanCode = aRawEvent.ScanCode(); + __AKNANIMLOGSTRING1("CAknKeyEventMap::OfferRawEvent KeyUp scancode=%x",aRawEvent.ScanCode()); + + // Check whether this up event finishes a keypress we've been handling. + TInt finishesKeyPress = + iDownEvents.Find( aRawEvent.ScanCode() STRIP_MODS ); + __AKNANIMLOGSTRING1("CAknKeyEventMap::OfferRawEvent finishesKeypress index %d", finishesKeyPress); + __AKNANIMLOGSTRING1("CAknKeyEventMap::OfferRawEvent Duration =%f",iKeyPressDuration); + + // We got an UP for a known DOWN + if ( finishesKeyPress != KErrNotFound ) + { + + if ( iPostMeOnUp.iKeyCode > KAknKeyDisabled && !iIsLongKeyPress ) + { + iKeyBeacon = iPostMeOnUp; + if ( ( iKeyBeacon.iEventType & KAknIsShortKey ) == EFalse ) + { + PostEvent( EAknPostRaw ); + } + else + { + PostEvent( EAknPostKey ); + } + iPostMeOnUp.iKeyCode = EKeyNull; + iPostMeOnUp.iEventType = 0; + } + + iKeyBeacon = ResolveMapping(); + + + // Remove key from the down array + iDownEvents.Remove( finishesKeyPress ); + + // if it is mapped on the up array -> generate the up event + TInt upKeyPressIndex = KErrNotFound; + for ( TInt i = 0 ; i < iUpEvents.Count() ; i++ ) + { + if ( (iUpEvents[i] STRIP_MODS) == ( iKeyBeacon.iKeyCode STRIP_MODS ) ) + { + upKeyPressIndex = i; + break; + } + } + + if ( upKeyPressIndex != KErrNotFound ) + { + iScanCodeIJustGenerated = iUpEvents[upKeyPressIndex]; + TRawEvent rawEvent; + rawEvent.Set( TRawEvent::EKeyUp, iScanCodeIJustGenerated ); + __AKNANIMLOGSTRING1("CAknKeyEventMap::OfferRawEvent POST RAW UP-EVENT for %x",iScanCodeIJustGenerated); + iFunctions->PostRawEvent( rawEvent ) ; + iUpEvents.Remove(upKeyPressIndex); + iConsume = ETrue; + } + + if ( !iDownEvents.Count() ) + { + CancelProcessingKeyPress(); + } + + } + else // finishesKeyPress == KErrNotFound + { + // probably a hardware/driver bug, in any case we're + // confused now. + __AKNANIMLOGSTRING("CAknKeyEventMap::OfferRawEvent INPUT IS CONFUSED: cancel key processing"); + CancelProcessingKeyPress(); + } + } + + return iConsume; + } + +// ----------------------------------------------------------------------------- +// CAknKeyEventMap::CancelProcessingKeyPress +// ----------------------------------------------------------------------------- + +void CAknKeyEventMap::CancelProcessingKeyPress() + { + iDownTimer->Cancel(); + iIsCanceled = ETrue; + iDownEvents.Reset(); + + // be sure to send out all raw upevents or CKeyTranslator + // will think the key is still being pressed. + TRawEvent rawEvent; + while ( iUpEvents.Count() > 0 ) + { + iScanCodeIJustGenerated = iUpEvents[0]; + rawEvent.Set( TRawEvent::EKeyUp, iScanCodeIJustGenerated ); + __AKNANIMLOGSTRING1("CAknKeyEventMap::CancelProcessingKeyPress POST RAW UP-EVENT for %x",iScanCodeIJustGenerated); + iFunctions->PostRawEvent( rawEvent ) ; + iUpEvents.Remove(0); + } + + iUpEvents.Reset(); + iRepeats = 0; + iKeyPressDuration = 0.0; + iIsLongKeyPress = EFalse; + __AKNANIMLOGSTRING1("CAknKeyEventMap::CancelProcessingKeyPress Last beacon sent: %x", iKeyBeacon.iKeyCode); + iKeyBeacon.iKeyCode = EKeyNull; + iKeyBeacon.iEventType = 0; + } + + +// ----------------------------------------------------------------------------- +// CAknKeyEventMap::HandlePropertyChangedL +// +// Callback for Publish & Subscribe key events, it tracks +// the device mode changes for internal use. +// ----------------------------------------------------------------------------- +// +void CAknKeyEventMap::HandlePropertyChangedL( + const TUid& aCategory, + const TUint aKey ) + { + TInt keyValue( 0 ); + TInt err = KErrNone; + err = RProperty::Get( aCategory, aKey, keyValue ); + if ( err == KErrNone ) + { + switch( aKey ) + { + case KAknPSDeviceMode: + { + __AKNANIMLOGSTRING1("CAknKeyEventMap::HandlePropertyChanged VALUE: %d",aKey); + iCurrentDeviceMode = keyValue; + + const TInt count = iRepeatRates.Count(); + TUint16 currentMode = iCurrentDeviceMode; + for ( TInt ii = 0; ii < count; ii++ ) + { + const TAknKeyRepeatRate& repeatRate = iRepeatRates[ ii ]; + + if ( ( currentMode & repeatRate.iModeMask ) == + repeatRate.iMode ) + { + SetRepeatRate( repeatRate.iRate ); + ii = count; // exit the loop. + } + } + } + break; + default: + { + __AKNANIMLOGSTRING1("CAknKeyEventMap::HandlePropertyChanged GOT A P&S VALUE: %d",aKey); + } + break; + } + } + } + +// ----------------------------------------------------------------------------- +// CAknKeyEventMap::InitializeKeyTableFromResourceL +// ----------------------------------------------------------------------------- +// +void CAknKeyEventMap::InitializeKeyTableFromResourceL( + const TDesC& aConfigFile ) + { + // Get a handle for the resource file. + RFs fsSession; + CleanupClosePushL( fsSession ); + TInt err = fsSession.Connect(); + if ( err != KErrNone ) + { + User::Leave( err ); + } + RResourceFile resourceFile; + CleanupClosePushL( resourceFile ); + + // Make sure we have the resource file. + if ( BaflUtils::FileExists( fsSession, aConfigFile ) ) + { + resourceFile.OpenL( fsSession, aConfigFile ); + } + else + { + __AKNANIMLOGSTRING( "CAknKeyEventMap::InitializeKeyTableFromResourceL: no configuration file!"); + CleanupStack::PopAndDestroy(); // CleanupClosePushL. + CleanupStack::PopAndDestroy(); // CleanupClosePushL. + return; + } + + // Confirm signature of the resource file. + resourceFile.ConfirmSignatureL( 0 ); + + // Now just get the resource chunk into a heap buffer and give it to a + // TResourceReader. + HBufC8* res; + res = resourceFile.AllocReadLC( R_AVKON_KEY_EVENT_MAP ); + + TResourceReader theReader; + theReader.SetBuffer( res ); + + // Check version field. + const TUint16 version = theReader.ReadUint16(); + if ( version != KAknKeyEventVersion1 ) + { + __AKNANIMLOGSTRING( "CAknKeyEventMap::InitializeKeyTableFromResourceL: key event map disabled!"); + CleanupStack::PopAndDestroy( res ); + CleanupStack::PopAndDestroy(); // CleanupClosePushL. + CleanupStack::PopAndDestroy(); // CleanupClosePushL. + return; + } + + iKeyEventMapEnabled = ETrue; + theReader.ReadUint16(); // read flags, not in use yet. + + // Read repeat rates. + const TUint count = theReader.ReadUint16(); + for ( TInt idx = 0; idx < count; idx++ ) + { + TUint16 mode = theReader.ReadUint16(); + TUint16 modeMask = theReader.ReadUint16(); + TUint8 rate = theReader.ReadUint8(); + + const TAknKeyRepeatRate repeatRate = + { + mode, + modeMask, + rate + }; + + User::LeaveIfError( iRepeatRates.Append( repeatRate ) ); + } + + // Parses the resource data + CAknKeyResourceArray* keyResourceArray = CAknKeyResourceArray::NewLC(); + CArrayPtrFlat* keyEvents = NULL; + keyEvents = keyResourceArray->GetKeyEventsL( theReader ); + + // At this point we have all the key event info; just put it in its place + // in iKeyMap and iCombos. + for ( TUint i = 0; i < (TUint)keyEvents->Count(); i++ ) + { + CAknKeyResource* keyEvent = keyEvents->At(i); + + if ( keyEvent->GetScanCodes().Count() > 0 ) + { + if ( (keyEvent->GetKeyCodes().Count() == keyEvent->GetDeviceModes().Count()) + && (keyEvent->GetKeyCodes().Count() == keyEvent->GetEventTypes().Count())) + { + for ( TUint j = 0; + j < (TUint)keyEvent->GetKeyCodes().Count(); + j++ ) + { + TUint16 thisMode = keyEvent->GetDeviceModes().At(j); + RArray theseModes; + CleanupClosePushL( theseModes ); + + ResolveDeviceModeL( + theseModes, + keyEvent->GetDeviceModes().At(j) ); + + TUint16 thisType = keyEvent->GetEventTypes().At(j); + for ( TUint k = 0; k < (TUint)theseModes.Count(); k++ ) + { + TAknKeyDefinition thisKey; + thisKey.iScanCodes = &(keyEvent->GetScanCodes()); + thisKey.iDeviceMode = theseModes[k]; + thisKey.iKeyCode = keyEvent->GetKeyCodes().At(j); + thisKey.iEventType = thisType; + + TRAPD( error, SetKeyMappingL( thisKey ) ); + if (error != KErrNone) + { + __AKNANIMLOGSTRING( "CAknKeyEventMap::InitializeKeyTableFromResourceL ERROR setting a mapping"); + } + } + + CleanupStack::PopAndDestroy( &theseModes ); + } + } + else + { + // Keyboard configuration is erroneous! + // When multiple keycodes have been defined, they must have + // an associated device mode, etc. + __AKNANIMLOGSTRING( "CAknKeyEventMap::InitializeKeyTableFromResourceL ERROR: inconsistent data in KEY_EVENT" ); + User::Leave( KErrGeneral ); + } + } + else + { + // Error. No scancodes for the key event. + User::Leave( KErrGeneral ); + } + } + + // All done! The maps are now ready for use! + CleanupStack::PopAndDestroy( 4 ); // keyResourceArray, res, + // &resourceFile, &fsSession + } + +// ----------------------------------------------------------------------------- +// CAknKeyEventMap::SetRepeatRate +// ----------------------------------------------------------------------------- +// +void CAknKeyEventMap::SetRepeatRate( TUint8 aRepeatRate ) + { + if ( iRepeatRate > 0 && iRepeatRate < 32 ) + { + iRepeatRate = aRepeatRate; + } + } + +// ----------------------------------------------------------------------------- +// CAknKeyEventMap::SetKeyMappingL +// +// Sets the keycodes in iKeyMap and iCombos for one device mode. +// ----------------------------------------------------------------------------- +// +void CAknKeyEventMap::SetKeyMappingL( const TAknKeyDefinition& aKey ) + { + if ( aKey.iScanCodes->Count() == 1 ) + { + // Normal (=single key) mapping + if ( (aKey.iEventType & KAknTypeShort) == KAknTypeShort ) + { + // handle anykey definition for single presses. + if ( aKey.iScanCodes->At(0) == KAknAnyKey) + { + for (TUint j = 0; j < KAknMaxScanCode; j++) + { + iKeyMap[j][aKey.iDeviceMode].iShortPress = + aKey.iKeyCode; + iKeyMap[j][aKey.iDeviceMode].iLongPress = + aKey.iKeyCode; + iKeyMap[j][aKey.iDeviceMode].iEventType |= + ResolveEventType ( aKey.iEventType ); + } + } + else + { + // default is to set iLongPress to same as iShortPress, which in + // practice means that the default behavior for long keypresses is + // to repeat. + iKeyMap[aKey.iScanCodes->At(0)][aKey.iDeviceMode].iShortPress = + aKey.iKeyCode; + iKeyMap[aKey.iScanCodes->At(0)][aKey.iDeviceMode].iLongPress = + aKey.iKeyCode; + iKeyMap[aKey.iScanCodes->At(0)][aKey.iDeviceMode].iEventType |= + ResolveEventType( aKey.iEventType ); + } + } + else if ( ((aKey.iEventType & KAknTypeLong) == KAknTypeLong) || + ((aKey.iEventType & KAknTypeLongOnly) == KAknTypeLongOnly)) + { + iKeyMap[aKey.iScanCodes->At(0)][aKey.iDeviceMode].iLongPress = + aKey.iKeyCode; + iKeyMap[aKey.iScanCodes->At(0)][aKey.iDeviceMode].iEventType |= + ResolveEventType( aKey.iEventType ); + } + else + { + // Ignored. + } + } + else + { + // Combo mapping + RArray* newCombo = + new (ELeave) RArray( KAknMaxComboKeys ); + TUint i; + TInt err = KErrNone; + for ( i = 0; i < aKey.iScanCodes->Count(); i++ ) + { + err = newCombo->Append( aKey.iScanCodes->At(i) ); + if ( err != KErrNone ) + { + User::Leave( err ); // we're trapped, just bail out. + } + } + + // For combos, the device mode information is encoded in to the + // upper 16 bits of the Key Code value. + TUint32 keyMode = aKey.iKeyCode + ( aKey.iDeviceMode << 16 ); + newCombo->Insert( keyMode, 0 ); + + TUint32 eventType = ResolveEventType( aKey.iEventType ); + newCombo->Insert( eventType, 1 ); + + err = iCombos->Append( newCombo ); + if ( err != KErrNone ) + { + User::Leave( KErrNone ); + } + } + } + +// ----------------------------------------------------------------------------- +// CAknKeyEventMap::ResolveEventType +// +// A conversion between the eventtype representation in the resource file +// and iKeyMap. +// ----------------------------------------------------------------------------- + +TUint8 CAknKeyEventMap::ResolveEventType( TUint8 aEventType ) + { + TUint8 result = 0; + + if ( (aEventType & (KAknTypeLongOnly)) == KAknTypeLongOnly ) + { + result |= KAknIsLongOnly; + result |= KAknIsLongPress; + if ( (aEventType & KAknTypeKey) ) + { + result |= KAknIsLongKey; + } + } + else if ( (aEventType & KAknTypeLong) == KAknTypeLong ) + { + result |= KAknIsLongPress; + if ( (aEventType & KAknTypeKey) == KAknTypeKey ) + { + result |= KAknIsLongKey; + } + } + else if ( (aEventType & KAknTypeShort) == KAknTypeShort ) + { + result = 0; // to be 101% sure + if ( (aEventType & KAknTypeKey) == KAknTypeKey ) + { + result |= KAknIsShortKey; + } + } + + return result; + } + +// ----------------------------------------------------------------------------- +// CAknKeyEventMap::ResolveDeviceModeLC +// +// Given a TUint specifying a device mode, this function figures out +// which rows of the iKeyMap are affected by a mapping. Only used in +// setting up the key tables, not when querying data from them (just use +// iCurrentDeviceMode then). +// ----------------------------------------------------------------------------- +// +void CAknKeyEventMap::ResolveDeviceModeL( + RArray& aModes, + TUint16 aMode ) + { + TInt err; + TUint16 myMode = aMode; + + for ( TUint k = 0; k < KAknKeyMapRows; k++ ) + { + // The loop index is now tested against aMode to see whether + // there should be a non-null value in iKeyMap. + + if ( k == myMode ) + { + err = aModes.InsertInOrder( k ); + if ( err != KErrNone && err != KErrAlreadyExists ) + { + User::Leave( err ); // we can't initialize the table. fail. + } + } + } + } + +// ----------------------------------------------------------------------------- +// CAknKeyEventMap::GetKeyMapping +// +// Returns mappings from iKeyMap. +// ----------------------------------------------------------------------------- +// +TAknKeyBeacon CAknKeyEventMap::GetKeyMapping( + TUint16 aScanCode, + TUint16 aDeviceMode ) + { + TAknKeyBeacon mapValue = { EKeyNull, 0 }; + + if ( aScanCode < KAknKeyMapColumns && aDeviceMode < KAknKeyMapRows ) + { + // Look up the mode specific value, if any + if ( iIsLongKeyPress == EFalse ) + { + mapValue.iKeyCode = iKeyMap[aScanCode][aDeviceMode].iShortPress; + } + else + { + mapValue.iKeyCode = iKeyMap[aScanCode][aDeviceMode].iLongPress; + } + + mapValue.iEventType = iKeyMap[aScanCode][aDeviceMode].iEventType; + + // With this, we always return the default mapping (if any) + // unless a mode-specific mapping was found. + if ( !mapValue.iKeyCode ) + { + if ( iIsLongKeyPress == EFalse ) + { + mapValue.iKeyCode = iKeyMap[aScanCode][ KAknModeDefault ].iShortPress; + } + else + { + mapValue.iKeyCode = iKeyMap[aScanCode][ KAknModeDefault ].iLongPress; + } + mapValue.iEventType = iKeyMap[aScanCode][ KAknModeDefault ].iEventType; + } + } + return mapValue; + } + +// ----------------------------------------------------------------------------- +// CAknKeyEventMap::GetComboMapping +// +// Returns mappings from iCombos. +// ----------------------------------------------------------------------------- +// +TAknKeyBeacon CAknKeyEventMap::GetComboMapping( + RArray& aComboCandidate ) + { + TAknKeyBeacon mapValue = { EKeyNull, 0 }; + + TUint i; + TUint j; + + // First we check the first index of aComboCandidate + // against the second index (= first scancode) of all combo + // arrays in iCombos. If they match, then the i'th combo + // array in iCombos could match aComboCandidate. + // + // iCombos[i][0] contains the key code that is to be returned for a + // succesful mapping, which introduces the offset-by-one in the following + // loops/lookups.also included in [i][0] are the device mode info and the + // event type info, both encoded into the upper 16 bits of iCombos[i][0]. + + RArray possibleMatch; // iCombos indices that might match + for (i = 0; i < iCombos->Count(); i++) + { + if ( (*(*iCombos)[i])[2] == aComboCandidate[0] && + ((*iCombos)[i])->Count()-2 == aComboCandidate.Count() ) + { + possibleMatch.Append(i); + __AKNANIMLOGSTRING1("CAknKeyEventMap::GetComboMapping ComboCandidate might hit iCombos[%d]",i); + } + } + + // If we have possibleMatches, then we just check whether any of + // them matches aComboCandidate precisely. For the first match, + // we return the keyvalue from the first element of the matching + // combo array. + if ( possibleMatch.Count() ) + { + TInt8 isAMatch = -1; + for ( i = 0; i < possibleMatch.Count() && isAMatch < 0; i++ ) + { + // If isAMatch doesn't go to -1 within this loop, + // then we got a match. + isAMatch = possibleMatch[i]; + for ( j = 2; j < (*(*iCombos)[possibleMatch[i]]).Count(); j++ ) + { + // Try to find all scan codes of the current event + // in a combo definition. + // NOTE: the order of the remaining scancodes is not matched, + // as long as the first scancode is OK, the others can come in + // any order. + TInt err = KErrNotFound; + for ( TInt f = (*(*iCombos)[possibleMatch[i]]).Count()-1; f >= 0 ; f-- ) + { + if ( (*(*iCombos)[possibleMatch[i]])[f] == aComboCandidate[j-2] ) + { + err = f; + break; + } + } + + + // err < 2 must be discarded, because the first two elements are + // used for device mode & event type data. + if ( err == KErrNotFound || err < 2) + { + // If a scan code cannot be found, this possibleMatch is + // not a real match. + isAMatch = -1; + } + } + + // If we get this far, a matching combination of keys has been + // found. Still need to check that the combo type (long/short) and + // the active device mode for this combo match those of the current + // event. + + TUint16 comboMode = ((*(*iCombos)[possibleMatch[i]])[0] GET_MODS) >> 16; + TUint16 comboType = ((*(*iCombos)[possibleMatch[i]])[1]); + + // If .. + // a matching set of scan codes was found, + // but it fails the long/short test, + // or it's defined for another device mode + if ( isAMatch > -1 && + ( iIsLongKeyPress && (!(comboType & KAknIsLongPress) ) || + ( ( iCurrentDeviceMode != comboMode ) && ( comboMode != KAknModeDefault ) ) ) ) + { + isAMatch = -1; + } + } + + if ( isAMatch > -1 && isAMatch < iCombos->Count() ) + { + // The correct return value can be found from the isAMatch'th + // combo-mapping's first element. + mapValue.iKeyCode = (*(*iCombos)[isAMatch])[0] STRIP_MODS; + mapValue.iEventType = (*(*iCombos)[isAMatch])[1]; + __AKNANIMLOGSTRING1("CAknKeyEventMap::GetComboMapping ComboCandidate DID hit iCombos[%d]", isAMatch); + __AKNANIMLOGSTRING1("CAknKeyEventMap::GetComboMapping mapValue = %d", mapValue.iKeyCode); + } + } + else + { + mapValue.iKeyCode = EKeyNull; + mapValue.iEventType = 0 ; + } + possibleMatch.Close(); + return mapValue; + } + +// ----------------------------------------------------------------------------- +// CAknKeyEventMap::ResolveMapping +// +// A little macro for figuring out how to map the current scan code. +// ----------------------------------------------------------------------------- +// +TAknKeyBeacon CAknKeyEventMap::ResolveMapping() + { + TAknKeyBeacon keyBeacon = + { + EKeyNull, 0 + }; + + if ( iDownEvents.Count() > 1 && iDownEvents.Count() <= KAknMaxComboKeys ) + { + // check that the current key is not disabled in this mode + // if it is, then any combo containing this key must be + // blocked. + TAknKeyBeacon tmp = GetKeyMapping( + iCurrentScanCode STRIP_MODS, iCurrentDeviceMode); + + if (tmp.iKeyCode != KAknKeyDisabled) + { + keyBeacon = GetComboMapping( iDownEvents ); + + // If we do not find combo, then try to map a single key. + if ( keyBeacon.iKeyCode == EKeyNull ) + { + __AKNANIMLOGSTRING( "CAknKeyEventMap::ResolveMapping Combo not found. Trying to get single." ); + keyBeacon = GetKeyMapping( + iCurrentScanCode STRIP_MODS, iCurrentDeviceMode ); + } + } + else + { + __AKNANIMLOGSTRING("CAknKeyEventMap::ResolveMapping Ignoring combo because component is disabled"); + keyBeacon.iKeyCode = KAknKeyDisabled; + keyBeacon.iEventType = 0; + } + } + else if ( iDownEvents.Count() == 1 ) + { + keyBeacon = GetKeyMapping( + iCurrentScanCode STRIP_MODS, iCurrentDeviceMode ); + } + + // Always consume succesful mappings, including EKeyDisabled + if ( keyBeacon.iKeyCode != EKeyNull ) + { + iConsume = ETrue; + __AKNANIMLOGSTRING1( "CAknKeyEventMap::ResolveMapping mapping found: %x", keyBeacon.iKeyCode ); + } + else + { + __AKNANIMLOGSTRING( "CAknKeyEventMap::ResolveMapping mapping not found" ); + } + + return keyBeacon; + } + +// ----------------------------------------------------------------------------- +// CAknKeyEventMap::KeyPressDuration +// +// Callback for iDownTimer, used to distinguish between short and +// long key presses and to handle repeats. +// ----------------------------------------------------------------------------- +// +TInt CAknKeyEventMap::KeyPressDuration( TAny* aSelf ) + { + if (aSelf != NULL) + { + CAknKeyEventMap* self = reinterpret_cast(aSelf); + + self->iKeyPressDuration += ((TReal32)self->iKeyPollInterval.Int())/1e6; + + // Distinguish between short and long key presses. + if ( self->iKeyPressDuration < KAknKeyLongThreshold ) + { + // nothing to do. if combo duration measurement is changed + // so that each participating key resets the timer, then + // un-comment the following lines: + // self->iIsLongKeyPress = EFalse; + // self->iKeyBeacon = self->ResolveMapping(); + } + else + { + // Keypress turned out to be a long one + if ( ! self->iIsLongKeyPress && ! self->iIsCanceled ) + { + self->iIsLongKeyPress = ETrue; + self->iKeyBeacon = self->ResolveMapping(); + + // post the KAknTypeLongOnly event + if ( self->iKeyBeacon.iKeyCode > KAknKeyDisabled + && (self->iKeyBeacon.iEventType & KAknIsLongPress)) + { + if ( (self->iKeyBeacon.iEventType & KAknIsLongKey ) ) + { + self->PostEvent( EAknPostKey ); + } + else + { + self->PostEvent( EAknPostRaw ); + } + } + } + } + + // Handle repeats. + if ( self->iKeyPressDuration >= KAknKeyLongThreshold && + self->iKeyBeacon.iKeyCode > KAknKeyDisabled ) + { + if ( ( self->iKeyBeacon.iEventType & KAknIsLongKey ) ) + { + self->iRepeats++; // perhaps don't do this? + self->PostEvent( EAknPostKey ); + } + else + { + __AKNANIMLOGSTRING("CAknKeyEventMap::KeyPressDuration RAW repeat not sent (CKeyTranslator handles that)"); + } + } + + if ( self->iIsCanceled ) + { + self->iDownTimer->Cancel(); + } + } + return KErrNone; + } + +// ----------------------------------------------------------------------------- +// CAknKeyEventMap::PostEvent +// ----------------------------------------------------------------------------- + +void CAknKeyEventMap::PostEvent(TUint aType) + { + switch ( aType ) + { + case EAknPostKey: + TKeyEvent keyEvent; + keyEvent.iCode = iKeyBeacon.iKeyCode; + keyEvent.iScanCode = iCurrentScanCode STRIP_MODS; + keyEvent.iRepeats = iRepeats; + keyEvent.iModifiers = iCurrentScanCode GET_MODS; + iFunctions->PostKeyEvent(keyEvent); + __AKNANIMLOGSTRING1( "CAknKeyEventMap::PostEvent() POST KEY code=%x", keyEvent.iCode); + + break; + + case EAknPostRaw: + default: + TRawEvent rawEvent; + rawEvent.Set(TRawEvent::EKeyDown, (iCurrentScanCode GET_MODS) + iKeyBeacon.iKeyCode ); + iScanCodeIJustGenerated = rawEvent.ScanCode(); + __AKNANIMLOGSTRING1( "CAknKeyEventMap::PostEvent() POST RAW scancode=%x", rawEvent.ScanCode()); + iFunctions->PostRawEvent( rawEvent ); + break; + } + iConsume = ETrue; + } + + + +// End of File