diff options
-rw-r--r-- | introspection/mm-modem.xml | 19 | ||||
-rw-r--r-- | marshallers/mm-marshal.list | 1 | ||||
-rw-r--r-- | src/Makefile.am | 4 | ||||
-rw-r--r-- | src/mm-modem-base.c | 20 | ||||
-rw-r--r-- | src/mm-modem.h | 2 | ||||
-rw-r--r-- | src/mm-properties-changed-signal.c | 276 | ||||
-rw-r--r-- | src/mm-properties-changed-signal.h | 28 |
7 files changed, 348 insertions, 2 deletions
diff --git a/introspection/mm-modem.xml b/introspection/mm-modem.xml index 88e28f10..7896fff5 100644 --- a/introspection/mm-modem.xml +++ b/introspection/mm-modem.xml @@ -1,6 +1,25 @@ <?xml version="1.0" encoding="UTF-8" ?> <node name="/" xmlns:tp="http://telepathy.freedesktop.org/wiki/DbusSpec#extensions-v0"> + + <interface name="org.freedesktop.DBus.Properties"> + <signal name="MmPropertiesChanged"> + <tp:docstring> + One or more properties' values changed. + </tp:docstring> + <arg name="interface" type="s"> + <tp:docstring> + The D-Bus interface of the changed properties. + </tp:docstring> + </arg> + <arg name="properties" type="a{sv}"> + <tp:docstring> + The changed property names and their new values. + </tp:docstring> + </arg> + </signal> + </interface> + <interface name="org.freedesktop.ModemManager.Modem"> <method name="Enable"> <tp:docstring> diff --git a/marshallers/mm-marshal.list b/marshallers/mm-marshal.list index 12c22c22..474d704d 100644 --- a/marshallers/mm-marshal.list +++ b/marshallers/mm-marshal.list @@ -4,4 +4,5 @@ VOID:OBJECT,UINT VOID:UINT,BOOLEAN VOID:UINT,UINT VOID:UINT,UINT,UINT +VOID:STRING,BOXED diff --git a/src/Makefile.am b/src/Makefile.am index d1e3091e..8a5ccb89 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -49,7 +49,9 @@ modem_manager_SOURCES = \ mm-plugin.c \ mm-plugin.h \ mm-plugin-base.c \ - mm-plugin-base.h + mm-plugin-base.h \ + mm-properties-changed-signal.c \ + mm-properties-changed-signal.h mm-manager-glue.h: $(top_srcdir)/introspection/mm-manager.xml dbus-binding-tool --prefix=mm_manager --mode=glib-server --output=$@ $< diff --git a/src/mm-modem-base.c b/src/mm-modem-base.c index 90ab4b8b..3d82f8e8 100644 --- a/src/mm-modem-base.c +++ b/src/mm-modem-base.c @@ -24,6 +24,7 @@ #include "mm-serial-port.h" #include "mm-errors.h" #include "mm-options.h" +#include "mm-properties-changed-signal.h" static void modem_init (MMModem *modem_class); @@ -176,6 +177,10 @@ mm_modem_base_init (MMModemBase *self) MMModemBasePrivate *priv = MM_MODEM_BASE_GET_PRIVATE (self); priv->ports = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_object_unref); + + mm_properties_changed_signal_register_property (G_OBJECT (self), + MM_MODEM_ENABLED, + MM_MODEM_DBUS_INTERFACE); } static void @@ -183,15 +188,26 @@ modem_init (MMModem *modem_class) { } +static gboolean +is_enabled (MMModemState state) +{ + return (state >= MM_MODEM_STATE_ENABLED); +} + static void set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) { MMModemBasePrivate *priv = MM_MODEM_BASE_GET_PRIVATE (object); + gboolean old_enabled; switch (prop_id) { case MM_MODEM_PROP_STATE: + /* Ensure we update the 'enabled' property when the state changes */ + old_enabled = is_enabled (priv->state); priv->state = g_value_get_uint (value); + if (old_enabled != is_enabled (priv->state)) + g_object_notify (object, MM_MODEM_ENABLED); break; case MM_MODEM_PROP_DRIVER: /* Construct only */ @@ -250,7 +266,7 @@ get_property (GObject *object, guint prop_id, g_value_set_boolean (value, priv->valid); break; case MM_MODEM_PROP_ENABLED: - g_value_set_boolean (value, priv->state >= MM_MODEM_STATE_ENABLED); + g_value_set_boolean (value, is_enabled (priv->state)); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); @@ -319,5 +335,7 @@ mm_modem_base_class_init (MMModemBaseClass *klass) g_object_class_override_property (object_class, MM_MODEM_PROP_ENABLED, MM_MODEM_ENABLED); + + mm_properties_changed_signal_new (object_class); } diff --git a/src/mm-modem.h b/src/mm-modem.h index 3102cea0..b59525e2 100644 --- a/src/mm-modem.h +++ b/src/mm-modem.h @@ -47,6 +47,8 @@ typedef enum { #define MM_IS_MODEM(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_MODEM)) #define MM_MODEM_GET_INTERFACE(obj) (G_TYPE_INSTANCE_GET_INTERFACE ((obj), MM_TYPE_MODEM, MMModem)) +#define MM_MODEM_DBUS_INTERFACE "org.freedesktop.ModemManager.Modem" + #define MM_MODEM_DATA_DEVICE "device" #define MM_MODEM_MASTER_DEVICE "master-device" #define MM_MODEM_DRIVER "driver" diff --git a/src/mm-properties-changed-signal.c b/src/mm-properties-changed-signal.c new file mode 100644 index 00000000..b7f3672c --- /dev/null +++ b/src/mm-properties-changed-signal.c @@ -0,0 +1,276 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * 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: + * + * Copyright (C) 2007 - 2008 Novell, Inc. + * Copyright (C) 2008 - 2009 Red Hat, Inc. + */ + +#include <string.h> +#include <stdio.h> + +#include <dbus/dbus-glib.h> +#include "mm-marshal.h" +#include "mm-properties-changed-signal.h" + +#define DBUS_TYPE_G_MAP_OF_VARIANT (dbus_g_type_get_map ("GHashTable", G_TYPE_STRING, G_TYPE_VALUE)) + +#define PC_SIGNAL_NAME "mm-properties-changed" +#define MM_DBUS_PROPERTY_CHANGED "MM_DBUS_PROPERTY_CHANGED" + +typedef struct { + /* Whitelist of GObject property names for which changes will be emitted + * over the bus. + * + * Mapping of {property-name -> dbus-interface} + */ + GHashTable *registered; + + /* Table of each D-Bus interface of the object for which one or more + * properties have changed, and those properties and their new values. + * Destroyed after the changed signal has been sent. + * + * Mapping of {dbus-interface -> {property-name -> value}} + */ + GHashTable *hash; + + gulong signal_id; + guint idle_id; +} PropertiesChangedInfo; + +static void +destroy_value (gpointer data) +{ + GValue *val = (GValue *) data; + + g_value_unset (val); + g_slice_free (GValue, val); +} + +static PropertiesChangedInfo * +properties_changed_info_new (void) +{ + PropertiesChangedInfo *info; + + info = g_slice_new0 (PropertiesChangedInfo); + + info->registered = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free); + info->hash = g_hash_table_new_full (g_str_hash, g_str_equal, + (GDestroyNotify) g_free, + (GDestroyNotify) g_hash_table_destroy); + return info; +} + +static void +properties_changed_info_destroy (gpointer data) +{ + PropertiesChangedInfo *info = (PropertiesChangedInfo *) data; + + if (info->idle_id) + g_source_remove (info->idle_id); + + g_hash_table_destroy (info->hash); + g_hash_table_destroy (info->registered); + g_slice_free (PropertiesChangedInfo, info); +} + +#ifdef DEBUG +static void +add_to_string (gpointer key, gpointer value, gpointer user_data) +{ + char *buf = (char *) user_data; + GValue str_val = { 0, }; + + g_value_init (&str_val, G_TYPE_STRING); + if (!g_value_transform ((GValue *) value, &str_val)) { + if (G_VALUE_HOLDS_OBJECT (value)) { + GObject *obj = g_value_get_object (value); + + if (g_value_get_object (value)) { + sprintf (buf + strlen (buf), "{%s: %p (%s)}, ", + (const char *) key, obj, G_OBJECT_TYPE_NAME (obj)); + } else { + sprintf (buf + strlen (buf), "{%s: %p}, ", (const char *) key, obj); + } + } else + sprintf (buf + strlen (buf), "{%s: <transform error>}, ", (const char *) key); + } else { + sprintf (buf + strlen (buf), "{%s: %s}, ", (const char *) key, g_value_get_string (&str_val)); + } + g_value_unset (&str_val); +} +#endif + +static gboolean +properties_changed (gpointer data) +{ + GObject *object = G_OBJECT (data); + PropertiesChangedInfo *info; + GHashTableIter iter; + gpointer key, value; + + info = (PropertiesChangedInfo *) g_object_get_data (object, MM_DBUS_PROPERTY_CHANGED); + g_assert (info); + + g_hash_table_iter_init (&iter, info->hash); + while (g_hash_table_iter_next (&iter, &key, &value)) { + const char *interface = (const char *) key; + GHashTable *props = (GHashTable *) value; + +#ifdef DEBUG + { + char buf[2048] = { 0, }; + g_hash_table_foreach (props, add_to_string, &buf); + g_message ("%s: %s -> (%s) %s", __func__, + G_OBJECT_TYPE_NAME (object), + interface, + buf); + } +#endif + + /* Send the PropertiesChanged signal */ + g_signal_emit (object, info->signal_id, 0, interface, props); + } + g_hash_table_remove_all (info->hash); + + return FALSE; +} + +static void +idle_id_reset (gpointer data) +{ + GObject *object = G_OBJECT (data); + PropertiesChangedInfo *info = (PropertiesChangedInfo *) g_object_get_data (object, MM_DBUS_PROPERTY_CHANGED); + + /* info is unset when the object is being destroyed */ + if (info) + info->idle_id = 0; +} + +static char* +uscore_to_wincaps (const char *uscore) +{ + const char *p; + GString *str; + gboolean last_was_uscore; + + last_was_uscore = TRUE; + + str = g_string_new (NULL); + p = uscore; + while (p && *p) { + if (*p == '-' || *p == '_') + last_was_uscore = TRUE; + else { + if (last_was_uscore) { + g_string_append_c (str, g_ascii_toupper (*p)); + last_was_uscore = FALSE; + } else + g_string_append_c (str, *p); + } + ++p; + } + + return g_string_free (str, FALSE); +} + +static PropertiesChangedInfo * +get_properties_changed_info (GObject *object) +{ + PropertiesChangedInfo *info = NULL; + + info = (PropertiesChangedInfo *) g_object_get_data (object, MM_DBUS_PROPERTY_CHANGED); + if (!info) { + info = properties_changed_info_new (); + g_object_set_data_full (object, MM_DBUS_PROPERTY_CHANGED, info, properties_changed_info_destroy); + info->signal_id = g_signal_lookup (PC_SIGNAL_NAME, G_OBJECT_TYPE (object)); + g_assert (info->signal_id); + } + + g_assert (info); + return info; +} + +static void +notify (GObject *object, GParamSpec *pspec) +{ + GHashTable *interfaces; + PropertiesChangedInfo *info; + const char *interface; + GValue *value; + + info = get_properties_changed_info (object); + + interface = g_hash_table_lookup (info->registered, pspec->name); + if (!interface) + return; + + /* Check if there are other changed properties for this interface already, + * otherwise create a new hash table for all changed properties for this + * D-Bus interface. + */ + interfaces = g_hash_table_lookup (info->hash, interface); + if (!interfaces) { + interfaces = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, destroy_value); + g_hash_table_insert (info->hash, g_strdup (interface), interfaces); + } + + /* Now put the changed property value into the hash table of changed values + * for its D-Bus interface. + */ + value = g_slice_new0 (GValue); + g_value_init (value, pspec->value_type); + g_object_get_property (object, pspec->name, value); + g_hash_table_insert (interfaces, uscore_to_wincaps (pspec->name), value); + + if (!info->idle_id) + info->idle_id = g_idle_add_full (G_PRIORITY_DEFAULT_IDLE, properties_changed, object, idle_id_reset); +} + +void +mm_properties_changed_signal_register_property (GObject *object, + const char *property, + const char *interface) +{ + PropertiesChangedInfo *info; + const char *tmp; + + /* All exported properties need to be registered explicitly for now since + * dbus-glib doesn't expose any method to find out the properties registered + * in the XML. + */ + + info = get_properties_changed_info (object); + tmp = g_hash_table_lookup (info->registered, property); + if (tmp) { + g_warning ("%s: property '%s' already registerd on interface '%s'", + __func__, property, tmp); + } else + g_hash_table_insert (info->registered, g_strdup (property), g_strdup (interface)); +} + +guint +mm_properties_changed_signal_new (GObjectClass *object_class) +{ + guint id; + + object_class->notify = notify; + + id = g_signal_new (PC_SIGNAL_NAME, + G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_FIRST, + 0, NULL, NULL, + mm_marshal_VOID__STRING_BOXED, + G_TYPE_NONE, 2, G_TYPE_STRING, DBUS_TYPE_G_MAP_OF_VARIANT); + + return id; +} + diff --git a/src/mm-properties-changed-signal.h b/src/mm-properties-changed-signal.h new file mode 100644 index 00000000..60e71b94 --- /dev/null +++ b/src/mm-properties-changed-signal.h @@ -0,0 +1,28 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * 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: + * + * Copyright (C) 2007 - 2008 Novell, Inc. + * Copyright (C) 2008 - 2009 Red Hat, Inc. + */ + +#ifndef _MM_PROPERTIES_CHANGED_SIGNAL_H_ +#define _MM_PROPERTIES_CHANGED_SIGNAL_H_ + +#include <glib-object.h> + +guint mm_properties_changed_signal_new (GObjectClass *object_class); + +void mm_properties_changed_signal_register_property (GObject *object, + const char *property, + const char *interface); + +#endif /* _MM_PROPERTIES_CHANGED_SIGNAL_H_ */ |