Location: Start / Client Usage Language: en

This chapter discusses basic client usage and has the following topics:

  1. Using the library
  2. Connecting to a bus
  3. Performing method calls
    1. Introspection
  4. Calling a method asynchronously
  5. Waiting for a signal
  6. More about introspection
    1. Walking the object tree

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.instance

Probably you already have guessed how to access the session bus. This can be done by:

bus = DBus::SessionBus.instance

Performing 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:

rb_service = bus.service("org.gnome.Rhythmbox")

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:

rb_player = rb_service.object("/org/gnome/Rhythmbox/Player")

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:

rb_player.introspect

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.getPlayingUri

As 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.getPlayingUri

The 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:

bus.proxy.ListNames[0]

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.run

This code will print the following:

See, I'm not waiting!
The playing URI is file:///music/papapingoin.mp3

Waiting 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
end

More 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.root

This 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
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.