diff -r 000000000000 -r d0f3a028347a libtelepathy/src/tp-props-iface.c --- /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 +#include +#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 + * properties-ready signal + * 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); +} +