cpw.gillux.detachablepurple: a8edb2e2: Added some generic code to register Purp...

gillux at soc.pidgin.im gillux at soc.pidgin.im
Thu May 19 16:21:22 EDT 2011


----------------------------------------------------------------------
Revision: a8edb2e23ff931398eef101872430240da47d7cf
Parent:   3e65e88487d511bac639b768dd5a4d68b11f8027
Author:   gillux at soc.pidgin.im
Date:     05/19/11 15:58:15
Branch:   im.pidgin.cpw.gillux.detachablepurple
URL: http://d.pidgin.im/viewmtn/revision/info/a8edb2e23ff931398eef101872430240da47d7cf

Changelog: 

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.

Changes against parent 3e65e88487d511bac639b768dd5a4d68b11f8027

  patched  libpurple/pobject.c
  patched  libpurple/pobject.h

-------------- next part --------------
============================================================
--- libpurple/pobject.c	f4b3c3a9683bd5f1cc581fabcb0900b5715b6319
+++ libpurple/pobject.c	f67614cf127fba0fdc066aa0877af3e14c22d73e
@@ -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 @@ struct _PurpleObjectPrivate
 {
 	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 @@ purple_object_class_init(PurpleObjectCla
 	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 @@ gpointer purple_object_get_ui_data(Purpl
 }
 
 #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 @@ purple_object_install_dbus_infos(PurpleO
 
 }
 
+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)
 {
============================================================
--- libpurple/pobject.h	18476ab80af15bff6acdd5cc7232c60c63ab01e4
+++ libpurple/pobject.h	24c4843b1462207bb851e2e9fe078db137d99422
@@ -51,7 +51,11 @@ struct _PurpleObjectClass
 #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 @@ gpointer purple_object_get_ui_data(Purpl
 
 #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,7 +182,116 @@ void purple_object_set_dbus_path(PurpleO
  * @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
+ * PurpleObjectClass klass must be the super-class the class intended to
+ * provide the method, e.g. the super-class of PurpleAccountClass.
+ * This function internally stores func in a GClosure and adds it to the
+ * dbus_methods data set, in klass.
+ *
+ * @param klass The super-class of the class providing method_name.
+ * @param method_name The name of the method on DBus.
+ * @param func The function called when one calls method_name on DBus.
+ *
+ */
+void purple_object_bind_dbus_method(PurpleObjectClass *klass,
+                                    const char        *method_name,
+                                    GCallback          func);
+
+/**
+ * Gets the GClosure created to handle the call of the DBus method
+ * named method_name.
+ *
+ * @param klass The super-class of the class providing method_name.
+ * @param method_name The name of the method on DBus.
+ * @see purple_object_bind_dbus_method
+ */
+GClosure *purple_object_get_dbus_closure(PurpleObjectClass *klass,
+                                         const char        *method_name);
+
+/**
+ * A GDBusInterfaceMethodCallFunc compilant DBus method handler.
+ * This method handler aims to be set in a GDBusInterfaceVTable.
+ * In fact, it is the default method handler, set by the default
+ * DBus object register purple_object_generic_dbus_register().
+ * It may be used e.g. along with non-default properties handlers
+ * in an interface virtual table, for very special DBus methods.
+ * 
+ * @see purple_object_generic_dbus_register
+ * @see purple_object_generic_dbus_get_property
+ * @see purple_object_generic_dbus_set_property
+ */
+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);
+
+/**
+ * A GDBusInterfaceMethodCallFunc compilant DBus property getter.
+ * This property getter aims to be set in a GDBusInterfaceVTable.
+ * In fact, it is the default property getter, set by the default
+ * DBus object register purple_object_generic_dbus_register().
+ * It may be used e.g. along with a non-default method handler
+ * in an interface virtual table, for very special DBus properties.
+ * 
+ * @see purple_object_generic_dbus_register
+ * @see purple_object_generic_dbus_method_handler
+ * @see purple_object_generic_dbus_set_property
+ */
+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);
+
+/**
+ * A GDBusInterfaceMethodCallFunc compilant DBus property setter.
+ * This property setter aims to be set in a GDBusInterfaceVTable.
+ * In fact, it is the default property setter, set by the default
+ * DBus object register purple_object_generic_dbus_register().
+ * It may be used e.g. along with a non-default method handler
+ * in an interface virtual table, for very special DBus properties.
+ * 
+ * @see purple_object_generic_dbus_register
+ * @see purple_object_generic_dbus_method_handler
+ * @see purple_object_generic_dbus_get_property
+ */
+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);
+
+/**
+ * Registers an object on DBus, with the default method and properties
+ * handlers, using the dbus_ifaceinfo class field as interface informations,
+ * and using the dbus_path object field as DBus object name. This function aims
+ * to be set as the dbus_register field of a class. Actually, it is already set
+ * so by default in PurpleObjectClass. So unless you have to perform specific
+ * stuff on the registration, like using your own interface virtual table, you
+ * shouldn't care about it.
+ * 
+ * @param object An object derived from PurpleObject.
+ * @param dbus_conn The GDBus handle.
+ * @return The registration id returned by g_dbus_connection_register_object().
+ */
+guint purple_object_generic_dbus_register(gpointer object, GDBusConnection *dbus_conn);
+
+/**
  * Calls the dbus_register function pointer on the class of object, and keeps
  * the registration id.
  *


More information about the Commits mailing list