11) 終わったら冷たいのね
 今回は、小ネタです。
現在、とあるアプリを作成中ですが、その中で気が付いた事がありましたので、ネタにしてみました。

 Palmのアプリを使っていて、あまり気にならないのが、アプリの切り替えですね。
ハードキーを押すか、ホームをタップすれば、大抵の場合、アプリの切り替えが可能で、その高速さがPalmの「ウリ」でもあります。
私は、Palmのアプリが「最終状態が自動的に保持され、自動的に起動時に再現される」という動きに見えましたので、OSがそれらをサポートしていると思い込んでいましたが、NS Basicを触るようになって、自分でしなきゃダメ!、ということを知りました。
そこで、AmiSolは、起動時にDBを読み取り、終了時にDBに保存するように作りました。が、BASICの宿命でしょうか(?)切り替えが若干遅いような気がします。

 終了時の状態を一気にDBに保存しようとするから、遅くなるのかもしれませんね。では、実行時、その都度DBへアクセスすれば終了時のもたつきは解消できるでしょうか?
答えは「引き分け」です。
AmiSolが使っている比較的大きな配列変数、NS Basicはそれらへのアクセスがあまり早くない気がしますので、ゲームの速度が遅くなる可能性があるわけです。
一方、速度が問題にならない場合や、直接データをDBへ保存するだけの場合、随時タイプの方が良いでしょう。
ということで、引き分けなのです。

さて、終了時に何らかの処理をする場合、NS Basicでは、Terminationというイベントを利用します。Terminationは、Startupの逆で、終了時に実行されるコードです。
ハンドブックでは「データベースの終了などの、終了プログラムの実行が行えます。」と記述があります。
AmiSolでも、Termination Codeに、

    Res=DbUpdate(AmiSolDb,"100",Lefcoma,Putmode,SoundMode,Sol,Apx,Apy)
    Res=DbUpdate(AmiSolDb,"101",undo_flg,undo_x1,undo_y1,undo_x2,undo_y2)
というような状態を保存する処理が行われています。

 今回作っているアプリは、AmiSolの10倍程度の大きな配列を使って作っていますが、配列を処理してDBへ保存するだけで結構な時間がかかるようになりました。
Windows上の「お待ち下さい」というよりは待ちませんが、Palm上では「したいことが、即できる」という操作に慣れていますので、我慢できない速度です。
そこで、苦肉の作として、Windowsアプリのように「データを保存してから終了しますか?」というダイアログを表示して、保存したいときだけ保存する仕様にしました。
このような、ダイアログとして簡単なものは、Alert()という関数です。

   Alert(タイトル名,コメント,タイプ,ボタン文字1,ボタン文字2,...,ボタン文字N)

という関数で、戻り値は「ボタン文字N」のボタンをタップしたら、N−1、が戻ってきます。
例えば、

   Res = Alert("Save Data","Do you wish to save data?",1,"Yes","No")
この状態で「Yes」をタップすれば、Resに「0」が返る、という具合です。
では、Termination Codeにこれを実装してみましょう。

・・・実行中・・・・ありゃ?!

一瞬、ダイアログのようなものが表示されたけど、終わってしまいました。
同様に、MsgBoxでも試してみましたが、ありゃ?!でした。
ダイアログらしきものが表示されるので、Form自体はまだ、存在しているのでしょうか?とにかく、ダイアログは使えない状態です。
キー入力を受け付けないのかなぁ。ということです。

「じゃあ、フォームのイベント内でダイアログ処理すれば良いじゃん!」
と思われるかもしれません。そう、ホームをタップしたイベントで取れば良い訳です。
ところで、ハードキーなどを使ったアプリには2種類のアプリが考えられます。1つは、ゲームのように、ハードキーを駆使してホーム以外のキーはアプリ用の機能を割り当てる場合、もう1つは、ハードキーがそのまま有効な場合で、MEMOを押したらメモ帳が起動する場合です。
後者の場合は、キー自体のイベントの処理がありませんので、イベントコード内にAlertなどを実装すればOKです。
前者の場合は、やや注意が必要です。
ゲームなどで、前進、後進、右、左などにキーを割り当てると、たいてい次のようなコードになると思います。

	strkey=GetKey()
            
        Select Case strKey
            Case &h1    'Shoot
                Call Shoot()
            Case &h4    'Shoot
                Call Shoot()
            Case &h2    'move Left
                Px(2)=Px(2)-1
            Case &h3    'move Right
                Px(2)=Px(2)+1
            Case &hb    'move Up
                Py(2)=Py(2)-1
            Case &hc    'move Down
                Py(2)=Py(2)+1
            Case &h6
                Stop
            Case &h11
                Stop
        End Select
        
        SetEventHandled
(STOPのキーコードが &H6と &H11(17)の2つがあるのは、ランタイムのバージョン1.10対策です。)
ハードキーを支配するときのポイントはSetEventHandledですね。OS側の処理を無効にしますので、MEMOを押してもメモ帳が起動しなくなります。
一方、ホームを押してもホームに戻らなくなるため、STOPの処理が必要なわけですね。(「06) ホントは好きじゃないの」参照)
さて、「ホントは好きじゃないの」の時は、気が付かなかったことなのですが、SetEventHandledがOS側の割り込みを無効にするだけではなくて、NS Basicコマンドの「STOP」まで無効にしてしまうようなのです。

	strkey=GetKey()
        
        SetEventHandled    
        
        Select Case strKey
            Case &h1    'Shoot
                Call Shoot()
            Case &h4    'Shoot        
       (中 略)
            Case &h6
                Stop
            Case &h11
                Stop
        End Select

こうすると、STOPコマンドが動かないんですね。それどころか、SetEventHandled後のSTOPは、アプリを固まらせるようです。無理矢理終わろうとしてるんでしょうか、リセットしなければならない危険なアプリになってしまいます。(バグかなぁ?)
とにもかくにも、SetEventHandledは、処理が終わってからの、End Subに近い方がよさそうですね。

 さて、まとめです。
Termination Code内では、入力系のAlert()やMsgBox()を無視して、さっさと終わろうとするので、イベントコード内でトラップするようにしましょう。ということです。
また、そのイベント内では、SetEventHandledの位置に気を付けよう、ということも追加しておきます。

(現在作成中のアプリは、上手くいけば今週中に公開しますが、夜桜が私を呼んでいるので、期待しないで下さいね。)