s60按键处理模型
做了一段時間航海桌面,被長按,短按,組合鍵各種按鍵邏輯搞的死去活來。
功能堆積的太多后,根本是一團漿糊。
?
正好開發到一個段落,重新整理了按鍵邏輯,把按鍵識別和執行功能分離。
?
識別模塊負責將按鍵事件串對應成可以接受的按鍵狀態。把不需要關心的噪音過濾掉。后面記作Filter
執行模塊根據按鍵狀態執行功能。
?
S60的按鍵事件分成3中類型:Down,Event,Up。
?
其中Event里的Repeats屬性會變化,有0和1兩種值。還有一個Modify屬性,不過基本沒用。后面把Event且Repeat(0)記作Event,Event且Repeat(1)記作Repeat
?
一個短按過程
Down Event Up
一個長按過程
Down Event (Repeat)+ Up
?
常見情況是一個鍵的長按,短按有不同的功能。
必須先出現一個Event事件,然后等待后面的時間出現,如果是Up則是短按,如果是Repeat則是長按。如果是長按,需要忽略后面的Repeat和Up事件。
?
理想的實現是,通過Filter,在長按的時候收到一個狀態表示長按,短按的時候收到一個狀態表示短按。
由于狀態和之前收到是按鍵事件串是一一對應的,所以,狀態可以用一個堆棧表示,按照接受順序講按鍵事件入棧就可以了。
?
可以用一棵樹來描述按鍵接受邏輯。當做DFA來處理。
?
終結節點是可能的接受,在這個節點可以返回。
非終結節點是過度用的輸入事件。
?
一個識別短按的DFA看起來是這樣的
?
?
一個短按長按都要識別的DFA看起來是這樣的
?
?
定義下這個DFA。
每個節點包含以下屬性
?
1. 按鍵掃描碼
2. 按鍵字符碼
3. 重復標志
4. 按鍵類型
5. 控制位(終結,匹配字符碼,匹配掃描碼,匹配重復標志)
?
匹配時根據控制位進行對應判斷。控制位不表明的屬性不參與匹配。
?
然后把各種按鍵的DFA組合成一個DFA,作為Filter的控制邏輯。
?
當一個鍵盤幾乎所有的鍵都有對應功能的時候,這個很管用
?
比如我現在做的這航海桌面。
每個按鍵的長按短按都有一種功能。
?
通過這個Filter,就可以簡單的把Event和Repeat組成的時間串,分解成互相沒關聯的事件。否還要記錄狀態,這十分麻煩。
?
下面 LS表示左鍵,RS表示右鍵,其他的都是字符按鍵。
?
這個看起來很漂亮。很多部分都一樣,都是長按短按都識別,創建代碼也會很簡單。只有一個左鍵(LS)比較特殊,因為有短按長按識別還有組合鍵識別。
?
?
這里有個組合鍵,LS再RS。
?
通過引入超時事件就很容易處理。每次收到UP事件后就引入一個超時。如果超時了,那么這個匹配路徑就被強制退棧。如果這個路徑上有終結點,就返回最后一個終結點作為最后結果。
?
其實這也就是一個普通了不能再普通的連續按鍵判斷邏輯用DFA畫一邊。
?
?
識別成功后怎么知道到底是匹配了那個路徑?把記錄堆棧掃描一下就行了。
?
終于不用再為這種東西煩惱了。
?
以后考慮加入用xml描述DFA,這樣就可以錄下操作,描述不同的行為。
?
定義
?
/** UiKeyFilter.h** Created on: 2009-6-2* Author: Slavik*/ #ifndef UIKEYFILTER_H_ #define UIKEYFILTER_H_ #include <e32base.h> #include <W32STD.H> class IUiKeyFilterCallback { public:virtual ~IUiKeyFilterCallback() {} public:virtual void ProcessKeyEventL( const TKeyEvent& aKeyEvent, TEventCode aType ) =0; }; class CUiKeyFilter : public CTimer {public: enum TUiKeyFilterDFAFlag {ETerminator = 0x01,EUseKeyCode = 0x02,EUseScanCode = 0x04,EUseRepeats = 0x08 }; enum TPopEventReason {ENoReason,EMismatch,ETimeout };class TUiKeyFilterDFA : public CBase {public:TInt iScanCode;TInt iKeyCode;TInt iRepeats;TEventCode iType;TUint iFlag;private:TUiKeyFilterDFA** iNext;TInt iNextCount;public:TUiKeyFilterDFA();~TUiKeyFilterDFA();void AddSuccessorL( TUiKeyFilterDFA* );TUiKeyFilterDFA* Successor(TInt aIndex);TInt SuccessorCount();};struct TUiKeyFilterStatus {TKeyEvent iKeyEvent;TEventCode iType;TUiKeyFilterDFA* iDFA;}; public:CUiKeyFilter( IUiKeyFilterCallback* aCallback );~CUiKeyFilter();void ConstructL();public:void FilterKeyEvent( const TKeyEvent& aKeyEvent, TEventCode aType );void LoadDFAL();void PopEventIfPossibleL( TPopEventReason aReason );const TUiKeyFilterStatus* Top();void Clear();// get status from the top// aPrevIndex = 0, return top// aPrecIndex = 1, return top-1;// and so onconst TUiKeyFilterStatus* Status( TInt aPrevIndex );TInt CombineKeyInterval();void SetCombineKeyInterval( TInt aMSec );protected:void Pop(); void Push( const TKeyEvent& aKeyEvent, TEventCode& aType, TUiKeyFilterDFA* aDFA );TUiKeyFilterDFA* DefaultUiKeyFilterDFAL();TUiKeyFilterDFA* CreateEventAndRepeatDFA_ForScanCodeL( TInt aScanCode );TUiKeyFilterDFA* CreateEventAndRepeatDFA_ForKeyCodeL( TInt aCode );TUiKeyFilterDFA* CreateEventDFA_ForScanCodeL( TInt aScanCode );TUiKeyFilterDFA* CreateEventDFA_ForKeyCodeL( TInt aCode );TUiKeyFilterDFA* CreateScanCodeDFALC( TInt aScanCode, TEventCode aType, TBool aTerminator );TUiKeyFilterDFA* CreateScanCodeDFALC( TInt aScanCode, TEventCode aType, TInt aRepeats, TBool aTerminator );TUiKeyFilterDFA* CreateKeyCodeDFALC( TInt aCode, TEventCode aType, TBool aTerminator );TUiKeyFilterDFA* CreateKeyCodeDFALC( TInt aCode, TEventCode aType, TInt aRepeats, TBool aTerminator );TUiKeyFilterDFA* CreateDefaultKeyNoDFAL();TUiKeyFilterDFA* CreateDefaultKeyYesDFAL();TUiKeyFilterDFA* CreateDefaultKeyLeftDFAL();TUiKeyFilterDFA* CreateDefaultKeyMidDFAL();TUiKeyFilterDFA* CreateDefaultKeyRightDFAL();protected:void RunL();private:IUiKeyFilterCallback* iCallback;TUiKeyFilterDFA* iDFA;TUiKeyFilterStatus iStack[32];TInt iStackSp; TInt iCombineKeyInterval; };
?
實現
?
#include "UiKeyFilter.h" #include "PhoneFeatureAfx.h" #include <COEAUI.H> CUiKeyFilter::TUiKeyFilterDFA::TUiKeyFilterDFA() : iScanCode(0),iKeyCode(0),iRepeats(0),iType(EEventKey),iFlag(0),iNext( NULL ), iNextCount(0) {} CUiKeyFilter::TUiKeyFilterDFA::~TUiKeyFilterDFA() {if( iNext ) {for( TInt i = 0; i < iNextCount; i++ ) {delete iNext[i];}User::Free( iNext );} }void CUiKeyFilter::TUiKeyFilterDFA::AddSuccessorL( CUiKeyFilter::TUiKeyFilterDFA* aNext ) {// new bufTInt count = iNextCount+1;TUiKeyFilterDFA** buf = (TUiKeyFilterDFA**)User::AllocZL( sizeof(TUiKeyFilterDFA*)*count );if( iNextCount ) {// dump old dataMem::Copy( buf, iNext, sizeof(TUiKeyFilterDFA*)*iNextCount ); }buf[count-1] = aNext;if( iNext ) {// release old bufUser::Free( iNext ); }iNext = buf;iNextCount = count; } CUiKeyFilter::TUiKeyFilterDFA* CUiKeyFilter::TUiKeyFilterDFA::Successor(TInt aIndex) {return iNext[aIndex]; } TInt CUiKeyFilter::TUiKeyFilterDFA::SuccessorCount() {return iNextCount; } CUiKeyFilter::CUiKeyFilter( IUiKeyFilterCallback* aCallback ) : CTimer( CActive::EPriorityUserInput ),iCallback( aCallback ),iStackSp(0),iCombineKeyInterval( 500000 ) {} CUiKeyFilter::~CUiKeyFilter() {if( iDFA ) {delete iDFA;} } void CUiKeyFilter::ConstructL() {CTimer::ConstructL();CActiveScheduler::Add( this );LoadDFAL(); }void CUiKeyFilter::FilterKeyEvent( const TKeyEvent& aKeyEvent, TEventCode aType ) {const TUiKeyFilterStatus* top = Top();TUiKeyFilterDFA* dfa = iDFA;if( top ) {dfa = top->iDFA;}TBool match = EFalse;// scan the successorTInt count = dfa->SuccessorCount();for( TInt i = 0; i < count; i++ ) {TUiKeyFilterDFA* n = dfa->Successor(i);if( aType != n->iType ) {// type not matchcontinue;}if( EUseKeyCode & n->iFlag ) {// check key codeif( aKeyEvent.iCode != n->iKeyCode ) {// the key code is not matchcontinue;}}if( EUseScanCode & n->iFlag ) {// check scan codeif( aKeyEvent.iScanCode != n->iScanCode ) {// the scan code is not matchcontinue;}}if( EUseRepeats & n->iFlag ) {// check repeatsif( aKeyEvent.iRepeats != n->iRepeats ) {// the repeats is not matchcontinue;}}// found matchmatch = ETrue;Push( aKeyEvent, aType, n );// write the output if there is oneif( 0 != (ETerminator & (n->iFlag)) && 0 == n->SuccessorCount() ) {PopEventIfPossibleL( ENoReason );}break;}if( !match ) {PopEventIfPossibleL( EMismatch );}if( EEventKeyUp == aType ) {if( IsActive() ) {Cancel();}// active timeoutAfter(iCombineKeyInterval);} } void CUiKeyFilter::PopEventIfPossibleL( TPopEventReason aReason ) {const TUiKeyFilterStatus* top = Top();if( top ) {if( aReason || 0 == top->iDFA->SuccessorCount() ) {// the current input is not acceptable or the DFA is terminatored by a leaf// return the topest terminator in the stack// pop topPop();while( top && 0 == ( ETerminator & (top->iDFA->iFlag) ) ) {// check nexttop = Top();Pop();}if( top ) {// found one value // aKeyEvent = top->iKeyEvent; // aType = top->iType;iCallback->ProcessKeyEventL( top->iKeyEvent, top->iType );}// clear the restClear();}} } void CUiKeyFilter::RunL() {PopEventIfPossibleL( ETimeout ); } void CUiKeyFilter::LoadDFAL() {if( iDFA ) {delete iDFA;iDFA = NULL;}iDFA = DefaultUiKeyFilterDFAL(); } CUiKeyFilter::TUiKeyFilterDFA* CUiKeyFilter::CreateEventAndRepeatDFA_ForScanCodeL( TInt aScanCode ) {// start by a event keyTUiKeyFilterDFA* s0 = CreateScanCodeDFALC( aScanCode, EEventKey, 0, ETrue );// accept the repeat eventTUiKeyFilterDFA* s1 = CreateScanCodeDFALC( aScanCode, EEventKey, 1, ETrue );s0->AddSuccessorL( s1 );CleanupStack::Pop(2,s0);return s0; } CUiKeyFilter::TUiKeyFilterDFA* CUiKeyFilter::CreateEventAndRepeatDFA_ForKeyCodeL( TInt aCode ) {// start by a event keyTUiKeyFilterDFA* s0 = CreateKeyCodeDFALC( aCode, EEventKey, 0, ETrue );// accept the repeat eventTUiKeyFilterDFA* s1 = CreateKeyCodeDFALC( aCode, EEventKey, 1, ETrue );s0->AddSuccessorL( s1 );CleanupStack::Pop(2,s0);return s0; } CUiKeyFilter::TUiKeyFilterDFA* CUiKeyFilter::CreateEventDFA_ForScanCodeL( TInt aScanCode ) {TUiKeyFilterDFA* s0 = CreateScanCodeDFALC( aScanCode, EEventKey, ETrue );CleanupStack::Pop( s0 );return s0; } CUiKeyFilter::TUiKeyFilterDFA* CUiKeyFilter::CreateEventDFA_ForKeyCodeL( TInt aCode ) {TUiKeyFilterDFA* s0 = CreateKeyCodeDFALC( aCode, EEventKey, ETrue );CleanupStack::Pop( s0 );return s0; } CUiKeyFilter::TUiKeyFilterDFA* CUiKeyFilter::CreateDefaultKeyNoDFAL() {return CreateEventAndRepeatDFA_ForScanCodeL( EStdKeyNo );// // accept the repeat event // TUiKeyFilterDFA* s0 = CreateScanCodeDFALC( EStdKeyNo, EEventKey, 0, EFalse ); // TUiKeyFilterDFA* s1 = CreateScanCodeDFALC( EStdKeyNo, EEventKeyUp, ETrue ); // s0->AddSuccessorL( s1 ); // CleanupStack::Pop(s1); // TUiKeyFilterDFA* s2 = CreateScanCodeDFALC( EStdKeyNo, EEventKey, 1, ETrue ); // s0->AddSuccessorL( s2 ); // CleanupStack::Pop(s2); // // CleanupStack::Pop(s0); // return s0; } CUiKeyFilter::TUiKeyFilterDFA* CUiKeyFilter::CreateDefaultKeyYesDFAL() {// start by a event keyTUiKeyFilterDFA* s0 = CreateScanCodeDFALC( EStdKeyYes, EEventKey, ETrue );CleanupStack::Pop( s0 );return s0; } CUiKeyFilter::TUiKeyFilterDFA* CUiKeyFilter::CreateDefaultKeyLeftDFAL() {// return CreateEventAndRepeatDFA_ForScanCodeL( EStdKeyDevice0 );// start by a event keyTUiKeyFilterDFA* s00 = CreateScanCodeDFALC( EStdKeyDevice0, EEventKey, 0, ETrue );// accept the repeat eventTUiKeyFilterDFA* s11 = CreateScanCodeDFALC( EStdKeyDevice0, EEventKey, 1, ETrue );s00->AddSuccessorL( s11 );CleanupStack::Pop( s11 );// accept the up eventTUiKeyFilterDFA* s21 = CreateScanCodeDFALC( EStdKeyDevice0, EEventKeyUp, EFalse );s00->AddSuccessorL( s21 );CleanupStack::Pop( s21 );// accept the right soft key down eventTUiKeyFilterDFA* s22 = CreateScanCodeDFALC( EStdKeyDevice1, EEventKeyDown, EFalse );s21->AddSuccessorL( s22 );CleanupStack::Pop( s22 );// accept the right soft key eventTUiKeyFilterDFA* s23 = CreateScanCodeDFALC( EStdKeyDevice1, EEventKey, 0, EFalse );s22->AddSuccessorL( s23 );CleanupStack::Pop( s23 );// accept the right soft key upTUiKeyFilterDFA* s24 = CreateScanCodeDFALC( EStdKeyDevice1, EEventKeyUp, ETrue );s23->AddSuccessorL( s24 );CleanupStack::Pop( s24 );// completedCleanupStack::Pop( s00 );return s00; } CUiKeyFilter::TUiKeyFilterDFA* CUiKeyFilter::CreateDefaultKeyMidDFAL() {TUiKeyFilterDFA* s0 = CreateScanCodeDFALC( EStdKeyDevice3, EEventKey, ETrue );CleanupStack::Pop( s0 );return s0; } CUiKeyFilter::TUiKeyFilterDFA* CUiKeyFilter::CreateDefaultKeyRightDFAL() {return CreateEventAndRepeatDFA_ForScanCodeL( EStdKeyDevice1 ); } CUiKeyFilter::TUiKeyFilterDFA* CUiKeyFilter::DefaultUiKeyFilterDFAL() {TBool hasQwertyKeyboard = SupportQwertyInputL();// TInt count = 5 + 12 + ( hasQwertyKeyboard ? 26*2 : 0 ); // TUiKeyFilterDFA* dfa = (TUiKeyFilterDFA*)User::AllocZL( sizeof(TUiKeyFilterDFA) ); // dfa->iNext = (TUiKeyFilterDFA**)User::AllocZL( count*sizeof(TUiKeyFilterDFA*) ); // dfa->iNextCount = count; // // TUiKeyFilterDFA** w = dfa->iNext;TUiKeyFilterDFA* dfa = new (ELeave) TUiKeyFilterDFA;CleanupStack::PushL( dfa );dfa->AddSuccessorL( CreateDefaultKeyNoDFAL() );dfa->AddSuccessorL( CreateDefaultKeyNoDFAL() );dfa->AddSuccessorL( CreateDefaultKeyYesDFAL() );dfa->AddSuccessorL( CreateDefaultKeyLeftDFAL() );dfa->AddSuccessorL( CreateDefaultKeyMidDFAL() );dfa->AddSuccessorL( CreateDefaultKeyRightDFAL() );// numbersTUint16 NUMS[] = {'1','2','3','4','5','6','7','8','9','0','*','#',0};TInt i =0;while( NUMS[i] ) {dfa->AddSuccessorL( CreateEventAndRepeatDFA_ForKeyCodeL( NUMS[i++] ) );}// charsif( hasQwertyKeyboard ) {for( TUint c = 'a'; c <= 'z'; c++ ) { // dfa->AddSuccessorL( CreateEventDFA_ForKeyCodeL( c ) );dfa->AddSuccessorL( CreateEventAndRepeatDFA_ForKeyCodeL( c ) );}for( TUint c = 'A'; c <= 'Z'; c++ ) { // dfa->AddSuccessorL( CreateEventDFA_ForKeyCodeL( c ) );dfa->AddSuccessorL( CreateEventAndRepeatDFA_ForKeyCodeL( c ) );}}CleanupStack::Pop( dfa );return dfa; } CUiKeyFilter::TUiKeyFilterDFA* CUiKeyFilter::CreateScanCodeDFALC( TInt aScanCode, TEventCode aType, TBool aTerminator ) {TUiKeyFilterDFA* dfa = new (ELeave) TUiKeyFilterDFA;CleanupStack::PushL( dfa );dfa->iType = aType;dfa->iScanCode = aScanCode;dfa->iFlag = EUseScanCode | ( aTerminator ? ETerminator : 0 );return dfa; } CUiKeyFilter::TUiKeyFilterDFA* CUiKeyFilter::CreateScanCodeDFALC( TInt aScanCode, TEventCode aType, TInt aRepeats, TBool aTerminator ) {TUiKeyFilterDFA* dfa = new (ELeave) TUiKeyFilterDFA;CleanupStack::PushL( dfa );dfa->iType = aType;dfa->iScanCode = aScanCode; dfa->iRepeats = aRepeats;dfa->iFlag = EUseScanCode | EUseRepeats | ( aTerminator ? ETerminator : 0 );return dfa; } CUiKeyFilter::TUiKeyFilterDFA* CUiKeyFilter::CreateKeyCodeDFALC( TInt aCode, TEventCode aType, TBool aTerminator ) {TUiKeyFilterDFA* dfa = new (ELeave) TUiKeyFilterDFA;CleanupStack::PushL( dfa );dfa->iType = aType;dfa->iKeyCode = aCode;dfa->iFlag = EUseKeyCode | ( aTerminator ? ETerminator : 0 );return dfa; } CUiKeyFilter::TUiKeyFilterDFA* CUiKeyFilter::CreateKeyCodeDFALC( TInt aCode, TEventCode aType, TInt aRepeats, TBool aTerminator ) {TUiKeyFilterDFA* dfa = new (ELeave) TUiKeyFilterDFA;CleanupStack::PushL( dfa );dfa->iType = aType;dfa->iKeyCode = aCode;dfa->iRepeats = aRepeats;dfa->iFlag = EUseKeyCode | EUseRepeats | ( aTerminator ? ETerminator : 0 );return dfa; }const CUiKeyFilter::TUiKeyFilterStatus* CUiKeyFilter::Top() {if( iStackSp ) {return iStack + (iStackSp-1);} return NULL; } // get status from the top // aPrevIndex = 0, return top // aPrecIndex = 1, return top-1; // and so on const CUiKeyFilter::TUiKeyFilterStatus* CUiKeyFilter::Status( TInt aPrevIndex ) {TInt offset = ( iStackSp-1-aPrevIndex );if( offset < 0 || offset >= iStackSp ) {return NULL;}return iStack + offset; } void CUiKeyFilter::Push( const TKeyEvent& aKeyEvent, TEventCode& aType, TUiKeyFilterDFA* aDFA ) {iStack[iStackSp].iKeyEvent = aKeyEvent;iStack[iStackSp].iType = aType;iStack[iStackSp].iDFA = aDFA;iStackSp++; } void CUiKeyFilter::Pop() {iStackSp--; } void CUiKeyFilter::Clear() {iStackSp = 0; } TInt CUiKeyFilter::CombineKeyInterval() {return iCombineKeyInterval; } void CUiKeyFilter::SetCombineKeyInterval( TInt aMSec ) {iCombineKeyInterval = aMSec; }
?
使用Filter
?
TKeyResponse CHDesktopAppUi::HandleKeyEventL( const TKeyEvent& aKeyEvent,TEventCode aType) { // filter is not custructed yetif( NULL == iKeyFilter ) { return EKeyWasNotConsumed; }// send system event to filterTKeyEvent e = aKeyEvent;TEventCode t = aType;iKeyFilter->FilterKeyEvent( e, t );return EKeyWasConsumed; } // receive event from filter void CHDesktopAppUi::ProcessKeyEventL( const TKeyEvent& aKeyEvent,TEventCode aType) {TBool repeat = ( aKeyEvent.iRepeats ? ETrue : EFalse );switch( aKeyEvent.iScanCode ) {...case EStdKeyYes:ShowLogsDialedCallL();return;case EStdKeyNo:if( repeat ) {LOG("long press red key");if( iUseRedKeyAsPowerKey ) {TurnOffPhone();} else {if( !IsKeyboardLocked() ) {LockKeyboard();}}} else {if( iTaskSwitcher ) {LOG("cycle task");iTaskSwitcher->CycleTaskL( KSelf, iEikonEnv->RootWin().WindowGroupId() );}}return;}switch( aKeyEvent.iCode ) {case '0':case 'm':case 'M':if( repeat ) {LaunchDefaultBrowserL();return;} break;case '#':case 'j':case 'J':if( repeat ) {if( IsSilentL() ) {SwitchProfileL( 0 );} else {SwitchProfileL( 1 ); }return;}break;}...}
總結
- 上一篇: 【调参07】不平衡分类问题中分类权重计算
- 下一篇: 图测1.0 在线地图测量与高清卫星图Ap