summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDanielle Madeley <danielle.madeley@collabora.co.uk>2010-04-02 09:02:28 +1100
committerDanielle Madeley <danielle.madeley@collabora.co.uk>2010-04-02 09:02:28 +1100
commit7dd5cbc99816c80aa9b3d2ff5f00855cdadb2ea0 (patch)
tree8e2b253e9106ef57fe0bdad88ed79db3e25ff085
parent5d68f7af4d4494d0c399fa30acf85c76be610a9e (diff)
Add a blinkenlight observer example
-rw-r--r--configure.ac1
-rw-r--r--docs/examples/Makefile.am1
-rw-r--r--docs/examples/glib_blinkenlight_observer/Makefile.am11
-rw-r--r--docs/examples/glib_blinkenlight_observer/channel.c232
-rw-r--r--docs/examples/glib_blinkenlight_observer/channel.h41
-rw-r--r--docs/examples/glib_blinkenlight_observer/main.c37
-rw-r--r--docs/examples/glib_blinkenlight_observer/observer.c303
-rw-r--r--docs/examples/glib_blinkenlight_observer/observer.h34
8 files changed, 660 insertions, 0 deletions
diff --git a/configure.ac b/configure.ac
index 724c87e..0759c6b 100644
--- a/configure.ac
+++ b/configure.ac
@@ -38,6 +38,7 @@ AC_OUTPUT([
docs/examples/glib_mc5_connections/Makefile
docs/examples/glib_mc5_dbus_tube_handler/Makefile
docs/examples/glib_mc5_observer/Makefile
+ docs/examples/glib_blinkenlight_observer/Makefile
docs/examples/glib_mc5_ft_handler/Makefile
docs/examples/gtk_presence_app/Makefile
docs/examples/pygtk_chat_client/Makefile
diff --git a/docs/examples/Makefile.am b/docs/examples/Makefile.am
index 91fbf65..6ce2f53 100644
--- a/docs/examples/Makefile.am
+++ b/docs/examples/Makefile.am
@@ -10,6 +10,7 @@ example_dirs = \
glib_mc5_connections \
glib_mc5_dbus_tube_handler \
glib_mc5_observer \
+ glib_blinkenlight_observer \
glib_mc5_ft_handler \
gtk_presence_app \
pygtk_chat_client \
diff --git a/docs/examples/glib_blinkenlight_observer/Makefile.am b/docs/examples/glib_blinkenlight_observer/Makefile.am
new file mode 100644
index 0000000..dced68c
--- /dev/null
+++ b/docs/examples/glib_blinkenlight_observer/Makefile.am
@@ -0,0 +1,11 @@
+INCLUDES = $(TELEPATHY_GLIB_CFLAGS)
+LDADD = $(TELEPATHY_GLIB_LIBS)
+
+noinst_PROGRAMS = blinkenlight-observer
+
+blinkenlight_observer_SOURCES = \
+ observer.c observer.h \
+ channel.c channel.h \
+ main.c
+
+include $(top_srcdir)/docs/rsync-dist.make
diff --git a/docs/examples/glib_blinkenlight_observer/channel.c b/docs/examples/glib_blinkenlight_observer/channel.c
new file mode 100644
index 0000000..6c20e14
--- /dev/null
+++ b/docs/examples/glib_blinkenlight_observer/channel.c
@@ -0,0 +1,232 @@
+#include <telepathy-glib/telepathy-glib.h>
+
+#include "channel.h"
+
+#define GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), TYPE_CHANNEL, ChannelPrivate))
+
+G_DEFINE_TYPE (Channel, channel, TP_TYPE_CHANNEL);
+
+enum /* properties */
+{
+ PROP_0,
+ PROP_UNREAD
+};
+
+typedef struct
+{
+ TpIntSet *pending;
+} ChannelPrivate;
+
+typedef struct
+{
+ TpChannelWhenReadyCb callback;
+ gpointer user_data;
+} ReadyCallbackData;
+
+static void
+_channel_update_pending_msgs (Channel *self)
+{
+ ChannelPrivate *priv = GET_PRIVATE (self);
+
+ g_print ("%s: pending messages %u\n",
+ tp_proxy_get_object_path (self),
+ tp_intset_size (priv->pending));
+
+ g_object_notify (G_OBJECT (self), "unread");
+}
+
+static void
+_channel_msg_received (TpChannel *self,
+ guint id,
+ guint timestamp,
+ guint sender,
+ guint type,
+ guint flags,
+ const char *text,
+ gpointer user_data,
+ GObject *weak_obj)
+{
+ ChannelPrivate *priv = GET_PRIVATE (self);
+
+ if (type == TP_CHANNEL_TEXT_MESSAGE_TYPE_NORMAL)
+ {
+ tp_intset_add (priv->pending, id);
+
+ _channel_update_pending_msgs (CHANNEL (self));
+ }
+}
+
+static void
+_channel_pending_msg_removed (TpChannel *self,
+ const GArray *ids,
+ gpointer user_data,
+ GObject *weak_obj)
+{
+ ChannelPrivate *priv = GET_PRIVATE (self);
+ guint i;
+
+ for (i = 0; i < ids->len; i++)
+ {
+ guint id = g_array_index (ids, guint, i);
+
+ tp_intset_remove (priv->pending, id);
+ }
+
+ _channel_update_pending_msgs (CHANNEL (self));
+}
+
+static void
+_channel_list_pending_messages (TpChannel *self,
+ const GPtrArray *messages,
+ const GError *error,
+ gpointer user_data,
+ GObject *weak_obj)
+{
+ ReadyCallbackData *data = user_data;
+
+ if (error == NULL)
+ {
+ guint i;
+
+ for (i = 0; i < messages->len; i++)
+ {
+ guint id, timestamp, sender, type, flags;
+ const char *text;
+
+ tp_value_array_unpack (g_ptr_array_index (messages, i), 6,
+ &id, &timestamp, &sender, &type, &flags, &text);
+
+ _channel_msg_received (self,
+ id, timestamp, sender, type, flags, text,
+ NULL, NULL);
+ }
+ }
+
+ data->callback (self, error, data->user_data);
+ g_slice_free (ReadyCallbackData, data);
+}
+
+static void
+_channel_ready (TpChannel *self,
+ const GError *error,
+ gpointer user_data)
+{
+ ReadyCallbackData *data = user_data;
+ GError *lerror = NULL;
+
+ /* get the pending messages on the channel */
+ tp_cli_channel_type_text_call_list_pending_messages (self, -1, FALSE,
+ _channel_list_pending_messages,
+ data, NULL, NULL);
+
+ tp_cli_channel_type_text_connect_to_received (self,
+ _channel_msg_received,
+ NULL, NULL, NULL, &lerror);
+ g_assert_no_error (lerror);
+
+ tp_cli_channel_interface_messages_connect_to_pending_messages_removed (self,
+ _channel_pending_msg_removed,
+ NULL, NULL, NULL, &lerror);
+ g_assert_no_error (lerror);
+}
+
+void
+channel_call_when_ready (Channel *self,
+ TpChannelWhenReadyCb callback,
+ gpointer user_data)
+{
+ ReadyCallbackData *data = g_slice_new0 (ReadyCallbackData);
+
+ data->callback = callback;
+ data->user_data = user_data;
+
+ tp_channel_call_when_ready (TP_CHANNEL (self), _channel_ready, data);
+}
+
+static void
+channel_get_property (GObject *self,
+ guint property_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ ChannelPrivate *priv = GET_PRIVATE (self);
+
+ switch (property_id)
+ {
+ case PROP_UNREAD:
+ g_value_set_uint (value, tp_intset_size (priv->pending));
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (self, property_id, pspec);
+ break;
+ }
+}
+
+static void
+channel_dispose (GObject *self)
+{
+ ChannelPrivate *priv = GET_PRIVATE (self);
+
+ if (priv->pending != NULL)
+ {
+ tp_intset_destroy (priv->pending);
+ priv->pending = NULL;
+ }
+
+ G_OBJECT_CLASS (channel_parent_class)->dispose (self);
+}
+
+static void
+channel_class_init (ChannelClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+ object_class->get_property = channel_get_property;
+ object_class->dispose = channel_dispose;
+
+ g_object_class_install_property (object_class, PROP_UNREAD,
+ g_param_spec_uint ("unread",
+ "Unread",
+ "Number of unread messages on this channel",
+ 0, G_MAXUINT, 0,
+ G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
+
+ g_type_class_add_private (object_class, sizeof (ChannelPrivate));
+}
+
+static void
+channel_init (Channel *self)
+{
+ ChannelPrivate *priv = GET_PRIVATE (self);
+
+ priv->pending = tp_intset_new ();
+}
+
+Channel *
+channel_new (TpConnection *conn,
+ const char *path,
+ const GHashTable *properties,
+ GError **error)
+{
+ Channel *self = NULL;
+
+ g_return_val_if_fail (TP_IS_CONNECTION (conn), NULL);
+ g_return_val_if_fail (path != NULL, NULL);
+ g_return_val_if_fail (properties != NULL, NULL);
+
+ if (!tp_dbus_check_valid_object_path (path, error))
+ goto finally;
+
+ self = g_object_new (TYPE_CHANNEL,
+ "connection", conn,
+ "dbus-daemon", TP_PROXY (conn)->dbus_daemon,
+ "bus-name", TP_PROXY (conn)->bus_name,
+ "object-path", path,
+ "handle-type", (guint) TP_UNKNOWN_HANDLE_TYPE,
+ "channel-properties", properties,
+ NULL);
+
+finally:
+ return self;
+}
diff --git a/docs/examples/glib_blinkenlight_observer/channel.h b/docs/examples/glib_blinkenlight_observer/channel.h
new file mode 100644
index 0000000..2949149
--- /dev/null
+++ b/docs/examples/glib_blinkenlight_observer/channel.h
@@ -0,0 +1,41 @@
+#ifndef __CHANNEL_H__
+#define __CHANNEL_H__
+
+#include <glib-object.h>
+#include <telepathy-glib/channel.h>
+
+G_BEGIN_DECLS
+
+#define TYPE_CHANNEL (channel_get_type ())
+#define CHANNEL(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), TYPE_CHANNEL, Channel))
+#define CHANNEL_CLASS(obj) (G_TYPE_CHECK_CLASS_CAST ((obj), TYPE_CHANNEL, ChannelClass))
+#define IS_CHANNEL(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), TYPE_CHANNEL))
+#define IS_CHANNEL_CLASS(obj) (G_TYPE_CHECK_CLASS_TYPE ((obj), TYPE_CHANNEL))
+#define CHANNEL_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), TYPE_CHANNEL, ChannelClass))
+
+typedef struct _Channel Channel;
+typedef struct _ChannelClass ChannelClass;
+
+struct _Channel
+{
+ TpChannel parent;
+};
+
+struct _ChannelClass
+{
+ TpChannelClass parent_class;
+};
+
+GType channel_get_type (void);
+Channel *channel_new (TpConnection *conn,
+ const char *path,
+ const GHashTable *properties,
+ GError **error);
+
+void channel_call_when_ready (Channel *channel,
+ TpChannelWhenReadyCb callback,
+ gpointer user_data);
+
+G_END_DECLS
+
+#endif
diff --git a/docs/examples/glib_blinkenlight_observer/main.c b/docs/examples/glib_blinkenlight_observer/main.c
new file mode 100644
index 0000000..06f310d
--- /dev/null
+++ b/docs/examples/glib_blinkenlight_observer/main.c
@@ -0,0 +1,37 @@
+#include <glib.h>
+#include <dbus/dbus-glib.h>
+
+#include <telepathy-glib/dbus.h>
+#include <telepathy-glib/defs.h>
+
+#include "observer.h"
+
+#define CLIENT_NAME "Blinkenlight"
+
+static GMainLoop *loop = NULL;
+
+int
+main (int argc, char **argv)
+{
+ GError *error = NULL;
+
+ g_type_init ();
+
+ loop = g_main_loop_new (NULL, FALSE);
+
+ TpDBusDaemon *tpdbus = tp_dbus_daemon_dup (NULL);
+ DBusGConnection *dbus = tp_get_bus ();
+
+ Observer *observer = observer_new ();
+
+ /* register well-known name */
+ g_assert (tp_dbus_daemon_request_name (tpdbus,
+ TP_CLIENT_BUS_NAME_BASE CLIENT_NAME,
+ TRUE, NULL));
+ /* register ExampleObserver on the bus */
+ dbus_g_connection_register_g_object (dbus,
+ TP_CLIENT_OBJECT_PATH_BASE CLIENT_NAME,
+ G_OBJECT (observer));
+
+ g_main_loop_run (loop);
+}
diff --git a/docs/examples/glib_blinkenlight_observer/observer.c b/docs/examples/glib_blinkenlight_observer/observer.c
new file mode 100644
index 0000000..a34bff1
--- /dev/null
+++ b/docs/examples/glib_blinkenlight_observer/observer.c
@@ -0,0 +1,303 @@
+#include <telepathy-glib/telepathy-glib.h>
+#include <telepathy-glib/svc-generic.h>
+#include <telepathy-glib/svc-client.h>
+
+#include "observer.h"
+#include "channel.h"
+
+static void client_iface_init (gpointer, gpointer);
+static void observer_iface_init (gpointer, gpointer);
+
+#define GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), TYPE_OBSERVER, ObserverPrivate))
+
+G_DEFINE_TYPE_WITH_CODE (Observer, observer, G_TYPE_OBJECT,
+ G_IMPLEMENT_INTERFACE (TP_TYPE_SVC_DBUS_PROPERTIES,
+ tp_dbus_properties_mixin_iface_init);
+ G_IMPLEMENT_INTERFACE (TP_TYPE_SVC_CLIENT, NULL);
+ G_IMPLEMENT_INTERFACE (TP_TYPE_SVC_CLIENT_OBSERVER, observer_iface_init);
+ );
+
+static const char *client_interfaces[] = {
+ TP_IFACE_CLIENT_OBSERVER,
+ NULL
+};
+
+enum /* properties */
+{
+ PROP_0,
+ PROP_INTERFACES,
+ PROP_CHANNEL_FILTER
+};
+
+typedef struct
+{
+ GList *channels;
+} ObserverPrivate;
+
+typedef struct
+{
+ Observer *self;
+ GList *pending;
+ DBusGMethodInvocation *context;
+} ReadyCallbackData;
+
+static void
+_update_count (Observer *self)
+{
+ ObserverPrivate *priv = GET_PRIVATE (self);
+ GList *ptr;
+ guint total = 0;
+
+ for (ptr = priv->channels; ptr != NULL; ptr = ptr->next)
+ {
+ guint unread;
+
+ g_object_get (ptr->data,
+ "unread", &unread,
+ NULL);
+
+ total += unread;
+ }
+
+ g_print ("TOTAL UNREAD MESSAGES: %u\n", total);
+}
+
+static void
+_channel_update_count (GObject *channel,
+ GParamSpec *pspec,
+ Observer *self)
+{
+ _update_count (self);
+}
+
+static void
+_channel_closed (TpProxy *channel,
+ guint domain,
+ guint code,
+ char *message,
+ Observer *self)
+{
+ ObserverPrivate *priv = GET_PRIVATE (self);
+
+ g_print ("Channel closed: %s\n", tp_proxy_get_object_path (channel));
+
+ priv->channels = g_list_remove (priv->channels, channel);
+ _update_count (self);
+
+ g_object_unref (channel);
+}
+
+static void
+_channel_ready (TpChannel *channel,
+ const GError *error,
+ gpointer user_data)
+{
+ ReadyCallbackData *data = user_data;
+ Observer *self = data->self;
+ ObserverPrivate *priv = GET_PRIVATE (self);
+
+ data->pending = g_list_remove (data->pending, channel);
+
+ if (error == NULL)
+ {
+ g_print ("Channel ready: %s\n", tp_proxy_get_object_path (channel));
+
+ priv->channels = g_list_prepend (priv->channels, channel);
+
+ tp_g_signal_connect_object (channel, "notify::unread",
+ G_CALLBACK (_channel_update_count), self, 0);
+ tp_g_signal_connect_object (channel, "invalidated",
+ G_CALLBACK (_channel_closed), self, 0);
+
+ _update_count (self);
+ }
+ else
+ {
+ /* drop the ref, this channel is dead */
+ g_object_unref (channel);
+ }
+
+ if (data->pending == NULL)
+ {
+ g_print ("All channels ready\n");
+
+ tp_svc_client_observer_return_from_observe_channels (data->context);
+
+ g_slice_free (ReadyCallbackData, data);
+ }
+}
+
+static void
+observer_observe_channels (TpSvcClientObserver *self,
+ const char *account_path,
+ const char *connection_path,
+ const GPtrArray *channels,
+ const char *dispatch_op_path,
+ const GPtrArray *requests_satisfied,
+ GHashTable *observer_info,
+ DBusGMethodInvocation *context)
+{
+ TpDBusDaemon *dbus = NULL;
+ TpConnection *conn = NULL;
+ ReadyCallbackData *data = NULL;
+ GError *error = NULL;
+ guint i;
+
+ dbus = tp_dbus_daemon_dup (&error);
+ if (error != NULL)
+ goto error;
+
+ conn = tp_connection_new (dbus, NULL, connection_path, &error);
+ if (error != NULL)
+ goto error;
+
+ data = g_slice_new0 (ReadyCallbackData);
+ data->self = OBSERVER (self);
+ data->context = context;
+
+ /* build a list of channels and queue them for preparation */
+ for (i = 0; i < channels->len; i++)
+ {
+ GValueArray *channel = g_ptr_array_index (channels, i);
+ Channel *chan;
+ char *path;
+ GHashTable *map;
+
+ tp_value_array_unpack (channel, 2,
+ &path, &map);
+
+ chan = channel_new (conn, path, map, &error);
+ if (error != NULL)
+ continue;
+
+ data->pending = g_list_prepend (data->pending, chan);
+ channel_call_when_ready (chan, _channel_ready, data);
+ }
+
+ goto finally;
+
+error:
+ dbus_g_method_return_error (context, error);
+
+ g_error_free (error);
+
+finally:
+ if (dbus != NULL)
+ g_object_unref (dbus);
+
+ if (conn != NULL)
+ g_object_unref (conn);
+}
+
+static void
+observer_get_property (GObject *self,
+ guint property_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ switch (property_id)
+ {
+ case PROP_INTERFACES:
+ g_value_set_boxed (value, client_interfaces);
+ break;
+
+ case PROP_CHANNEL_FILTER:
+ {
+ GPtrArray *array = g_ptr_array_new ();
+
+ g_ptr_array_add (array, tp_asv_new (
+ TP_IFACE_CHANNEL ".ChannelType",
+ G_TYPE_STRING,
+ TP_IFACE_CHANNEL_TYPE_TEXT,
+
+ NULL));
+
+ g_value_take_boxed (value, array);
+ break;
+ }
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (self, property_id, pspec);
+ break;
+ }
+}
+
+static void
+observer_class_init (ObserverClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+ object_class->get_property = observer_get_property;
+
+ /* D-Bus properties are exposed as GObject properties through the
+ * TpDBusPropertiesMixin */
+ /* properties on the Client interface */
+ static TpDBusPropertiesMixinPropImpl client_props[] = {
+ { "Interfaces", "interfaces", NULL },
+ { NULL }
+ };
+
+ /* properties on the Client.Observer interface */
+ static TpDBusPropertiesMixinPropImpl client_observer_props[] = {
+ { "ObserverChannelFilter", "channel-filter", NULL },
+ { NULL }
+ };
+
+ /* complete list of interfaces with properties */
+ static TpDBusPropertiesMixinIfaceImpl prop_interfaces[] = {
+ { TP_IFACE_CLIENT,
+ tp_dbus_properties_mixin_getter_gobject_properties,
+ NULL,
+ client_props
+ },
+ { TP_IFACE_CLIENT_OBSERVER,
+ tp_dbus_properties_mixin_getter_gobject_properties,
+ NULL,
+ client_observer_props
+ },
+ { NULL }
+ };
+
+ g_object_class_install_property (object_class, PROP_INTERFACES,
+ g_param_spec_boxed ("interfaces",
+ "Interfaces",
+ "Available D-Bus Interfaces",
+ G_TYPE_STRV,
+ G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
+
+ g_object_class_install_property (object_class, PROP_CHANNEL_FILTER,
+ g_param_spec_boxed ("channel-filter",
+ "Channel Filter",
+ "Filter for channels we observe",
+ TP_ARRAY_TYPE_CHANNEL_CLASS_LIST,
+ G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
+
+ /* call our mixin class init */
+ klass->dbus_props_class.interfaces = prop_interfaces;
+ tp_dbus_properties_mixin_class_init (object_class,
+ G_STRUCT_OFFSET (ObserverClass, dbus_props_class));
+
+ g_type_class_add_private (object_class, sizeof (ObserverPrivate));
+}
+
+static void
+observer_init (Observer *self)
+{
+}
+
+static void
+observer_iface_init (gpointer g_iface, gpointer iface_data)
+{
+ TpSvcClientObserverClass *klass = (TpSvcClientObserverClass *) g_iface;
+
+#define IMPLEMENT(x) tp_svc_client_observer_implement_##x (klass, \
+ observer_##x)
+ IMPLEMENT (observe_channels);
+#undef IMPLEMENT
+}
+
+Observer *
+observer_new (void)
+{
+ return g_object_new (TYPE_OBSERVER, NULL);
+}
diff --git a/docs/examples/glib_blinkenlight_observer/observer.h b/docs/examples/glib_blinkenlight_observer/observer.h
new file mode 100644
index 0000000..07079c6
--- /dev/null
+++ b/docs/examples/glib_blinkenlight_observer/observer.h
@@ -0,0 +1,34 @@
+#ifndef __OBSERVER_H__
+#define __OBSERVER_H__
+
+#include <glib-object.h>
+#include <telepathy-glib/dbus-properties-mixin.h>
+
+G_BEGIN_DECLS
+
+#define TYPE_OBSERVER (observer_get_type ())
+#define OBSERVER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), TYPE_OBSERVER, Observer))
+#define OBSERVER_CLASS(obj) (G_TYPE_CHECK_CLASS_CAST ((obj), TYPE_OBSERVER, ObserverClass))
+#define IS_OBSERVER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), TYPE_OBSERVER))
+#define IS_OBSERVER_CLASS(obj) (G_TYPE_CHECK_CLASS_TYPE ((obj), TYPE_OBSERVER))
+#define OBSERVER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), TYPE_OBSERVER, ObserverClass))
+
+typedef struct _Observer Observer;
+struct _Observer
+{
+ GObject parent;
+};
+
+typedef struct _ObserverClass ObserverClass;
+struct _ObserverClass
+{
+ GObjectClass parent_class;
+ TpDBusPropertiesMixinClass dbus_props_class;
+};
+
+GType observer_get_type (void);
+Observer *observer_new (void);
+
+G_END_DECLS
+
+#endif