/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 (¶mv[0], G_TYPE_FROM_INSTANCE(object));
+ g_value_set_object (¶mv[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, ¶mv[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 (¶mv[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