Development Guides: Moblin Linux
Creating a DBUS Service File and Using Available/All DBUS Commands
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:

  • Basic Linux knowledge
  • Mobile Internet Devices (MIDs)

This document introduces:

  1. D-Bus concepts, its main usage in Moblin, and preparations for D-Bus programming
  2. Programming with libdbus-glib

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:

  • A library, libdbus, which allows two applications to connect to each other and exchange messages. libdbus supports only one-to-one connections, just like a raw network socket. However, rather than sending byte streams over the connection, you send messages. Messages have a header, identifying the kind of message, and a body containing a data payload. libdbus also abstracts the exact transport used (sockets vs. whatever else), and handles details, such as authentication.[2]
  • A message bus daemon executable, built on libdbus, that multiple applications can connect to. The daemon can route messages from one application to other applications or to none. The message bus daemon forms the hub of a wheel. Each spoke of the wheel is a one-to-one connection to an application using libdbus. An application sends a message to the bus daemon over its spoke, and the bus daemon forwards the message to other connected applications, as appropriate. You can think of the daemon as a router.[2]
  • Wrapper libraries or bindings, based on particular application frameworks, for example, libdbus-glib and libdbus-qt. There are also bindings to languages such as Python*. These wrapper libraries are the API most people should use, as they simplify the details of D-Bus programming. libdbus is intended to be a low-level backend for the higher level bindings. Much of the libdbus API is useful only for binding implementation.[2]

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:

  • Launching application

    When registering an application to D-Bus service, the Hildon framework can allow applications to launch only once, running a single instance of an application is an optimized feature for small mobile devices. It is also used to make sure the application survives the task killing process.

    Note: For more details about how to register an application to D-Bus, refer to "Programming with LibOSSO".
  • System notification

    The Moblin application can connect and listen to system events, like hardware state and device mode changes, and proceed accordingly. It is recommended that you use the LibOSSO library to receive and handle system events. For more details about LibOSSO, refer to Programming with LibOSSO.
  • Separating applications, user interfaces, and engines

    It's easier to use D-Bus to separate applications, user interfaces, and engines. Then the engine can be used easily from different applications.

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.
For more details about Python binding, refer to http://dbus.freedesktop.org/doc/dbus-python/doc/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:
# apt-get install libdbus-1-dev
# apt-get install libdbus-glib-1-dev

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"
# Checks for D-Bus libraries.
PKG_CHECK_MODULES(GLIB_DBUS, [dbus-1 dbus-glib-1])

# 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.
#include <dbus/dbus-glib.h>

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:

  1. Exposing a method to be called
  2. Calling a method
  3. Emitting a signal
  4. Catching a signal

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.

  1. Run dbus-binding-tool to generate the dbus-glib binding code for server side
  2. Create a simple GObject for D-Bus
    • Create the per-instance and per-class state structures
    • Define convenience macros in a way expected for all GTypes
    • Implement the instance initialization and class initialization
    • Implement the method functions
  3. Register the object to the D-Bus
    • Initialize the GType system and open a connection to the session bus
    • Attach the server to a well-known name
    • Create an instance of your object and register it to the D-Bus

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]:
<?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">
    <!-- 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:
# cd glib-dbus-method/src
# dbus-binding-tool --prefix=example_object --mode=glib-server –-output=example-expose-method.h example-method.xml     

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.
. . . . . . #include <dbus/dbus-glib.h> static const DBusGMethodInfo dbus_glib_example_object_methods[] = { { (GCallback) example_object_hello_method, /*method function address*/ dbus_glib_marshal_example_object_BOOLEAN__STRING_POINTER_POINTER, /*the function used to marshal data */ 0 /*offset into the introspection data*/ }, }; const DBusGObjectInfo dbus_glib_example_object_object_info = { 0, /*Format version, allows us to change the rest of the struct by adding DBusGObjectInfo2, DBusGObjectInfo3, etc */ dbus_glib_example_object_methods, /*MethodInfo structure*/ 1, /*the number of methods*/ "org.moblin.sample\0hello_method\0S\0hello_message\0I\0s\0ret_value\0O\0F\0N\0i\0\0\0", /*list the method data*/ "\0", /*exported signals*/ "\0" /*exported properties*/ };

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

  1. Create the Per-instance and Per-class State Structures

  2. First, you should create per-class and per-instance state structures for your object. The per-class structure contains the bare minimum contents, which are required for all classes in GObject. The per-instance structure contains the required parent object state and some internal values.[8]
    [glib-dbus-method/src/example-expose-method.c]
    typedef struct
    {
        GObject parent;
        gint ret_value;
    }SampleObject;

    typedef struct
    {
        GObjectClass parent;
    }SampleObjectClass;

  3. Define Convenience Macros in a Way Expected for All GTypes

  4. Then you can continue by defining convenience macros in a way expected for all GTypes.[8] For more details about GType macros, refer to http://maemo.org/api_refs/4.0/gobject/index.html.
    [glib-dbus-method/src/example-expose-method.c]
    /* Forward declaration of the function that will return the GType of
       the Value implementation. Not used in this program. */
    GType Sample_object_get_type (void);

    /* 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)

  5. Implement the Instance Initialization and Class Initialization

  6. After the macros, you should finish the instance initialization and class initialization functions. The class initialization function contains the integration call into GLib/D-Bus: dbus_g_object_type_install_info, which take a pointer to the structure describing the D-Bus integration (dbus_glib_value_object_object_info). This function will create all the necessary runtime information for GType.[8]
    [glib-dbus-method/src/example-expose-method.c]
    /*
     * Object initializer
     * Set ret_value to 0
     */
    static void
    sample_object_init (SampleObject *obj)
    {
        g_print (PROGNAME " : sample_object_init is called.\n");
        g_assert(obj != NULL);
        obj->ret_value = 0;
    }

    /*
     * 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);
    }

  7. Implement the Method Functions

  8. Next, you can implement the method functions which are dictated by the method table in the generated binding code.
    The example_object_hello_method function in our example includes four parameters:
    • SampleObject *obj – The pointer to the object.
    • const char *hello_message – The input parameters.
    • gint *ret – The pointer to method return value. Note that the return value is not done by using “return Val”, instead return values are written with the given pointer[8].
    • GError **error – The pointer to the GError objects to store the error messages.
    •  [glib-dbus-method/src/example-expose-method.c]
    /*
     * Function that gets executed on "hello_method".
     */
    gboolean
    example_object_hello_method (SampleObject *obj, const char *hello_message, gint *ret, GError **error)
    {
        g_assert(obj != NULL);

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

  1. Initialize the GType System and Open a Connection to the Session Bus
  2. First, the GType system should be initialized by calling g_type_init, before using any of the GType, and then you should open a connection to the session bus. [glib-dbus-method/src/example-expose-method.c]
    /*
     * The main service 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 *bus_proxy;
        /* Will hold one instance of SampleObject that will serve all the requsts. */
        SampleObject *obj;
        /* GMainLoop object */
        GMainLoop *mainloop;
        guint result;
        GError *error = NULL;

        /* 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);
        }


  3. Attach the Server to a Well-known Name
  4. For prospective clients to find the object on the session bus, you should attach the server to a well-known name. This is done with the RequestName method call on the D-Bus server. For more details about D-Bus introspection, refer to http://maemo.org/maemo_training_material/maemo4.x/html/maemo_Platform_Development/
    Chapter_03_Using_the_GLib_wrappers_for_DBus.html
    .
    [glib-dbus-method/src/example-expose-method.c]
    bus_proxy = dbus_g_proxy_new_for_name (bus, "org.freedesktop.DBus",
                                   "/org/freedesktop/DBus",
                                   "org.freedesktop.DBus");
        /* Attempt to register the well-known name. */
        if (!dbus_g_proxy_call (bus_proxy, "RequestName", &error,
                         G_TYPE_STRING, SAMPLE_SERVICE_NAME,
                         G_TYPE_UINT, 0,
                         G_TYPE_INVALID,
                         G_TYPE_UINT, &result,
                         G_TYPE_INVALID)) {
            /* Print error and terminate. */
            g_print (PROGNAME " : D-Bus.RequestName RPC failed %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);
        }


  5. Create an Instance of Your Object and Register It to the D-Bus
  6. After the name is successfully registered, you should create an instance of the SampleObject and register it to the D-Bus. All the callback registration will be done automatically by the Glib/D-Bus wrappers on this operation. Next, main will enter into the main loop, and start to serve client requests.[8]
    [glib-dbus-method/src/example-expose-method.c]
    /*Create one instance*/
        obj = g_object_new (SAMPLE_TYPE_OBJECT, NULL);
        if(!obj) {
            /* Print error and terminate. */
            g_print (PROGNAME " : Failed to create one instance.\n");
            exit(1);
        }
        /*Register the instance to D-Bus*/
        dbus_g_connection_register_g_object (bus, SAMPLE_SERVICE_OBJECT_PATH, G_OBJECT (obj));

        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.
noinst_PROGRAMS= example-expose-method
example_expose_method_SOURCES= example-expose-method.c

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.
# cd glib-dbus-method
# ./autogen.sh
# ./configure
# make
# cd src
# ./example-expose-method &
[1] 15319
example-expose-method : Connecting to Session D-Bus.
example-expose-method : RequestName returned 1.
example-expose-method : sample_object_class_init is called.
example-expose-method : Binding to Glib/D-Bus.
example-expose-method : sample_object_init is called.
service running     
Here you can use dbus-send tool to invoke the hello_method to test the program example-expose-method.
# dbus-send --type=method_call --print-reply --dest=org.moblin.sample /hellosample org.moblin.sample.hello_method string:'hello from dbus-send'

If everything goes well, you should see this output:
example-expose-method : hello_method is Called, arg = hello from dbus-send, ret = 1.
method return sender=:1.125 -> dest=:1.126 reply_serial=2
   int32 1
After testing, terminate the program example-expose-method.
# kill 15319

Calling a Method

Overview

Here is a list of the general steps you can follow to implement the operation of calling a method:

  1. Run dbus-binding-tool to generate the dbus-glib binding code for client side
  2. Initialize the GType system and connect to the D-Bus
  3. Create a GProxy object
  4. Call the generated functions to invoke a method when you need to

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.
# cd glib-dbus-method/src
# dbus-binding-tool --prefix=example_object --mode=glib-client –-output=example-call-method.h example-method.xml

The generated file includes the call functions for all methods. The following code snippet shows the generated _hello_method function.
[glib-dbus-method/src/example-call-method.h]
/* Generated by dbus-binding-tool; do not edit! */

#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.
[glib-dbus-method/src/example-call-method.c]
#include <dbus/dbus-glib.h>
#include <stdio.h>
#include <stdlib.h>
#include "common.h"
#include "example-call-method.h"

#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.
[glib-dbus-method/src/example-call-method.c]
/* Create the proxy object that will be used to access the object */
    g_print (PROGNAME " : Creating a Glib proxy object.\n");
    remote_object = dbus_g_proxy_new_for_name (bus,
                                     SAMPLE_SERVICE_NAME,          /* name */
                                     SAMPLE_SERVICE_OBJECT_PATH,   /* obj path */
                                     SAMPLE_SERVICE_INTERFACE);    /* interface */

    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.
[glib-dbus-method/src/example-call-method.c]
/*call hello_method*/
    g_print (PROGNAME " : Call hello_method\n");
    org_moblin_sample_hello_method (remote_object,  "This is hello from example-client", &reply, &error);
 
    if (error  != NULL)
        g_print (PROGNAME " : fail, %s.\n", error->message);
    else
        g_print (PROGNAME " : success, got reply %d.\n", reply);

    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.
[glib-dbus-method/src/Makefile.am]
noinst_PROGRAMS= example-expose-method example-call-method
example_expose_method_SOURCES= example-expose-method.c
example_call_method_SOURCES= example-call-method.c

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)    

Now you can build and try the calling method operation. You can launch example-expose-method to expose hello_method, and then launch example-call-method to call it.
# cd glib-dbus-method
# ./autogen.sh
# ./configure
# make
# cd src
# ./make-expose-method &
[1] 15354
# example-expose-method : Connecting to Session D-Bus.
example-expose-method : RequestName returned 1.
example-expose-method : sample_object_class_init is called.
example-expose-method : Binding to Glib/D-Bus.
example-expose-method : sample_object_init is called.
service running

# ./example-call-method

If everything goes well, you should see this output:
example-call-method : Connecting to Session D-Bus.
example-call-method : Creating a Glib proxy object.
example-call-method : Call hello_method
example-expose-method : hello_method is Called, arg = This is hello from example-client, ret = 1.

After testing, terminate the program example-expose-method.
# kill 15354

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.

  1. Run dbus-binding-tool to generate the dbus-glib binding code for server side;
  2. Create a simple GObject for D-Bus;
    • Create the per-instance and per-class state structures
    • Define signals
    • Define convenience macros in a way expected for all GTypes
    • Implement the instance initialization and class initialization, which includes the code for creating signals
    • Implement the functions to emit signals
  3. Register the object to the D-Bus;
    • Initialize the GType system and open a connection to the session bus
    • Attach the server to a well-known name
    • Create an instance of your object and register it to the D-Bus
  4. Call the function to emit signals when you need to.

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.
[glib-dbus-signal/src/example-emit-signal.c]
typedef enum
{
    E_HELLO_SIGNAL,
    E_LAST_SIGNAL
}SampleSignalNumber;

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]


/*
 * This function is used to emit hello_signal with the passed string as the signal data.
 */
gboolean
sample_object_emit_hello_signal (SampleObject *obj)
{
    SampleObjectClass* klass = SAMPLE_OBJECT_GET_CLASS(obj);
    g_signal_emit (obj, klass->signals[E_HELLO_SIGNAL], 0, "hello signal from service.");
    return TRUE;
}

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 glib-dbus-signal
# ./autogen.sh
# ./configure
# make

Here you can use the dbus-monitor tool to monitor the messages for your testing. You can launch dbus-monitor first, and then launch example-emit-signal to emit hello_signal.
# dbus-monitor &
[1] 17072
# signal sender=org.freedesktop.DBus -> dest=:1.143 path=/org/freedesktop/DBus; interface=org.freedesktop.DBus; member=NameAcquired
   string ":1.143"
method call sender=:1.143 -> dest=org.freedesktop.DBus path=/org/freedesktop/DBus; interface=org.freedesktop.DBus; member=AddMatch
   string "type='method_call'"
method call sender=:1.143 -> dest=org.freedesktop.DBus path=/org/freedesktop/DBus; interface=org.freedesktop.DBus; member=AddMatch
   string "type='method_return'"
method call sender=:1.143 -> dest=org.freedesktop.DBus path=/org/freedesktop/DBus; interface=org.freedesktop.DBus; member=AddMatch
   string "type='error'"

# cd src
# ./example-emit-signal


If everything goes well, you should see this output:
example-emit-signal : Connecting to Session D-Bus.
. . . . . .
method call sender=:1.144 -> dest=org.freedesktop.DBus path=/org/freedesktop/DBus; interface=org.freedesktop.DBus; member=RequestName
   string "org.moblin.sample"
   uint32 0
example-emit-signal : RequestName returned 1.
example-emit-signal : sample_object_class_init is called.
example-emit-signal : Binding to Glib/D-Bus.
example-emit-signal : sample_object_init is called.
example-emit-signal : Emit hello_signal.
signal sender=:1.144 -> dest=(null destination) path=/hellosample; interface=org.moblin.sample; member=hello_signal
   string "hello signal from server"
. . . . . .   

After testing, terminate the dbus-monitor to clean the testing environment.
# kill 17072         

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:

  1. Implement the callbacks for the signals
  2. Initialize the GType system and connect to the D-Bus
  3. Create a GProxy object
  4. Register for the signals you are interested in

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.
 [glib-dbus-signal/src/example-catch-signal.c]
/**
 * Signal handler for the "hello_signal".
 */
static void
hello_signal_handler (DBusGProxy *proxy, const char *hello_string, gpointer user_data)
{
      /*print the signal parameter*/
      g_print (PROGNAME " : received hello_signal=%s, hello_signal_handler is called.\n", hello_string);
}

Register for the Signals

Registering for the signals includes two steps:

  1. Specify the argument signature of signals you are interested in
  2. Register the signal handlers for these signals
[glib-dbus-signal/src/example-catch-signal.c]
/**
 * The main client code.
 */
int
main (int argc, char **argv)
{
    . . . . . .
    /* Register the signatures for the signal handlers */

    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. 
# cd glib-dbus-method
# ./autogen.sh
# ./configure
# make
# cd src
# /example-catch-signal &
[1] 17083
# example-catch-signal : Connecting to Session D-Bus.
example-catch-signal : Creating a Glib proxy object.
example-catch-signal : Add the argument signatures for the signals.
example-catch-signal : Register D-Bus signal handlers.
example-catch-signal : Starting main loop.

# ./example-emit-signal

If everything goes well, you should see this output:
example-emit-signal : Connecting to Session D-Bus.
example-emit-signal : RequestName returned 1.
example-emit-signal : sample_object_class_init is called.
example-emit-signal : Binding to Glib/D-Bus.
example-emit-signal : sample_object_init is called.
example-emit-signal : Emit hello_signal.
example-catch-signal : received hello_signal=hello signal from server, hello_signal_handler is called.

After testing, terminate the program example-catch-signal to clean the testing environment.
# kill 17083

Reference Documents

These documents were used as references in the writing of this document. Refer to these documents for further details.

  1. software/dbus
    http://www.freedesktop.org/wiki/Software/dbus
  2. dbus tutorial
    http://dbus.freedesktop.org/doc/dbus-tutorial.html
  3. dbus-python tutorial
    http://dbus.freedesktop.org/doc/dbus-python/doc/tutorial.html
  4. Using the D-Bus C API
    http://dbus.freedesktop.org/doc/dbus/libdbus-tutorial.html
  5. D-Bus introduction
    http://techbase.kde.org/Development/Tutorials/D-Bus/Introduction
  6. GLib bindings
    http://techbase.kde.org/Development/Tutorials/D-Bus/Introduction
  7. maemo Platform Development chapter 1
    http://maemo.org/maemo_training_material/maemo4.x/html/
    maemo_Platform_Development/Chapter_01_DBus_The_Message_Bus_System.html
  8. maemo Platform Development chapter 3
    http://maemo.org/maemo_training_material/maemo4.x/html/
    maemo_Platform_Development/Chapter_03_Using_the_GLib_wrappers_for_DBus.html
  9. maemo Platform Development chapter 4
    http://maemo.org/maemo_training_material/maemo4.x/html/
    maemo_Platform_Development/Chapter_04_Implementing_and_using_DBus_signals.html
  10. dbus-send
    http://dbus.freedesktop.org/doc/dbus-send.1.html
  11. dbus-monitor
    http://dbus.freedesktop.org/doc/dbus-monitor.1.html