[gjs] Complete porting to GDBus



commit e5b6e9663f12541fd1b84cce56e187fa7993b180
Author: Giovanni Campagna <gcampagna src gnome org>
Date:   Sat Apr 23 17:38:12 2011 +0200

    Complete porting to GDBus
    
    Introduces a new introspected library, libgjs-gdbus, which hosts
    GjsDBusImplementation, a standalone, signal-driven GDBusInterfaceSkeleton.
    This is exposed, thanks to overrides, as Gio.DBusExportedObject, where
    it gains some JS code to expose normal JS objects on the bus
    and some glue to use E4X in place of XML strings for introspection.
    
    https://bugzilla.gnome.org/show_bug.cgi?id=622921

 Makefile-gjs-dbus.am         |   29 +++-
 Makefile.am                  |    8 +-
 configure.ac                 |    5 +
 gjs-dbus/gjs-gdbus-wrapper.c |  317 ++++++++++++++++++++++++++++++++++++++++++
 gjs-dbus/gjs-gdbus-wrapper.h |   60 ++++++++
 gjs/context.c                |    3 +
 modules/overrides/Gio.js     |  220 +++++++++++++++++++++++++++--
 7 files changed, 620 insertions(+), 22 deletions(-)
---
diff --git a/Makefile-gjs-dbus.am b/Makefile-gjs-dbus.am
index e2606f3..e4e8da2 100644
--- a/Makefile-gjs-dbus.am
+++ b/Makefile-gjs-dbus.am
@@ -1,5 +1,5 @@
 
-lib_LTLIBRARIES += libgjs-dbus.la
+lib_LTLIBRARIES += libgjs-dbus.la libgjs-gdbus.la
 
 gjs_dbusheaderdir=$(gjs_module_includedir)/gjs-dbus
 gjs_dbusheader_HEADERS =	\
@@ -13,19 +13,38 @@ libgjs_dbus_la_SOURCES =	\
 	gjs-dbus/dbus-proxy.c
 
 libgjs_dbus_la_CPPFLAGS =		\
-	$(AM_CPPFLAGS)				\
+	$(AM_CPPFLAGS)			\
 	-DG_LOG_DOMAIN=\"GjsDBus\"
 libgjs_dbus_la_CFLAGS = 		\
-	$(AM_CFLAGS)		\
+	$(AM_CFLAGS)			\
 	$(GJS_DBUS_CFLAGS)
 libgjs_dbus_la_LDFLAGS =		\
 	-no-undefined
 libgjs_dbus_la_LIBADD = 		\
-	libgjs.la					\
+	libgjs.la			\
 	$(GJS_DBUS_LIBS)
 
-########################################################################
 pkgconfig_DATA += gjs-dbus-1.0.pc
 
 EXTRA_DIST += 			\
 	gjs-dbus-1.0.pc.in
+
+######################################################################
+
+libgjs_gdbus_la_SOURCES = 	\
+	gjs-dbus/gjs-gdbus-wrapper.c	\
+	gjs-dbus/gjs-gdbus-wrapper.h
+
+libgjs_gdbus_la_CPPFLAGS = $(AM_CPPFLAGS) -DG_LOG_DOMAIN=\"GjsGDBus\"
+libgjs_gdbus_la_CFLAGS = $(AM_CFLAGS) $(GJS_GDBUS_CFLAGS)
+libgjs_gdbus_la_LIBADD = $(GJS_GDBUS_LIBS)
+
+-include $(INTROSPECTION_MAKEFILE)
+
+GjsDBus-1.0.gir: libgjs-gdbus.la
+GjsDBus_1_0_gir_LIBS = libgjs-gdbus.la
+GjsDBus_1_0_gir_INCLUDES = GObject-2.0 Gio-2.0
+GjsDBus_1_0_gir_FILES = $(libgjs_gdbus_la_SOURCES)
+GjsDBus_1_0_gir_SCANNERFLAGS = --identifier-prefix=GjsDBus --symbol-prefix=gjs_dbus --warn-all
+
+INTROSPECTION_GIRS += GjsDBus-1.0.gir
diff --git a/Makefile.am b/Makefile.am
index faf6d10..298fee2 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -13,6 +13,8 @@ EXTRA_DIST =
 gjstest_files_with_tests =
 TEST_PROGS =
 check_PROGRAMS = $(TEST_PROGS)
+INTROSPECTION_GIRS =
+ACLOCAL_AMFLAGS = ${ACLOCAL_FLAGS}
 
 gjsjsdir = @gjsjsdir@
 gjsoverridedir = $(gjsjsdir)/overrides
@@ -58,7 +60,8 @@ EXTRA_DIST += 			\
 gjs_directory_defines = 				\
 	-DGJS_TOP_SRCDIR=\"$(top_srcdir)\"		\
 	-DGJS_JS_DIR=\"$(gjsjsdir)\"			\
-	-DGJS_NATIVE_DIR=\"$(gjsnativedir)\"
+	-DGJS_NATIVE_DIR=\"$(gjsnativedir)\"		\
+	-DPKGLIBDIR=\"$(pkglibdir)\"
 
 ########################################################################
 lib_LTLIBRARIES += libgjs.la
@@ -163,6 +166,9 @@ endif
 include Makefile-gjs-dbus.am
 include Makefile-modules.am
 include Makefile-examples.am
+
+pkglib_DATA = $(INTROSPECTION_GIRS) $(INTROSPECTION_GIRS:.gir=.typelib)
+
 ########################################################################
 bin_PROGRAMS += gjs-console
 
diff --git a/configure.ac b/configure.ac
index ec5e164..ba40cec 100644
--- a/configure.ac
+++ b/configure.ac
@@ -217,11 +217,14 @@ else
 fi
 CFLAGS="$save_CFLAGS"
 
+GOBJECT_INTROSPECTION_CHECK([1.29.0])
+
 common_packages="gobject-2.0 >= gobject_required_version $JS_PACKAGE"
 gjs_packages="gmodule-2.0 gthread-2.0 gobject-introspection-1.0 >= 0.10.1 $common_packages"
 gjs_cairo_gobject_packages="cairo-gobject $common_packages"
 gjs_cairo_packages="cairo $common_packages"
 gjs_dbus_packages="dbus-glib-1 $common_packages"
+gjs_gdbus_packages="gobject-2.0 >= gobject_required_version gio-2.0"
 # gjs-tests links against everything
 gjstests_packages="$gjstests_packages $gjs_packages"
 
@@ -233,6 +236,8 @@ if test x${have_cairo_gobject} != xyes; then
   PKG_CHECK_MODULES([GJS_CAIRO], [$gjs_cairo_packages])
 fi
 PKG_CHECK_MODULES([GJS_DBUS], [$gjs_dbus_packages])
+PKG_CHECK_MODULES([GJS_GDBUS], [$gjs_gdbus_packages])
+
 saved_CFLAGS=$CFLAGS
 CFLAGS=$GJS_DBUS_CFLAGS
 saved_LIBS=$LIBS
diff --git a/gjs-dbus/gjs-gdbus-wrapper.c b/gjs-dbus/gjs-gdbus-wrapper.c
new file mode 100644
index 0000000..fc0692b
--- /dev/null
+++ b/gjs-dbus/gjs-gdbus-wrapper.c
@@ -0,0 +1,317 @@
+/* -*- mode: C; c-basic-offset: 4; indent-tabs-mode: nil; -*- */
+/* Copyright 2011 Giovanni Campagna. All Rights Reserved. */
+
+#include <config.h>
+#include <string.h>
+
+#include "gjs-gdbus-wrapper.h"
+
+enum {
+    PROP_0,
+    PROP_G_INTERFACE_INFO,
+    PROP_LAST
+};
+
+enum {
+    SIGNAL_HANDLE_METHOD,
+    SIGNAL_HANDLE_PROPERTY_GET,
+    SIGNAL_HANDLE_PROPERTY_SET,
+    SIGNAL_LAST,
+};
+
+static guint signals[SIGNAL_LAST];
+
+struct _GjsDBusImplementationPrivate {
+    GDBusInterfaceVTable  vtable;
+    GDBusInterfaceInfo   *ifaceinfo;
+
+    // from gchar* to GVariant*
+    GHashTable           *outstanding_properties;
+    guint                 idle_id;
+};
+
+G_DEFINE_TYPE(GjsDBusImplementation, gjs_dbus_implementation, G_TYPE_DBUS_INTERFACE_SKELETON)
+
+static void
+gjs_dbus_implementation_method_call(GDBusConnection       *connection,
+                                    const char            *sender,
+                                    const char            *object_path,
+                                    const char            *interface_name,
+                                    const char            *method_name,
+                                    GVariant              *parameters,
+                                    GDBusMethodInvocation *invocation,
+                                    gpointer               user_data)
+{
+    GjsDBusImplementation *self = GJS_DBUS_IMPLEMENTATION (user_data);
+
+    g_signal_emit(self, signals[SIGNAL_HANDLE_METHOD], 0, method_name, parameters, invocation);
+}
+
+static GVariant *
+gjs_dbus_implementation_property_get(GDBusConnection       *connection,
+                                     const char            *sender,
+                                     const char            *object_path,
+                                     const char            *interface_name,
+                                     const char            *property_name,
+                                     GError               **error,
+                                     gpointer               user_data)
+{
+    GjsDBusImplementation *self = GJS_DBUS_IMPLEMENTATION (user_data);
+    GVariant *value;
+
+    g_signal_emit(self, signals[SIGNAL_HANDLE_PROPERTY_GET], 0, property_name, &value);
+
+    /* Marshaling GErrors is not supported, so this is the best we can do
+       (GIO will assert if value is NULL and error is not set) */
+    if (!value)
+        g_set_error(error, g_quark_from_static_string("gjs-error-domain"), 0, "Property retrieval failed");
+
+    return value;
+}
+
+static gboolean
+gjs_dbus_implementation_property_set(GDBusConnection       *connection,
+                                     const char            *sender,
+                                     const char            *object_path,
+                                     const char            *interface_name,
+                                     const char            *property_name,
+                                     GVariant              *value,
+                                     GError               **error,
+                                     gpointer               user_data)
+{
+    GjsDBusImplementation *self = GJS_DBUS_IMPLEMENTATION (user_data);
+
+    g_signal_emit(self, signals[SIGNAL_HANDLE_PROPERTY_SET], 0, property_name, value);
+
+    return TRUE;
+}
+
+static void
+gjs_dbus_implementation_init(GjsDBusImplementation *self) {
+    GjsDBusImplementationPrivate *priv = G_TYPE_INSTANCE_GET_PRIVATE (self, GJS_TYPE_DBUS_IMPLEMENTATION, GjsDBusImplementationPrivate);
+
+    self->priv = priv;
+
+    priv->vtable.method_call = gjs_dbus_implementation_method_call;
+    priv->vtable.get_property = gjs_dbus_implementation_property_get;
+    priv->vtable.set_property = gjs_dbus_implementation_property_set;
+
+    priv->outstanding_properties = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, (GDestroyNotify)g_variant_unref);
+}
+
+static void
+gjs_dbus_implementation_finalize(GObject *object) {
+    GjsDBusImplementation *self = GJS_DBUS_IMPLEMENTATION (object);
+
+    g_dbus_interface_info_unref (self->priv->ifaceinfo);
+    g_hash_table_unref (self->priv->outstanding_properties);
+
+    G_OBJECT_CLASS(gjs_dbus_implementation_parent_class)->finalize(object);
+}
+
+static void
+gjs_dbus_implementation_set_property(GObject *object, guint property_id, const GValue *value, GParamSpec *pspec)
+{
+    GjsDBusImplementation *self = GJS_DBUS_IMPLEMENTATION (object);
+
+    switch (property_id) {
+    case PROP_G_INTERFACE_INFO:
+        self->priv->ifaceinfo = g_value_dup_boxed (value);
+        break;
+    default:
+        G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+    }
+}
+
+static GDBusInterfaceInfo *
+gjs_dbus_implementation_get_info (GDBusInterfaceSkeleton *skeleton) {
+    GjsDBusImplementation *self = GJS_DBUS_IMPLEMENTATION (skeleton);
+
+    return self->priv->ifaceinfo;
+}
+
+static GDBusInterfaceVTable *
+gjs_dbus_implementation_get_vtable (GDBusInterfaceSkeleton *skeleton) {
+    GjsDBusImplementation *self = GJS_DBUS_IMPLEMENTATION (skeleton);
+
+    return &(self->priv->vtable);
+}
+
+static GVariant *
+gjs_dbus_implementation_get_properties (GDBusInterfaceSkeleton *skeleton) {
+    GjsDBusImplementation *self = GJS_DBUS_IMPLEMENTATION (skeleton);
+
+    GDBusInterfaceInfo *info = self->priv->ifaceinfo;
+    GDBusPropertyInfo **props;
+    GVariantBuilder builder;
+
+    g_variant_builder_init(&builder, G_VARIANT_TYPE_VARDICT);
+
+    for (props = info->properties; *props; ++props) {
+        GDBusPropertyInfo *prop = *props;
+        GVariant *value;
+
+        /* If we have a cached value, we use that instead of querying again */
+        if ((value = g_hash_table_lookup(self->priv->outstanding_properties, prop->name))) {
+            g_variant_builder_add(&builder, "{sv}", prop->name, value);
+            continue;
+        }
+
+        g_signal_emit(self, signals[SIGNAL_HANDLE_PROPERTY_GET], 0, prop->name, &value);
+        g_variant_builder_add(&builder, "{sv}", prop->name, value);
+    }
+
+    return g_variant_builder_end(&builder);
+}
+
+static void
+gjs_dbus_implementation_flush (GDBusInterfaceSkeleton *skeleton) {
+    GjsDBusImplementation *self = GJS_DBUS_IMPLEMENTATION (skeleton);
+
+    GVariantBuilder changed_props;
+    GVariantBuilder invalidated_props;
+    GHashTableIter iter;
+    GVariant *val;
+    gchar *prop_name;
+
+    g_variant_builder_init(&changed_props, G_VARIANT_TYPE_VARDICT);
+    g_variant_builder_init(&invalidated_props, G_VARIANT_TYPE_STRING_ARRAY);
+
+    g_hash_table_iter_init(&iter, self->priv->outstanding_properties);
+    while (g_hash_table_iter_next(&iter, (void**) &prop_name, (void**) &val)) {
+        if (val)
+            g_variant_builder_add(&changed_props, "{sv}", prop_name, val);
+        else
+            g_variant_builder_add(&invalidated_props, "s", prop_name);
+    }
+
+    g_dbus_connection_emit_signal(g_dbus_interface_skeleton_get_connection(skeleton),
+                                  NULL, /* bus name */
+                                  g_dbus_interface_skeleton_get_object_path(skeleton),
+                                  "org.freedesktop.DBus.Properties",
+                                  "PropertiesChanged",
+                                  g_variant_new("(s a{sv}@as)",
+                                                self->priv->ifaceinfo->name,
+                                                g_variant_builder_end(&changed_props),
+                                                g_variant_builder_end(&invalidated_props)),
+                                   NULL /* error */);
+
+    g_hash_table_remove_all(self->priv->outstanding_properties);
+    if (self->priv->idle_id) {
+        g_source_remove(self->priv->idle_id);
+        self->priv->idle_id = 0;
+    }
+}
+
+void
+gjs_dbus_implementation_class_init(GjsDBusImplementationClass *klass) {
+    GObjectClass *gobject_class = G_OBJECT_CLASS(klass);
+    GDBusInterfaceSkeletonClass *skeleton_class = G_DBUS_INTERFACE_SKELETON_CLASS(klass);
+
+    g_type_class_add_private(klass, sizeof(GjsDBusImplementationPrivate));
+
+    gobject_class->finalize = gjs_dbus_implementation_finalize;
+    gobject_class->set_property = gjs_dbus_implementation_set_property;
+
+    skeleton_class->get_info = gjs_dbus_implementation_get_info;
+    skeleton_class->get_vtable = gjs_dbus_implementation_get_vtable;
+    skeleton_class->get_properties = gjs_dbus_implementation_get_properties;
+    skeleton_class->flush = gjs_dbus_implementation_flush;
+
+    g_object_class_install_property(gobject_class, PROP_G_INTERFACE_INFO,
+                                    g_param_spec_boxed("g-interface-info",
+                                                       "Interface Info",
+                                                       "A DBusInterfaceInfo representing the exported object",
+                                                       G_TYPE_DBUS_INTERFACE_INFO,
+                                                       G_PARAM_STATIC_STRINGS | G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY));
+
+    signals[SIGNAL_HANDLE_METHOD] = g_signal_new("handle-method-call",
+                                                 G_TYPE_FROM_CLASS(klass),
+                                                 0, /* flags */
+                                                 0, /* closure */
+                                                 NULL, /* accumulator */
+                                                 NULL, /* accumulator data */
+                                                 NULL, /* C marshal */
+                                                 G_TYPE_NONE,
+                                                 3,
+                                                 G_TYPE_STRING, /* method name */
+                                                 G_TYPE_VARIANT, /* parameters */
+                                                 G_TYPE_DBUS_METHOD_INVOCATION);
+
+    signals[SIGNAL_HANDLE_PROPERTY_GET] = g_signal_new("handle-property-get",
+                                                       G_TYPE_FROM_CLASS(klass),
+                                                       0, /* flags */
+                                                       0, /* closure */
+                                                       g_signal_accumulator_first_wins,
+                                                       NULL, /* accumulator data */
+                                                       NULL, /* C marshal */
+                                                       G_TYPE_VARIANT,
+                                                       1,
+                                                       G_TYPE_STRING /* property name */);
+
+
+    signals[SIGNAL_HANDLE_PROPERTY_SET] = g_signal_new("handle-property-set",
+                                                       G_TYPE_FROM_CLASS(klass),
+                                                       0, /* flags */
+                                                       0, /* closure */
+                                                       NULL, /* accumulator */
+                                                       NULL, /* accumulator data */
+                                                       NULL, /* C marshal */
+                                                       G_TYPE_NONE,
+                                                       2,
+                                                       G_TYPE_STRING, /* property name */
+                                                       G_TYPE_VARIANT /* parameters */);
+}
+
+static gboolean
+idle_cb (gpointer data) {
+    GDBusInterfaceSkeleton *skeleton = G_DBUS_INTERFACE_SKELETON (data);
+
+    g_dbus_interface_skeleton_flush(skeleton);
+    return FALSE;
+}
+
+/**
+ * gjs_dbus_implementation_emit_property_changed:
+ * @self: a #GjsDBusImplementation
+ * @property: the name of the property that changed
+ * @newvalue: (allow-none): the new value, or %NULL to just invalidate it
+ *
+ * Queue a PropertyChanged signal for emission, or update the one queued
+ * adding @property
+ */
+void
+gjs_dbus_implementation_emit_property_changed (GjsDBusImplementation *self,
+                                               gchar                 *property,
+                                               GVariant              *newvalue)
+{
+    g_hash_table_replace (self->priv->outstanding_properties, g_strdup (property), g_variant_ref (newvalue));
+
+    if (!self->priv->idle_id)
+        self->priv->idle_id = g_idle_add(idle_cb, self);
+}
+
+/**
+ * gjs_dbus_implementation_emit_signal:
+ * @self: a #GjsDBusImplementation
+ * @signal_name: the name of the signal
+ * @parameters: (allow-none): signal parameters, or %NULL for none
+ *
+ * Emits a signal named @signal_name from the object and interface represented
+ * by @self. This signal has no destination.
+ */
+void
+gjs_dbus_implementation_emit_signal (GjsDBusImplementation *self,
+                                     gchar                 *signal_name,
+                                     GVariant              *parameters)
+{
+    GDBusInterfaceSkeleton *skeleton = G_DBUS_INTERFACE_SKELETON (self);
+
+    g_dbus_connection_emit_signal(g_dbus_interface_skeleton_get_connection(skeleton),
+                                  NULL,
+                                  g_dbus_interface_skeleton_get_object_path(skeleton),
+                                  self->priv->ifaceinfo->name,
+                                  signal_name,
+                                  parameters,
+                                  NULL);
+}
diff --git a/gjs-dbus/gjs-gdbus-wrapper.h b/gjs-dbus/gjs-gdbus-wrapper.h
new file mode 100644
index 0000000..949ab0e
--- /dev/null
+++ b/gjs-dbus/gjs-gdbus-wrapper.h
@@ -0,0 +1,60 @@
+/* -*- mode: C; c-basic-offset: 4; indent-tabs-mode: nil; -*- */
+/* Copyright 2011 Giovanni Campagna
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ */
+
+#ifndef __GJS_UTIL_DBUS_H__
+#define __GJS_UTIL_DBUS_H__
+
+#include <glib.h>
+#include <glib-object.h>
+#include <gio/gio.h>
+
+G_BEGIN_DECLS
+
+typedef struct _GjsDBusImplementation        GjsDBusImplementation;
+typedef struct _GjsDBusImplementationClass   GjsDBusImplementationClass;
+typedef struct _GjsDBusImplementationPrivate GjsDBusImplementationPrivate;
+
+#define GJS_TYPE_DBUS_IMPLEMENTATION              (gjs_dbus_implementation_get_type ())
+#define GJS_DBUS_IMPLEMENTATION(object)           (G_TYPE_CHECK_INSTANCE_CAST ((object), GJS_TYPE_DBUS_IMPLEMENTATION, GjsDBusImplementation))
+#define GJS_DBUS_IMPLEMENTATION_CLASS(klass)      (G_TYPE_CHECK_CLASS_CAST ((klass), GJS_TYPE_DBUS_IMPLEMENTATION, GjsDBusImplementationClass))
+#define GJS_IS_DBUS_IMPLEMENTATION(object)        (G_TYPE_CHECK_INSTANCE_TYPE ((object), GJS_TYPE_DBUS_IMPLEMENTATION))
+#define GJS_IS_DBUS_IMPLEMENTATION_CLASS(klass)   (G_TYPE_CHECK_CLASS_TYPE ((klass), GJS_TYPE_DBUS_IMPLEMENTATION))
+#define GJS_DBUS_IMPLEMENTATION_GET_CLASS(obj)    (G_TYPE_INSTANCE_GET_CLASS ((obj), GJS_TYPE_DBUS_IMPLEMENTATION, GjsDBusImplementationClass))
+
+struct _GjsDBusImplementation {
+    GDBusInterfaceSkeleton parent;
+
+    GjsDBusImplementationPrivate *priv;
+};
+
+struct _GjsDBusImplementationClass {
+    GDBusInterfaceSkeletonClass parent_class;
+};
+
+GType                  gjs_dbus_implementation_get_type (void);
+
+void                   gjs_dbus_implementation_emit_property_changed (GjsDBusImplementation *self, gchar *property, GVariant *newvalue);
+void                   gjs_dbus_implementation_emit_signal           (GjsDBusImplementation *self, gchar *signal_name, GVariant *parameters);
+
+G_END_DECLS
+
+#endif  /* __GJS_UTIL_DBUS_H__ */
diff --git a/gjs/context.c b/gjs/context.c
index 377ea61..8697fec 100644
--- a/gjs/context.c
+++ b/gjs/context.c
@@ -671,6 +671,9 @@ gjs_context_constructor (GType                  type,
     if (!gjs_is_registered_native_module(js_context->context, NULL, "gi"))
         gjs_register_native_module("gi", gjs_define_gi_stuff, 0);
 
+    /* For GjsDBus */
+    g_irepository_prepend_search_path(PKGLIBDIR);
+
     JS_EndRequest(js_context->context);
 
     g_static_mutex_lock (&contexts_lock);
diff --git a/modules/overrides/Gio.js b/modules/overrides/Gio.js
index 261f559..1fbdc63 100644
--- a/modules/overrides/Gio.js
+++ b/modules/overrides/Gio.js
@@ -20,6 +20,8 @@
 // IN THE SOFTWARE.
 
 var GLib = imports.gi.GLib;
+var GjsDBus = imports.gi.GjsDBus;
+var Lang = imports.lang;
 var Signals = imports.signals;
 var Gio;
 
@@ -93,18 +95,18 @@ function _proxyInvoker(methodName, sync, inSignature, arg_array) {
 			      -1,
 			      cancellable).deep_unpack();
     } else {
-	return this.call_async(methodName,
-			       inVariant,
-			       flags,
-			       -1,
-			       cancellable,
-			       asyncCallback);
+	return this.call(methodName,
+			 inVariant,
+			 flags,
+			 -1,
+			 cancellable,
+			 asyncCallback);
     }
 }
 
 function _logReply(result, exc) {
     if (result != null) {
-        log("Ignored reply to dbus method: " + result.toSource());
+        log("Ignored reply to dbus method: " + result.deep_unpack().toSource());
     }
     if (exc != null) {
         log("Ignored exception from dbus method: " + exc.toString());
@@ -126,23 +128,113 @@ function _makeProxyMethod(method, sync) {
 }
 
 function _convertToNativeSignal(proxy, sender_name, signal_name, parameters) {
-    Signals._emit.call(this, signal_name, sender_name, GLib.Variant.deep_unpack(paramters));
+    Signals._emit.call(proxy, signal_name, sender_name, parameters.deep_unpack());
+}
+
+function _propertyGetter(name) {
+    let value = this.get_cached_property(name);
+    return value ? value.deep_unpack() : null;
+}
+
+function _propertySetter(value, name, signature) {
+    let variant = new GLib.Variant(signature, value);
+    proxy.set_cached_property(name, variant);
+
+    this.call('org.freedesktop.DBus.Properties.Set',
+	      GLib.Variant.new('(ssv)',
+			       this.g_interface_name,
+			       name, variant),
+	      Gio.DBusCallFlags.NONE, -1, null,
+	      function(proxy, result) {
+		  try {
+		      proxy.call_finish(result);
+		  } catch(e) {
+		      log('Could not set property ' + name + ' on remote object ' +
+			  proxy.g_object_path, '. Error is ' + e.message);
+		  }
+	      });
 }
 
 function _addDBusConvenience() {
-    var info = this.g_interface_info;
+    let info = this.g_interface_info;
     if (!info)
 	return;
 
     if (info.signals.length > 0)
-	this.connect('g-signal', Lang.bind(this, _convertToNativeSignal));
+	this.connect('g-signal', _convertToNativeSignal);
 
-    var i, methods = info.methods;
+    let i, methods = info.methods;
     for (i = 0; i < methods.length; i++) {
 	var method = methods[i];
 	this[method.name + 'Remote'] = _makeProxyMethod(methods[i], false);
 	this[method.name + 'Sync'] = _makeProxyMethod(methods[i], true);
     }
+
+    let properties = info.properties;
+    for (i = 0; i < properties.length; i++) {
+	let name = properties[i].name;
+	let signature = properties[i].signature;
+	Lang.defineAccessorProperty(this, name,
+				    Lang.bind(this, _propertyGetter, name),
+				    Lang.bind(this, _propertySetter, name, signature));
+    }
+}
+
+function _makeProxyWrapper(interfaceXml) {
+    var info = _newInterfaceInfo(interfaceXml);
+    var iname = info.name;
+    return function(bus, name, object, asyncCallback, cancellable) {
+	var obj = new Gio.DBusProxy({ g_connection: bus,
+				      g_interface_name: iname,
+				      g_interface_info: info,
+				      g_name: name,
+				      g_object_path: object });
+	if (!cancellable)
+	    cancellable = null;
+	if (asyncCallback)
+	    obj.init_async(GLib.PRIORITY_DEFAULT, cancellable, function(initable, result) {
+		try {
+		    initable.init_finish(result);
+		    asyncCallback(initable, null);
+		} catch(e) {
+		    asyncCallback(null, e);
+		}
+	    });
+	else
+	    obj.init(cancellable);
+	return obj;
+    };
+}
+
+
+function _newNodeInfo(constructor, value) {
+    if (typeof value == 'string')
+	return constructor(value);
+    else if (value instanceof XML)
+	return constructor(value.toXMLString());
+    else
+	throw TypeError('Invalid type ' + Object.prototype.toString.call(value));
+}
+
+function _newInterfaceInfo(value) {
+    var xml;
+    if (typeof value == 'string')
+	xml = new XML(value);
+    else if (value instanceof XML)
+	xml = value;
+    else
+	throw TypeError('Invalid type ' + Object.prototype.toString.call(value));
+
+    var node;
+    if (value.name() == 'interface') {
+	// wrap inside a node
+	node = <node/>;
+	node.node += xml;
+    } else
+	node = xml;
+
+    var nodeInfo = Gio.DBusNodeInfo.new_for_xml(node);
+    return nodeInfo.interfaces[0];
 }
 
 function _injectToMethod(klass, method, addition) {
@@ -154,7 +246,94 @@ function _injectToMethod(klass, method, addition) {
     }
 }
 
- function __init__() {
+function _wrapFunction(klass, method, addition) {
+    var previous = klass[method];
+
+    klass[method] = function() {
+	var args = Array.prototype.slice.call(arguments);
+	args.unshift(previous);
+	return addition.apply(this, args);
+    }
+}
+
+function _makeOutSignature(args) {
+    var ret = '(';
+    for (var i = 0; i < args.length; i++)
+	ret += args[i].signature;
+
+    return ret + ')';
+}
+
+function _wrapJSObject(interfaceInfo, jsObj) {
+    var info;
+    if (interfaceInfo instanceof Gio.DBusInterfaceInfo)
+	info = interfaceInfo
+    else
+	info = Gio.DBusInterfaceInfo.new_for_xml(interfaceInfo);
+    info.cache_build();
+
+    var impl = new GjsDBus.Implementation({ g_interface_info: info });
+    impl.connect('handle-method-call', function(impl, method_name, parameters, invocation) {
+	// prefer a sync version if available
+	if (jsObj[method_name]) {
+	    var retval;
+	    try {
+		retval = jsObj[method_name].apply(jsObj, parameters.deep_unpack());
+	    } catch (e) {
+		if (e.name.indexOf('.') == -1) {
+		    // likely to be a normal JS error
+		    e.name = 'org.gnome.gjs.JSError.' + e.name;
+		}
+		invocation.return_dbus_error(e.name, e.message);
+		return;
+	    }
+	    if (retval === undefined) {
+		// undefined (no return value) is the empty tuple
+		retval = GLib.Variant.new_tuple([], 0);
+	    }
+	    try {
+		if (!(retval instanceof GLib.Variant)) {
+		    // attemp packing according to out signature
+		    var methodInfo = info.lookup_method(method_name);
+		    var outArgs = methodInfo.out_args;
+		    var outSignature = _makeOutSignature(outArgs);
+		    if (outArgs.length == 1) {
+			// if one arg, we don't require the handler wrapping it
+			// into an Array
+			retval = [retval];
+		    }
+		    retval = GLib.Variant.new(outSignature, retval);
+		}
+		invocation.return_value(retval);
+	    } catch(e) {
+		// if we don't do this, the other side will never see a reply
+		invocation.return_dbus_error('org.gnome.gjs.JSError.ValueError',
+					     "The return value from the method handler was not in the correct format");
+	    }
+	} else if (jsObj[method_name + 'Async']) {
+	    jsObj[method_name + 'Async'](parameters.deep_unpack(), invocation);
+	} else {
+	    log('Missing handler for DBus method ' + method_name);
+	    invocation.return_dbus_error('org.gnome.gjs.NotImplementedError',
+					 "Method ' + method_name + ' is not implemented");
+	}
+    });
+    impl.connect('handle-property-get', function(impl, property_name) {
+	var propInfo = info.lookup_property(property_name);
+	var jsval = jsObj[property_name];
+	if (jsval != undefined)
+	    return GLib.Variant.new(propInfo.signature, jsval);
+	else
+	    return null;
+    });
+    impl.connect('handle-property-set', function(impl, property_name, new_value) {
+	jsObj[property_name] = new_value.deep_unpack();
+    });
+
+    return impl;
+}
+
+function _init() {
     Gio = this;
 
     Gio.DBus = {
@@ -190,13 +369,22 @@ function _injectToMethod(klass, method, addition) {
 	return Gio.bus_own_name_on_connection(this, name, flags, acquired, lost);
     };
     Gio.DBusConnection.prototype.unown_name = function(id) {
-	return Gio.unown_name(id);
+	return Gio.bus_unown_name(id);
     };
 
     // This should be done inside a constructor, but it cannot currently
-    
     _injectToMethod(Gio.DBusProxy.prototype, 'init', _addDBusConvenience);
-    _injectToMethod(Gio.DBusProxy.prototype, 'async_init', _addDBusConvenience);
+    _injectToMethod(Gio.DBusProxy.prototype, 'init_async', _addDBusConvenience);
     Gio.DBusProxy.prototype.connectSignal = Signals._connect;
     Gio.DBusProxy.prototype.disconnectSignal = Signals._disconnect;
- }
+
+    Gio.DBusProxy.makeProxyWrapper = _makeProxyWrapper;
+
+    // Some helpers
+    _wrapFunction(Gio.DBusNodeInfo, 'new_for_xml', _newNodeInfo);
+    Gio.DBusInterfaceInfo.new_for_xml = _newInterfaceInfo;
+
+    // More or less...
+    Gio.DBusExportedObject = GjsDBus.Implementation;
+    Gio.DBusExportedObject.wrapJSObject = _wrapJSObject;
+}



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]