Clutter+GJS で時計を作ってみた
Gnome Shell (やそれからフォークされた Cinnamon など) の、アプレットなどの目に見える部品の多くは JavaScript で作られている。 これは GJS なる Gnome の JavaScript バインディングをつかって書かれている。 JavaScript から Clutterを叩いている。 Clutter を介した描画処理自体は OpenGL を使って描画されるので高速に処理されることになっている。
しかし、実際に触ったことはなかったので、お試しで時計をつくることで触ってみた。
時計仕様
時計は、現在時刻と、引数で指定した時刻の差分をリアルタイム表示することとした。 また Clutter の OpenGL 描画を活かすために引数で指定した時刻の差分が60秒以下の場合は赤く点滅するようにした。
これをつくる経緯としては、とあるライブのDJセットにて、現在時刻、ストップウォッチ(開始からの時間)、カウントダウン(終了までの残り時間) の3段表示のデジタルクロックを見かけたことである。 そして、これは普通にDJとか関係なく便利そうだと思ったことにある。 その動画は今は見つからなくなってしまったが、HTML上で再現するとおおよそ下記のようになる:
88:88:88.88
88:88:88.88
00:00:00.00
02:00:00.00
成果物
https://gist.github.com/cat-in-136/64759185fc206fa4c8cdf49ec4de361e
gjs
をインタプリタとして実行する。shebang (#!
)も指定しているので実行権限を付与して実行しても良い。
$ gjs path/to/djclock.js
メモ
Clutter.Stage
がメインウィンドウである。
const stage = new Clutter.Stage({
title: "djclock",
layout_manager: new Clutter.BoxLayout(),
user_resizable: true,
x_expand: true,
y_expand: true,
background_color: Clutter.Color.from_string("#000")[1],
});
stage.set_size(256, (1+TIMES.length)*24);
stage.connect("destroy", () => Clutter.main_quit());
Clutter自体は簡素な部品と簡素なレイアウトマネージャがあるため、簡単な配置はこれで十分である。 ボタンなど高級な部品は提供されていないので、そういうのが必要なときは何らかの方法で頑張る必要があるが、 今回はテキストを表示するだけであるのでこれで十分である。
const box = new Clutter.Box({
layout_manager: new Clutter.BoxLayout({
orientation: Clutter.Orientation.VERTICAL,
spacing: 2,
}),
x_expand: true,
});
stage.add_child(box);
const time_current = new Clutter.Text({
x_expand: true,
text: '...',
color: Clutter.Color.from_string("#fff")[1],
});
box.add_child(time_current);
点滅アニメーションは予めアニメーションを登録して、それをClutter.Text
にセットすればよい。
点滅処理自体はClutter自身が面倒を見る。ここはCSS Animation的な思想に近い。
const pt_60sec_before = new Clutter.PropertyTransition({ property_name: 'background-color' });
pt_60sec_before.set_from(Clutter.Color.from_string("#000")[1]);
pt_60sec_before.set_from(Clutter.Color.from_string("#800")[1]);
pt_60sec_before.set_progress_mode(Clutter.AnimationMode.LINEAR);
const tg_60sec_before = new Clutter.TransitionGroup();
tg_60sec_before.set_duration(1000);
tg_60sec_before.set_repeat_count(-1);
tg_60sec_before.add_transition(pt_60sec_before);
const TG_NAME_60SEC_BEFORE = "60sec-before";
// ...snip...
times_dj[i].remove_all_transitions();
times_dj[i].add_transition(TG_NAME_30SEC_BEFORE, tg_30sec_before);
その他はClutterに関係ない純粋なJavaScriptの知識で解せる内容ではあるが、 SpiderMonkey由来ということもあり普通にモダンなJavaScriptがかけるというのはいいことだ。 2011年のGnome Shell登場時はまだ使えない文法も多かったはずで、かなり面倒くさい書き方をしていたのではないかと想像する。
const text = [
(diff < 0)? "-" : "+",
(days != 0)? `${days} ` : "",
`${hours}`.padStart(2, "0"),
":",
`${minutes}`.padStart(2, "0"),
":",
`${seconds}`.padStart(2, "0"),
".",
`${microseconds}`.padStart(6, "0"),
].join("");
times_dj[i].set_text(text);
あとClutterの情報は日本語はもちろん英語も資料はすくない。 Gnome Shellの拡張に関する文献とClutterのAPIを参考するのが一番近道だ。 ふとしのブログにいくつか記事があって参考になるが、 ソースコードが古いので手を加える必要がある。