Jan.04.2000
xscreensaver用のモジュール作成のヒント。
製作作品のほうにある「雪降る世界のシステムインフォメーション」
を例題に xscreensaver 用のスクリーンセーバー用モジュールを
作成する方法について。
X-Window におけるスクリーンセーバーというと、デフォルトの画面シェード
(画面が真っ黒になるだけ)や xlock などぱらぱらとあるのですが、今最も
使われているのは xscreensaver ではないでしょうか。
これを使うと X-Window の screensave をフックした上に色々なモジュール
が実行でき、華やかなスクリーンセーバーを実装できます。実際、ものすごい
数(しかもお馴染み)のモジュールが付属しているのでそういった意味でも
デファクトスタンダードかしらん。
で、このモジュール(というか他プロセスフォーク)形式で色々な画面を 表示できるというところに目をつけて「これ用のスクリーンセーバーモジュール (しかも萌え系限定)を作成できないかなあ」と考えた次第。
xscreensaver が起動するスクリーンセーバーモジュールを作るには
どうしたら良いのでしょうか?
※実はモジュールという言葉は正しくない。「xscreensaver が利用できる
program」と表現すべき。
xscreensaver の man を読むと「モジュールは二つの約束事を守ること」
と書いてあります。
ひとつが描画をルートウィンドウに行うこと(X-Window の root)。
そしてもう一つが、バーチャルルートウィンドウに対応すること。
このバーチャルルートウィンドウってのがいまいち理解しきれて
いないのですが (^^; 「vroot.h をインクルードすれば済む」とも
書いてあるので素直にそれに従うことに。
どうやら X11 のヘッダーマクロを書き換えて、ルートウィンドウ情報
を得るところをほげほげするみたいです。ま、うまいことやってくれる
のなら利用するが吉。
一般的な xscreensaver のモジュールでは -root スイッチがあって、 起動時に指定されているとルートウィンドウに表示するようになって います。まあ、これに習って作成すれば目的は達せられるでしょう。 逆に言うとスイッチが無いときは Window で表示しなければならない、 というかした方が良いと言うことですね。
これら条件を満たしていれば他は xscreensaver が面倒を見てくれる わけですから、後は面白そうな画面表示を行う「普通のプログラム」 を組むだけで良いということになります。「まっ*たく*簡*単*だ!」 (ブルワーカーなんて最近の若者は知らないか…)
さて、次は画面表示を伴う「面白そうなプログラム」を組むわけですが
GTK+(GDK)を利用するルーチンが手元にあるのでそれを使ってなるべく
簡単に済ませたいところであります。
ここで一つ問題点。今まで GTK+(GDK) を使ってそのコール内で処理を
まとめるというポリシーを通してきたのはそうすることによって機種や
環境に(なるべく)依存しないコードとなるからでした。
ルートウィンドウという概念は X-Window の物で一般的でないため、
GDK においても表だって公開されていません。よって、
ここで X-Window に依存したプログラムを組む事は可搬性を落しめる
ことなのですが、xscreensaver って時点で依存しているので取りあえず
問題なしとします。よって以下の記述は X-Window & GTK+(GDK) での
お話しです。
さて、Xlib に触ること無く root window の情報を得ることが出来る
のでしょうか。取り合えず調べてみることから始めましょう。
まずは Electric Eyes のソースを眺めるところから始めます。なぜって?
Electric Eyes にはルートに画像を張り付ける機能があるからです。
で、ソースディレクトリにおりて root という単語を grep で検索。
どうやら、ルートウィンドウに対し gdk_window_set_back_pixmap を
用いる事で画像を張り付けられる様です。
では次。どうやって root window の window ポインタを得れば良いのでしょうか。
gdk のヘッダー郡から root という単語を grep します。色々さかのぼったり
すると gdk/gdkx.h の中にそれらしい部分がある事がわかります。
結論から言うと (GdkWindow)gdk_root_parent というグローバル変数があって
その中に root window の情報が入っています。で、このグローバル変数は
通常アクセスできませんので、利用したいソースの中で gdk/gdkx.h を
include してやらねばならない様です。
で、こうして得た情報を元に Electric Eyes のソースを見てみると、
やっぱりそういうことみたいです。よかったよかった。
こうして得た情報を元に root window に画像を表示させてみると無事出来た
ので問題はおおよそ解決できました。今度はそれを扱いやすくします。
拙作の rr_screen.[ch] では表示スクリーンを
設定するのに window と drawalbe の二つを要求していました。このままだと
少々具合が悪いので手を加えます。ですが、以前の手法も必要性があって
そうなっていたため今一つ惜しいところがあります。
そこで、従来の RRScreenInit, RRScreenSwitch を並行して、RRScreenInitRoot,
RRScreenSwitchRoot という関数を製作しました。
美しいかどうかはともかくとして…。
それと expose の部分も変わるので RRScreenRedraw は場合分けします。
(この部分はちょっと危なっかしいなあ)
後は -root スイッチによってスクリーン初期化と管理を呼び分けるだけです。
これで、使いなれた rr_screen そのままに root window へ描画する アプリケーションが作成出来るようになりました。
続いては vroot.h の適用による virtual root window の利用です。
これをやらないと xscreensaver で表示されません。
vroot.h の中身を見れば分かるのですけれど DefaultRootWindow() という
マクロのフックになっています。これで得られた window structure を
なんとかして GDK の仕組みに埋め込めれば良いわけです。
ちなみに DefaultRootWindow の引値は現在のディスプレイ。これは
gdk_display という変数でラップされています。
結論的に言うと得られた virtual root window を gdk_root_parent 内の
xwindow に代入します。
gdk_root_parent.xwindow = DefaultRootWindow(gdk_display);
これでおっけー。
以上で、GDK から root window へのアクセスが大体出来ることになり 目的達成というわけです。