Jump on the D-Bus

by Harald Fernengel

If you are running a GNU/Linux system, chances are high that there's a lively conversation happening on your desktop. D-Bus, the freedesktop.org message bus system is currently the standard for desktop communication. It offers an easy way for applications to talk to one another, and integrates well with various frameworks, toolkits and languages.

Qt has had built-in D-Bus support via the QtDBus library since version 4.2. Here is a simple "Hello world" example that connects to your desktop's session bus and lists all registered service names:

    #include <QtCore>
    #include <QtDBus>
    
    int main(int argc, char *argv[])
    {
        QCoreApplication app(argc, argv);
    
        QDBusConnection bus = QDBusConnection::sessionBus();
        QStringList serviceNames = bus.interface()->
                                   registeredServiceNames();
        qDebug() << serviceNames;
    
        return 0;
    }

The static function QDBusConnection::sessionBus() returns a handle to the session bus. The bus itself implements an interface which lets you query the existing services. Under the hood, a message will be sent to the bus, asking for all registered service names. The results are printed via qDebug().

Sending and Receiving

Let's send something to the bus. First, we create a new message &emdash; in our case, a signal.

    QDBusMessage message = QDBusMessage::createSignal("/",
                               "com.mycompany.ping", "ping");

The first argument to createSignal() is the object path, describing where the sender object is located; in our case, we pass a single slash to refer to the root path. The second argument is the object interface; we choose com.mycompany.ping. In the third argument, we pass the name of the signal: ping.

We can add an arbitrary amount of arguments to our signals &emdash; in our case, we add one string containing a nice hello message.

    message << "Hello, just wanted to say that I'm here.";

Finally, we send the message via the session bus.

    bus.send(message);

Now that we know how to send signals to the bus, we also want to receive something. D-Bus and Qt connections are similar. Here is how we do connections within Qt code:

    QObject::connect(<sender>, <signal>, <receiver>, <slot>);

And here is the syntax for D-Bus connections:

    QDBusConnection::connect(<service name>, <object path>,
         <interface name>, <signal name>, <receiver>, <slot>);

Connecting to our ping signal is therefore quite simple:

    bus.connect("", "", "com.mycompany.ping", "ping",
                myObject, SLOT(pingReceived(QString)));

The line above connects signals originating from any connected application on any object path that are named "ping" to the pingReceived() slot. Any time we emit the "ping" signal on the bus, this slot will be invoked.

In order to provide a service on the Bus, we can register a nice name for it:

    bus.interface()->registerService("MyHome");

Talking to the Animals

Let's assume we want to make the mood of our household pets available on the bus for other interested parties:

    class Animal : public QObject
    {
        Q_OBJECT
    
    public:
        Animal() { mood = 0; }
    
    public slots:
        void feed() { ++mood; }
        void shoutAt() { --mood; }
    
        int currentMood() const { return mood; }
    
    private:
        int mood;
    };

Now we can create two animals and connect them to the bus:

    Animal myCat, myDog;
    
    bus.registerObject("/pets/snowflake", &myCat,
                       QDBusConnection::ExportAllSlots);
    bus.registerObject("/pets/waldi", &myDog,
                       QDBusConnection::ExportAllSlots);

Each animal gets a unique object path so they can be referenced unambiguously. Anyone can now inquire about our dog's mood:

    QDBusMessage msg = QDBusMessage::createMethodCall(
                 "MyHome", "/pets/waldi", "", "currentMood");
    
    QDBusMessage reply = bus.call(msg);
    qDebug() << reply;

In the example above, call() will block until the answer arrives. To make the call asynchronous, we can use callWithCallback() instead:

    bus.callWithCallback(msg, this,
                         SLOT(answerReceived(QDBusMessage)));

The callWithCallback() function additionally takes a QObject and a slot as arguments. Once the reply arrives, Qt will invoke the answerReceived() slot, passing the reply as the slot's argument.

Summary

The QtDBus module integrates D-Bus nicely into the world of Qt's signals and slots. Similarly, other bindings solutions integrate D-Bus into other programming languages and toolkits, making D-Bus a very popular technology for exchanging messages.


This document is licensed under the Creative Commons Attribution-Share Alike 2.5 license.

Copyright © 2007 Trolltech Trademarks