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