diff options
author | Danielle Madeley <danielle.madeley@collabora.co.uk> | 2010-04-02 09:02:28 +1100 |
---|---|---|
committer | Danielle Madeley <danielle.madeley@collabora.co.uk> | 2010-04-02 09:02:28 +1100 |
commit | 7dd5cbc99816c80aa9b3d2ff5f00855cdadb2ea0 (patch) | |
tree | 8e2b253e9106ef57fe0bdad88ed79db3e25ff085 | |
parent | 5d68f7af4d4494d0c399fa30acf85c76be610a9e (diff) |
Add a blinkenlight observer example
-rw-r--r-- | configure.ac | 1 | ||||
-rw-r--r-- | docs/examples/Makefile.am | 1 | ||||
-rw-r--r-- | docs/examples/glib_blinkenlight_observer/Makefile.am | 11 | ||||
-rw-r--r-- | docs/examples/glib_blinkenlight_observer/channel.c | 232 | ||||
-rw-r--r-- | docs/examples/glib_blinkenlight_observer/channel.h | 41 | ||||
-rw-r--r-- | docs/examples/glib_blinkenlight_observer/main.c | 37 | ||||
-rw-r--r-- | docs/examples/glib_blinkenlight_observer/observer.c | 303 | ||||
-rw-r--r-- | docs/examples/glib_blinkenlight_observer/observer.h | 34 |
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, ×tamp, &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 |