スクリーンセーバー機能の実装

 

Step-1

まずは、操作せずに一定時間が経過したら、真っ黒い画面を表示する機能を作ろう。スクリーンセーバー用のタスクを用意して、OS起動時に実行。とりあえずシステム時刻のタスクと同じ優先度にしてみた。そしてキーボードおよびマウス操作時に受け取るメッセージを、スクリーンセーバータスクのFIFOに転送する様にした。タスクの方では、入力がなかった場合はスクリーンセーバー起動までのタイマーを設定してスリープ。タイムアウト前に入力があったらタイマーをキャンセル。タイムアウトした場合はスクリーンセーバーの起動といった具合に制御した。また、スクリーンセーバー実行中に入力があった場合は実行を中止する。ささ、早速実行しよう・・・ぉぉ、マウスも消えて見事真っ黒な画面に!! わーい。・・・まだこの時点では致命的な欠陥があることをrapperは知らない・・・。(書いてんジャン!!)

 

Step-1

真っ黒な画面がすべてを覆い尽くしました。

 


Step-2

あああ、発見してしまいました・・・。致命的な欠陥を。と、まあ発見できて良かったというべきか・・・。何かというと、ハングするんです。ぅぅ。1回目はうまく起動/復帰するんだけど、2回目に入るときに・・・ピタ。ポリポリ・・・。ん?ここで気が付いた。アプリならともかく、マウス/キーボード入力まで受け付けなくなるのはヘンだ。スクリーンセーバーのタスクは、優先度がマウス/キーボードとアプリの中間なので、例えスリープがうまくできていなくても、ハングするはずがないのだ。また、2回目で、というのも気になる。と、その辺りを見ていくと・・・ぁ。スミマセン、タイマーキャンセルと間違えて、タイマー開放してました(汗。ご存知の通り、タイマー処理はタイムアウトが近い順にポインタでつながっており、チェーンの様な構造になっている。キャンセル処理をせずにタイマーを開放すると、チェーンが切れた状態になり、タイムアウトが発生しなくなってしまうのだ。偉そうに語っているが、これはかなり恥ずかしい間違いだ。例えるなら、「少林サッカー」を「小林サッカー」と間違えるくらい・・・。えーい、直って良かったな、よし実行!!(強引。

 

Step-2

何回か起動/復帰を繰り返しても問題ない様です。ほっ。

 

まだ試してないんだけど、これってセキュリティホールになりますかね? アプリからタイマー設定して、キャンセルせずにタイマー開放したらハングする?? まあ、開放する前にOS側(API側)でキャンセルする様に修正すればいいんだけどね。時間があったらちょっと試してみよう。

 


Step-3

スクリーンセーバーとしての枠組みとなる仕組みができたので、実際に動作するモノを作ろう。実は、この仕組みができた時点で、スクリーンセーバー機能が組み込まれたOSとしてリリースしてしまった(笑。詳細は「物置小屋」のページを参照してほしい。と、話がそれてしまったが、本題に戻そう。既にrapperは動作するモノの内容を考えているのだが、この実現には新しいAPIが必要となる。ということで、まずはそのAPIを作成しようと思う。「動作するモノ」についてはまだ発表できないが、乞うご期待ということで・・・(期待されてない予感)。

 

1つめはビットマップをコピーするAPIの拡張版で、転送元の転送開始座標を指定できる、というものだ。ビットマップコピーAPIについては、「まるい時計」の実装で作成したのだが、引数に使えるレジスタの数の関係で、必要最低限に簡略化したものを作成した。ただ、ここではど〜〜〜しても必要なので、ニガテのアセンブラで工夫してなんとかすることにした。どうしたかというと、このAPIは座標指定が多いのだが、座標って、画面のドット数しか必要じゃないジャン、少なくともrapperは10000×nnnnという解像度を見たことがない。こりゃ半分のビット数で十分だ、ってなわけで、x座標を16ビット左にシフトして、下位にy座標を詰め込んだ。構造体を使うって手もあるんだけど、アプリ側でいろいろ気遣いが必要なのもメンドイかな、と思ってやめた。実現方法はともかく、引数の数の限界を超えたAPIが完成した(大袈裟。また、前に作成したAPIは互換性のために残しておいた。APIから呼び出される関数の方を少しいじって、両方のAPIからの呼び出しに対応した。ビットマップコピーを使用しているアプリは、「まるい時計」しかないので、新しいAPIを呼び出す様に改造してテストしてみた。てい・・・ふむふむ、とりあえず動作に問題はないようです。元に戻して実行してもOKっぽいので完成としておきます(笑。

 

2つめは文字を表示するAPIの拡張版で、文字のサイズを指定できる、というものだ。正確には文字の倍率を指定でき、例えば「2」を指定すれば、タテコヨ2倍の大きさの文字が表示できるというわけだ。このAPIも上記と同様、引数の制約に引っかかるため、文字列の長さを指定する部分におじゃました。また、互換性のために前のAPIを残しているところも同じだ。さて、APIから呼ばれる関数だが、倍率を指定できる様に修正する必要がある。さらに、そこから呼ばれる関数(この関数が直接文字を表示する関数)を倍率指定に対応するため、大幅に改造した。やっていることは単純で、1ドットを書き込むところを、倍率分のドットを書き込む様にしただけだ。元のプログラムが洗練されていたため、大幅が書き直しが必要となっただけなのだ。少々ループが多くなってしまったが、そこそこすっきりしている。また、直接OSから呼ばれる部分も含めて、この関数を使用している部分を修正した。「winhelo3」を2倍指定に改造して、と。さぁいきますよ〜。えい・・・ほほ〜、なかなかいけてます。「まるい時計」の時刻表示も問題なさそうですな。

 

Step-3

ぉぉぉ、文字がでかい!! リリースの関係でrapuOSのロゴも入ってます。

 


Step-4

さて、必要なAPIも実装できたところで作りますよ〜。ジャンジャン、発表です。どぉだあああ・・・(画面参照)。そうです、その通り、昔流行った(?)16パズルの変形版であ〜る。うむ、もったいつけるほどのモノでもなかった気もするけど、ちょっと開発が遅れていたので(汗、ごまかしてみた。とりあえず、アプリとして実行できる様に作成してみた。これが完成すれば、いよいよタスクからの呼び出しに組み込んで、めでたくスクリーンセーバーの完成となる。

 

プログラムの詳細は近日公開される(はずの)ソースを見てほしいのだが、概要は以下の通りだ。登録された画像を一定サイズに分割し、ばらばら入れ替える。このとき、一定回数で元の画像に戻る様に入れ替える。16パズルの要領で、順次分割されたコマをスライドし、元の画像まで戻す。画像が複数登録されている場合は、次の画像に対して処理を行う。最後の画像まで処理を行ったら、最初の画像に戻る。(現在は画面にある画像1枚のみ登録)

 

開発が遅れていた原因として(いいわけとして?)、以下の様な理由があった。実は、最初にコンパイルしたときには、おびただしい数のエラーがでた。rapperはGOでコンパイルしているのだが、どうやら(少なくともデフォルトでは)bool型をサポートしていない様だ。bool型を多用していたこのプログラムは、えらく怒られまくった(汗。また、コンパイルが通った後も、NASKが「TMPBUFが足りん」と言って怒っていた。どうやら外部変数を使いまくっていたのがアダとなった様だ。あまり外部変数を使いまくるのも美しくないので、とりあえず内部に入れて対応した。実行ファイルができてからは、四捨五入をしようとして、0.5を足していた部分で保護例外が出た(悲。と、まあ、こんな(しなくてもいい)苦労をしていたので遅れたのだ。その分うまくいったときはさぞや、と想像しながら実行してみる・・・Oh、ヒデキ感激(激古。

 

Step-4

さて何の画像でしょう? アプリとしての実行なので、マウス表示はご愛嬌ってことで(笑。

 


Step-5

ようやくここまできた、という感じです。最後の仕上げにかかりますよ〜。まあ、スクリーンセーバータスクから呼び出す様にするだけなんだけどね。呼び出す方法としては、「ncst」コマンドと同じ方法が良さそうだ。このコマンドは、コンソールなしでアプリを起動するコマンドで、「No Console STart」から名付けられたものである。簡単に解説すると、ウィンドウを持たないコンソールタスクを生成して、そこからアプリを起動し、アプリ終了時にタスクを消滅させるという、今のrapperにとって、この上なく便利な機能だ。ということで、ありがたく使わせてもらおう(笑。後はマウス表示をコントロールするだけ。ぉぉっと、マウス表示を消すタイミングはちゃんと考えねば。早すぎると現在の画面の下にスクリーンセーバーが表示されてしまうしね。そこで、起動直前にウィンドウのTOPを調べ、起動後にTOPが増えたら(スクリーンセーバーのウィンドウが表示されたら)、マウスを消すことにした。ここで気をつけなければならなのが、タスクの優先順位をアプリと同じレベルに下げておかないといけないことだ。これをしておかないと、この章ではおなじみの(?)、フリーズするです(汗。すべての問題が片付いたところで、うりゃ!!・・・しばし待ち・・・やた〜うまくいきましたよ。長い道のりでしたが、とうとう完成です。うるうる。

 

Step-5

画像を追加しました。ようやくスクリーンセーバーの完成です。長かった・・・。