Ruby D-Bus Tutorial - Client Usage
This chapter discusses basic client usage and has the following topics:
- Using the library
- Connecting to a bus
- Performing method calls
- Calling a method asynchronously
- Waiting for a signal
- More about introspection
Using the library
If you want to use the library, you have to make Ruby load it by issuing:
require ‘dbus’That’s all! Now we can move on to really using it…
Connecting to a bus
On a typical system, two buses are running, the system bus and the session bus. The system bus can be accessed by:
bus = DBus::SystemBus.instanceProbably you already have guessed how to access the session bus. This can be done by:
bus = DBus::SessionBus.instancePerforming method calls
Let me continue this example using the session bus. Let’s say that I want
to access an object of some client on the session bus. This particular
D-Bus client provides a service called org.gnome.Rhythmbox
. Let me
access this service:
In this example I access the org.gnome.Rhythmbox
service, which is
provided by the application
Rhythmbox.
OK, I have a service handle now, and I know that it exports the object
“/org/gnome/Rhythmbox/Player”. I will trivially access this remote object
using:
Introspection
Well, that was easy. Let’s say that I know that this particular object is
introspectable. In real life most of them are. The rb_object
object we
have here is just a handle of a remote object, in general they are called
proxy objects, because they are the local handle of a remote object. It
would be nice to be able to make it have methods, and that its methods send
a D-Bus call to remotely execute the actual method in another process.
Well, instating these methods for a introspectable object is trivial:
And there you go. Note that not all services or objects can be introspected, therefore you have to do this manually! Let me remind you that objects in D-Bus have interfaces and interfaces have methods. Let’s now access these methods:
rb_player_iface = rb_player[“org.gnome.Rhythmbox.Player”] puts rb_player_iface.getPlayingUriAs you can see, when you want to call a method on an instance object, you have to get the correct interface. It is a bit tedious, so we have the following shortcut that does the same thing as before:
rb_player.default_iface = “org.gnome.Rhythmbox.Player” puts rb_player.getPlayingUriThe default_iface=
call specifies the default interface that should be
used when non existing methods are called directly on a proxy object, and
not on one of its interfaces.
Note that the bus itself has a corresponding introspectable object. You can
access it with bus.proxy
method. For example, you can retrieve an array of
exported service names of a bus like this:
Calling a method asynchronously
D-Bus is asynchronous. This means that you do not have to wait for a reply when you send a message. When you call a remote method that takes a lot of time to process remotely, you don’t want your application to hang, right? Well the asychronousness exists for this reason. What if you dont’ want to wait for the return value of a method, but still you want to take some action when you receive it?
There is a classical method to program this event-driven mechanism. You do some computation, perform some method call, and at the same time you setup a callback that will be triggered once you receive a reply. Then you run a main loop that is responsible to call the callbacks properly. Here is how you do it:
rb_player.getPlayingUri do |resp| puts “The playing URI is #{resp}” end puts “See, I’m not waiting!” loop = DBus::Main.new loop << bus loop.runThis code will print the following:
See, I’m not waiting! The playing URI is file:///music/papapingoin.mp3Waiting for a signal
Signals are calls from the remote object to your program. As a client, you set yourself up to receive a signal and handle it with a callback. Then running the main loop triggers the callback. You can register a callback handler as allows:
rb_player.on_signal(“elapsedChanged”) do |u| puts u endMore about introspection
There are various ways to inspect a remote service. You can simply call
Introspect()
and read the XML output. However, in this tutorial I assume
that you want to do it using the Ruby D-Bus API.
Notice that you can introspect a service, and not only objects:
rb_service = bus.service(“org.gnome.Rhythmbox”) rb_service.introspect p rb_service.rootThis dumps a tree-like structure that represents multiple object paths. In this particular case the output is:
</: {org => {gnome => {Rhythmbox => {Player => ..fdbe625de {},Shell => ..fdbe6852e {},PlaylistManager => ..fdbe4e340 {}}>Read this left to right: the root node is “/”, it has one child node “org”, “org” has one child node “gnome”, and “gnome” has one child node “Rhythmbox”. Rhythmbox has Tree child nodes “Player”, “Shell” and “PlaylistManager”. These three last child nodes have a weird digit that means it has an object instance. Such object instances are already introspected.
If the prose wasn’t clear, maybe the following ASCII art will help you:
/ org gnome Rhythmbox Shell (with object) Player (with object) PlaylistManager (with object)Walking the object tree
You can have an object on any node, i.e. it is not limited to leaves. You can access a specific node like this:
rb_player = rb_service.root[“org”][“gnome”][“Rhythmbox”][“Player”] rb_player = rb_service.object(“/org/gnome/Rhythmbox/Player”)The difference between the two is that for the first one, rb_service
needs to have been introspected. Also the obtained rb_player
is already
introspected whereas the second rb_player
isn’t yet.