/srv/mercurial-server/detachablepurple: 2c9a4ee64914: Added some...

Gilles Bedel gillux at cpw.pidgin.im
Fri Jun 15 22:01:29 EDT 2012


Changeset: 2c9a4ee64914d00cce8b5c619c2a34b03377524d
Author:	 Gilles Bedel <gillux at cpw.pidgin.im>
Date:	 2011-05-19 19:58 +0000
Branch:	 cpw.gillux.detachablepurple
URL: http://hg.pidgin.im/srv/mercurial-server/detachablepurple/rev/2c9a4ee64914

Description:

Added some generic code to register PurpleObject-derived objects on DBus.
With the default handlers, a subclass should only have to set the DBus path
name and the DBus interface data generated by dbusxml-to-c.py, and then all
the properties defined in the XML file will be accessible (in accordance to
their read/write flags).
Additionaly, a simple call to purple_object_bind_dbus_method() will bind
a random function to a DBus method name, and the function will be called
with the parameters as defined in the XML file.

diffstat:

 libpurple/pobject.c |  211 ++++++++++++++++++++++++++++++++++++++++++++++++++++
 libpurple/pobject.h |  132 ++++++++++++++++++++++++++++++++-
 2 files changed, 342 insertions(+), 1 deletions(-)

diffs (truncated from 405 to 300 lines):

diff --git a/libpurple/pobject.c b/libpurple/pobject.c
--- a/libpurple/pobject.c
+++ b/libpurple/pobject.c
@@ -25,6 +25,8 @@
 #include "marshallers.h"
 #include "pobject.h"
 
+#include "glib-2.30.h"
+
 #ifdef HAVE_DBUS
 /* Provides DBUS_SERVICE_DBUS etc. */
 #  include <dbus/dbus-glib-bindings.h>
@@ -37,6 +39,8 @@
 {
 	gpointer proto_data;
 	gpointer ui_data;
+	/* The gdbus id used to unregister registered objects. */
+	guint dbus_reg_id;
 	/* The dbus interface we use for purple related methods and signals,
 	 * e.g. im.pidgin.purple.account.
 	 */
@@ -201,6 +205,8 @@
 	g_type_class_add_private(klass, sizeof(PurpleObjectPrivate));
 
 #ifdef HAVE_DBUS
+	if (purple_core_is_daemon_mode())
+		klass->dbus_register = purple_object_generic_dbus_register;
 	/* In remote mode we need to register the marshallers
 	 * we will use to receive the signals sent by the daemon */
 	if (purple_core_is_remote_mode()) {
@@ -280,6 +286,26 @@
 }
 
 #ifdef HAVE_DBUS
+guint
+purple_object_get_dbus_reg_id(PurpleObject *pobj)
+{
+	PurpleObjectPrivate *priv;
+
+	g_return_val_if_fail(pobj, NULL);
+	priv = PURPLE_OBJECT_GET_PRIVATE(pobj);
+	return priv->dbus_reg_id;
+}
+
+void
+purple_object_set_dbus_reg_id(PurpleObject *pobj, guint registration_id)
+{
+	PurpleObjectPrivate *priv;
+
+	g_return_if_fail(pobj);
+	priv = PURPLE_OBJECT_GET_PRIVATE(pobj);
+	priv->dbus_reg_id = registration_id;
+}
+
 char*
 purple_object_get_dbus_obj_interface(PurpleObject *pobj)
 {
@@ -507,6 +533,191 @@
 
 }
 
+void
+purple_object_bind_dbus_method(PurpleObjectClass *klass,
+                               const char *method_name,
+                               GCallback func)
+{
+	GClosure* closure;
+
+	closure = g_cclosure_new(G_CALLBACK(func), NULL, NULL);
+	g_closure_set_marshal(closure, g_cclosure_marshal_generic);
+	g_datalist_set_data(&klass->dbus_methods, method_name, closure);
+}
+
+GClosure *
+purple_object_get_dbus_closure(PurpleObjectClass *klass,
+                               const char *method_name)
+{
+	return g_datalist_get_data(&klass->dbus_methods, method_name);
+}
+
+void
+purple_object_generic_dbus_method_handler(GDBusConnection       *connection,
+                                          const gchar           *sender,
+                                          const gchar           *object_path,
+                                          const gchar           *interface_name,
+                                          const gchar           *method_name,
+                                          GVariant              *params,
+                                          GDBusMethodInvocation *invoc,
+                                          gpointer               object)
+{
+	guint num_params = g_variant_n_children(params);
+	GValue *paramv = g_new0(GValue, num_params + 1);
+	GVariant *next;
+	GVariantIter iter;
+	guint i;
+	GClosure *closure;
+
+	/* Put the right parameters in paramv. First, the object. */
+	g_value_init (&paramv[0], G_TYPE_FROM_INSTANCE(object));
+	g_value_set_object (&paramv[0], object);
+	/* Then the others, if any. */
+	i = 1;
+	g_variant_iter_init (&iter, params);
+	while ((next = g_variant_iter_next_value(&iter)) != NULL) {
+		g_dbus_gvariant_to_gvalue(next, &paramv[i++]);
+		g_variant_unref(next);
+	}
+
+	/* Get the binded function back, and execute it. */
+	closure = purple_object_get_dbus_closure(PURPLE_OBJECT_GET_CLASS(object),
+	                                         method_name);
+	if (!closure)
+		return g_dbus_method_invocation_return_error
+		        (invoc, G_DBUS_ERROR, G_DBUS_ERROR_UNKNOWN_METHOD,
+		         "Method %s is not yet implemented on interface %s.",
+		         method_name, interface_name);
+
+	g_closure_invoke(closure, NULL, num_params + 1, paramv, NULL);
+	/* TODO: handle output parameters. */
+	g_dbus_method_invocation_return_value(invoc, NULL);
+
+	for (i = 0; i < num_params + 1; i++)
+		g_value_unset (&paramv[i]);
+	g_free (paramv);
+}
+
+GVariant *
+purple_object_generic_dbus_get_property(GDBusConnection  *connection,
+                                        const gchar      *sender,
+                                        const gchar      *object_path,
+                                        const gchar      *interface_name,
+                                        const gchar      *property_name,
+                                        GError          **error,
+                                        gpointer          object)
+{
+	GVariant *ret = NULL;
+	GValue value = {0, };
+	GValue tmp   = {0, };
+	GParamSpec *pspec;
+	GDBusPropertyInfo *prop_info;
+
+	pspec = g_object_class_find_property(G_OBJECT_GET_CLASS(G_OBJECT(object)),
+	                                     property_name);
+	if (!pspec)
+		g_set_error(error, G_DBUS_ERROR, G_DBUS_ERROR_INVALID_ARGS,
+			"No property named %s in interface %s.",
+		    property_name, interface_name);
+	else {
+		g_value_init(&value, G_PARAM_SPEC_VALUE_TYPE(pspec));
+		g_object_get_property(object, property_name, &value);
+		if (G_VALUE_HOLDS_OBJECT(&value)) {
+			/* Convert gobjects into dbus path names. */
+			g_value_init(&tmp, G_TYPE_OBJECT);
+			g_value_copy(&value, &tmp);
+			g_value_unset(&value);
+			g_value_init(&value, G_TYPE_STRING);
+			g_value_transform(&tmp, &value);
+			g_value_unset(&tmp);
+		}
+		prop_info = g_dbus_interface_info_lookup_property
+			(PURPLE_OBJECT_GET_CLASS(object)->dbus_ifaceinfo,
+			 property_name);
+		ret = g_dbus_gvalue_to_gvariant(&value,
+		                                G_VARIANT_TYPE(prop_info->signature));
+		g_value_unset(&value);
+	}
+	return ret;
+}
+
+gboolean
+purple_object_generic_dbus_set_property(GDBusConnection  *connection,
+                                        const gchar      *sender,
+                                        const gchar      *object_path,
+                                        const gchar      *interface_name,
+                                        const gchar      *property_name,
+                                        GVariant         *variant,
+                                        GError          **error,
+                                        gpointer          object)
+{
+	gboolean ret = FALSE;
+	GParamSpec *pspec;
+	GValue value = {0, };
+	GValue tmp   = {0, };
+
+	pspec = g_object_class_find_property(G_OBJECT_GET_CLASS(object),
+	                                     property_name);
+	if (!pspec)
+		g_set_error(error, G_DBUS_ERROR, G_DBUS_ERROR_INVALID_ARGS,
+			"No property named %s in interface %s.",
+		    property_name, interface_name);
+	else {
+		g_dbus_gvariant_to_gvalue(variant, &value);
+		g_value_init(&value, G_PARAM_SPEC_VALUE_TYPE(pspec));
+		if (G_TYPE_IS_OBJECT(G_PARAM_SPEC_VALUE_TYPE(pspec))
+		    && G_VALUE_HOLDS_STRING(&value)) {
+			/* Convert dbus path names into gobjects. */
+			g_value_init(&tmp, G_TYPE_OBJECT);
+			g_value_transform(&value, &tmp);
+			g_value_unset(&value);
+			g_value_init(&value, G_TYPE_OBJECT);
+			g_value_copy(&tmp, &value);
+			g_value_unset(&tmp);
+		}
+		g_object_set_property(G_OBJECT(object), property_name, &value);
+		g_value_unset(&value);
+	}
+	return ret;
+}
+
+guint
+purple_object_generic_dbus_register(gpointer object, GDBusConnection *dbus_conn)
+{
+	PurpleObject *pobj = PURPLE_OBJECT(object);
+	GError *error = NULL;
+	guint reg_id;
+	static const GDBusInterfaceVTable interface_vtable =
+	{ purple_object_generic_dbus_method_handler,
+	  purple_object_generic_dbus_get_property,
+	  purple_object_generic_dbus_set_property };
+
+	reg_id = g_dbus_connection_register_object
+	           (dbus_conn,
+	            purple_object_get_dbus_path(pobj),
+	            PURPLE_OBJECT_GET_CLASS(pobj)->dbus_ifaceinfo,
+	            &interface_vtable,
+	            object, NULL, &error);
+	if (error) {
+		purple_debug_error("dbus", "Failed to register a %s: %s\n",
+                           G_OBJECT_TYPE_NAME(object), error->message);
+		g_error_free(error);
+	}
+	return reg_id;
+}
+
+void
+purple_object_register_on_dbus(gpointer object, GDBusConnection *dbus_conn)
+{
+	PurpleObjectClass *pclass = PURPLE_OBJECT_GET_CLASS(object);
+	guint reg_id;
+
+	if (pclass->dbus_register) {
+		reg_id = pclass->dbus_register(object, dbus_conn);
+		purple_object_set_dbus_reg_id(PURPLE_OBJECT(object), reg_id);
+	}
+}
+
 static GQuark
 purple_object_type_dbus_metadata_quark(void)
 {
diff --git a/libpurple/pobject.h b/libpurple/pobject.h
--- a/libpurple/pobject.h
+++ b/libpurple/pobject.h
@@ -51,7 +51,11 @@
 #ifdef HAVE_DBUS
 	/* A function that should register the object on DBus. */
 	guint (*dbus_register)(gpointer object, GDBusConnection *connection);
-	void (*_purple_reserved[3])(void);
+	/* A data set of DBus methods name as keys and GClosures as values. */
+	GData *dbus_methods;
+	/* The C structure based representation of the XML DBus interface. */
+	GDBusInterfaceInfo *dbus_ifaceinfo;
+	void (*_purple_reserved[1])(void);
 #else
 	void (*_purple_reserved[4])(void);
 #endif
@@ -71,6 +75,23 @@
 
 #ifdef HAVE_DBUS
 /**
+ * Gets the DBus registration id of the object pobj.
+ * An id of zero means no registration, or a registration failed.
+ *
+ * @param pobj The PurpleObject we want to get the registration id from.
+ * @return The registration id of pobj.
+ */
+guint purple_object_get_dbus_reg_id(PurpleObject *pobj);
+
+/**
+ * Sets the DBus registration id of the object pobj.
+ *
+ * @param pobj The PurpleObject we want to set the registration id in.
+ * @param registration_id The registration id we want to set.
+ */
+void purple_object_set_dbus_reg_id(PurpleObject *pobj, guint registration_id);
+
+/**
  * Gets the dbus proxy. This is used in detachable sessions context only.
  * When a client is in remote mode, the gobjects it creates are kind of mirrors
  * of the remote object. They hold 2 dbus proxies and the last know state of
@@ -161,6 +182,115 @@
  * @param path The path name.
  */
 void purple_object_set_dbus_path(PurpleObject *pobj, char* path);
+
+/**
+ * Associates the function func with the DBus method named method_name.
+ * If the default generic dbus handlers are in place, a call to the method
+ * named method_name will result in the call of the function func, with the
+ * parameters as described in the dbus_ifaceinfo field of the class (which is
+ * a C structure based representation of the DBus XML interface). The given



More information about the Commits mailing list