libtelepathy/src/tp-props-iface.c
changeset 0 d0f3a028347a
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libtelepathy/src/tp-props-iface.c	Tue Feb 02 01:10:06 2010 +0200
@@ -0,0 +1,653 @@
+/* tp-props-iface.c
+ *
+ * Copyright (C) 2005 Collabora Ltd.
+ * 
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#include <dbus/dbus-glib.h>
+#include <string.h>
+#include "tp-interfaces.h"
+#include "tp-ifaces-signals-marshal.h"
+#include "tp-props-iface-gen.h"
+#include "tp-props-iface.h"
+
+#ifdef EMULATOR
+#include "libtelepathy_wsd_solution.h"
+#endif
+
+
+#define TP_TYPE_PROPERTY_DESCRIPTION (dbus_g_type_get_struct ("GValueArray", \
+      G_TYPE_UINT, \
+      G_TYPE_STRING, \
+      G_TYPE_STRING, \
+      G_TYPE_UINT, \
+      G_TYPE_INVALID))
+
+#define TP_TYPE_PROPERTY_CHANGE (dbus_g_type_get_struct ("GValueArray", \
+      G_TYPE_UINT, \
+      G_TYPE_VALUE, \
+      G_TYPE_INVALID))
+
+#define TP_TYPE_PROPERTY_FLAGS_CHANGE (dbus_g_type_get_struct ("GValueArray", \
+      G_TYPE_UINT, \
+      G_TYPE_UINT, \
+      G_TYPE_INVALID))
+
+
+
+
+/*signal enum*/
+enum
+{
+  PROPERTIES_READY,
+  PROPERTY_CHANGED,
+  LAST_SIGNAL 
+#ifdef EMULATOR  
+  = LAST_SIGNAL_TP_PROPS_IFACE
+#endif
+  
+};
+
+#ifndef EMULATOR
+	static guint signals[LAST_SIGNAL] = {0};
+#endif
+
+/* looking up properties is linear time on the grounds that number of properties
+ * will always be small, so this will be more cache-friendly
+ */
+typedef struct _PropertyMapping PropertyMapping;
+struct _PropertyMapping
+{
+  guint user_id;
+  guint32 server_id;
+  gchar *name;
+  GValue *value;
+  guint32 flags;
+};
+
+typedef struct _TpPropsPrivate TpPropsPrivate;
+
+struct _TpPropsPrivate
+{
+  gboolean properties_ready;
+
+  int mappings_len;
+  PropertyMapping *mappings;
+};
+
+#ifndef EMULATOR
+	static GObjectClass *parent_class = NULL;
+#endif
+
+
+#ifdef EMULATOR
+	
+	GET_STATIC_ARRAY_FROM_TLS(signals,tp_props_iface,guint)
+	#define signals (GET_WSD_VAR_NAME(signals,tp_props_iface, s)())	
+	
+	GET_STATIC_VAR_FROM_TLS(parent_class,tp_props_iface,GObjectClass *)
+	#define parent_class (*GET_WSD_VAR_NAME(parent_class,tp_props_iface,s)())
+	
+	GET_STATIC_VAR_FROM_TLS(type1,tp_props_iface,GType)
+	#define type1 (*GET_WSD_VAR_NAME(type1,tp_props_iface,s)())
+	
+	GET_STATIC_VAR_FROM_TLS(ret,tp_props_iface,GQuark)
+	#define ret (*GET_WSD_VAR_NAME(ret,tp_props_iface,s)())
+	
+#endif
+	
+
+#define PRIV(o) ((TpPropsPrivate*)(o->priv))
+
+static void properties_listed_cb (DBusGProxy *proxy, GPtrArray *properties, GError *error, gpointer user_data);
+
+static void tp_props_iface_init(GTypeInstance *instance, gpointer g_class)
+{
+  TpPropsIface *self = TELEPATHY_PROPS_IFACE(instance);
+
+  self->priv = G_TYPE_INSTANCE_GET_PRIVATE ((self),
+                 TELEPATHY_PROPS_IFACE_TYPE, TpPropsPrivate);
+
+}
+
+static GObject *
+tp_props_iface_constructor (GType type, guint n_props,
+                             GObjectConstructParam *props)
+{
+  GObject *obj;
+
+  obj = G_OBJECT_CLASS (parent_class)->
+           constructor (type, n_props, props);
+
+  dbus_g_proxy_add_signal(DBUS_G_PROXY(obj), "PropertiesChanged",
+      dbus_g_type_get_collection ("GPtrArray", TP_TYPE_PROPERTY_CHANGE),
+                                  G_TYPE_INVALID);
+  dbus_g_proxy_add_signal(DBUS_G_PROXY(obj), "PropertyFlagsChanged",
+      dbus_g_type_get_collection ("GPtrArray", TP_TYPE_PROPERTY_FLAGS_CHANGE),
+                                  G_TYPE_INVALID);
+
+  return obj;
+}
+
+static void tp_props_iface_dispose(GObject *obj)
+{
+
+  /* Call parent class dispose method */
+  if (G_OBJECT_CLASS(parent_class)->dispose)
+  {
+    G_OBJECT_CLASS(parent_class)->dispose(obj);
+  }
+
+}
+
+
+static void tp_props_iface_finalize(GObject *obj)
+{
+  TpPropsIface *self = TELEPATHY_PROPS_IFACE(obj);
+  int i;
+  for (i=0; i < PRIV(self)->mappings_len; i++)
+    {
+      if (PRIV(self)->mappings[i].value)
+        {
+          g_value_unset (PRIV(self)->mappings[i].value);
+          g_free (PRIV(self)->mappings[i].value);
+        }
+      if (PRIV(self)->mappings[i].name)
+        g_free (PRIV(self)->mappings[i].name);
+    }
+  
+  g_free (PRIV(self)->mappings);
+
+  if (G_OBJECT_CLASS(parent_class)->finalize)
+    {
+      G_OBJECT_CLASS(parent_class)->finalize(obj);
+    }
+}
+
+
+static void tp_props_iface_class_init(TpPropsIfaceClass *klass)
+{
+  GObjectClass *obj = G_OBJECT_CLASS(klass);
+  parent_class = g_type_class_peek_parent(klass);
+
+  obj->set_property = parent_class->set_property;
+  obj->get_property = parent_class->get_property;
+
+  obj->constructor = tp_props_iface_constructor;
+  obj->dispose = tp_props_iface_dispose;
+  obj->finalize = tp_props_iface_finalize;
+
+  g_type_class_add_private (klass, sizeof (TpPropsPrivate));
+  /**
+   * TpPropsIface::properties-ready:
+   * @self: #TpPropsIface that emmitted the signal
+   * @property_id: property that changed
+   * @change_flags: #TpPropsChanged for what changed on the property
+   *
+   * This signal is emitted when the properties 1st become avaible for
+   * reading or writing.
+   */
+
+  signals[PROPERTIES_READY] =
+  g_signal_new ("properties-ready",
+                G_OBJECT_CLASS_TYPE (klass),
+                G_SIGNAL_RUN_LAST | G_SIGNAL_DETAILED,
+                0,
+                NULL, NULL,
+                g_cclosure_marshal_VOID__VOID,
+                G_TYPE_NONE, 0);
+
+
+
+  /**
+   * TpPropsIface::properties-changed:
+   * @self: #TpPropsIface that emmitted the signal
+   * @property_id: property that changed
+   * @change_flags: #TpPropsChanged for what changed on the property
+   *
+   * This signal is emitted when a property changes.
+   */
+
+  signals[PROPERTY_CHANGED] =
+  g_signal_new ("properties-changed",
+                G_OBJECT_CLASS_TYPE (klass),
+                G_SIGNAL_RUN_LAST | G_SIGNAL_DETAILED,
+                0,
+                NULL, NULL,
+                tp_ifaces_signals_marshal_VOID__UINT_UINT,
+                G_TYPE_NONE, 2, G_TYPE_UINT, G_TYPE_UINT);
+
+
+  /* register marshaller for  PropertiesChanged and PropertyFlagsChanged*/
+  dbus_g_object_register_marshaller(tp_ifaces_signals_marshal_VOID__UINT_UINT_UINT, G_TYPE_NONE, G_TYPE_UINT, G_TYPE_UINT, G_TYPE_UINT, G_TYPE_INVALID);
+
+}
+
+#ifdef SYMBIAN
+EXPORT_C
+#endif
+GType tp_props_iface_get_type(void)
+{
+#ifndef EMULATOR
+  static GType type1 = 0;
+#endif
+  
+  if (type1 == 0)
+    {
+      static const GTypeInfo info =
+        {
+          sizeof(TpPropsIfaceClass),
+          NULL,
+          NULL,
+          (GClassInitFunc)tp_props_iface_class_init,
+          NULL,
+          NULL,
+          sizeof(TpPropsIface),
+          0,
+          (GInstanceInitFunc)tp_props_iface_init
+        };
+      type1 = g_type_register_static(DBUS_TYPE_G_PROXY,
+                                    "TpPropsIface", &info, 0);
+    }
+  return type1;
+}
+
+/* The interface name getters */
+#ifdef SYMBIAN
+EXPORT_C
+#endif
+GQuark
+tp_get_props_interface (void)
+{
+#ifndef EMULATOR
+  static GQuark ret = 0;
+#endif
+
+  if (ret == 0)
+  {
+    ret = g_quark_from_static_string(TP_IFACE_PROPERTIES);
+  }
+
+  return ret;
+}
+
+TpPropsIface *
+tp_props_iface_new (DBusGConnection *connection,
+                   const char      *name,
+                   const char      *path_name)
+{
+  /* The properties are order dependant in dbus <= 0.61. Thanks dbus*/
+  return g_object_new (TELEPATHY_PROPS_IFACE_TYPE,
+                       "name", name,
+                       "path", path_name,
+                       "interface",TP_IFACE_PROPERTIES,
+                       "connection", connection,
+                       NULL);
+}
+
+static void properties_changed_cb (DBusGProxy *proxy, GPtrArray *properties, gpointer user_data);
+static void property_flags_changed_cb (DBusGProxy *proxy, GPtrArray *properties, gpointer user_data);
+
+/**
+ * tp_props_iface_set_mapping:
+ * @iface: #TpPropsIface on which to set mapping
+ * @first_name: First name in list to set a mapping for
+ *
+ * Set a mapping between propery names and your chosen ID's for these
+ * names. Takes a list of property name, id pairs, terminated by NULL.
+ *
+ * Typically the user will define an enum of properties that they're
+ * interested in, and set a mapping like:
+ *   tp_props_iface_set_mapping (props, "foo", FOO,
+ *                                     "bar", BAR,
+ *                                     "baz", BAZ,
+ *                                     NULL);
+ * the user should bind to the
+ * <link linkend="TpPropsIface-properties-ready">properties-ready signal</link>
+ * before calling this. Property queries will only be possible *after* this
+ * signal has been emitted.
+ */
+void tp_props_iface_set_mapping (TpPropsIface *self,
+                                const gchar *first_property_name,
+                                ...)
+{
+  va_list var_args;
+  const gchar *name = first_property_name;
+  guint id;
+  GArray *array;
+  PropertyMapping map = {0,0,NULL,NULL,0};
+
+  g_return_if_fail (TELEPATHY_IS_PROPS_IFACE (self));
+  g_return_if_fail (PRIV(self)->mappings == NULL);
+
+  va_start (var_args, first_property_name);
+
+  array = g_array_new (FALSE, FALSE, sizeof (PropertyMapping));
+
+  while (name)
+    {
+      id = va_arg (var_args, guint);
+      map.user_id = id;
+      map.name = g_strdup (name);
+      g_array_append_val (array, map);
+      name = va_arg (var_args, gchar *);
+    }
+
+  va_end (var_args);
+
+  PRIV (self)->mappings_len = array->len;
+  PRIV (self)->mappings = (PropertyMapping*) g_array_free (array, FALSE);
+
+  dbus_g_proxy_connect_signal (DBUS_G_PROXY (self), "PropertiesChanged",
+                               G_CALLBACK(properties_changed_cb), self, NULL);
+  dbus_g_proxy_connect_signal (DBUS_G_PROXY (self), "PropertyFlagsChanged",
+                               G_CALLBACK(property_flags_changed_cb),
+                               self, NULL);
+
+  tp_props_iface_list_properties_async (DBUS_G_PROXY (self),
+                                        properties_listed_cb, self);
+}
+
+/**
+ * tp_props_iface_get_value:
+ * @self: #TpPropsIface on which to get a property value
+ * @prop_id: Identifier for property as set in #tp_props_iface_set_mapping
+ * @value: GValue to return the property's value in.
+ *
+ * Get the value of a property on this interface
+ */
+gboolean tp_props_iface_get_value (TpPropsIface* self, guint prop_id,
+                                  GValue *return_value)
+{
+  int i;
+
+  if (!PRIV (self)->properties_ready)
+    return FALSE;
+
+  for (i = 0; i < PRIV (self)->mappings_len; i++)
+    {
+      if (PRIV (self)->mappings[i].user_id == prop_id)
+        {
+          if (PRIV (self)->mappings[i].value)
+            {
+              g_value_copy (PRIV (self)->mappings[i].value, return_value);
+              return TRUE;
+            }
+          else
+            {
+              return FALSE;
+            }
+        }
+    }
+
+  return FALSE;
+}
+
+/* dummy callback handler for async calling calls with no return values */
+static void
+dummy_callback (DBusGProxy *proxy, GError *error, gpointer user_data)
+{
+  if (error)
+    {
+      g_warning ("%s calling %s", error->message, (char*)user_data);
+      g_error_free (error);
+    }
+}
+
+/**
+ * tp_props_iface_set_value:
+ * @self: #TpPropsIface on which to set a property value
+ * @prop_id: Identifier for property as set in #tp_props_iface_set_mapping
+ * @value: GValue to use to set the property's value
+ *
+ * Set the value of a property on this interface
+ */
+gboolean tp_props_iface_set_value (TpPropsIface* self, guint prop_id,
+                                  const GValue *value)
+{
+  /*TODO add option for an error callback*/
+  int i;
+  GPtrArray *props;
+
+  if (!PRIV(self)->properties_ready)
+    return FALSE;
+
+  for (i=0; i < PRIV(self)->mappings_len; i++)
+    {
+      if (PRIV(self)->mappings[i].user_id == prop_id)
+        {
+          GValue prop = {0,};
+          g_value_init (&prop, TP_TYPE_PROPERTY_CHANGE);
+          g_value_take_boxed (&prop,
+              dbus_g_type_specialized_construct (TP_TYPE_PROPERTY_CHANGE));
+
+          dbus_g_type_struct_set (&prop,
+              0, PRIV(self)->mappings[i].server_id,
+              1, value,
+              G_MAXUINT);
+
+          props = g_ptr_array_sized_new (1);
+          g_ptr_array_add (props, g_value_get_boxed (&prop));
+          tp_props_iface_set_properties_async (DBUS_G_PROXY(self), props,
+                                               dummy_callback, "SetProperties");
+          g_value_unset (&prop);
+          g_ptr_array_free (props, TRUE);
+          return TRUE;
+        }
+    }
+  return FALSE;
+}
+
+
+static void
+set_properties_values (TpPropsIface *self, GPtrArray *properties)
+{
+  int i,j;
+
+  for (i = 0; i < properties->len; i++)
+    {
+      GValue property = {0};
+      guint32 id;
+      GValue *value;
+
+      g_value_init (&property, TP_TYPE_PROPERTY_CHANGE);
+      g_value_set_static_boxed (&property, g_ptr_array_index (properties, i));
+      dbus_g_type_struct_get (&property, 0, &id, G_MAXUINT);
+
+      for (j = 0; j < PRIV(self)->mappings_len; j++)
+        {
+          PropertyMapping *mapping = &(PRIV (self)->mappings[j]);
+
+          if (mapping->server_id == id)
+            {
+              dbus_g_type_struct_get (&property, 1, &value, G_MAXUINT);
+              g_assert (value);
+
+              if (mapping->value)
+                {
+                  g_value_unset (mapping->value);
+                  g_free (mapping->value);
+                }
+
+              mapping->value = value;
+              value = NULL; /* just to be on the safe side... */
+
+              if (PRIV (self)->properties_ready)
+                g_signal_emit (self, signals[PROPERTY_CHANGED], 0,
+                               mapping->user_id, TP_PROPS_CHANGED_VALUE);
+
+              break;
+            }
+        }
+    }
+
+}
+
+static void
+properties_changed_cb (DBusGProxy *proxy, GPtrArray *properties,
+                       gpointer user_data)
+{
+  TpPropsIface *self = TELEPATHY_PROPS_IFACE (user_data);
+  if (!PRIV(self)->properties_ready)
+    return;
+  set_properties_values (self, properties);
+}
+
+static void
+properties_got_cb (DBusGProxy *proxy, GPtrArray *properties, GError *error, gpointer user_data)
+{
+  TpPropsIface *self = TELEPATHY_PROPS_IFACE (user_data);
+
+  if (error)
+    {
+      g_debug ("getting properties failed: %s (%s)", error->message,
+        dbus_g_error_get_name (error));
+      g_error_free (error);
+      return;
+    }
+
+  set_properties_values (self, properties);
+
+  if (!PRIV (self)->properties_ready)
+    {
+      PRIV (self)->properties_ready = TRUE;
+      g_signal_emit (self, signals[PROPERTIES_READY], 0);
+    }
+}
+
+static void
+property_flags_changed_cb (DBusGProxy *proxy, GPtrArray *properties,
+                       gpointer user_data)
+{
+  TpPropsIface *self = TELEPATHY_PROPS_IFACE (user_data);
+  GArray *get_props;
+  int i, j;
+
+  if (!PRIV(self)->properties_ready)
+    return;
+
+  get_props = g_array_sized_new (FALSE, FALSE, sizeof (guint32),
+                                 properties->len);
+
+  for (i = 0; i < properties->len; i++)
+    {
+      GValue property = {0};
+      guint32 id, flags;
+
+      g_value_init (&property, TP_TYPE_PROPERTY_CHANGE);
+      g_value_set_static_boxed (&property, g_ptr_array_index (properties, i));
+      dbus_g_type_struct_get (&property, 0, &id, G_MAXUINT);
+
+      for (j = 0; j < PRIV (self)->mappings_len; j++)
+        {
+          PropertyMapping *mapping = &(PRIV (self)->mappings[j]);
+
+          if (mapping->server_id == id)
+            {
+              dbus_g_type_struct_get (&property, 1, &flags, G_MAXUINT);
+
+              if (!(mapping->flags & TP_PROPERTY_FLAG_READ) &&
+                  flags & TP_PROPERTY_FLAG_READ)
+                /* property has become readable; fetch it */
+                g_array_append_val (get_props, mapping->server_id);
+
+              mapping->flags = flags;
+              g_signal_emit (self, signals[PROPERTY_CHANGED], 0,
+                mapping->user_id, TP_PROPS_CHANGED_FLAGS);
+              break;
+            }
+        }
+    }
+
+  tp_props_iface_get_properties_async (DBUS_G_PROXY (self), get_props,
+                                       properties_got_cb, self);
+  g_array_free (get_props, TRUE);
+}
+
+static void
+properties_listed_cb (DBusGProxy *proxy, GPtrArray *properties, GError *error, gpointer user_data)
+{
+  TpPropsIface *self = TELEPATHY_PROPS_IFACE (user_data);
+  int i,j;
+  guint32 id, flags;
+  gchar *name;
+  GArray *get_props;
+
+  if (error)
+    {
+      g_debug ("listing properties failed: %s (%s)", error->message,
+        dbus_g_error_get_name (error));
+      g_error_free (error);
+      return;
+    }
+
+  for (i = 0; i < properties->len; i++)
+    {
+      GValue property = {0};
+      g_value_init (&property, TP_TYPE_PROPERTY_DESCRIPTION);
+      g_value_set_static_boxed (&property, g_ptr_array_index (properties, i));
+
+      dbus_g_type_struct_get (&property,
+          0, &id,
+          1, &name,
+          3, &flags,
+          G_MAXUINT);
+
+      for (j = 0; j < PRIV (self)->mappings_len; j++)
+        {
+          if (0 == strcmp (PRIV (self)->mappings[j].name, name))
+            {
+              PRIV (self)->mappings[j].server_id = id;
+              PRIV (self)->mappings[j].flags = flags;
+            }
+        }
+
+      g_free (name);
+    }
+
+  get_props = g_array_sized_new (FALSE, FALSE, sizeof (guint32),
+                                 properties->len);
+
+  for (i = 0; i < PRIV(self)->mappings_len; i++)
+    {
+      PropertyMapping *mapping = &(PRIV(self)->mappings[i]);
+
+      if (mapping->flags & TP_PROPERTY_FLAG_READ)
+        g_array_append_val (get_props, mapping->server_id);
+    }
+
+  tp_props_iface_get_properties_async (DBUS_G_PROXY (self), get_props,
+                                       properties_got_cb, self);
+  g_array_free (get_props, TRUE);
+}
+
+void
+tp_props_interface_set_signatures (DBusGProxy *proxy)
+{
+  dbus_g_proxy_add_signal(proxy, "PropertiesChanged",
+      dbus_g_type_get_collection ("GPtrArray",
+          dbus_g_type_get_struct ("GValueArray", G_TYPE_UINT,
+                                   G_TYPE_VALUE, G_TYPE_INVALID)),
+      G_TYPE_INVALID);
+  dbus_g_proxy_add_signal(proxy, "PropertyFlagsChanged",
+      dbus_g_type_get_collection ("GPtrArray",
+          dbus_g_type_get_struct ("GValueArray", G_TYPE_UINT,
+                                   G_TYPE_UINT, G_TYPE_INVALID)),
+      G_TYPE_INVALID);
+}
+