Palm Programmer's Laboratory

トップ 差分 一覧 ソース 検索 ヘルプ RSS ログイン

【C/C++】POLでハードボタン1〜4をフックする方法

[開発情報]

概要

 CForm 派生クラスのイベントマップマクロでは、vchrHard1 などのハードボタンイベントを処理することができません。このページでは、この問題について説明し、これらのボタンイベントを処理する方法を解説します。

 

説明

問題点

 以下のコードで示される CMyForm クラスでは、EVENT_MAP マクロでハードボタン1〜4に対して OnHardButton( ) メソッドを割り当てています。

class CMyForm : public CForm {
public:
    CMyForm( );

public:
    // Form event handlers
    Boolean OnOpen(EventType* pEvent, Boolean& bHandled);
    Boolean OnClose(EventType* pEvent, Boolean& bHandled);
    Boolean OnHardButton(EventType* pEvent, Boolean& bHandled);

private:
    // Event map
    BEGIN_EVENT_MAP(CForm)
        EVENT_MAP_ENTRY(frmOpenEvent, OnOpen)
        EVENT_MAP_ENTRY(frmCloseEvent, OnClose)
        EVENT_MAP_KEY_DOWN_ENTRY(vchrHard1, 0, OnHardButton)
        EVENT_MAP_KEY_DOWN_ENTRY(vchrHard2, 0, OnHardButton)
        EVENT_MAP_KEY_DOWN_ENTRY(vchrHard3, 0, OnHardButton)
        EVENT_MAP_KEY_DOWN_ENTRY(vchrHard4, 0, OnHardButton)
    END_EVENT_MAP()

};

 しかし、この OnHardButton( ) は決して呼び出されることがありません。なぜなら、POL のイベントハンドリングでは、ハードボタンの押下は CForm 派生クラスまで到達しないからです。ハードボタンの押下イベントは、ユーザー定義アプリケーションクラスの基底クラスである CPalmApp のレベルで処理されてしまいます。そのため、ハードボタン押下をフックしたければアプリケーションクラスで対処する必要があります。

 

CPalmApp::EventLoop( )の実装

 アプリケーションクラスで対処するといっても、POL ではイベント処理のほとんどは CPalmApp クラスの内部で行われています。ユーザー定義のメソッドでオーバーライドできる仮想関数は AppHandleEvent( ) と PreSystemEventHook( ) しかありません。これらの仮想関数を呼び出している CPalmApp::EventLoop( ) の実装は以下のようになっています(元のソースから括弧のスタイルなどを整形してあります)。

void CPalmApp::EventLoop( ) {
    if( m_wCommand == sysAppLaunchCmdNormalLaunch ) {
        UInt16 error;
        EventType event;
        do {
            EvtGetEvent( &event, m_dwEventWaitDuration );
            if( !PreSystemEventHook( &event ) )
                if( !SysHandleEvent( &event ) )
                    if( !MenuHandleEvent( 0, &event, &error ) )
                        if( !AppHandleEvent( &event ) )
                            FrmDispatchEvent( &event );
        } while( event.eType != appStopEvent );
    }
}

 ハードボタンの処理は SysHandleEvent( ) で行われてしまうため、AppHandleEvent( ) をオーバーライドしてもハードボタンの処理をフックすることはできません。そのため、PreSystemEventHook( ) をオーバーライドする必要があります。

 PreSystemEventHook( ) をオーバーライドしても、実際に押下されたハードボタンに対してどのような動作をするのは CForm 派生クラス側になります。そのため、アプリケーションクラスがフックしたハードボタンイベントを CForm 派生クラスに伝達する方法が必要になります。

 

PreSystemEventHook( ) から CForm 派生クラスへの伝達

 フックしたイベントを他のオブジェクト( 画面クラス )へ伝達するための仕組みについて考えます。通常の(たとえば画面上のボタンなどのタップ)イベントについては POL の正規のルートで処理するという前提で、PreSystemEventHook( ) でフックする必要のあるイベントに限定した仕組みとして、以下の EventReceiver インタフェイスを考えます。

class EventReceiver {
public:
    virtual Boolean HandleEvent( EventType* pEvent ) = 0;
};

 このインタフェイス(を実装するクラスのオブジェクト)をアプリケーションクラスに登録し、CPalmApp 派生クラスでオーバーライドする PreSystemEventHook( ) から呼び出すようにします。

class EventReceiver;

class CMyApp : public CPalmApp {
public:
    CMyApp( );

public:
    // :
    // :
    virtual Boolean PreSystemEventHook( EventType* pEvent );
    // :
    // :

    // Form map
    BEGIN_FORM_MAP()
        FORM_MAP_ENTRY(MYFORM, CMyForm)
    END_FORM_MAP()

private:
    EventReceiver* m_pEventReceiver;

public:
    static void RegisterEventReceiver( EventReceiver* pReceiver );

};

 大雑把ですが、CMyApp::PreSystemEventHook( ) の実装は以下のような感じになるでしょう。

Boolean CMyApp::PreSystemEventHook( EventType* pEvent ) {
    if( pEvent->eType != keyDownEvent || !m_pEventHandler )
        return false;
    switch( pEvent->data.keyDown.chr ) {
    case vchrHard1:
    case vchrHard2:
    case vchrHard3:
    case vchrHard4:
        return m_pEventReceiver->HandleEvent( pEvent );
    }
    return false;
}

 画面クラス側では、CForm( あるいは CModalForm )と同時に EventReceiver も継承します。

class CMyForm : public CForm, public EventReceiver {
    // :
    // :
public:
    // <<EventReceiver>>
    virtual Boolean HandleEvent( EventType* pEvent );
    // :
    // :
};

 この CMyForm クラスの frmOpenEvent ハンドラで CMyApp::RegisterEventReceiver( ) を呼び出して自身を登録することで、PreSystemEventHook( ) から CMyForm オブジェクトへハードボタン押下イベントを伝達できるようになります。また、frmCloseEvent ハンドラで登録を解除するために NULL を指定して再び CMyApp::RegisterEventReceiver( ) を呼び出す必要があることに注意してください。

 

注意事項

 このページで紹介した方法は、筆者がハードボタン押下イベントを処理するために試行錯誤した結果であり、もっと良い(あるいはより正しい)方法が別に存在するかもしれません。より良い情報をお持ちの方はコメントいただけると幸いです。

 

コメント

Re: - 陰郎 (2007年05月07日 08時54分02秒)
思いついたので自分でコメントしてしまいますが、PreSystemEventHook( ) 内から FrmDispatchEvent( ) を呼び出してしまえばそれで済むような気が...(無論条件を満たした場合だけ)。今職場なので確認できませんが、後ほど試してみます。
Re: - 陰郎 (2007年05月08日 00時43分53秒)
PreSystemEventHook( ) 内から FrmDispatchEvent( ) を呼び出す方式を試してみたところ、何の問題もなく動作しました。ということで、時間を見つけて記事本体を修正することにします。
お名前: コメント:


表示された文字列