This section of the manual assumes that you're already familiar with implementing a GObject in the C programming language. If you're not, we recommend reading the FIXME NEED SOME RESOURCES HERE.
The telepathy-glib bindings provide a mechanism for implementing D-Bus interfaces as GInterface interfaces on a GObject. These interfaces begin with the namespace TpSvc… and have GTypes beginning with TP_TYPE_SVC_….
telepathy-glib also provides class mixins that can be used to implement support for functionality such as D-Bus properties and other Telepathy interfaces.
GObjects that implement these interfaces and mixins can be published via D-Bus using the standard dbus-glib function call dbus_g_connection_register_g_object.
D-Bus method interfaces are provided by telepathy-glib as regular GInterfaces to be implemented by your class.
For example, we have implemented our own class ExampleObserver which implements the Client and Client.Observer interfaces (also the D-Bus properties mixin, see below). The type definition for this class is presented in Example 12-1.
G_DEFINE_TYPE_WITH_CODE (ExampleObserver, example_observer, G_TYPE_OBJECT,
G_IMPLEMENT_INTERFACE (TP_TYPE_SVC_DBUS_PROPERTIES,
tp_dbus_properties_mixin_iface_init);
G_IMPLEMENT_INTERFACE (TP_TYPE_SVC_CLIENT, NULL);
G_IMPLEMENT_INTERFACE (TP_TYPE_SVC_CLIENT_OBSERVER, observer_iface_init);
);
In Example 12-1, interfaces that implement no methods (i.e. Client) do not need to provide initialisation methods and NULL may be passed instead.
Unlike with most GInterfaces, which use a vcall table to map interface methods to implementations, telepathy-glib uses method calls. This is simply to avoid potential ABI problems in the future.
Each method has an "implement" function, of the form tp_svc_interface_name_implement_method_name which accepts a method of the prototype tp_svc_interface_name_method_name_impl. For example, the method call Client.Observer.ObserveChannels is connected using the function tp_svc_client_observer_implement_observe_channels which accepts a function with the prototype of tp_svc_client_observer_observe_channels_impl. This is shown in Example 12-2. Example 12-4 shows the corresponding method implementation.
static void observer_iface_init (gpointer g_iface, gpointer iface_data) { TpSvcClientObserverClass *klass = (TpSvcClientObserverClass *) g_iface; tp_svc_client_observer_implement_observe_channels (klass, example_observer_observer_channels); }
To save excessive typing for interfaces with lots of methods, you can use a temporary IMPLEMENT macro, as demonstrated in Example 12-3 which is functionality identical to Example 12-2.
static void observer_iface_init (gpointer g_iface, gpointer iface_data) { TpSvcClientObserverClass *klass = (TpSvcClientObserverClass *) g_iface; #define IMPLEMENT(x) tp_svc_client_observer_implement_##x (klass, \ example_observer_##x) IMPLEMENT (observe_channels); #undef IMPLEMENT }
static void example_observer_observe_channels (TpSvcClientObserver *self, const char *account, const char *connection, const GPtrArray *channels, const char *dispatch_op, const GPtrArray *requests_satisfied, GHashTable *observer_info, DBusGMethodInvocation *context) { g_print (" > example_observer_observe_channels\n"); g_print (" account = %s\n", account); g_print (" connection = %s\n", connection); g_print (" dispatchop = %s\n", dispatch_op); /* channels is of type a(oa{sv}) */ int i; for (i = 0; i < channels->len; i++) { GValueArray *channel = g_ptr_array_index (channels, i); char *path; GHashTable *map; tp_value_array_unpack (channel, 2, &path, &map); g_print (" channel = %s\n", path); } tp_svc_client_observer_return_from_observe_channels (context); }
Method implementations may be either synchronous or asynchronous as you desire, however while your mainloop is blocked in a method call your service will be unable to handle any other incoming method calls (or other mainloop events, such as a client UI). When you are ready to return from the method call, call the appropriate return_from function, which will be of the form tp_svc_interface_name_return_from_method_name, e.g. tp_svc_client_observer_return_from_observe_channels (Example 12-4).
telepathy-glib provides a special class mixin for handling D-Bus properties, TpDBusPropertiesMixin.
Mixins are so called because they are mixed into a class to extend its functionality. For example, a class might inherit from GObject or some subclass, but then has the TpDBusPropertiesMixin to add additional functionality without having to inherit from a different class.
It is functionally similar to multiple inheritance, which is not available in GLib.
To add the D-Bus properties mixin to your class, several steps are required:
When you register support for the D-Bus properties interface, pass the mixin initialiser, tp_dbus_properties_mixin_iface_init. See Example 12-1.
Your class structure needs to include the TpDBusPropertiesMixinClass (Example 12-5).
The simplest way to use this mixin is to map D-Bus properties to GObject properties. Your class' class_init method provides the mapping from D-Bus property names to GObject property names.
Each D-Bus interface requires a NULL-terminated array of TpDBusPropertiesMixinPropImpl structures which provide the mappings between property names. The class also requires a NULL-terminated array of TpDBusPropertiesMixinIfaceImpl, which gives the D-Bus interface name, and getter and setter for each interface as shown in Example 12-6.
/* D-Bus properties are exposed as GObject properties through the * TpDBusPropertiesMixin */ /* properties on the Client interface */ static TpDBusPropertiesMixinPropImpl client_props[] = { { "Interfaces", "interfaces", NULL }, { NULL } }; /* properties on the Client.Observer interface */ static TpDBusPropertiesMixinPropImpl client_observer_props[] = { { "ObserverChannelFilter", "channel-filter", NULL }, { NULL } }; /* complete list of interfaces with properties */ static TpDBusPropertiesMixinIfaceImpl prop_interfaces[] = { { TP_IFACE_CLIENT, tp_dbus_properties_mixin_getter_gobject_properties, NULL, client_props }, { TP_IFACE_CLIENT_OBSERVER, tp_dbus_properties_mixin_getter_gobject_properties, NULL, client_observer_props }, { NULL } };
The mixin provides default getter and setter functions, tp_dbus_properties_mixin_getter_gobject_properties and tp_dbus_properties_mixin_setter_gobject_properties for mapping D-Bus properties to GObject properties.
If you wish, rather than using GObject properties, you may write your own getter and setter functions for handling D-Bus properties.
The data provided to TpDBusPropertiesMixinPropImpl will be passed as the data to your callback.
You must chain up the mixin's class_init method at the end of your class' class_init, as shown in Example 12-7.
Emitting a signal is done via calling the function of the form tp_svc_interface_name_emit_signal_name, for instance emitting the Account.AccountPropertyChanged signal is done via the tp_svc_account_emit_account_property_changed function.