
| Sample Code |
Introduction
This document introduces D-Bus and describes how it is
being used on the mobile platform to meet the usability requirements of Moblin.
This document assumes that the reader is familiar with the following:
This document introduces:
D-Bus Overview
Introduction to D-Bus
D-Bus is an interprocess communication (IPC) system. It provides a simple way for applications to talk to one another. It is part of the freedesktop.org project and used in a wide range of applications.
Architecturally, D-Bus has several layers:
For more details about D-Bus terminology and concepts, refer to http://dbus.freedesktop.org/doc/dbus-tutorial.html.
D-Bus Usage in the Moblin Application
Moblin uses D-Bus as the primary IPC mechanism for applications. The main usages of D-Bus in Moblin are listed below:
D-Bus Programming Preparation
Overview
The low-level API for D-Bus is written in C but application developers usually use higher level bindings like libdbus-glib, libdbus-qt, and Python for easier use and improved functionality. If you are writing in C, then the glib bindings are recommended. In this tutorial, we will cover the D-Bus programming with libdbus-glib because it is commonly used in Moblin applications. For D-Bus low-level API and other higher level bindings, we provide some useful links for your reference.
For more details about D-Bus low-level API, refer to http://dbus.freedesktop.org/doc/dbus/libdbus-tutorial.html.Install the Library Package
Before developing with libdbus-glib, you should install the development packages: libdbus-1-dev and libdbus-glib-1-dev. In a Debian system, it can be done by the following command:Add D-Bus Reference into Build File
After the packages are installed, you should modify the build file configure.ac or configure.in to tell the compiler where to find the header files and inform the linker what library file should be linked into the application.
The following code shows how to add D-Bus references into the build file "configure.ac"# Add D-Bus specific preprocessor, compile & link flags
CFLAGS="$CFLAGS $GLIB_DBUS_CFLAGS"
LIBS="$LIBS $GLIB_DBUS_LIBS"
Include the Right Header File
To use the dbus-glib API, you should include the right header file in your source.Useful Tools
dbus-send and dbus-monitor are useful tools for you to develop applications that use D-Bus. These tool are already installed if you have D-Bus installed.
Programming with libdbus-glib
Overview
Glib is the base library of GNOME. It provides several convenience functions, portability wrappers, a family of string functions and a complete object and type system. The Glib library provides an object system, the main loop implementation, making object-based, event-driven programming possible. The D-Bus Glib bindings take advantage of these features.
In this section, we will introduce how to program with libdbus-glib, we will cover four basic D-Bus operations here that you usually use in application:
The first two operations are about methods. Methods are messages that are sent to cause code to be executed in the receiving application. If the method is successfully called, an optional return value will be returned to the calling application, or else an error will be returned.
The next two operations are about signals. Signals are like method calls, except that the signals happen in the "reverse" direction and are not tied to a single destination. A signal is emitted by the application, which is exporting the interface and is available to any application on the same bus.[5]
Signals can carry information. Each signal has its own name and argument list, which is a list of information that is passed along the signal.
Exposing a Method to be Called
Overview
Here is a list of the general steps you can follow to implement the D-Bus operation of exposing a method to be called.
We will cover more detailed information in next parts and provide the sample code for your reference.
Generate the dbus-glib Binding Code for Server Side
A tool called dbus-binding-tool can help developers generate the dbus-glib binding code for both the server (exposing a method) and client (calling a method) side. It uses an XML file which describes the method and signal definitions as input.
An example xml file should look like this:
[glib-dbus-method/src/example-method.xml]:<!DOCTYPE node PUBLIC
"-//freedesktop//DTD D-Bus Object Introspection 1.0//EN"
"http://standards.freedesktop.org/dbus/1.0/introspect.dtd">
<node>
<interface name="org.moblin.sample">
<!-- Method definition-->
<!-- int hello_method(char *hello_message) -->
<method name="hello_method">
<arg type="s" name="hello_message" direction="in"/>
<arg type="i" name="ret_value" direction="out"/>
</method>
</interface>
</node>
When the XML interface file is ready, you can use it as the input to run dbus-binding-tool to generate the glib binding code for the server side.
Here is the example command to generate the dbus-binding code for the server side and store it in example-expose-method.h:After running the command, the file example-expose-method.h should be generated.
The following code snippet shows the method table and the _object_info structure in this filer which you should focus on.The method table lists the method function address, the function to use to marshal data and the offset into the introspection data.
The _object_info structure will be passed to D-Bus daemon when you create the instance of your object and register with D-Bus to register it to the D-Bus. The structure includes format_version, methodinfo structure, method datas, signal datas etc.
Create a Simple GObject for D-Bus
typedef struct
{
GObjectClass parent;
}SampleObjectClass;
/* Macro for the above. It is common to define macros using the
naming convention (seen below) for all GType implementations,
and that's why we're going to do that here as well. */
#define SAMPLE_TYPE_OBJECT (sample_object_get_type ())
#define SAMPLE_OBJECT(object) (G_TYPE_CHECK_INSTANCE_CAST ((object), SAMPLE_TYPE_OBJECT, SampleObject))
#define SAMPLE_OBJECT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), SAMPLE_TYPE_OBJECT, SampleObjectClass))
#define SAMPLE_IS_OBJECT(object) (G_TYPE_CHECK_INSTANCE_TYPE ((object), SAMPLE_TYPE_OBJECT))
#define SAMPLE_IS_OBJECT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), SAMPLE_TYPE_OBJECT))
#define SAMPLE_OBJECT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), SAMPLE_TYPE_OBJECT, SampleObjectClass))
G_DEFINE_TYPE(SampleObject, sample_object, G_TYPE_OBJECT)
/*
* Class initializer
*/
static void
sample_object_class_init (SampleObjectClass *klass)
{
g_print (PROGNAME " : sample_object_class_init is called.\n");
g_assert(klass != NULL);
g_print (PROGNAME " : Binding to Glib/D-Bus.\n");
/*Bind this GType into the Glib/D-Bus wrappers*/
dbus_g_object_type_install_info (SAMPLE_TYPE_OBJECT, &dbus_glib_example_object_object_info);
}
obj->ret_value ++;
*ret = obj->ret_value;
g_print (PROGNAME " : hello_method is Called, arg = %s, ret = %d.\n", hello_message, *ret);
return TRUE;
}
Register the Object to the D-Bus
After the D-Bus method function is implemented, you should add the code for registering the object to the D-Bus. All the code is added to the main function in our test program (glib-dbus-method/src/example-expose-method.c).
/* Initialize the GType/GObject system. */
g_type_init ();
/* Create a main loop that will dispatch callbacks. */
mainloop = g_main_loop_new (NULL, FALSE);
if (mainloop == NULL) {
/* Print error and terminate. */
g_print (PROGNAME " : Couldn't create GMainLoop.\n");
exit(1);
}
/*Connect to the session bus*/
g_print (PROGNAME " : Connecting to Session D-Bus.\n");
bus = dbus_g_bus_get (DBUS_BUS_SESSION, &error);
if (bus == NULL) {
g_print (PROGNAME " : Couldn't connect to session bus, %s\n", error->message);
exit(1);
}
g_print (PROGNAME " : RequestName returned %d.\n", result);
/*Check the result*/
if(result != 1) {
/* Print error and terminate. */
g_print (PROGNAME " : Failed to get the primary well-known name.\n");
exit(1);
}
printf ("service running\n");
/*Run the program*/
g_main_loop_run (mainloop);
return (1);
}
Build and Run Sample Code
You can write a Makefile.am for automake to generate makefile template – Makefile.in.
Here is the example Makefile.am you can use to generate example-expose-method.BUILT_SOURCES = example-expose-method.h
example-expose-method.h: example-method.xml
dbus-binding-tool --prefix=example_object --mode=glib-server --output=example-expose-method.h $(srcdir)/example-method.xml
CLEANFILES = $(BUILT_SOURCES)
For more details about the makefile.am, refer to SDK doc, Using GNU Autotools.
After Makefile.am is ready, you can build and run program example-expose-method.
Note: You should launch the example programs under the same user you start the display, or else it will fail to connect to the session bus.Overview
Here is a list of the general steps you can follow to implement the operation of calling a method:
We will cover more detailed information in the next sections and provide sample code for your reference.
Generate the dbus-glib Binding Code for Client Side
First, you can use the dbus-binding-tool to generate the glib binding code for client side. Here is the example command to generate example-call-method.h file.#include <glib/gtypes.h>
#include <glib/gerror.h>
#include <dbus/dbus-glib.h>
G_BEGIN_DECLS
#ifndef DBUS_GLIB_CLIENT_WRAPPERS_org_moblin_sample
#define DBUS_GLIB_CLIENT_WRAPPERS_org_moblin_sample
static
#ifdef G_HAVE_INLINE
inline
#endif
gboolean
org_moblin_sample_hello_method (DBusGProxy *proxy, const char * IN_hello_message, gint* OUT_ret_value, GError **error)
{
return dbus_g_proxy_call (proxy, "hello_method", error, G_TYPE_STRING, IN_hello_message, G_TYPE_INVALID, G_TYPE_INT, OUT_ret_value, G_TYPE_INVALID);
}
. . . . . .
Initialize the GType System and Connect to the D-Bus
To use the generated functions to call the method, first you should initialize the GType system, and connect to the D-Bus.#define PROGNAME "example-call-method"
/**
* The main client code.
*/
int
main (int argc, char **argv)
{
/* The GObject representing a D-Bus connection. */
DBusGConnection *bus;
/* Proxy object representing the D-Bus service object. */
DBusGProxy *remote_object;
/* GMainLoop object */
GMainLoop *mainloop;
GError *error = NULL;
gint reply;
/* Initialize the GType/GObject system. */
g_type_init ();
g_print (PROGNAME " : Connecting to Session D-Bus.\n");
bus = dbus_g_bus_get (DBUS_BUS_SESSION, &error);
if (!bus) {
/* Print error and terminate. */
g_print (PROGNAME " : Couldn't connect to session bus, %s\n", error->message);
exit(1);
}
Create a GProxy Object
Next, you should create the proxy object that you will use to access the object on the server. if(remote_object == NULL) {
/* Print error and terminate. */
g_print (PROGNAME " : Couldn't create the proxy object, %s\n", error->message);
exit(1);
}
Call the Generated Functions to Invoke a Method
After the proxy object is created, you can use the generated functions to invoke a method. return(0);
}
I installed libdbus-1-dev and libdbus-glib-1-dev and ran " cd glib-dbus-method
# ./autogen.sh
# ./configure
# make
# cd src
# ./example-expose-method &
Build and Run Sample Code
You can integrate the example-call-method into the Makefile.am. Here is the example Makefile.am( glib-dbus-method/src/Makefile.am), for your reference.BUILT_SOURCES = example-expose-method.h example-call-method.h
example-expose-method.h: example-method.xml
dbus-binding-tool --prefix=example_object --mode=glib-server --output=example-expose-method.h $(srcdir)/example-method.xml
example-call-method.h: example-method.xml
dbus-binding-tool --prefix=example_object --mode=glib-client --output=example-call-method.h $(srcdir)/example-method.xml
CLEANFILES = $(BUILT_SOURCES)
# ./example-call-method
Emitting a Signal
Overview
Emitting a signal should follow similar steps as exposing a method because they are both operations on the server side D-Bus. The general steps are listed below, we will skip the steps you use to expose a method and focus on the steps that differ.
Define Signals in the Interface XML File
To make the signals available to introspection data, you should generate the interface XML file to define the signal.
[glib-dbus-signal/src/example-signal.xml]
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE node PUBLIC
"-//freedesktop//DTD D-Bus Object Introspection 1.0//EN"
"http://standards.freedesktop.org/dbus/1.0/introspect.dtd">
<node>
<interface name="org.moblin.sample">
<!-- Signal definition-->
<!-- signals to say hello to interested clients-->
<signal name="hello_signal">
<arg type="s" name="hello_signal" direction="out"/>
</signal>
</interface>
</node>
Define and Create the Signals
Before emitting a signal, you should define and create the signals. You can keep the signal types in the class structure and create the signals in class initialization code.typedef struct
{
GObject parent;
gint ret_value;
}SampleObject;
typedef struct
{
GObjectClass parent;
/* Signals created for this class. */
guint signals[E_LAST_SIGNAL];
}SampleObjectClass;
. . . . . .
/*
* Class initializer
* Create the signals and register the type into the GLib/D-Bus wrapper.
*/
static void
sample_object_class_init (SampleObjectClass *klass)
{
g_print (PROGNAME " : sample_object_class_init is called.\n");
g_assert(klass != NULL);
/* Create the signals */
klass->signals[E_HELLO_SIGNAL] = g_signal_new (SIGNAL_HELLO,
G_OBJECT_CLASS_TYPE (klass),
G_SIGNAL_RUN_LAST | G_SIGNAL_DETAILED,
0,
NULL, NULL,
g_cclosure_marshal_VOID__STRING,
G_TYPE_NONE, 1, G_TYPE_STRING);
g_print (PROGNAME " : Binding to Glib/D-Bus.\n");
/*Bind this GType into the Glib/D-Bus wrappers*/
dbus_g_object_type_install_info (SAMPLE_TYPE_OBJECT, &dbus_glib_example_object_object_info);
}
. . . . . .
Implement the Functions to Emit Signals
Then you can create functions to emit signals, the simplest way is to call the g_signal_emit API.
[glib-dbus-signal/src/example-emit-signal.c]
Build and Run Sample Code
You can write the Makefile.am according to the example in Register the object to the D-Bus in this document. Then you can build and test the program example-emit-signal.# cd src
# ./example-emit-signal
Catching a Signal
Overview
To catch a signal, you should tell what signals are you interested in, implement the callbacks for the signals, and register it as the signal handler.
Here is a list of the general steps you can follow to implement the operation of catching a signal:
We will cover more detailed information about steps 1 and 4 in next sections. For step 2 and 3, you can refer to Calling a Method in this document.
Implement the Callbacks for the Signals
The callback in our example includes three parameters it will receive. The first one is the proxy object through which the signal was received. The second one is the string that was attached with the hello_signal, and the third one is optional user-specified data.Register for the Signals
Registering for the signals includes two steps:
g_print (PROGNAME " : Add the argument signatures for the signals.\n");
dbus_g_proxy_add_signal (remote_object, /*Proxy to use*/
SIGNAL_HELLO, /*Signal name*/
G_TYPE_STRING, /*Will receive one string argument*/
G_TYPE_INVALID); /*Termination of the argument list*/
g_print (PROGNAME " : Register D-Bus signal handlers.\n");
dbus_g_proxy_connect_signal(remote_object, /*Proxy object*/
SIGNAL_HELLO, /*Signal name*/
G_CALLBACK(hello_signal_handler), /*Signal handler to use.*/
NULL, NULL);
g_print (PROGNAME " : Starting main loop.\n");
/*Run the program*/
g_main_loop_run(mainloop);
return(1);
}
Build and Run Sample Code
You can integrate the example-catch-signal into the Makefile.am.
Now you can build and try program example-catch-signal. You can launch example-catch-signal to listen to hello_signal, and then launch example-emit-signal to emit hello_signal.# ./example-emit-signal
These documents were used as references in the writing of this document. Refer to these documents for further details.