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 (¶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)
{
============================================================
--- 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