Palm Programmer's Laboratory
Palm OS Programmer's Companion Volume I/3-1
3-1 アプリケーションのイベントループ
前章の「 アプリケーションの開始と終了 」で説明したように、アプリケーションは起動コード sysAppLaunchCmdNormalLaunch を受信した場合に完全なスタートアップ処理を行ないます。最初にスタートアップルーチンが実行され、次にイベントループに入り、最後にストップルーチンが実行されて終了します。
イベントループでは、アプリケーションはキューからイベントを取り出し、それぞれの処理にディスパッチします。この方法の利点は、適切なシステムのデフォルト処理を利用できることです。
イベントループでは、アプリケーションはイベントキューにイベントが入っていないか継続的にチェックします。キューにイベントがあれば、アプリケーションはイベントループの決まりに従って処理を行なわなければなりません。ルールとして、イベントはシステムがそれをどう処理すべきかを知っているものとして渡されます。例えば、システムはフォームやメニューにおけるペンタップに対してどのように応答すれば良いか知っています。
アプリケーションは通常、システムからイベントキュー経由で渡される appStopEvent(これは起動コードではありません)によって終了指示を受けるまでイベントループを続けます。アプリケーションはこのイベントを受信して終了しなければなりません。
リスト 3.1 トップレベルのイベントループの例( Datebook.c より)
static void EventLoop (void) { UInt16 error; EventType event; do { EvtGetEvent (&event, evtWaitForever); PreprocessEvent (&event); if (! SysHandleEvent (&event)) if (! MenuHandleEvent (NULL, &event, &error)) if (! ApplicationHandleEvent (&event)) FrmDispatchEvent (&event); #if EMULATION_LEVEL != EMULATION_NONE ECApptDBValidate (ApptDB); #endif } while (event.eType != appStopEvent); }
イベントループでは、アプリケーションは以下のステップを繰り返します( 図 3.1 とリスト 3.1 を参照して下さい )。
- イベントキューからイベントを取り出します。
- 他のイベントハンドラに取られる前に datebook がコマンドキーを見つけられるよう、PreprocessEvent 関数をコールします。datebook は自動的に非表示にされる UI を表示しますが、この UI はシステムイベントハンドラやメニューイベントハンドラによって他の UI オブジェクトが表示される前に消去されなければなりません。全てのアプリケーションが PreprocessEvent を必要とするわけではありません。すぐに SysHandleEvent をコールするのが適切な場合もあります。
- システムにイベントを処理する機会を与えるために SysHandleEvent をコールします。システムは、例えば電源ボタンのオン/オフや Graffiti 入力、入力エリアアイコンのタップやボタンの押下といったイベントを処理します。SysHandleEvent の処理中に、ユーザーはバッテリ残量警告の通知を受けたり他のアプリケーションのデータ検索を行なうかもしれません。また、SysHandleEvent はイベントの処理中に新しいイベントを生成してキューに追加するかもしれません。例えば、システムはペンイベントからキーイベントへの変換を行うことで Graffiti2 の入力を処理しています。これらのキーイベントもまたイベントキューに追加され、最終的にはアプリケーションによって処理されることになります。SysHandleEvent はイベントが完全に処理され、それ以上そのイベントに対して処理する必要がなくなると true を返します。するとアプリケーションはキューから次のイベントを取り出すことができます。
- SysHandleEvent がイベントの処理を完了しなかった場合、アプリケーションは MenuHandleEvent をコールします。MenuHandleEvent は2種類のイベントを処理します。MenuHandleEvent はイベントを完全に処理すれば true を返します。
- MenuHandleEvent がイベントの処理を完了しなかった場合、アプリケーションは ApplicationHandleEvent をコールします。この関数はアプリケーション自身が用意しなければなりません。ApplicationHandleEvent は frmLoadEvent だけを処理します。この処理ではアプリケーションのフォームリソースをロードして有効にし、FrmSetEventHandler 関数をコールしてアクティブフォームのイベントハンドラを設定します。
- ApplicationHandleEvent がイベントの処理を完了しなかった場合、アプリケーションは FrmDispatchEvent をコールします。FrmDispatchEvent はまずアプリケーション定義のアクティブフォーム用イベントハンドラにイベントを転送します。これは ApplicationHandleEvent によって設定されたイベントハンドラルーチンです。従って、アプリケーションのコードは現在のフォームに関係するイベントを処理する最初の機会を得ることになります。アプリケーションのイベントハンドラがイベントを完全に処理できれば、FrmDispatchEvent からの呼び出しに対して true を返します。この場合、FrmDispatchEvent はアプリケーションのイベントループに復帰します。それ以外の場合、FrmDispatchEvent はイベントに対するシステムのデフォルト処理を行なうために FrmHandleEvent をコールします。例えば、イベントの処理においては、アプリケーションは以下のようにして現在のフォームを閉じてから別のフォームを開かなければならない場合があります。
- アプリケーションは FrmGotoForm をコールして別のフォームをロードします。FrmGotoForm はその時点でアクティブなフォームを閉じるために frmCloseEvent をキューに追加し、続いて frmLoadEvent と frmOpenEvent を新しいフォームのためにキューに追加します。
- アプリケーションは frmCloseEvent を受け取ると、その時点でアクティブなフォームを閉じ、非表示にします。
- アプリケーションは frmLoadEvent を受け取ると、新しいフォームをロードしてアクティブにします。通常、フォームはクローズされるまでアクティブであり続けます。(全てのフォームを事前にロードする場合にはこれは当てはまりませんが、フォームの事前ロードはとにかくおすすめできません。フォームのロード処理は必要に応じていつでも実行できるほど高速なので、オーバーヘッドを気にする必要はありません。)新しいフォーム用のアプリケーション定義のイベントハンドラも設定されます。
- アプリケーションは frmOpenEvent を受け取ると、そのフォームに必要な初期化処理を行ないます。その後画面にフォームを描画します。
- FrmGotoForm がコールされた後、メインのイベントループを介して FrmDispatchEvent に送られるイベントは全て、その時点でアクティブなフォームのイベントハンドラに送られます。それぞれのフォームに関して、イベントハンドラはイベントにどのように応答すれば良いか知っています。例えば、オープン、クローズ、ハイライト、その他のアクションです。FrmHandleEvent はデフォルトの UI 処理機能を呼び出します。
- 特定のフォームに対するイベントを処理するためにシステムができることを全て終えると、アプリケーションは最後にアクティブフォーム自身のイベント処理関数をコールします。例えば Datebook アプリケーションでは、DayViewHandleEvent や WeekViewHandleEvent をコールします。
※訳注 wiki 書法には限界があるため、上記の番号付きリストは原文とやや異なっ ています。まず、項番4の最後の一文は、原文では2項目ある配下のサブ リストの後に来ています。 また、項番7,8は原文では項番6に含まれていますが、サブリストの後 に配置されているため、番号を分けています。
このイベントフローが、アプリケーションが望むだけシステムの処理に依存できるようになっていることは注目に値します。アプリケーションがボタン押下を知りたいのであれば、ctlSelectEvent だけを待っていれば良いのです。イベントキューの細かなことは全てシステムが処理します。
いくつかのイベントは、アプリケーションが実際になんらかの処理を行なうことを要求します。例えば frmOpenEvent がそうです。全てのアプリケーションが行なう典型的な処理は、システムが提供する機能を利用して自身のユーザーインターフェースを描画することと、キューから処理できるイベントが到着するのを待つことです。
イベントを処理しなければならないのはアクティブなフォームだけです。