SVX日記
2025-01-25(Sat) DBusで数当てゲーム
まー、保険タイプの仕事つうのは、何も起こらなければ、何もしないでチャリンチャリンなわけだが、まっとうな神経を持っていれば、だからこそ客に何か起こった時には、イザという働きをしなければな、と構えているべきである。
で、起きた。決して、事前にアレコレしていたワケではないので、決してベストな体勢ではないが、そこからは誠心誠意やらせてもらう。どうもDBusがカラんでいるらしい。GUIを下支えする何か、という程度の認識だったが、この機に学んでおくか。なんだか面白そうだし。というわけで、基本的な概念について、学んでみることにした。自分にとって学ぶとは、実際に使ってみることであり、可能な限りそれを何かに役立ててみることである。
DBusの基本については、ネット上にそれなりの情報があるが、ごく簡単にまとめると以下だ。まず、バスという道路がある。1本のシステムバスと、ユーザ毎のセッションバス。道路の脇にはサービスという店舗が並んでいて、オブジェクトパスという看板が掲げられている。店舗にはインターフェイスという職人がいて、メソッドという仕事を受け付ける。
$ dbus-send --session --dest=org.mate.ScreenSaver --print-reply --type=method_call \
/org/mate/ScreenSaver \
org.mate.ScreenSaver.GetActiveTime
uint32 60
セッションバスという道路脇の、org.mate.ScreenSaverというサービスの店舗の、/org/mate/ScreenSaverというオブジェクトパスの看板を見て、org.mate.ScreenSaverというインターフェイスの職人に、GetActiveTimeというメソッドの仕事を依頼したら「uint32 60」という答えを返してくれた、という感じ。つまり、スクリーンセーバ起動までの時間は60秒って知れたわけだ。
$ dbus-send --session --dest=org.mate.ScreenSaver --print-reply --type=method_call \
/org/mate/ScreenSaver \
org.mate.ScreenSaver.SetActive boolean:true
なお、すべての店舗には共通にorg.freedesktop.DBus.Introspectableというインターフェイスの職人がいて、Introspectというメソッドの仕事を依頼できる。依頼すると、自分の店舗の職人と仕事のリストを返してくれる。
$ dbus-send --session --dest=org.mate.ScreenSaver --print-reply --type=method_call \
/org/mate/ScreenSaver \
org.freedesktop.DBus.Introspectable.Introspect
:
<interface name="org.mate.ScreenSaver">
:
<method name="GetActiveTime">
<arg name="seconds" direction="out" type="u"/>
</method>
<method name="SetActive">
<arg name="value" direction="in" type="b"/>
</method>
:
自分はサンプルを作るにしても、何らかの意味を持たせることで学びが深まると思っているので、今回は古典的な「数当てゲーム」を実装してみた。自分にとって「数当てゲーム」は往年のBASIC言語における「hello, world」だ。幾度となく空で打ち込んで動かして遊んだのがコンピュータとの原体験。
#!/usr/bin/env ruby
require 'bundler/setup'
require 'dbus' # bundle add ruby-dbus
class DBusKazuate < DBus::Object
@@range = 10
@@answer = nil
# プレイヤ用のインタフェイス
dbus_interface('jp.itline.test.Kazuate') { # Interface
# ゲームの開始メソッド
dbus_method(:ready, 'out res:s') { # Member
@@answer ||= (rand * @@range).to_i + 1
"I'm thinking of a number between 1 and %d." % @@range
}
# 予想した数へ対応メソッド
dbus_method(:try, 'in guess:i, out hint:s') {|guess| # Member
unless(@@answer)
'Not ready yet...'
else
if(guess < @@answer)
'Too low!'
elsif(guess > @@answer)
'Too high!'
else
@@answer = nil
'Congratulations!'
end
end
}
}
# ゲームマスタ用のインタフェイス
dbus_interface('jp.itline.test.Kazuate.master') { # Interface
# 数の範囲の変更メソッド
dbus_method(:range, 'in range:i, out res:s') {|range| # Member
@@range = range
'Range is set.'
}
# 答えを見るメソッド
dbus_method(:answer, 'out ans:i') { # Member
@@answer || -1
}
}
end
# DBus のセットアップ
bus = DBus::SessionBus.instance # Bus is session (not system)
bus.request_name('jp.itline.test.Kazuate') # Service (for --dest)
bus.object_server.export(DBusKazuate.new('/jp/itline/test/Kazuate')) # Object path
DBus::Main.new.tap {|main|
main << bus
}.run
$ dbus-send --session --dest=jp.itline.test.Kazuate --print-reply --type=method_call \
/jp/itline/test/Kazuate \
jp.itline.test.Kazuate.ready
string "I'm thinking of a number between 1 and 10."
$ dbus-send --session --dest=jp.itline.test.Kazuate --print-reply --type=method_call \
/jp/itline/test/Kazuate \
jp.itline.test.Kazuate.try int32:5
string "Too low!"
$ dbus-send --session --dest=jp.itline.test.Kazuate --print-reply --type=method_call \
/jp/itline/test/Kazuate \
jp.itline.test.Kazuate.master.answer
int32 8
$ dbus-send --session --dest=jp.itline.test.Kazuate --print-reply --type=method_call \
/jp/itline/test/Kazuate \
jp.itline.test.Kazuate.try int32:8
string "Congratulations!"
$ dbus-send --session --dest=jp.itline.test.Kazuate --print-reply --type=method_call \
/jp/itline/test/Kazuate \
org.freedesktop.DBus.Introspectable.Introspect
:
<interface name="jp.itline.test.Kazuate">
<method name="ready">
<arg name="res" direction="out" type="s"/>
</method>
<method name="try">
<arg name="guess" direction="in" type="i"/>
<arg name="hint" direction="out" type="s"/>
</method>
</interface>
<interface name="jp.itline.test.Kazuate.master">
<method name="range">
<arg name="range" direction="in" type="i"/>
<arg name="res" direction="out" type="s"/>
</method>
<method name="answer">
<arg name="ans" direction="out" type="i"/>
</method>
</interface>
</node>"