D-Bus allows applications to expose internal API to the outside world. These APIs can then be accessed at run-time via the D-Bus protocol using command line applications or D-Bus libraries and bindings themselves. This tutorial looks at the latter method with examples that you can use in your applications.

D-Bus allows applications to expose their internal API to the outside world. These APIs can then be accessed at run-time via the D-Bus protocol using command line applications or D-Bus libraries and bindings themselves. This tutorial looks at the latter method with examples that you can use in your applications.

== Using QDBusMessage ==

== Using QDBusMessage ==

−

{{qt|QDBusMessage}} represents a D-Bus messages that can be sent or has been received over a given bus. Each message is one of five types, depending on the purpose of the message:

+

{{qt|QDBusMessage}} represents a D-Bus messages that can be sent or has been received over a given bus. Each message is one of four types, depending on the purpose of the message:

* method call

* method call

* signal

* signal

* reply

* reply

* error

* error

−

* invalid

An enumeration covering each of these possibilities is defined in {{qt|QDBusMessage}}. A message's type can be access via the <tt>{{qt|QDBusMessage}}::type()</tt> method.

An enumeration covering each of these possibilities is defined in {{qt|QDBusMessage}}. A message's type can be access via the <tt>{{qt|QDBusMessage}}::type()</tt> method.

=== Calling a D-Bus Method ===

=== Calling a D-Bus Method ===

−

A {{qt|QDBusMessage}} can be used directly to call methods in D-Bus services using the <tt>{{qt|QDbusMessage}}::createMethod( const QString & service, const QString & path, const QString & interface, const QString & method )</tt> static method. It returns a {{qt|QDBusMessage}} object that you can then use to make the call.

+

A {{qt|QDBusMessage}} can be used directly to call methods in D-Bus services using the <tt>{{qt|QDBusMessage}}::createMethodCall( const QString & service, const QString & path, const QString & interface, const QString & method )</tt> static method. It returns a {{qt|QDBusMessage}} object that you can then use to make the call.

−

The <tt>interface</tt> parameter is optional and only necessary if the method to be called is not unique in the accessed object address by the <tt>path</tt>. This can happen with the object implements multiple interfaces and those interfaces have methods with the same name. In such (rare) cases, if you do not define the interface to use there is not guarantee as to which method will actually get called. However, usually you can simply pass an empty string (e.g. <tt>""</tt>) as the argument for <tt>interface</tt>.

+

The <tt>interface</tt> parameter is optional and only necessary if the method to be called is not unique in the object associated with the <tt>path</tt>. This can happen if the object implements multiple interfaces which have methods that are named the same. In such (rare) cases, if you do not explicitly define the interface to use there is not guarantee as to which method will actually get called. However, usually you can simply pass an empty string (e.g. <tt>""</tt>) as the argument for <tt>interface</tt>.

By way of example, to access the (fictional) <tt>ping</tt> method on the {{path|/network}} object in the <tt>org.foo.bar</tt> service, one might do this:

By way of example, to access the (fictional) <tt>ping</tt> method on the {{path|/network}} object in the <tt>org.foo.bar</tt> service, one might do this:

In line 5 of the above example we queue the message for sending on the current session bus. We get a <tt>bool</tt> returned letting us know if the queuing was successful or not.

+

In line 5 of the above example we queue the message for sending on the current session bus. We get a <tt>bool</tt> returned letting us know if the queueing was successful or not.

This leaves us with two questions, however:

This leaves us with two questions, however:

Line 47:

Line 50:

=== Setting Parameters ===

=== Setting Parameters ===

−

Sending arguments along with the method call is quite straight forward. First we need to create a {{qt|QList}} of {{qt|QVariant}} objects and then add those to our dbus message. So if the <tt>ping</tt> method in the above took a hostname as a parameter, we might alter the code in this way (note lines 5 through 7):

+

Sending arguments along with the method call is quite straight forward. First we need to create a {{qt|QList}} of {{qt|QVariant}} objects and then add those to our D-Bus message. So if the <tt>ping</tt> method in the above took a hostname as a parameter, we might alter the code in this way (note lines 5 through 7):

{{note|The arguments must appear in the {{qt|QList}} in the same order they are expected by the D-Bus method being called.}}

{{note|The arguments must appear in the {{qt|QList}} in the same order they are expected by the D-Bus method being called.}}

Line 62:

Line 74:

=== Getting Replies ===

=== Getting Replies ===

−

If we wish to actually receive information back from the D-Bus method, we use the <tt>{{qt|QDBusConnect}}::call</tt> method instead. It will block until there is a reply or the call times out. If our <tt>ping</tt> method returned information on the host we provided in the arguments above, we might alter our code to look like this:

+

If we wish to actually receive information back from the D-Bus method, we use the <tt>{{qt|QDBusConnection}}::call</tt> method instead. It will block until there is a reply or the call times out. If our <tt>ping</tt> method returned information on the host we provided in the arguments above, we might alter our code to look like this:

The <tt>response</tt> will be either of type <tt>QDBusMessage::ReplyMessage</tt> or <tt>QDBusMessage::ErrorMessage</tt> depending on whether it was successful or not. We can look through the values returned by retreving the arguments with the <tt>{{qt|QDBusMessage}}::arguments()</tt> method which returns a <tt>{{qt|QList}}&lt;{{qt|QVariant}}&gt;</tt>.

+

The <tt>response</tt> will be either of type <tt>QDBusMessage::ReplyMessage</tt> or <tt>QDBusMessage::ErrorMessage</tt> depending on whether it was successful or not. We can look through the values returned by retrieving the arguments with the <tt>{{qt|QDBusMessage}}::arguments()</tt> method which returns a <tt>{{qt|QList}}&lt;{{qt|QVariant}}&gt;</tt>.

=== Is This The Best Way? ===

=== Is This The Best Way? ===

−

Using QDBusMessage directly in this way to invoke remote D-Bus methods is not the easiest, best or even recommend way of doing things. Next we will look at the more convnient {{qt|QDBusInterface}} class and then look at accessing remote D-Bus interfaces as if they were local methods using a class auto-generated from the XML D-Bus interface description.

+

Using QDBusMessage directly in this way to invoke remote D-Bus methods is not the easiest, best or even recommend way of doing things. We will now look at the more convenient {{qt|QDBusInterface}} class and then look at accessing remote D-Bus interfaces as if they were local methods using proxy classes auto-generated from XML.

== Using QDBusInterface ==

== Using QDBusInterface ==

−

== Auto-Generated Classes From D-Bus XML ==

+

{{qt|QDBusInterface}} provides a simple and direct method to make D-Bus calls and connect to D-Bus signals.

+

+

A {{qt|QDBusInterface}} object represents a given D-Bus interface. The constructor accepts as parameters (in order) a service name, an object path, an optional interface and optionally which bus (e.g. system or session) to use. If no bus is explicitly defined, it defaults to the session bus. If no interface is given, the returned object will be used to call all interfaces on the bus.

+

+

However, note that explicitly passing an interface name to the {{qt|QDBusInterface}} constructor is recommended. Due to the internals of QtDBus, if you pass an empty interface, you will always cause a round-trip to the remote application to verify which methods are available. On the other hand, if you pass a non-empty interface name, QtDBus may cache the result for further uses.

+

+

As {{qt|QDBusInterface}} is a QObject, you can also pass it a parent object. This helps simplify the bookkeeping associated with creating new {{qt|QDBusInterface}} objects by letting Qt clean up for you when the parent object is deleted.

+

+

Here is an example of {{qt|QDBusInterface}} usage which we will then step through line by line:

+

+

<syntaxhighlight lang="cpp-qt" line>

+

QString hostname("kde.org");

+

QDBusConnection bus = QDBusConnection::sessionBus();

+

QDBusInterface *interface = new QDBusInterface("org.foo.bar",

+

"/network",

+

"org.foo.bar.network",

+

bus,

+

this);

+

+

interface->call("ping");

+

interface->call("ping", hostname);

+

+

QList<QVariant> args;

+

args.append("kde.org");

+

interface->callWithArgumentList("ping", args);

+

+

QDBusReply<int> reply = interface->call("ping",

+

hostname);

+

if (reply.isValid())

+

{

+

KMessageBox::information(winId(),

+

i18n("Ping to %1 took %2s")

+

.arg(hostname)

+

.arg(reply.value()),

+

i18n("Pinging %1")

+

.arg(hostname));

+

}

+

+

args.clear();

+

interface->callWithCallback("listInterfaces", args,

+

this,

+

SLOT(interfaceList(QDBusMessage)));

+

+

connect(interface, SIGNAL(interfaceUp(QString)),

+

this, SLOT(interfaceUp(QString)));

+

</syntaxhighlight>

+

+

=== Synchronous Calls ===

+

+

The first thing we did was create a {{qt|QDBusInterface}} on line 3 that represents the same object we were accessing in the QDBusMessage examples above.

+

+

We then called several D-Bus methods on that object using a few different techniques. On line 9 we make a simple call to a method called <tt>ping</tt> without any arguments. On line 10, we call the same method but with a parameter. Note that we didn't have to create a <tt>{{qt|QList}}&lt;{{qt|QVariant}}&gt;</tt> for the arguments. We can pass up to 8 arguments to a D-Bus method this way.

+

+

If you need to pass more than 8 arguments or for some other reason a <tt>{{qt|QList}}&lt;{{qt|QVariant}}&gt;</tt> is simply a better approach for the circumstances, then you may use the <tt>callWithArgumentList</tt> method instead as seen on lines 12-14 above.

+

+

=== Handling Replies ===

+

+

On line 16 we call the <tt>ping</tt> method yet again, but this time save the reply in a {{qt|QDBusReply}} object. We check to make sure the reply was valid (e.g. no errors were returned and we did indeed get an <tt>int</tt> back) and then use the returned data to populate a message in an informational popup.

+

+

=== Asynchronous Method Calls and Signals ===

+

+

Up to this point in the example all of the calls made were synchronous and the application would block until a reply was received. The last two uses of {{qt|QDBusInterface}} in the example show asynchronous usage of D-Bus, and in both cases we rely on Qt's signal and slot mechanism.

+

+

On line 29 we use <tt>callWithCallback</tt> and provide a regular QObject slot to be called when the D-Bus reply returns. This way the application will not block as <tt>callWithCallback</tt> returns immediately after queueing the message to be sent on the bus. Later, the <tt>interfaceList</tt> slot would get called. Note that this method requires a <tt>{{qt|QList}}&lt;{{qt|QVariant}}&gt;</tt>; there is no shortcut for us this time.

+

+

Finally, on line 33 we connect to a D-Bus signal. Using {{qt|QDBusInterface}} to do this looks exactly like connecting to a regular, local signal in our own application. We even use the standard <tt>{{qt|QObject}}::{{qt|connect}}</tt> method! This is accomplished by {{qt|QDBusInterface}} using Qt's meta object system to dynamically add the signals the D-Bus interface advertises. Very slick!

+

+

=== Is ''This'' the Best Way? ===

+

+

This ease of use over {{qt|QDBusMessage}} does come with some prices, however. First, since {{qt|QDBusInterface}} is a {{qt|QObject}} it carries the overhead that implies. It also will perform at least one round-trip to the requested D-Bus object on creation to set up the interface object with things such as the available signals. Often this additional overhead is negligible in the larger scheme of things and well made up for by the convenience it provides.

+

+

There are still some annoyances we have to deal with, however, such as having to know the name of the interface, setting up the correct {{qt|QDBusReply}} object such as we did above by templating it with an <tt>int</tt> and having to debug method name typos and the like at runtime versus letting the compiler do it for us. So while it's an improvement over {{qt|QDBusMessage}}, it's still not perfect.

+

+

And that's precisely where <tt>qdbusxml2cpp</tt> comes to our rescue.

+

+

== Using Classes Generated From D-Bus XML ==

+

+

What would be truly great is if we could simply instantiate a local object that represented a given service and start using it right away. Perhaps something like this:

+

+

<syntaxhighlight lang="cpp-qt" line>

+

org::foo::bar::network *interface =

+

new org::foo::bar::network("org.foo.bar", "/network",

+

QDBusConnection::sessionBus(),

+

this);

+

interface->ping("kde.org");

+

</syntaxhighlight>

+

+

Fortunately for us, this is precisely what Qt allows us to do. The only requirement is an XML file describing the D-Bus service. Such files are installed in the D-Bus prefix in the {{path|interfaces}} directory.

+

+

{{tip|The D-Bus prefix can be found by issuing the following command in a terminal:<br><tt><nowiki>pkg-config dbus-1 --variable=prefix</nowiki></tt>}}

+

+

We can also create our own XML files from the C++ header files and use those directly. This is covered in the next tutorial, [[../Creating_Interfaces|Creating D-Bus Interfaces]].

+

+

With the path to the XML in hand, we then add something like this to our {{path|CMakeLists.txt}}:

+

+

<syntaxhighlight lang="text">

+

PKGCONFIG_GETVAR(dbus-1 prefix DBUS_PREFIX)

+

set(network_xml ${DBUS_PREFIX}/interfaces/org.foo.bar.xml)

+

qt4_add_dbus_interface(myapp_SRCS ${network_xml} network_interface )

+

</syntaxhighlight>

+

+

This will generate two files at build time, {{path|network_interface.h}} and {{path|network_interface.cpp}}, and add them to the compiled application. We can then simply <tt>#include "network_interface.h"</tt> and use the generated class as seen in the example above.

+

+

Examining the generated header file, we can see exactly what methods, signals as well as their signatures and return values are according to the provider of the service. Using the class directly will let the compiler do type checking on method calls leaving fewer run-time breakages to track down.

+

+

Due to the generated class being a subclass of {{qt|QDBusAbstractInterface}} just as {{qt|QDBusInterface}} is, anything we can do with {{qt|QDBusInterface}} is also available to us.

+

+

Due to this combination of ease of use and compile-time checking, this is generally the preferred mechanism to use when accessing D-Bus interfaces.

+

+

{{tip|If your CMake installation does not provide the PKGCONFIG_GETVAR, you can add [[Development/Tutorials/D-Bus/Accessing Interfaces/PkgConfigGetVar.cmake|this cmake module]] to your project.}}

+

+

== Doing A Little Introspection ==

+

+

It may also be helpful to find out if a given service is available or to check which application is providing it. Another {{qt|QDBusAbstractInterface}} subclass, {{qt|QDBusConnectionInterface}}, provides methods to query for such information as which services are registered and who owns them.

−

== Connecting To Signals ==

+

Once you have a service name, you can then use {{qt|QDBusInterface}} to get the <tt>org.freedesktop.DBus.Introspectable</tt> interface and call <tt>Introspect</tt> on it. This will return an XML block describing the objects, which can in turn be introspected for what they provide. The XML itself can be processed using <tt>QDomDocument</tt>, making it a fairly simple process.

−

== Other Resources ==

+

The {{path|qdbus}} application that ships with Qt4 provides a nice example of code doing exactly this. It can be found in {{path|tools/qdbus/tools/qdbus/qdbus.cpp}} in the Qt4 source distribution.

D-Bus allows applications to expose their internal API to the outside world. These APIs can then be accessed at run-time via the D-Bus protocol using command line applications or D-Bus libraries and bindings themselves. This tutorial looks at the latter method with examples that you can use in your applications.

A QDBusMessage can be used directly to call methods in D-Bus services using the QDBusMessage::createMethodCall( const QString & service, const QString & path, const QString & interface, const QString & method ) static method. It returns a QDBusMessage object that you can then use to make the call.

The interface parameter is optional and only necessary if the method to be called is not unique in the object associated with the path. This can happen if the object implements multiple interfaces which have methods that are named the same. In such (rare) cases, if you do not explicitly define the interface to use there is not guarantee as to which method will actually get called. However, usually you can simply pass an empty string (e.g. "") as the argument for interface.

By way of example, to access the (fictional) ping method on the /network object in the org.foo.bar service, one might do this:

QDBusMessage m =QDBusMessage::createMethodCall("org.foo.bar",

"/network",

"",

"ping");

bool queued =QDBusConnection::sessionBus().send(m);

In line 5 of the above example we queue the message for sending on the current session bus. We get a bool returned letting us know if the queueing was successful or not.

This leaves us with two questions, however:

How can one set parameters for a method call?

How can one get a return message in the case of D-Bus methods that have a return value?

Sending arguments along with the method call is quite straight forward. First we need to create a QList of QVariant objects and then add those to our D-Bus message. So if the ping method in the above took a hostname as a parameter, we might alter the code in this way (note lines 5 through 7):

QDBusMessage m =QDBusMessage::createMethodCall("org.foo.bar",

"/network",

"",

"ping");

QList<QVariant> args;

args.append("kde.org");

m.setArguments(args);

bool queued =QDBusConnection::sessionBus().send(m);

Alternatively, QDBusMessage provides a convenience method to appending parameters to the message, by way of its "operator<<" function. The above example becomes:

QDBusMessage m =QDBusMessage::createMethodCall("org.foo.bar",

"/network",

"",

"ping");

m <<"kde.org";

bool queued =QDBusConnection::sessionBus().send(m);

Note

The arguments must appear in the QList in the same order they are expected by the D-Bus method being called.

If we wish to actually receive information back from the D-Bus method, we use the QDBusConnection::call method instead. It will block until there is a reply or the call times out. If our ping method returned information on the host we provided in the arguments above, we might alter our code to look like this:

QDBusMessage m =QDBusMessage::createMethodCall("org.foo.bar",

"/network",

"",

"ping");

m <<"kde.org";

QDBusMessage response =QDBusConnection::sessionBus().call(m);

The response will be either of type QDBusMessage::ReplyMessage or QDBusMessage::ErrorMessage depending on whether it was successful or not. We can look through the values returned by retrieving the arguments with the QDBusMessage::arguments() method which returns a QList<QVariant>.

Using QDBusMessage directly in this way to invoke remote D-Bus methods is not the easiest, best or even recommend way of doing things. We will now look at the more convenient QDBusInterface class and then look at accessing remote D-Bus interfaces as if they were local methods using proxy classes auto-generated from XML.

QDBusInterface provides a simple and direct method to make D-Bus calls and connect to D-Bus signals.

A QDBusInterface object represents a given D-Bus interface. The constructor accepts as parameters (in order) a service name, an object path, an optional interface and optionally which bus (e.g. system or session) to use. If no bus is explicitly defined, it defaults to the session bus. If no interface is given, the returned object will be used to call all interfaces on the bus.

However, note that explicitly passing an interface name to the QDBusInterface constructor is recommended. Due to the internals of QtDBus, if you pass an empty interface, you will always cause a round-trip to the remote application to verify which methods are available. On the other hand, if you pass a non-empty interface name, QtDBus may cache the result for further uses.

As QDBusInterface is a QObject, you can also pass it a parent object. This helps simplify the bookkeeping associated with creating new QDBusInterface objects by letting Qt clean up for you when the parent object is deleted.

Here is an example of QDBusInterface usage which we will then step through line by line:

The first thing we did was create a QDBusInterface on line 3 that represents the same object we were accessing in the QDBusMessage examples above.

We then called several D-Bus methods on that object using a few different techniques. On line 9 we make a simple call to a method called ping without any arguments. On line 10, we call the same method but with a parameter. Note that we didn't have to create a QList<QVariant> for the arguments. We can pass up to 8 arguments to a D-Bus method this way.

If you need to pass more than 8 arguments or for some other reason a QList<QVariant> is simply a better approach for the circumstances, then you may use the callWithArgumentList method instead as seen on lines 12-14 above.

On line 16 we call the ping method yet again, but this time save the reply in a QDBusReply object. We check to make sure the reply was valid (e.g. no errors were returned and we did indeed get an int back) and then use the returned data to populate a message in an informational popup.

Up to this point in the example all of the calls made were synchronous and the application would block until a reply was received. The last two uses of QDBusInterface in the example show asynchronous usage of D-Bus, and in both cases we rely on Qt's signal and slot mechanism.

On line 29 we use callWithCallback and provide a regular QObject slot to be called when the D-Bus reply returns. This way the application will not block as callWithCallback returns immediately after queueing the message to be sent on the bus. Later, the interfaceList slot would get called. Note that this method requires a QList<QVariant>; there is no shortcut for us this time.

Finally, on line 33 we connect to a D-Bus signal. Using QDBusInterface to do this looks exactly like connecting to a regular, local signal in our own application. We even use the standard QObject::connect method! This is accomplished by QDBusInterface using Qt's meta object system to dynamically add the signals the D-Bus interface advertises. Very slick!

This ease of use over QDBusMessage does come with some prices, however. First, since QDBusInterface is a QObject it carries the overhead that implies. It also will perform at least one round-trip to the requested D-Bus object on creation to set up the interface object with things such as the available signals. Often this additional overhead is negligible in the larger scheme of things and well made up for by the convenience it provides.

There are still some annoyances we have to deal with, however, such as having to know the name of the interface, setting up the correct QDBusReply object such as we did above by templating it with an int and having to debug method name typos and the like at runtime versus letting the compiler do it for us. So while it's an improvement over QDBusMessage, it's still not perfect.

What would be truly great is if we could simply instantiate a local object that represented a given service and start using it right away. Perhaps something like this:

org::foo::bar::network*interface =

new org::foo::bar::network("org.foo.bar","/network",

QDBusConnection::sessionBus(),

this);

interface->ping("kde.org");

Fortunately for us, this is precisely what Qt allows us to do. The only requirement is an XML file describing the D-Bus service. Such files are installed in the D-Bus prefix in the interfaces directory.

Tip

The D-Bus prefix can be found by issuing the following command in a terminal:pkg-config dbus-1 --variable=prefix

We can also create our own XML files from the C++ header files and use those directly. This is covered in the next tutorial, Creating D-Bus Interfaces.

With the path to the XML in hand, we then add something like this to our CMakeLists.txt:

This will generate two files at build time, network_interface.h and network_interface.cpp, and add them to the compiled application. We can then simply #include "network_interface.h" and use the generated class as seen in the example above.

Examining the generated header file, we can see exactly what methods, signals as well as their signatures and return values are according to the provider of the service. Using the class directly will let the compiler do type checking on method calls leaving fewer run-time breakages to track down.

It may also be helpful to find out if a given service is available or to check which application is providing it. Another QDBusAbstractInterface subclass, QDBusConnectionInterface, provides methods to query for such information as which services are registered and who owns them.

Once you have a service name, you can then use QDBusInterface to get the org.freedesktop.DBus.Introspectable interface and call Introspect on it. This will return an XML block describing the objects, which can in turn be introspected for what they provide. The XML itself can be processed using QDomDocument, making it a fairly simple process.

The qdbus application that ships with Qt4 provides a nice example of code doing exactly this. It can be found in tools/qdbus/tools/qdbus/qdbus.cpp in the Qt4 source distribution.