「りも」トップページへ
TIPS index へ

スクリーンセーバーを作る

Jan.04.2000

xscreensaver用のモジュール作成のヒント。
製作作品のほうにある「雪降る世界のシステムインフォメーション」 を例題に xscreensaver 用のスクリーンセーバー用モジュールを 作成する方法について。


・X-Window とスクリーンセーバー

X-Window におけるスクリーンセーバーというと、デフォルトの画面シェード (画面が真っ黒になるだけ)や xlock などぱらぱらとあるのですが、今最も 使われているのは xscreensaver ではないでしょうか。
これを使うと X-Window の screensave をフックした上に色々なモジュール が実行でき、華やかなスクリーンセーバーを実装できます。実際、ものすごい 数(しかもお馴染み)のモジュールが付属しているのでそういった意味でも デファクトスタンダードかしらん。

で、このモジュール(というか他プロセスフォーク)形式で色々な画面を 表示できるというところに目をつけて「これ用のスクリーンセーバーモジュール (しかも萌え系限定)を作成できないかなあ」と考えた次第。


・xscreensaver のモジュール

xscreensaver が起動するスクリーンセーバーモジュールを作るには どうしたら良いのでしょうか?
※実はモジュールという言葉は正しくない。「xscreensaver が利用できる program」と表現すべき。
xscreensaver の man を読むと「モジュールは二つの約束事を守ること」 と書いてあります。 ひとつが描画をルートウィンドウに行うこと(X-Window の root)。 そしてもう一つが、バーチャルルートウィンドウに対応すること。
このバーチャルルートウィンドウってのがいまいち理解しきれて いないのですが (^^; 「vroot.h をインクルードすれば済む」とも 書いてあるので素直にそれに従うことに。 どうやら X11 のヘッダーマクロを書き換えて、ルートウィンドウ情報 を得るところをほげほげするみたいです。ま、うまいことやってくれる のなら利用するが吉。

一般的な xscreensaver のモジュールでは -root スイッチがあって、 起動時に指定されているとルートウィンドウに表示するようになって います。まあ、これに習って作成すれば目的は達せられるでしょう。 逆に言うとスイッチが無いときは Window で表示しなければならない、 というかした方が良いと言うことですね。

これら条件を満たしていれば他は xscreensaver が面倒を見てくれる わけですから、後は面白そうな画面表示を行う「普通のプログラム」 を組むだけで良いということになります。「まっ*たく*簡*単*だ!」 (ブルワーカーなんて最近の若者は知らないか…)


・GTK+ とルートウィンドウ

さて、次は画面表示を伴う「面白そうなプログラム」を組むわけですが 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 へのアクセスが大体出来ることになり 目的達成というわけです。


rero2@yuumu.rim.or.jp