soc.2010.detachablepurple: d5eca9b1: Added the PurpleDBusCallback singleton o...

gillux at soc.pidgin.im gillux at soc.pidgin.im
Wed Jul 28 13:46:02 EDT 2010


----------------------------------------------------------------------
Revision: d5eca9b19a96e36d49abc257439a5a0f38832399
Parent:   9a2dfa0d95577eef69825f350ad2a29255fbb9c4
Author:   gillux at soc.pidgin.im
Date:     07/28/10 13:34:42
Branch:   im.pidgin.soc.2010.detachablepurple
URL: http://d.pidgin.im/viewmtn/revision/info/d5eca9b19a96e36d49abc257439a5a0f38832399

Changelog: 

Added the PurpleDBusCallback singleton object, used for the handling of
application provided callbacks as parameters. See the comments in
dbus-callback.c for how it works.

Changes against parent 9a2dfa0d95577eef69825f350ad2a29255fbb9c4

  added    libpurple/dbus-callback.c
  added    libpurple/dbus-callback.h
  added    libpurple/dbus-prototypes/callback.xml
  patched  libpurple/Makefile.am
  patched  libpurple/dbus-purple.h
  patched  libpurple/dbus-server.c
  patched  libpurple/marshallers.list

-------------- next part --------------
============================================================
--- libpurple/dbus-purple.h	7bd285c8383aa7aedfa4f3ad9966d11b9db854d6
+++ libpurple/dbus-purple.h	9272bd6c1ec2a64164d7bd5bf4ae9ad6a88b1cd1
@@ -35,4 +35,7 @@
 #define DBUS_CONSTRUCTOR_PATH      "/im/pidgin/purple/constructor"
 #define DBUS_CONSTRUCTOR_INTERFACE "im.pidgin.purple.constructor"
 
+#define DBUS_CALLBACK_PATH      "/im/pidgin/purple/callback"
+#define DBUS_CALLBACK_INTERFACE "im.pidgin.purple.callback"
+
 #endif /* _DBUS_PURPLE_H_ */
============================================================
--- libpurple/dbus-server.c	cb3969c74637d0e9c75c853818739afd963d72f9
+++ libpurple/dbus-server.c	8e420a9796afd2a677344942f50c6b4a764312b6
@@ -40,6 +40,7 @@
 #include "accountlist.h"
 #include "blist.h"
 #include "conversation.h"
+#include "dbus-callback.h"
 #include "dbus-constructor.h"
 #include "dbus-purple.h"
 #include "dbus-server.h"
@@ -784,6 +785,11 @@ purple_dbus_g_init(void)
 		dbus_g_object_register_marshaller(purple_smarshal_VOID__STRING_BOXED,
 						G_TYPE_NONE, G_TYPE_STRING,
 						G_TYPE_VALUE, G_TYPE_INVALID);
+		/* Marshaller for the RunCallback dbus signal */
+		dbus_g_object_register_marshaller(
+			purple_smarshal_VOID__UINT64_BOXED,
+			G_TYPE_NONE,  G_TYPE_UINT64, G_TYPE_BOXED,
+			G_TYPE_INVALID);
 
 		/* We also use the "dbus path name -> gobject" hash table */
 		dbus_path_gobjects = g_hash_table_new_full(g_str_hash, g_str_equal,
@@ -816,6 +822,9 @@ purple_dbus_g_init(void)
 	}
 
 	/* In remote and daemon mode, we create our singleton objects */
+	/* Instantiate a PurpleDBusCallback */
+	g_object_new(PURPLE_TYPE_DBUS_CALLBACK, NULL);
+
 	/* Instantiate and publish the PurpleConstructor */
 	purple_constructor_get_instance();
 }
============================================================
--- libpurple/Makefile.am	c346d96300b3650b47d717e5064f173d66765d07
+++ libpurple/Makefile.am	0cdfbdc8607837c1f934dbd3b44243f03f0e762b
@@ -206,7 +206,8 @@ purple_builtheaders = \
 purple_builtheaders = \
 	purple.h version.h marshallers.h \
 	dbus-account-server.h dbus-account-client.h \
-	dbus-constructor-server.h dbus-constructor-client.h
+	dbus-constructor-server.h dbus-constructor-client.h \
+	dbus-callback-server.h dbus-callback-client.h
 
 marshallers.h: marshallers.list
 	$(AM_V_GEN)$(GLIB_GENMARSHAL) --prefix=purple_smarshal $(srcdir)/marshallers.list --header > marshallers.h
@@ -223,6 +224,12 @@ dbus-constructor-client.h: dbus-prototyp
 dbus-constructor-client.h: dbus-prototypes/constructor.xml
 	$(AM_V_GEN)dbus-binding-tool --prefix=DBUS_purple_constructor --mode=glib-client --output=$@ $<
 
+dbus-callback-server.h: dbus-prototypes/callback.xml
+	$(AM_V_GEN)dbus-binding-tool --prefix=DBUS_purple_callback --mode=glib-server --output=$@ $<
+
+dbus-callback-client.h: dbus-prototypes/callback.xml
+	$(AM_V_GEN)dbus-binding-tool --prefix=DBUS_purple_callback --mode=glib-client --output=$@ $<
+
 dbus-account-server.h: dbus-prototypes/account.xml
 	$(AM_V_GEN)dbus-binding-tool --prefix=DBUS_purple_account --mode=glib-server --output=$@ $<
 
@@ -245,7 +252,7 @@ dbus_sources  = dbus-server.c dbus-usefu
 # purple dbus server
 
 dbus_sources  = dbus-server.c dbus-useful.c \
-	dbus-constructor.c account-dbus.c
+	dbus-constructor.c dbus-callback.c account-dbus.c
 dbus_headers  = dbus-bindings.h dbus-purple.h dbus-server.h dbus-useful.h dbus-define-api.h dbus-types.h \
 	dbus-constructor.c account-dbus.h
 
============================================================
--- libpurple/marshallers.list	3ee72ae8b7449434fbd10d01edf16982893546b3
+++ libpurple/marshallers.list	f26599578dcc98d6210da239fffa03e963e84727
@@ -14,3 +14,5 @@ VOID:STRING,BOXED
 VOID:FLAGS,FLAGS
 VOID:STRING,STRING,OBJECT,OBJECT
 VOID:STRING,BOXED
+# Marshaller for the RunCallback dbus signal
+VOID:UINT64,BOXED
============================================================
--- /dev/null	
+++ libpurple/dbus-callback.c	a45a5f5c4d48871e3ee651dbf62123c0d9e4d407
@@ -0,0 +1,237 @@
+/* purple
+ *
+ * Purple is the legal property of its developers, whose names are too numerous
+ * to list here.  Please refer to the COPYRIGHT file distributed with this
+ * source distribution.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02111-1301  USA
+ */
+
+#include <gobject/gvaluecollector.h> /* G_VALUE_COLLECT macro */
+
+#include "internal.h"
+#include "core.h"
+#include "dbus-callback.h"
+#include "dbus-callback-server.h"
+#include "dbus-purple.h"
+#include "dbus-server.h"
+#include "debug.h"
+#include "marshallers.h"
+
+/**
+ * PurpleDBusCallback, a singleteon class that aims to run handle application
+ * provided callbacks, in detachable sessions context. On the client side,
+ * when we want to remotely run a method that takes a callback as parameter,
+ * the following happens:
+ * 1. Client side: the method is remotely called.
+ * 2. Daemon side: the method wrapper gets called, we generate a unique id for
+ *    the callback. We locally run the wrapped method, giving a fake callback,
+ *    i.e. one of the purple_dbus_callback__*() functions. We give the id for
+ *    the traditionnal void* user_data callback parameter.
+ * 3. Client side: the method return the unique id which identify the pending
+ *    callback. We store this id and the provided callback for future execution
+ *    using purple_dbus_callback_register().
+ * 4. Daemon side: the fake callback is called, which sends a RunCallback dbus
+ *    signal, with the id of the callback and its parameters.
+ * 5. Client side: We receive the RunCallback signal, which invokes the
+ *    run_callback_cb() sighandler. It gets the stored callback with the
+ *    provided id, and run it with its parameters.
+ */
+
+G_DEFINE_TYPE(PurpleDBusCallback, purple_dbus_callback, PURPLE_TYPE_OBJECT)
+
+/* Our signals */
+enum
+{
+	SIG_DBUS_RUN_CALLBACK,
+	SIG_LAST
+};
+
+static guint signals[SIG_LAST] = {0, };
+
+static void run_callback_cb(DBusGProxy *proxy, const guint64 callback_id, GPtrArray *params, gpointer data);
+
+/* This constructor make the singleton thing, using the class field "instance"
+ * to store the unique instance of PurpleDBusCallback */
+static GObject*
+purple_dbus_callback_constructor(GType type, guint n_params,
+                                 GObjectConstructParam *params)
+{
+	DBusGProxy* proxy;
+	PurpleDBusCallbackClass *klass;
+
+	klass = g_type_class_peek(PURPLE_TYPE_DBUS_CALLBACK);
+	if (!klass->instance) {
+		/* Call parent constructor */
+		klass->instance = G_OBJECT_CLASS(purple_dbus_callback_parent_class)->constructor(type, n_params, params);
+
+		/* Register this object on DBus */
+		purple_object_install_dbus_infos(PURPLE_OBJECT(klass->instance),
+		                                 DBUS_CALLBACK_INTERFACE,
+		                                 DBUS_CALLBACK_PATH);
+
+		/* In remote mode, get informed from its signals */
+		if (purple_core_is_remote_mode()) {
+			proxy = purple_object_get_dbus_obj_proxy(PURPLE_OBJECT(klass->instance));
+			dbus_g_proxy_add_signal(proxy, "RunCallback", 
+			                        G_TYPE_UINT64,
+			                        DBUS_GVALUE_ARRAY,
+			                        G_TYPE_INVALID);
+			dbus_g_proxy_connect_signal(proxy, "RunCallback",
+			                            G_CALLBACK(run_callback_cb),
+			                            klass->instance, NULL);
+		}
+	} else {
+		klass->instance = g_object_ref(klass->instance);
+	}
+
+	return klass->instance;
+}
+
+static void
+purple_dbus_callback_finalize(GObject *self)
+{
+	PurpleDBusCallbackClass *klass = NULL;
+
+	klass = g_type_class_peek(PURPLE_TYPE_DBUS_CALLBACK);
+	klass->instance = NULL;
+	g_hash_table_destroy(PURPLE_DBUS_CALLBACK(self)->callbacks);
+	G_OBJECT_CLASS(purple_dbus_callback_parent_class)->finalize(self);
+}
+
+static void
+purple_dbus_callback_class_init(PurpleDBusCallbackClass *klass)
+{
+	GObjectClass *gobject_class = G_OBJECT_CLASS(klass);
+
+	gobject_class->constructor  = purple_dbus_callback_constructor;
+	gobject_class->finalize     = purple_dbus_callback_finalize;
+	klass->instance = NULL;
+
+	/* dbus_glib_DBUS_purple_callback_object_info is defined in
+	 * dbus-callback-server.h, which is autogenerated. */
+	purple_object_type_install_dbus_infos(PURPLE_TYPE_DBUS_CALLBACK,
+	                          &dbus_glib_DBUS_purple_callback_object_info);
+
+	if (purple_core_is_daemon_mode()) {
+		/* Register the signals */
+		signals[SIG_DBUS_RUN_CALLBACK] =
+			g_signal_new("run_callback",
+			 G_OBJECT_CLASS_TYPE(klass),
+			 G_SIGNAL_RUN_LAST, 0, NULL, NULL,
+			 purple_smarshal_VOID__UINT64_BOXED,
+			 G_TYPE_NONE, 2, G_TYPE_UINT64, DBUS_GVALUE_ARRAY);
+	}
+}
+
+static void
+purple_dbus_callback_init(PurpleDBusCallback *self)
+{
+	self->last_id = 0;
+	self->callbacks = g_hash_table_new_full(g_int64_hash, g_int64_equal,
+	                              g_free, (GDestroyNotify)g_closure_unref);
+}
+
+guint64
+purple_dbus_callback_new_id(void)
+{
+	PurpleDBusCallback* handler = g_object_new(PURPLE_TYPE_DBUS_CALLBACK, NULL);
+	return ++handler->last_id;
+}
+
+/**
+ * Builds a n_args sized array of GValues, giving a alternate GType,typed arg
+ * list. E.g. build_args_list(2, G_TYPE_BOOLEAN, TRUE, G_TYPE_STRING, "foobar")
+ */
+static GPtrArray*
+build_args_list(unsigned int n_args, ...)
+{
+	GPtrArray *values;
+	GValue *val;
+	va_list ap;
+	gchar *err;
+	int i;
+
+	values = g_ptr_array_sized_new(n_args);
+	va_start(ap, n_args);
+	for (i = 0; i < n_args; i++) {
+		val = g_new0(GValue, 1);
+		G_VALUE_COLLECT_INIT(val, va_arg(ap, GType), ap, 0, &err);
+		if (err) {
+			g_warning("%s\n", err);
+			g_free(err);
+		}
+		g_ptr_array_add(values, val);
+	}
+
+	return values;
+}
+
+/**
+ * Callback, called when we receive a dbus "RunCallback" signal.
+ */
+static void
+run_callback_cb(DBusGProxy *proxy, const guint64 callback_id, GPtrArray *params, gpointer data)
+{
+	PurpleDBusCallback* handler = g_object_new(PURPLE_TYPE_DBUS_CALLBACK, NULL);
+	GClosure *closure;
+	GObject *obj;
+	GValue *values;
+	int i;
+
+	g_return_if_fail(handler != NULL);
+	g_return_if_fail(handler->callbacks != NULL);
+
+	closure = g_hash_table_lookup(handler->callbacks, &callback_id);
+	/* Not getting the pending callback is normal, this means this callback
+	 * were from another client. */
+	if (!closure)
+		return;
+
+	obj = purple_dbus_get_gobject_by_path(dbus_g_proxy_get_path(proxy));
+	g_return_if_fail(obj != NULL);
+
+	purple_debug_info("dbus", "Running callback %llu\n", callback_id, params->len);
+
+	/* Build the GValue array for the closure */
+	values = g_new0(GValue, params->len+1);
+	/* First parameter is the object */
+	g_value_init(&values[0], G_TYPE_OBJECT);
+	g_value_set_object(&values[0], obj);
+	/* Then the received parameters */
+	for (i = 1; i < params->len; i++)
+		memcpy(&values[i], params->pdata[i], sizeof(GValue));
+	g_closure_invoke(closure, NULL, params->len+1, values, NULL);
+
+	g_free(values);
+	/* The following frees the closure and the id */
+	g_hash_table_remove(handler->callbacks, &callback_id);
+}
+
+void
+purple_dbus_callback_register(guint64 id, GClosure *closure)
+{
+	PurpleDBusCallback* handler = g_object_new(PURPLE_TYPE_DBUS_CALLBACK, NULL);
+	guint64 *new_id;
+
+	g_return_if_fail(closure != NULL);
+	g_return_if_fail(handler != NULL);
+	g_return_if_fail(handler->callbacks != NULL);
+
+	new_id = g_new0(guint64, 1);
+	*new_id = id;
+	purple_debug_info("dbus", "Registering pending callback %llu!\n", *new_id);
+	g_hash_table_insert(handler->callbacks, new_id, closure);
+}
============================================================
--- /dev/null	
+++ libpurple/dbus-callback.h	88ebfb4d399c5499ed04c8f2b972035a8a258f59
@@ -0,0 +1,63 @@
+/* purple
+ *
+ * Purple is the legal property of its developers, whose names are too numerous
+ * to list here.  Please refer to the COPYRIGHT file distributed with this
+ * source distribution.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02111-1301  USA
+ */
+#ifndef _DBUS_CALLBACK_H_
+#define _DBUS_CALLBACK_H_
+
+#include "pobject.h"
+
+/* GObject definitions */
+
+#define PURPLE_TYPE_DBUS_CALLBACK       (purple_dbus_callback_get_type())
+#define PURPLE_DBUS_CALLBACK(obj)       (G_TYPE_CHECK_INSTANCE_CAST((obj), PURPLE_TYPE_DBUS_CALLBACK, PurpleDBusCallback))
+
+GType purple_dbus_callback_get_type(void);
+
+typedef struct {
+	PurpleObjectClass parent;
+	GObject *instance;
+} PurpleDBusCallbackClass;
+
+typedef struct {
+	PurpleObject parent;
+	GHashTable *callbacks; /* id -> GClosure map */
+	guint64 last_id;
+} PurpleDBusCallback;
+
+/* DBus transfered data formats */
+#define DBUS_GVALUE_ARRAY \
+	dbus_g_type_get_collection("GPtrArray",	G_TYPE_VALUE)
+
+/**
+ * Generates a new unique callback id. Only used in daemon mode.
+ *
+ * @return The new unique callback id.
+ */
+guint64 purple_dbus_callback_new_id(void);
+
+/**
+ * Registers a pending callback for future execution.
+ *
+ * @param id The callback id
+ * @param closure The callback, in a gclosure.
+ */
+void purple_dbus_callback_register(guint64 id, GClosure *closure);
+
+#endif /* _DBUS_CALLBACK_H_ */
============================================================
--- /dev/null	
+++ libpurple/dbus-prototypes/callback.xml	862322ae1693f45a8f2d60423fbcb6a8bf6b302e
@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<node name="/im/pidgin/purple/callback">
+	<interface name="im.pidgin.purple.callback">
+		<signal name="RunCallback">
+			<arg type="t"  name="callback_id" />
+			<arg type="av" name="args" />
+		</signal>
+	</interface>
+</node>


More information about the Commits mailing list