Language Bindings

As mentioned in Section 2.2 ― Using D-Bus, many programming languages have their own generic ways of using D-Bus APIs. In addition, there are some Telepathy-specific APIs to make the use of Telepathy easier. For instance, telepathy-glib provides an API that aims to be more familiar to users of Glib and GTK+. Likewise, Telepathy-Qt provides a Qt API for Telepathy and Telepathy-Python for Python.

Remember that, like raw use of D-Bus from these programming languages, the Telepathy language bindings only create proxies to the D-Bus objects, providing a way to use their API. The actual objects are instantiated in the service's process.

2.3.1. telepathy-python

telepathy-python builds on top of the standard dbus module for Python. The telepathy module provides some convenience classes, as well as enumerations and interface names. If you're planning on using the GLib mainloop you'll also need the dbus.mainloop.glib module.

The telepathy.client provides the client D-Bus proxy objects. The telepathy.interfaces provides the names of Telepathy interfaces (e.g. CONNECTION_MANAGER, CONNECTION_INTERFACE_REQUESTS, CHANNEL_TYPE_CONTACT_LIST, etc.). The telepathy.constants module provides named constants in the Telepathy spec (e.g. CONNECTION_STATUS_CONNECTED, HANDLE_TYPE_GROUP).

Example 2-1 shows how to configure D-Bus to use the GLib mainloop for its event dispatching and then makes a connection to the session bus.

Example 2-1Setting Up The Mainloop
import dbus.mainloop.glib
dbus.mainloop.glib.DBusGMainLoop(set_as_default = True)

Complete Source Code

2.3.1.1. Proxy Objects

In order to interact with a D-Bus object (i.e. call methods or connect signals) we need to create a proxy object for it. A proxy object is a Python object that we call methods on in our application. In telepathy-python they are provided in the telepathy.client module.

The proxy objects you will use are Connection and Channel.

Example 2-2Connection and Channel Proxy Objects
connection = telepathy.client.Connection(bus_name, object_path)

channel = telepathy.client.Channel(connection.service_name, object_path)

telepathy-python proxies differ from regular Python dbus proxies in that they aren't created with a given D-Bus interface. Instead the interface is specified when calling a method or connecting a signal similar to how you might look up a key in a dictionary (see Example 2-3 below). Interface names are available from the module telepathy.interfaces.

2.3.1.2. Calling Methods

D-Bus method calls on a proxy object look like any other Python method call. Python's D-Bus support can automatically convert native Python types into D-Bus types and back, so no complicated type marshalling is required. Example 2-3 shows calling the RequestConnection method (more on this in Section 5.2.2 ― From a Connection Manager).

The reply_handler and error_handler keywords are important to make your method call asynchronous. Method calls in D-Bus should always be made asynchronously, for the reasons outlined in Section 2.2 ― Using D-Bus. Python features such as inline functions and lambdas are permitted as callback functions. The parameters to a callback are specified by the D-Bus return type, e.g. Example 2-4.

Example 2-3Calling a Method
cm[CONNECTION_MANAGER].RequestConnection('jabber',
    {
        'account':  account,
        'password': password,
    },
    reply_handler = self.request_connection_cb,
    error_handler = self.error_cb)

Complete Source Code

Example 2-4Method Callback
def request_connection_cb (self, bus_name, object_path):
    print bus_name, object_path

Complete Source Code

2.3.1.3. Using Properties

D-Bus properties are available via the org.freedesktop.DBus.Properties interface, which provides the Get, Set and GetAll methods to get or set property values for the object's other interfaces.

Python D-Bus currently has no specific API for dealing with properties, so they must be accessed through the D-Bus method calls. Example 2-5 is therefore very similar to Example 2-3 presented above.

Example 2-5Accessing D-Bus Properties Using Methods
conn[DBUS_PROPERTIES].Get(CONNECTION_INTERFACE_REQUESTS,
                        'Channels',
                        reply_handler = self.get_channels_cb,
                        error_handler = self.error_cb)

Complete Source Code

2.3.1.4. Handling Signals

D-Bus signal handlers may be specified in Python with the proxy object's connect_to_signal method. The specified callback function will then be called when the signal is emitted. Example 2-6 connects to the NewChannels signal in Telepathy.

Example 2-6Connecting a Signal
conn[CONNECTION_INTERFACE_REQUESTS].connect_to_signal(
                        'NewChannels',
                        self.get_channels_cb)

Complete Source Code

2.3.2. telepathy-glib

The C examples in this book will use telepathy-glib, which provides a GObject-based API for the Telepathy framework.

2.3.2.1. TpProxy Objects

telepathy-glib uses what it calls TpProxy objects to make D-Bus method calls. TpProxy objects are GObjects that expose common Telepathy features (like the interfaces they implement) as GObject properties and struct members.

There are several common TpProxy subclasses that you will use when programming with telepathy-glib: TpAccount, TpAccountManager, TpConnectionManager, TpConnection and TpChannel.

Proxy objects need to make D-Bus method calls to acquire additional information (e.g. available interfaces) to populate the properties and structures of the object. When they have received this information they are considered "ready". You can prepare a proxy using the method call tp_proxy_prepare_async. This method call takes a NULL-terminated array of features to prepare on the proxy (you can pass NULL to indicate you would like just the core features prepared); and a callback to call when preparation has succeeded or failed.

Proxy preparation may potentially fail. In the callback you must use tp_proxy_prepare_finish to check whether preparation was successful. See Example 2-7 and Example 2-8.

Features have names such as TP_CONNECTION_FEATURE_CONNECTED and TP_CHANNEL_FEATURE_GROUP.

Example 2-7Preparing a TpProxy
/* get the account */
account = tp_account_new (dbus, account_path, &error);
if (account == NULL)
  handle_error (error);

/* prepare the core account features */
tp_proxy_prepare_async (account, NULL, _account_ready, user_data);

Complete Source Code

Example 2-8Preparation Callback
static void
_account_ready (GObject *account,
    GAsyncResult *res,
    gpointer user_data)
{
  GError *error = NULL;

  if (!tp_proxy_prepare_finish (account, res, &error))
    handle_error (error);

  ...
}
Example 2-9Preparing extra features
TpChannel *channel = ...;
GQuark features[] = { TP_CHANNEL_FEATURE_CORE, TP_CHANNEL_FEATURE_GROUP, 0 };

tp_proxy_prepare_async (channel, features, callback, user_data);
TpConnection Readiness

If TP_CONNECTION_FEATURE_CONNECTED is requested on a TpConnection, you still have to call the Connect method. This feature will block preparation until the connection has entered the CONNECTED state.

See Section 5.2 ― Obtaining a Connection for more details on setting up a Connection.

2.3.2.2. Method Calls

telepathy-glib provides the Telepathy D-Bus API method calls as generated functions. The generated functions have the following prefixes:

tp_cli_

These functions are used for client applications. For instance, tp_cli_connection_call_connect.

tp_svc_

These functions are used for service implementations, such as connection managers. For instance, tp_svc_connection_implement_connect.

Asynchronous Calls

For each Telepathy D-Bus method, telepathy-glib generally provides both a "run" and a "call" function. For instance, tp_cli_connection_run_connect and tp_cli_connection_call_connect.

The "run" functions are synchronous, and blocks until the D-Bus service has returned the value. They have been deprecated and should never be used in Telepathy programs (see Section 2.2 ― Using D-Bus). Instead use the "call" functions, which are asynchronous; immediately returning and later providing the result to a callback function.

The generated client functions have the following general signature:

  • TpProxy object;
  • Timeout in milliseconds (-1 sets the default);
  • Method arguments (see Section 2.3.2.4 ― Types);
  • Callback function;
  • Optional user data for the callback;
  • Optional destroy function for the user data;
  • An optional object to weakly reference, if this object is destroyed the call will be cancelled.

Each method call has its own unique callback. The general type signature for a method callback is:

Example 2-10 shows a the construction of a telepathy-glib method call, in this case to RequestConnection.

Example 2-10Example Method Call and Associated Callback
/* method call for ConnectionManager.RequestConnection */
tp_cli_connection_manager_call_request_connection (
	cm,			/* TpProxy */
	-1,			/* timeout */
	"jabber",		/* method arguments */
	parameters,
	request_connection_cb,	/* callback */
	NULL,			/* user data */
	NULL,			/* destroy notify */
	NULL);			/* weak object */

/* callback for ConnectionManager.RequestConnection */
static void
request_connection_cb (TpConnectionManager      *cm,		/* TpProxy */
                       const char               *bus_name,	/* return args */
		       const char               *object_path,
		       const GError             *in_error,	/* error condition */
		       gpointer                  user_data,	/* user data */
		       GObject                  *weak_object)	/* weak object */

2.3.2.3. D-Bus Properties

telepathy-glib provides a convenience API for accessing the D-Bus properties of any TpProxy proxy: tp_cli_dbus_properties_call_get, tp_cli_dbus_properties_call_get_all and tp_cli_dbus_properties_call_set. These functions work similar to all other TpProxy method calls.

For tp_cli_dbus_properties_call_get, the return argument is a GValue of the type specified by the property (see Section 2.3.2.4 ― Types). For tp_cli_dbus_properties_call_get_all the return value is an a{sv} map. Example 2-11 demonstates how to access a D-Bus property.

Example 2-11Getting a D-Bus Property with telepathy-glib
tp_cli_dbus_properties_call_get (conn, -1,
        TP_IFACE_CONNECTION_INTERFACE_REQUESTS,
        "Channels",
        get_channels_cb,
        NULL, NULL, NULL);


static void
get_channels_cb (TpProxy        *proxy,
                 const GValue   *value,
                 const GError   *in_error,
                 gpointer        user_data,
                 GObject        *weak_obj)
{
        /* handle error */

        GPtrArray *channels = g_value_get_boxed (value);

        /* ... */
}

2.3.2.4. Types

The handling of variant types in telepathy-glib is done using GLib's built-in GValue type system.

GType types for simple D-Bus types are given in Table 2-1. Telepathy's complex types are all mapped to generated boxed GTypes (see the telepathy-glib manual). For example Channel_Details maps to the GType TP_STRUCT_TYPE_CHANNEL_DETAILS.

To check that a given GValue is of the correct type, use the macro G_VALUE_HOLDS. To retrieve the type of a GValue use the macro G_VALUE_TYPE.

Table 2-1D-Bus Simple GTypes
Single Values Lists/Arrays
D-Bus Type GType C type D-Bus Type GType C type
o DBUS_TYPE_G_OBJECT_PATH String (char *) ao TP_ARRAY_TYPE_OBJECT_PATH_LIST GPtrArray of Strings (char *)
y G_TYPE_UCHAR guchar ay DBUS_TYPE_G_UCHAR_ARRAY GArray of guchar
u G_TYPE_UINT guint au DBUS_TYPE_G_UINT_ARRAY GArray of guint
i G_TYPE_INT int ai DBUS_TYPE_G_INT_ARRAY GArray of int
t G_TYPE_UINT64 guint64 at DBUS_TYPE_G_UINT64_ARRAY GArray of guint64
x G_TYPE_INT64 gint64 ax DBUS_TYPE_G_INT64_ARRAY GArray of gint64
d G_TYPE_DOUBLE double ad DBUS_TYPE_G_DOUBLE_ARRAY GArray of double
b G_TYPE_BOOLEAN gboolean ab DBUS_TYPE_G_BOOLEAN_ARRAY GArray of gboolean
s G_TYPE_STRING char * as G_TYPE_STRV char **

Decoding the contents of the GValue can be done either from the manual or from the D-Bus type signature. For example the property Requests.Channels is of the type Channel_Details_List (a(oa{sv})). From the manual, this is a GPtrArray of TP_STRUCT_TYPE_CHANNEL_DETAILS, which in turn is a GValueArray containing a object path and an a{sv} map (hashtable). The complete unpacking is shown in Example 2-14.

More generally, the structure of the type can be determined from its D-Bus type signature by following several rules:

  • Arrays of complex types (e.g. a(suv)) and of object paths (ao) are stored as a GPtrArray.
  • Arrays of strings (as), which are stored as a NULL-terminated gchar **.
  • Arrays of all other simple types (e.g. au) are stored as a GArray.
  • Structures (e.g. (oa{sv})) are stored as a GValueArray where each member of the structure is a GValue in the array in the respective order (see Section 2.3.2.6 ― Structs).
  • Maps (e.g. a{sv}) are stored as a GHashTable.
  • Object paths (o) are stored as a string (e.g. when used in a a{uo} map), or in a boxed GValue of type DBUS_TYPE_G_OBJECT_PATH (when stored in a struct or variant type).
  • Variant types (v) are stored as GValues of type specified by the spec (see Section 2.3.2.7 ― Variant Types).

For example, the D-Bus type a{sa(usuu)} is a GHashTable of string keys (char *) to GPtrArrays containing GValueArrays. This is shown graphically in Figure 2-4.

Figure 2-4Expanded Type for a{sa(usuu)}
G_VALUE_TYPE_NAME()

The macro G_VALUE_TYPE_NAME can also be useful for unpacking Telepathy types.

For example the listed type name of TP_STRUCT_TYPE_CHANNEL_DETAILS_LIST is given as GPtrArray_GValueArray_DBusGObjectPath+GHashTable_gchararray+GValue___.

2.3.2.5. a{sv} Maps

Telepathy makes extensive use of a{sv} maps (i.e. a map of string keys to variant types) for passing and returning properties from calls (this allows for something similar to polymorphic functions via D-Bus). Unforunately, C and GLib do not offer many functions for convieniently handling this type of map.

To make handling these maps easier, Telepathy provides a number of functions, tp_asv_* for manipulating these maps.

Telepathy uses a standard GHashTable that is set up to use string keys, and GSlice-allocated GValue values. The easiest way to create a new map with tp_asv_new. This function will automatically destroy values when they are overwritten, removed or the hash table is destroyed. The standard GHashTable functions g_hash_table_destroy, g_hash_table_ref, etc. can all be used with this map.

tp_asv_new can optionally take a NULL-terminated list of initial values as (key, type, value) tuples, as shown in Example 2-12.

Example 2-12Creating an a{sv} map with tp_asv_new()
GHashTable *parameters = tp_asv_new (
	"account", G_TYPE_STRING, "bob@mcbadgers.com"
	"password", G_TYPE_STRING, password,
	NULL);

The map can be further edited or appended to with the tp_asv_set_* functions (e.g. tp_asv_set_string).

To read values from the map, use the tp_asv_get_* functions (e.g. tp_asv_get_string). These functions return the value on success, or NULL/False/0 if the key is unavailable. Some accessor functions (e.g. tp_asv_get_boolean, tp_asv_get_double, tp_asv_get_int32, etc.) take an optional parameter *valid, which can be used to determine, without ambiguity, if the key exists in the map.

tp_asv_dump()

The function tp_asv_dump can be used to dump a string representation of the map out to the debugging console.

2.3.2.6. Structs

Structs (e.g. ussu) are stored as a GValueArray. To make these easier to create use the utility function tp_value_array_build. This function requires a number of struct entries, followed by (GType, value) pairs, and terminated with the type G_TYPE_INVALID, as shown in Example 2-13.

Example 2-13Creating an (ussu) struct with tp_value_array_build()
GValueArray *entry = tp_value_array_build (4,
         G_TYPE_UINT, 12,
         G_TYPE_STRING, "subject",
	 G_TYPE_STRING, "Building a GValueArray",
	 G_TYPE_UINT, 0,
	 G_TYPE_INVALID);

tp_value_array_build takes copies/references of all of the supplied data, which can be freed immediately. These copies/references are released when the GValueArray is freed.

Structs can be unpacked using the utility function tp_value_array_unpack. This function takes a number of values to unpack (may be less than the number of values in the array) followed by that number of pointers to appropriate variables (NULL may be passed to skip a value you're not interested in). Example 2-14 shows unpacking the object path, and properties for an array of channels.

Values are not copied out of the array, and these pointers will become invalid when the array is freed. If you want to keep a copy of a value you should copy/reference it explicitly.

Example 2-14Decoding a GValue containing a Channel_Details_List
g_return_if_fail (G_VALUE_HOLDS (value, TP_ARRAY_TYPE_CHANNEL_DETAILS_LIST));

GPtrArray *channels = g_value_get_boxed (value);

int i;
for (i = 0; i < channels->len; i++)
{
        GValueArray *channel = g_ptr_array_index (channels, i);

        char *object_path;
        GHashTable *map;

	tp_value_unpack (channel, 2,
		&object_path,
		&map);

        const char *type = tp_asv_get_string (map, TP_IFACE_CHANNEL ".ChannelType");

        g_print ("Path: %s\n", object_path);
        g_print ("Type: %s\n", type);
}

Individual values can be retrieved from a structure using g_value_array_get_nth plus the appropriate g_value_get_... function for the returned GValue. Example 2-15 shows how this is done. Similar to using tp_value_array_unpack, this value is not copied or referenced, however GLib provides several g_value_dup_... functions that do make copies.

Example 2-15Unpacking oa{sv}
GValueArray *channel = ...; /* contains a struct of type (oa{sv}) */

char *object_path;
GHashTable *map;

object_path = g_value_get_boxed (g_value_array_get_nth (channel, 0));
map = g_value_get_boxed (g_value_array_get_nth (channel, 1));

2.3.2.7. Variant Types

Variant types are stored as a GValue of type given by the specification. For example, Example 2-16 shows how to unpack the type a(uv).

Example 2-16Unpacking Type a(uv)
int i;
for (i = 0; i < properties->len; i++)
{
        GValueArray *property = g_ptr_array_index (properties, i);
        /* the id is a GValue<UINT>
         * the variant is a GValue<GValue<??> */
        guint id = g_value_get_uint (g_value_array_get_nth (property, 0));
        GValue *value = g_value_get_boxed (g_value_array_get_nth (property, 1));

        /* get a string representation of value */
        char *str = g_strdup_value_contents (value);
        g_print ("Property %i: %s\n", id, str);
        g_free (str);
}
Example 2-17Packing Type a(uv)
GPtrArray *array = g_ptr_array_new_with_free_func ((GDestroyNotify) g_value_array_free);

/* pack structs into array */
GValue value = { 0, };

g_value_init (&value, G_TYPE_STRING);
g_value_set_static_string (&value, "Test Subject");

g_ptr_array_add (array, tp_value_array_build (2,
	G_TYPE_UINT, id,
	G_TYPE_VALUE, value,
	G_TYPE_INVALID);

g_value_unset (&value);

...

/* free array */
g_ptr_array_free (array, TRUE);

2.3.2.8. Headers and Linking

To use the telepathy-glib API, you must include the headers for the library, and link to its shared library. The necessary compiler and linker commands can be obtained from the pkg-config utiltity like so:

pkg-config telepathy-glib --cflags
pkg-config telepathy-glib --libs

However, if you are using the "autotools" (automake, autoconf, etc) build system, you will find it more convenient to use the PKG_CHECK_MODULES macro in your configure.ac file.

PKG_CHECK_MODULES(EXAMPLE, telepathy-glib)
AC_SUBST(EXAMPLE_CFLAGS)
AC_SUBST(EXAMPLE_LIBS)

You should then use the generated _CFLAGS and _LIBS definitions in your Makefile.am files.