aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDan Williams <dcbw@redhat.com>2009-12-22 16:58:23 -0800
committerDan Williams <dcbw@redhat.com>2009-12-22 16:58:23 -0800
commitb94627572fa09ed71bfcc633e9b5a40f5e49c596 (patch)
tree99f18a3d5311375c9decde35ad6f85d7b61af78f
parent710986bd4b630095eff7b53bc5d46d0bed94495e (diff)
core: implement a PropertiesChanged signal for the MMModem class
At the moment only the Enabled property is exported.
-rw-r--r--introspection/mm-modem.xml19
-rw-r--r--marshallers/mm-marshal.list1
-rw-r--r--src/Makefile.am4
-rw-r--r--src/mm-modem-base.c20
-rw-r--r--src/mm-modem.h2
-rw-r--r--src/mm-properties-changed-signal.c276
-rw-r--r--src/mm-properties-changed-signal.h28
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_ */