[Telepathy-commits] [telepathy-qt4/master] Import the echo example from telepathy-glib
Simon McVittie
simon.mcvittie at collabora.co.uk
Wed Feb 18 08:29:47 PST 2009
Also add a note to tests/lib/Makefile.am about why it's not dealt with in
the same way as our modified versions of the csh and echo2 examples: we
should fix that divergence in future.
---
configure.ac | 1 +
tests/lib/Makefile.am | 12 +-
tests/lib/echo/Makefile.am | 25 ++
tests/lib/echo/chan.c | 531 +++++++++++++++++++++++++++++++++++
tests/lib/echo/chan.h | 58 ++++
tests/lib/echo/conn.c | 194 +++++++++++++
tests/lib/echo/conn.h | 55 ++++
tests/lib/echo/connection-manager.c | 95 +++++++
tests/lib/echo/connection-manager.h | 53 ++++
tests/lib/echo/im-manager.c | 389 +++++++++++++++++++++++++
tests/lib/echo/im-manager.h | 54 ++++
11 files changed, 1465 insertions(+), 2 deletions(-)
create mode 100644 tests/lib/echo/Makefile.am
create mode 100644 tests/lib/echo/chan.c
create mode 100644 tests/lib/echo/chan.h
create mode 100644 tests/lib/echo/conn.c
create mode 100644 tests/lib/echo/conn.h
create mode 100644 tests/lib/echo/connection-manager.c
create mode 100644 tests/lib/echo/connection-manager.h
create mode 100644 tests/lib/echo/im-manager.c
create mode 100644 tests/lib/echo/im-manager.h
diff --git a/configure.ac b/configure.ac
index c64d20a..92337ec 100644
--- a/configure.ac
+++ b/configure.ac
@@ -249,6 +249,7 @@ AC_OUTPUT([
tests/dbus-1/services/account-manager.service
tests/lib/Makefile
tests/lib/csh/Makefile
+ tests/lib/echo/Makefile
tests/lib/echo2/Makefile
tests/pinocchio/Makefile
tests/prototype/Makefile
diff --git a/tests/lib/Makefile.am b/tests/lib/Makefile.am
index dcee207..b8ee757 100644
--- a/tests/lib/Makefile.am
+++ b/tests/lib/Makefile.am
@@ -1,4 +1,4 @@
-SUBDIRS = . csh echo2
+SUBDIRS =
AM_CFLAGS = $(ERROR_CFLAGS)
@@ -25,6 +25,7 @@ libtp_qt4_tests_la_LIBADD = $(top_builddir)/TelepathyQt4/libtelepathy-qt4.la
if ENABLE_TP_GLIB_TESTS
+SUBDIRS += echo
AM_CFLAGS += $(TP_GLIB_CFLAGS)
AM_CXXFLAGS += $(TP_GLIB_CFLAGS)
@@ -39,7 +40,14 @@ libtp_glib_tests_la_SOURCES = \
simple-manager.c \
simple-manager.h
libtp_glib_tests_la_LIBADD = \
- $(TP_GLIB_LIBS)
+ $(TP_GLIB_LIBS) \
+ echo/libexample-cm-echo.la
+
+# Currently, the csh and echo2 subdirectories contain hacked-up versions of
+# the telepathy-glib examples. In future we should fix this divergence and
+# make them behave like the echo subdirectory instead. However, until then,
+# we need to compile this directory before those subdirectories.
+SUBDIRS += . csh echo2
endif
diff --git a/tests/lib/echo/Makefile.am b/tests/lib/echo/Makefile.am
new file mode 100644
index 0000000..85c55b9
--- /dev/null
+++ b/tests/lib/echo/Makefile.am
@@ -0,0 +1,25 @@
+# Taken from telepathy-glib. The only change is to remove main.c and cut down
+# the Makefile.am accordingly.
+#
+# PLEASE DO NOT MODIFY THIS CONNECTION MANAGER. Either subclass it,
+# copy-and-modify (moving it to a better namespace), or make changes in the
+# copy in telepathy-glib first.
+
+noinst_LTLIBRARIES = libexample-cm-echo.la
+
+libexample_cm_echo_la_SOURCES = \
+ chan.c \
+ chan.h \
+ conn.c \
+ conn.h \
+ connection-manager.c \
+ connection-manager.h \
+ im-manager.c \
+ im-manager.h
+
+libexample_cm_echo_la_LIBADD = \
+ $(TP_GLIB_LIBS)
+
+AM_CFLAGS = \
+ $(ERROR_CFLAGS) \
+ $(TP_GLIB_CFLAGS)
diff --git a/tests/lib/echo/chan.c b/tests/lib/echo/chan.c
new file mode 100644
index 0000000..63050e0
--- /dev/null
+++ b/tests/lib/echo/chan.c
@@ -0,0 +1,531 @@
+/*
+ * chan.c - an example text channel talking to a particular
+ * contact. Similar code is used for 1-1 IM channels in many protocols
+ * (IRC private messages ("/query"), XMPP IM etc.)
+ *
+ * Copyright (C) 2007 Collabora Ltd. <http://www.collabora.co.uk/>
+ * Copyright (C) 2007 Nokia Corporation
+ *
+ * Copying and distribution of this file, with or without modification,
+ * are permitted in any medium without royalty provided the copyright
+ * notice and this notice are preserved.
+ */
+
+#include "chan.h"
+
+#include <telepathy-glib/base-connection.h>
+#include <telepathy-glib/channel-iface.h>
+#include <telepathy-glib/dbus.h>
+#include <telepathy-glib/interfaces.h>
+#include <telepathy-glib/svc-channel.h>
+#include <telepathy-glib/svc-generic.h>
+
+static void text_iface_init (gpointer iface, gpointer data);
+static void channel_iface_init (gpointer iface, gpointer data);
+static void destroyable_iface_init (gpointer iface, gpointer data);
+
+G_DEFINE_TYPE_WITH_CODE (ExampleEchoChannel,
+ example_echo_channel,
+ G_TYPE_OBJECT,
+ G_IMPLEMENT_INTERFACE (TP_TYPE_SVC_DBUS_PROPERTIES,
+ tp_dbus_properties_mixin_iface_init);
+ G_IMPLEMENT_INTERFACE (TP_TYPE_SVC_CHANNEL, channel_iface_init);
+ G_IMPLEMENT_INTERFACE (TP_TYPE_SVC_CHANNEL_TYPE_TEXT, text_iface_init);
+ G_IMPLEMENT_INTERFACE (TP_TYPE_SVC_CHANNEL_INTERFACE_DESTROYABLE,
+ destroyable_iface_init);
+ G_IMPLEMENT_INTERFACE (TP_TYPE_CHANNEL_IFACE, NULL);
+ G_IMPLEMENT_INTERFACE (TP_TYPE_EXPORTABLE_CHANNEL, NULL))
+
+/* type definition stuff */
+
+enum
+{
+ PROP_OBJECT_PATH = 1,
+ PROP_CHANNEL_TYPE,
+ PROP_HANDLE_TYPE,
+ PROP_HANDLE,
+ PROP_TARGET_ID,
+ PROP_REQUESTED,
+ PROP_INITIATOR_HANDLE,
+ PROP_INITIATOR_ID,
+ PROP_CONNECTION,
+ PROP_INTERFACES,
+ PROP_CHANNEL_DESTROYED,
+ PROP_CHANNEL_PROPERTIES,
+ N_PROPS
+};
+
+struct _ExampleEchoChannelPrivate
+{
+ TpBaseConnection *conn;
+ gchar *object_path;
+ TpHandle handle;
+ TpHandle initiator;
+
+ /* These are really booleans, but gboolean is signed. Thanks, GLib */
+ unsigned closed:1;
+ unsigned disposed:1;
+};
+
+static const char * example_echo_channel_interfaces[] = {
+ TP_IFACE_CHANNEL_INTERFACE_DESTROYABLE,
+ NULL
+};
+
+static void
+example_echo_channel_init (ExampleEchoChannel *self)
+{
+ self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, EXAMPLE_TYPE_ECHO_CHANNEL,
+ ExampleEchoChannelPrivate);
+}
+
+static GObject *
+constructor (GType type,
+ guint n_props,
+ GObjectConstructParam *props)
+{
+ GObject *object =
+ G_OBJECT_CLASS (example_echo_channel_parent_class)->constructor (type,
+ n_props, props);
+ ExampleEchoChannel *self = EXAMPLE_ECHO_CHANNEL (object);
+ TpHandleRepoIface *contact_repo = tp_base_connection_get_handles
+ (self->priv->conn, TP_HANDLE_TYPE_CONTACT);
+ DBusGConnection *bus;
+
+ tp_handle_ref (contact_repo, self->priv->handle);
+
+ if (self->priv->initiator != 0)
+ tp_handle_ref (contact_repo, self->priv->initiator);
+
+ bus = tp_get_bus ();
+ dbus_g_connection_register_g_object (bus, self->priv->object_path, object);
+
+ tp_text_mixin_init (object, G_STRUCT_OFFSET (ExampleEchoChannel, text),
+ contact_repo);
+
+ tp_text_mixin_set_message_types (object,
+ TP_CHANNEL_TEXT_MESSAGE_TYPE_NORMAL,
+ TP_CHANNEL_TEXT_MESSAGE_TYPE_ACTION,
+ TP_CHANNEL_TEXT_MESSAGE_TYPE_NOTICE,
+ G_MAXUINT);
+
+ return object;
+}
+
+static void
+get_property (GObject *object,
+ guint property_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ ExampleEchoChannel *self = EXAMPLE_ECHO_CHANNEL (object);
+
+ switch (property_id)
+ {
+ case PROP_OBJECT_PATH:
+ g_value_set_string (value, self->priv->object_path);
+ break;
+ case PROP_CHANNEL_TYPE:
+ g_value_set_static_string (value, TP_IFACE_CHANNEL_TYPE_TEXT);
+ break;
+ case PROP_HANDLE_TYPE:
+ g_value_set_uint (value, TP_HANDLE_TYPE_CONTACT);
+ break;
+ case PROP_HANDLE:
+ g_value_set_uint (value, self->priv->handle);
+ break;
+ case PROP_TARGET_ID:
+ {
+ TpHandleRepoIface *contact_repo = tp_base_connection_get_handles (
+ self->priv->conn, TP_HANDLE_TYPE_CONTACT);
+
+ g_value_set_string (value,
+ tp_handle_inspect (contact_repo, self->priv->handle));
+ }
+ break;
+ case PROP_REQUESTED:
+ g_value_set_boolean (value,
+ (self->priv->initiator == self->priv->conn->self_handle));
+ break;
+ case PROP_INITIATOR_HANDLE:
+ g_value_set_uint (value, self->priv->initiator);
+ break;
+ case PROP_INITIATOR_ID:
+ {
+ TpHandleRepoIface *contact_repo = tp_base_connection_get_handles (
+ self->priv->conn, TP_HANDLE_TYPE_CONTACT);
+
+ g_value_set_string (value,
+ self->priv->initiator == 0
+ ? ""
+ : tp_handle_inspect (contact_repo, self->priv->initiator));
+ }
+ break;
+ case PROP_CONNECTION:
+ g_value_set_object (value, self->priv->conn);
+ break;
+ case PROP_INTERFACES:
+ g_value_set_boxed (value, example_echo_channel_interfaces);
+ break;
+ case PROP_CHANNEL_DESTROYED:
+ g_value_set_boolean (value, self->priv->closed);
+ break;
+ case PROP_CHANNEL_PROPERTIES:
+ g_value_take_boxed (value,
+ tp_dbus_properties_mixin_make_properties_hash (object,
+ TP_IFACE_CHANNEL, "ChannelType",
+ TP_IFACE_CHANNEL, "TargetHandleType",
+ TP_IFACE_CHANNEL, "TargetHandle",
+ TP_IFACE_CHANNEL, "TargetID",
+ TP_IFACE_CHANNEL, "InitiatorHandle",
+ TP_IFACE_CHANNEL, "InitiatorID",
+ TP_IFACE_CHANNEL, "Requested",
+ TP_IFACE_CHANNEL, "Interfaces",
+ NULL));
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+ break;
+ }
+}
+
+static void
+set_property (GObject *object,
+ guint property_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ ExampleEchoChannel *self = EXAMPLE_ECHO_CHANNEL (object);
+
+ switch (property_id)
+ {
+ case PROP_OBJECT_PATH:
+ g_free (self->priv->object_path);
+ self->priv->object_path = g_value_dup_string (value);
+ break;
+ case PROP_HANDLE:
+ /* we don't ref it here because we don't necessarily have access to the
+ * contact repo yet - instead we ref it in the constructor.
+ */
+ self->priv->handle = g_value_get_uint (value);
+ break;
+ case PROP_INITIATOR_HANDLE:
+ /* likewise */
+ self->priv->initiator = g_value_get_uint (value);
+ break;
+ case PROP_HANDLE_TYPE:
+ case PROP_CHANNEL_TYPE:
+ /* these properties are writable in the interface, but not actually
+ * meaningfully changable on this channel, so we do nothing */
+ break;
+ case PROP_CONNECTION:
+ self->priv->conn = g_value_get_object (value);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+ break;
+ }
+}
+
+static void
+dispose (GObject *object)
+{
+ ExampleEchoChannel *self = EXAMPLE_ECHO_CHANNEL (object);
+
+ if (self->priv->disposed)
+ return;
+
+ self->priv->disposed = TRUE;
+
+ if (!self->priv->closed)
+ {
+ self->priv->closed = TRUE;
+ tp_svc_channel_emit_closed (self);
+ }
+
+ ((GObjectClass *) example_echo_channel_parent_class)->dispose (object);
+}
+
+static void
+finalize (GObject *object)
+{
+ ExampleEchoChannel *self = EXAMPLE_ECHO_CHANNEL (object);
+ TpHandleRepoIface *contact_handles = tp_base_connection_get_handles
+ (self->priv->conn, TP_HANDLE_TYPE_CONTACT);
+
+ tp_handle_unref (contact_handles, self->priv->handle);
+
+ if (self->priv->initiator != 0)
+ tp_handle_unref (contact_handles, self->priv->initiator);
+
+ g_free (self->priv->object_path);
+
+ tp_text_mixin_finalize (object);
+
+ ((GObjectClass *) example_echo_channel_parent_class)->finalize (object);
+}
+
+static void
+example_echo_channel_class_init (ExampleEchoChannelClass *klass)
+{
+ static TpDBusPropertiesMixinPropImpl channel_props[] = {
+ { "TargetHandleType", "handle-type", NULL },
+ { "TargetHandle", "handle", NULL },
+ { "ChannelType", "channel-type", NULL },
+ { "Interfaces", "interfaces", NULL },
+ { "TargetID", "target-id", NULL },
+ { "Requested", "requested", NULL },
+ { "InitiatorHandle", "initiator-handle", NULL },
+ { "InitiatorID", "initiator-id", NULL },
+ { NULL }
+ };
+ static TpDBusPropertiesMixinIfaceImpl prop_interfaces[] = {
+ { TP_IFACE_CHANNEL,
+ tp_dbus_properties_mixin_getter_gobject_properties,
+ NULL,
+ channel_props,
+ },
+ { NULL }
+ };
+ GObjectClass *object_class = (GObjectClass *) klass;
+ GParamSpec *param_spec;
+
+ g_type_class_add_private (klass, sizeof (ExampleEchoChannelPrivate));
+
+ object_class->constructor = constructor;
+ object_class->set_property = set_property;
+ object_class->get_property = get_property;
+ object_class->dispose = dispose;
+ object_class->finalize = finalize;
+
+ g_object_class_override_property (object_class, PROP_OBJECT_PATH,
+ "object-path");
+ g_object_class_override_property (object_class, PROP_CHANNEL_TYPE,
+ "channel-type");
+ g_object_class_override_property (object_class, PROP_HANDLE_TYPE,
+ "handle-type");
+ g_object_class_override_property (object_class, PROP_HANDLE, "handle");
+
+ g_object_class_override_property (object_class, PROP_CHANNEL_DESTROYED,
+ "channel-destroyed");
+ g_object_class_override_property (object_class, PROP_CHANNEL_PROPERTIES,
+ "channel-properties");
+
+ param_spec = g_param_spec_object ("connection", "TpBaseConnection object",
+ "Connection object that owns this channel",
+ TP_TYPE_BASE_CONNECTION,
+ G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
+ g_object_class_install_property (object_class, PROP_CONNECTION, param_spec);
+
+ param_spec = g_param_spec_boxed ("interfaces", "Extra D-Bus interfaces",
+ "Additional Channel.Interface.* interfaces",
+ G_TYPE_STRV,
+ G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
+ g_object_class_install_property (object_class, PROP_INTERFACES, param_spec);
+
+ param_spec = g_param_spec_string ("target-id", "Peer's ID",
+ "The string obtained by inspecting the target handle",
+ NULL,
+ G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
+ g_object_class_install_property (object_class, PROP_TARGET_ID, param_spec);
+
+ param_spec = g_param_spec_uint ("initiator-handle", "Initiator's handle",
+ "The contact who initiated the channel",
+ 0, G_MAXUINT32, 0,
+ G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
+ g_object_class_install_property (object_class, PROP_INITIATOR_HANDLE,
+ param_spec);
+
+ param_spec = g_param_spec_string ("initiator-id", "Initiator's ID",
+ "The string obtained by inspecting the initiator-handle",
+ NULL,
+ G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
+ g_object_class_install_property (object_class, PROP_INITIATOR_ID,
+ param_spec);
+
+ param_spec = g_param_spec_boolean ("requested", "Requested?",
+ "True if this channel was requested by the local user",
+ FALSE,
+ G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
+ g_object_class_install_property (object_class, PROP_REQUESTED, param_spec);
+
+ tp_text_mixin_class_init (object_class,
+ G_STRUCT_OFFSET (ExampleEchoChannelClass, text_class));
+
+ klass->dbus_properties_class.interfaces = prop_interfaces;
+ tp_dbus_properties_mixin_class_init (object_class,
+ G_STRUCT_OFFSET (ExampleEchoChannelClass, dbus_properties_class));
+}
+
+static void
+example_echo_channel_close (ExampleEchoChannel *self)
+{
+ GObject *object = (GObject *) self;
+
+ if (!self->priv->closed)
+ {
+ TpHandle first_sender;
+
+ /* The manager wants to be able to respawn the channel if it has pending
+ * messages. When respawned, the channel must have the initiator set
+ * to the contact who sent us those messages (if it isn't already),
+ * and the messages must be marked as having been rescued so they
+ * don't get logged twice. */
+ if (tp_text_mixin_has_pending_messages (object, &first_sender))
+ {
+ if (self->priv->initiator != first_sender)
+ {
+ TpHandleRepoIface *contact_repo = tp_base_connection_get_handles
+ (self->priv->conn, TP_HANDLE_TYPE_CONTACT);
+ TpHandle old_initiator = self->priv->initiator;
+
+ if (first_sender != 0)
+ tp_handle_ref (contact_repo, first_sender);
+
+ self->priv->initiator = first_sender;
+
+ if (old_initiator != 0)
+ tp_handle_unref (contact_repo, old_initiator);
+ }
+
+ tp_text_mixin_set_rescued (object);
+ }
+ else
+ {
+ /* No pending messages, so it's OK to really close */
+ self->priv->closed = TRUE;
+ }
+
+ tp_svc_channel_emit_closed (self);
+ }
+}
+
+static void
+channel_close (TpSvcChannel *iface,
+ DBusGMethodInvocation *context)
+{
+ ExampleEchoChannel *self = EXAMPLE_ECHO_CHANNEL (iface);
+
+ example_echo_channel_close (self);
+ tp_svc_channel_return_from_close (context);
+}
+
+static void
+channel_get_channel_type (TpSvcChannel *iface,
+ DBusGMethodInvocation *context)
+{
+ tp_svc_channel_return_from_get_channel_type (context,
+ TP_IFACE_CHANNEL_TYPE_TEXT);
+}
+
+static void
+channel_get_handle (TpSvcChannel *iface,
+ DBusGMethodInvocation *context)
+{
+ ExampleEchoChannel *self = EXAMPLE_ECHO_CHANNEL (iface);
+
+ tp_svc_channel_return_from_get_handle (context, TP_HANDLE_TYPE_CONTACT,
+ self->priv->handle);
+}
+
+static void
+channel_get_interfaces (TpSvcChannel *iface,
+ DBusGMethodInvocation *context)
+{
+ tp_svc_channel_return_from_get_interfaces (context,
+ example_echo_channel_interfaces);
+}
+
+static void
+channel_iface_init (gpointer iface,
+ gpointer data)
+{
+ TpSvcChannelClass *klass = iface;
+
+#define IMPLEMENT(x) tp_svc_channel_implement_##x (klass, channel_##x)
+ IMPLEMENT (close);
+ IMPLEMENT (get_channel_type);
+ IMPLEMENT (get_handle);
+ IMPLEMENT (get_interfaces);
+#undef IMPLEMENT
+}
+
+static void
+text_send (TpSvcChannelTypeText *iface,
+ guint type,
+ const gchar *text,
+ DBusGMethodInvocation *context)
+{
+ ExampleEchoChannel *self = EXAMPLE_ECHO_CHANNEL (iface);
+ time_t timestamp = time (NULL);
+ gchar *echo;
+ guint echo_type = type;
+
+ /* Send should return just before Sent is emitted. */
+ tp_svc_channel_type_text_return_from_send (context);
+
+ /* Tell the client that the message was submitted for sending */
+ tp_svc_channel_type_text_emit_sent ((GObject *) self, timestamp, type, text);
+
+ /* Pretend that the remote contact has replied. Normally, you'd
+ * call tp_text_mixin_receive or tp_text_mixin_receive_with_flags
+ * in response to network events */
+
+ switch (type)
+ {
+ case TP_CHANNEL_TEXT_MESSAGE_TYPE_NORMAL:
+ echo = g_strdup_printf ("You said: %s", text);
+ break;
+ case TP_CHANNEL_TEXT_MESSAGE_TYPE_ACTION:
+ echo = g_strdup_printf ("notices that the user %s", text);
+ break;
+ case TP_CHANNEL_TEXT_MESSAGE_TYPE_NOTICE:
+ echo = g_strdup_printf ("You sent a notice: %s", text);
+ break;
+ default:
+ echo = g_strdup_printf ("You sent some weird message type, %u: \"%s\"",
+ type, text);
+ echo_type = TP_CHANNEL_TEXT_MESSAGE_TYPE_NORMAL;
+ }
+
+ tp_text_mixin_receive ((GObject *) self, echo_type, self->priv->handle,
+ timestamp, echo);
+
+ g_free (echo);
+}
+
+static void
+text_iface_init (gpointer iface,
+ gpointer data)
+{
+ TpSvcChannelTypeTextClass *klass = iface;
+
+ tp_text_mixin_iface_init (iface, data);
+#define IMPLEMENT(x) tp_svc_channel_type_text_implement_##x (klass, text_##x)
+ IMPLEMENT (send);
+#undef IMPLEMENT
+}
+
+static void
+destroyable_destroy (TpSvcChannelInterfaceDestroyable *iface,
+ DBusGMethodInvocation *context)
+{
+ ExampleEchoChannel *self = EXAMPLE_ECHO_CHANNEL (iface);
+
+ tp_text_mixin_clear ((GObject *) self);
+ example_echo_channel_close (self);
+ g_assert (self->priv->closed);
+ tp_svc_channel_interface_destroyable_return_from_destroy (context);
+}
+
+static void
+destroyable_iface_init (gpointer iface,
+ gpointer data)
+{
+ TpSvcChannelInterfaceDestroyableClass *klass = iface;
+
+#define IMPLEMENT(x) \
+ tp_svc_channel_interface_destroyable_implement_##x (klass, destroyable_##x)
+ IMPLEMENT (destroy);
+#undef IMPLEMENT
+}
diff --git a/tests/lib/echo/chan.h b/tests/lib/echo/chan.h
new file mode 100644
index 0000000..81afc2e
--- /dev/null
+++ b/tests/lib/echo/chan.h
@@ -0,0 +1,58 @@
+/*
+ * chan.h - header for an example channel
+ *
+ * Copyright (C) 2007 Collabora Ltd. <http://www.collabora.co.uk/>
+ * Copyright (C) 2007 Nokia Corporation
+ *
+ * Copying and distribution of this file, with or without modification,
+ * are permitted in any medium without royalty provided the copyright
+ * notice and this notice are preserved.
+ */
+
+#ifndef __EXAMPLE_CHAN_H__
+#define __EXAMPLE_CHAN_H__
+
+#include <glib-object.h>
+#include <telepathy-glib/base-connection.h>
+#include <telepathy-glib/text-mixin.h>
+
+G_BEGIN_DECLS
+
+typedef struct _ExampleEchoChannel ExampleEchoChannel;
+typedef struct _ExampleEchoChannelClass ExampleEchoChannelClass;
+typedef struct _ExampleEchoChannelPrivate ExampleEchoChannelPrivate;
+
+GType example_echo_channel_get_type (void);
+
+#define EXAMPLE_TYPE_ECHO_CHANNEL \
+ (example_echo_channel_get_type ())
+#define EXAMPLE_ECHO_CHANNEL(obj) \
+ (G_TYPE_CHECK_INSTANCE_CAST ((obj), EXAMPLE_TYPE_ECHO_CHANNEL, \
+ ExampleEchoChannel))
+#define EXAMPLE_ECHO_CHANNEL_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_CAST ((klass), EXAMPLE_TYPE_ECHO_CHANNEL, \
+ ExampleEchoChannelClass))
+#define EXAMPLE_IS_ECHO_CHANNEL(obj) \
+ (G_TYPE_CHECK_INSTANCE_TYPE ((obj), EXAMPLE_TYPE_ECHO_CHANNEL))
+#define EXAMPLE_IS_ECHO_CHANNEL_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_TYPE ((klass), EXAMPLE_TYPE_ECHO_CHANNEL))
+#define EXAMPLE_ECHO_CHANNEL_GET_CLASS(obj) \
+ (G_TYPE_INSTANCE_GET_CLASS ((obj), EXAMPLE_TYPE_ECHO_CHANNEL, \
+ ExampleEchoChannelClass))
+
+struct _ExampleEchoChannelClass {
+ GObjectClass parent_class;
+ TpTextMixinClass text_class;
+ TpDBusPropertiesMixinClass dbus_properties_class;
+};
+
+struct _ExampleEchoChannel {
+ GObject parent;
+ TpTextMixin text;
+
+ ExampleEchoChannelPrivate *priv;
+};
+
+G_END_DECLS
+
+#endif /* #ifndef __EXAMPLE_CHAN_H__ */
diff --git a/tests/lib/echo/conn.c b/tests/lib/echo/conn.c
new file mode 100644
index 0000000..cba3834
--- /dev/null
+++ b/tests/lib/echo/conn.c
@@ -0,0 +1,194 @@
+/*
+ * conn.c - an example connection
+ *
+ * Copyright (C) 2007 Collabora Ltd. <http://www.collabora.co.uk/>
+ * Copyright (C) 2007 Nokia Corporation
+ *
+ * Copying and distribution of this file, with or without modification,
+ * are permitted in any medium without royalty provided the copyright
+ * notice and this notice are preserved.
+ */
+
+#include "conn.h"
+
+#include <dbus/dbus-glib.h>
+
+#include <telepathy-glib/dbus.h>
+#include <telepathy-glib/errors.h>
+#include <telepathy-glib/handle-repo-dynamic.h>
+#include <telepathy-glib/interfaces.h>
+
+#include "im-manager.h"
+
+G_DEFINE_TYPE (ExampleEchoConnection,
+ example_echo_connection,
+ TP_TYPE_BASE_CONNECTION)
+
+/* type definition stuff */
+
+enum
+{
+ PROP_ACCOUNT = 1,
+ N_PROPS
+};
+
+struct _ExampleEchoConnectionPrivate
+{
+ gchar *account;
+};
+
+static void
+example_echo_connection_init (ExampleEchoConnection *self)
+{
+ self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, EXAMPLE_TYPE_ECHO_CONNECTION,
+ ExampleEchoConnectionPrivate);
+}
+
+static void
+get_property (GObject *object,
+ guint property_id,
+ GValue *value,
+ GParamSpec *spec)
+{
+ ExampleEchoConnection *self = EXAMPLE_ECHO_CONNECTION (object);
+
+ switch (property_id) {
+ case PROP_ACCOUNT:
+ g_value_set_string (value, self->priv->account);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, spec);
+ }
+}
+
+static void
+set_property (GObject *object,
+ guint property_id,
+ const GValue *value,
+ GParamSpec *spec)
+{
+ ExampleEchoConnection *self = EXAMPLE_ECHO_CONNECTION (object);
+
+ switch (property_id) {
+ case PROP_ACCOUNT:
+ g_free (self->priv->account);
+ self->priv->account = g_utf8_strdown (g_value_get_string (value), -1);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, spec);
+ }
+}
+
+static void
+finalize (GObject *object)
+{
+ ExampleEchoConnection *self = EXAMPLE_ECHO_CONNECTION (object);
+
+ g_free (self->priv->account);
+
+ G_OBJECT_CLASS (example_echo_connection_parent_class)->finalize (object);
+}
+
+static gchar *
+get_unique_connection_name (TpBaseConnection *conn)
+{
+ ExampleEchoConnection *self = EXAMPLE_ECHO_CONNECTION (conn);
+
+ return g_strdup (self->priv->account);
+}
+
+static gchar *
+example_echo_normalize_contact (TpHandleRepoIface *repo,
+ const gchar *id,
+ gpointer context,
+ GError **error)
+{
+ if (id[0] == '\0')
+ {
+ g_set_error (error, TP_ERRORS, TP_ERROR_INVALID_HANDLE,
+ "ID must not be empty");
+ return NULL;
+ }
+
+ return g_utf8_strdown (id, -1);
+}
+
+static void
+create_handle_repos (TpBaseConnection *conn,
+ TpHandleRepoIface *repos[NUM_TP_HANDLE_TYPES])
+{
+ repos[TP_HANDLE_TYPE_CONTACT] = tp_dynamic_handle_repo_new
+ (TP_HANDLE_TYPE_CONTACT, example_echo_normalize_contact, NULL);
+}
+
+static GPtrArray *
+create_channel_managers (TpBaseConnection *conn)
+{
+ GPtrArray *ret = g_ptr_array_sized_new (1);
+
+ g_ptr_array_add (ret, g_object_new (EXAMPLE_TYPE_ECHO_IM_MANAGER,
+ "connection", conn,
+ NULL));
+
+ return ret;
+}
+
+static gboolean
+start_connecting (TpBaseConnection *conn,
+ GError **error)
+{
+ ExampleEchoConnection *self = EXAMPLE_ECHO_CONNECTION (conn);
+ TpHandleRepoIface *contact_repo = tp_base_connection_get_handles (conn,
+ TP_HANDLE_TYPE_CONTACT);
+
+ /* In a real connection manager we'd ask the underlying implementation to
+ * start connecting, then go to state CONNECTED when finished, but here
+ * we can do it immediately. */
+
+ conn->self_handle = tp_handle_ensure (contact_repo, self->priv->account,
+ NULL, NULL);
+
+ tp_base_connection_change_status (conn, TP_CONNECTION_STATUS_CONNECTED,
+ TP_CONNECTION_STATUS_REASON_REQUESTED);
+
+ return TRUE;
+}
+
+static void
+shut_down (TpBaseConnection *conn)
+{
+ /* In a real connection manager we'd ask the underlying implementation to
+ * start shutting down, then call this function when finished, but here
+ * we can do it immediately. */
+ tp_base_connection_finish_shutdown (conn);
+}
+
+static void
+example_echo_connection_class_init (ExampleEchoConnectionClass *klass)
+{
+ static const gchar *interfaces_always_present[] = {
+ TP_IFACE_CONNECTION_INTERFACE_REQUESTS,
+ NULL };
+ TpBaseConnectionClass *base_class =
+ (TpBaseConnectionClass *) klass;
+ GObjectClass *object_class = (GObjectClass *) klass;
+ GParamSpec *param_spec;
+
+ object_class->get_property = get_property;
+ object_class->set_property = set_property;
+ object_class->finalize = finalize;
+ g_type_class_add_private (klass, sizeof (ExampleEchoConnectionPrivate));
+
+ base_class->create_handle_repos = create_handle_repos;
+ base_class->get_unique_connection_name = get_unique_connection_name;
+ base_class->create_channel_managers = create_channel_managers;
+ base_class->start_connecting = start_connecting;
+ base_class->shut_down = shut_down;
+ base_class->interfaces_always_present = interfaces_always_present;
+
+ param_spec = g_param_spec_string ("account", "Account name",
+ "The username of this user", NULL,
+ G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE |
+ G_PARAM_STATIC_NAME | G_PARAM_STATIC_NICK | G_PARAM_STATIC_BLURB);
+ g_object_class_install_property (object_class, PROP_ACCOUNT, param_spec);
+}
diff --git a/tests/lib/echo/conn.h b/tests/lib/echo/conn.h
new file mode 100644
index 0000000..dfbf365
--- /dev/null
+++ b/tests/lib/echo/conn.h
@@ -0,0 +1,55 @@
+/*
+ * conn.h - header for an example connection
+ *
+ * Copyright (C) 2007 Collabora Ltd. <http://www.collabora.co.uk/>
+ * Copyright (C) 2007 Nokia Corporation
+ *
+ * Copying and distribution of this file, with or without modification,
+ * are permitted in any medium without royalty provided the copyright
+ * notice and this notice are preserved.
+ */
+
+#ifndef __EXAMPLE_ECHO_CONN_H__
+#define __EXAMPLE_ECHO_CONN_H__
+
+#include <glib-object.h>
+#include <telepathy-glib/base-connection.h>
+
+G_BEGIN_DECLS
+
+typedef struct _ExampleEchoConnection ExampleEchoConnection;
+typedef struct _ExampleEchoConnectionClass ExampleEchoConnectionClass;
+typedef struct _ExampleEchoConnectionPrivate ExampleEchoConnectionPrivate;
+
+struct _ExampleEchoConnectionClass {
+ TpBaseConnectionClass parent_class;
+};
+
+struct _ExampleEchoConnection {
+ TpBaseConnection parent;
+
+ ExampleEchoConnectionPrivate *priv;
+};
+
+GType example_echo_connection_get_type (void);
+
+/* TYPE MACROS */
+#define EXAMPLE_TYPE_ECHO_CONNECTION \
+ (example_echo_connection_get_type ())
+#define EXAMPLE_ECHO_CONNECTION(obj) \
+ (G_TYPE_CHECK_INSTANCE_CAST((obj), EXAMPLE_TYPE_ECHO_CONNECTION, \
+ ExampleEchoConnection))
+#define EXAMPLE_ECHO_CONNECTION_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_CAST((klass), EXAMPLE_TYPE_ECHO_CONNECTION, \
+ ExampleEchoConnectionClass))
+#define EXAMPLE_IS_ECHO_CONNECTION(obj) \
+ (G_TYPE_CHECK_INSTANCE_TYPE((obj), EXAMPLE_TYPE_ECHO_CONNECTION))
+#define EXAMPLE_IS_ECHO_CONNECTION_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_TYPE((klass), EXAMPLE_TYPE_ECHO_CONNECTION))
+#define EXAMPLE_ECHO_CONNECTION_GET_CLASS(obj) \
+ (G_TYPE_INSTANCE_GET_CLASS ((obj), EXAMPLE_TYPE_ECHO_CONNECTION, \
+ ExampleEchoConnectionClass))
+
+G_END_DECLS
+
+#endif
diff --git a/tests/lib/echo/connection-manager.c b/tests/lib/echo/connection-manager.c
new file mode 100644
index 0000000..9eb72ad
--- /dev/null
+++ b/tests/lib/echo/connection-manager.c
@@ -0,0 +1,95 @@
+/*
+ * manager.c - an example connection manager
+ *
+ * Copyright (C) 2007 Collabora Ltd.
+ *
+ * Copying and distribution of this file, with or without modification,
+ * are permitted in any medium without royalty provided the copyright
+ * notice and this notice are preserved.
+ */
+
+#include "connection-manager.h"
+
+#include <dbus/dbus-protocol.h>
+#include <dbus/dbus-glib.h>
+
+#include <telepathy-glib/dbus.h>
+#include <telepathy-glib/errors.h>
+
+#include "conn.h"
+
+G_DEFINE_TYPE (ExampleEchoConnectionManager,
+ example_echo_connection_manager,
+ TP_TYPE_BASE_CONNECTION_MANAGER)
+
+/* type definition stuff */
+
+static void
+example_echo_connection_manager_init (ExampleEchoConnectionManager *self)
+{
+}
+
+/* private data */
+
+typedef struct {
+ gchar *account;
+} ExampleParams;
+
+static const TpCMParamSpec example_params[] = {
+ { "account", DBUS_TYPE_STRING_AS_STRING, G_TYPE_STRING,
+ TP_CONN_MGR_PARAM_FLAG_REQUIRED | TP_CONN_MGR_PARAM_FLAG_REGISTER, NULL,
+ G_STRUCT_OFFSET (ExampleParams, account),
+ tp_cm_param_filter_string_nonempty, NULL },
+
+ { NULL }
+};
+
+static gpointer
+alloc_params (void)
+{
+ return g_slice_new0 (ExampleParams);
+}
+
+static void
+free_params (gpointer p)
+{
+ ExampleParams *params = p;
+
+ g_free (params->account);
+
+ g_slice_free (ExampleParams, params);
+}
+
+static const TpCMProtocolSpec example_protocols[] = {
+ { "example", example_params, alloc_params, free_params },
+ { NULL, NULL }
+};
+
+static TpBaseConnection *
+new_connection (TpBaseConnectionManager *self,
+ const gchar *proto,
+ TpIntSet *params_present,
+ gpointer parsed_params,
+ GError **error)
+{
+ ExampleParams *params = parsed_params;
+ ExampleEchoConnection *conn = EXAMPLE_ECHO_CONNECTION
+ (g_object_new (EXAMPLE_TYPE_ECHO_CONNECTION,
+ "account", params->account,
+ "protocol", proto,
+ NULL));
+
+ return (TpBaseConnection *) conn;
+}
+
+static void
+example_echo_connection_manager_class_init (
+ ExampleEchoConnectionManagerClass *klass)
+{
+ TpBaseConnectionManagerClass *base_class =
+ (TpBaseConnectionManagerClass *) klass;
+
+ base_class->new_connection = new_connection;
+ base_class->cm_dbus_name = "example_echo";
+ base_class->protocol_params = example_protocols;
+}
diff --git a/tests/lib/echo/connection-manager.h b/tests/lib/echo/connection-manager.h
new file mode 100644
index 0000000..7cbf615
--- /dev/null
+++ b/tests/lib/echo/connection-manager.h
@@ -0,0 +1,53 @@
+/*
+ * manager.h - header for an example connection manager
+ * Copyright (C) 2007 Collabora Ltd.
+ *
+ * Copying and distribution of this file, with or without modification,
+ * are permitted in any medium without royalty provided the copyright
+ * notice and this notice are preserved.
+ */
+
+#ifndef __EXAMPLE_ECHO_CONNECTION_MANAGER_H__
+#define __EXAMPLE_ECHO_CONNECTION_MANAGER_H__
+
+#include <glib-object.h>
+#include <telepathy-glib/base-connection-manager.h>
+
+G_BEGIN_DECLS
+
+typedef struct _ExampleEchoConnectionManager ExampleEchoConnectionManager;
+typedef struct _ExampleEchoConnectionManagerClass
+ ExampleEchoConnectionManagerClass;
+
+struct _ExampleEchoConnectionManagerClass {
+ TpBaseConnectionManagerClass parent_class;
+};
+
+struct _ExampleEchoConnectionManager {
+ TpBaseConnectionManager parent;
+
+ gpointer priv;
+};
+
+GType example_echo_connection_manager_get_type (void);
+
+/* TYPE MACROS */
+#define EXAMPLE_TYPE_ECHO_CONNECTION_MANAGER \
+ (example_echo_connection_manager_get_type ())
+#define EXAMPLE_ECHO_CONNECTION_MANAGER(obj) \
+ (G_TYPE_CHECK_INSTANCE_CAST((obj), EXAMPLE_TYPE_ECHO_CONNECTION_MANAGER, \
+ ExampleEchoConnectionManager))
+#define EXAMPLE_ECHO_CONNECTION_MANAGER_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_CAST((klass), EXAMPLE_TYPE_ECHO_CONNECTION_MANAGER, \
+ ExampleEchoConnectionManagerClass))
+#define EXAMPLE_IS_ECHO_CONNECTION_MANAGER(obj) \
+ (G_TYPE_CHECK_INSTANCE_TYPE((obj), EXAMPLE_TYPE_ECHO_CONNECTION_MANAGER))
+#define EXAMPLE_IS_ECHO_CONNECTION_MANAGER_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_TYPE((klass), EXAMPLE_TYPE_ECHO_CONNECTION_MANAGER))
+#define EXAMPLE_ECHO_CONNECTION_MANAGER_GET_CLASS(obj) \
+ (G_TYPE_INSTANCE_GET_CLASS ((obj), EXAMPLE_TYPE_ECHO_CONNECTION_MANAGER, \
+ ExampleEchoConnectionManagerClass))
+
+G_END_DECLS
+
+#endif
diff --git a/tests/lib/echo/im-manager.c b/tests/lib/echo/im-manager.c
new file mode 100644
index 0000000..9be1838
--- /dev/null
+++ b/tests/lib/echo/im-manager.c
@@ -0,0 +1,389 @@
+/*
+ * im-manager.c - an example channel manager for channels talking to a
+ * particular contact. Similar code is used for 1-1 IM channels in many
+ * protocols (IRC private messages ("/query"), XMPP IM etc.)
+ *
+ * Copyright (C) 2007 Collabora Ltd. <http://www.collabora.co.uk/>
+ * Copyright (C) 2007 Nokia Corporation
+ *
+ * Copying and distribution of this file, with or without modification,
+ * are permitted in any medium without royalty provided the copyright
+ * notice and this notice are preserved.
+ */
+
+#include "im-manager.h"
+
+#include <dbus/dbus-glib.h>
+
+#include <telepathy-glib/base-connection.h>
+#include <telepathy-glib/channel-manager.h>
+#include <telepathy-glib/dbus.h>
+#include <telepathy-glib/errors.h>
+#include <telepathy-glib/interfaces.h>
+
+#include "chan.h"
+
+static void channel_manager_iface_init (gpointer, gpointer);
+
+G_DEFINE_TYPE_WITH_CODE (ExampleEchoImManager,
+ example_echo_im_manager,
+ G_TYPE_OBJECT,
+ G_IMPLEMENT_INTERFACE (TP_TYPE_CHANNEL_MANAGER,
+ channel_manager_iface_init))
+
+/* type definition stuff */
+
+enum
+{
+ PROP_CONNECTION = 1,
+ N_PROPS
+};
+
+struct _ExampleEchoImManagerPrivate
+{
+ TpBaseConnection *conn;
+
+ /* GUINT_TO_POINTER (handle) => ExampleEchoChannel */
+ GHashTable *channels;
+ gulong status_changed_id;
+};
+
+static void
+example_echo_im_manager_init (ExampleEchoImManager *self)
+{
+ self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, EXAMPLE_TYPE_ECHO_IM_MANAGER,
+ ExampleEchoImManagerPrivate);
+
+ self->priv->channels = g_hash_table_new_full (g_direct_hash, g_direct_equal,
+ NULL, g_object_unref);
+}
+
+static void example_echo_im_manager_close_all (ExampleEchoImManager *self);
+
+static void
+dispose (GObject *object)
+{
+ ExampleEchoImManager *self = EXAMPLE_ECHO_IM_MANAGER (object);
+
+ example_echo_im_manager_close_all (self);
+ g_assert (self->priv->channels == NULL);
+
+ ((GObjectClass *) example_echo_im_manager_parent_class)->dispose (object);
+}
+
+static void
+get_property (GObject *object,
+ guint property_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ ExampleEchoImManager *self = EXAMPLE_ECHO_IM_MANAGER (object);
+
+ switch (property_id)
+ {
+ case PROP_CONNECTION:
+ g_value_set_object (value, self->priv->conn);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+ }
+}
+
+static void
+set_property (GObject *object,
+ guint property_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ ExampleEchoImManager *self = EXAMPLE_ECHO_IM_MANAGER (object);
+
+ switch (property_id)
+ {
+ case PROP_CONNECTION:
+ /* We don't ref the connection, because it owns a reference to the
+ * channel manager, and it guarantees that the manager's lifetime is
+ * less than its lifetime */
+ self->priv->conn = g_value_get_object (value);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+ }
+}
+
+static void
+status_changed_cb (TpBaseConnection *conn,
+ guint status,
+ guint reason,
+ ExampleEchoImManager *self)
+{
+ if (status == TP_CONNECTION_STATUS_DISCONNECTED)
+ example_echo_im_manager_close_all (self);
+}
+
+static void
+constructed (GObject *object)
+{
+ ExampleEchoImManager *self = EXAMPLE_ECHO_IM_MANAGER (object);
+ void (*chain_up) (GObject *) =
+ ((GObjectClass *) example_echo_im_manager_parent_class)->constructed;
+
+ if (chain_up != NULL)
+ {
+ chain_up (object);
+ }
+
+ self->priv->status_changed_id = g_signal_connect (self->priv->conn,
+ "status-changed", (GCallback) status_changed_cb, self);
+}
+
+static void
+example_echo_im_manager_class_init (ExampleEchoImManagerClass *klass)
+{
+ GParamSpec *param_spec;
+ GObjectClass *object_class = (GObjectClass *) klass;
+
+ object_class->constructed = constructed;
+ object_class->dispose = dispose;
+ object_class->get_property = get_property;
+ object_class->set_property = set_property;
+
+ param_spec = g_param_spec_object ("connection", "Connection object",
+ "The connection that owns this channel manager",
+ TP_TYPE_BASE_CONNECTION,
+ G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE |
+ G_PARAM_STATIC_NAME | G_PARAM_STATIC_NICK | G_PARAM_STATIC_BLURB);
+ g_object_class_install_property (object_class, PROP_CONNECTION, param_spec);
+
+ g_type_class_add_private (klass, sizeof (ExampleEchoImManagerPrivate));
+}
+
+static void
+example_echo_im_manager_close_all (ExampleEchoImManager *self)
+{
+ if (self->priv->channels != NULL)
+ {
+ GHashTable *tmp = self->priv->channels;
+
+ self->priv->channels = NULL;
+ g_hash_table_destroy (tmp);
+ }
+
+ if (self->priv->status_changed_id != 0)
+ {
+ g_signal_handler_disconnect (self->priv->conn,
+ self->priv->status_changed_id);
+ self->priv->status_changed_id = 0;
+ }
+}
+
+static void
+example_echo_im_manager_foreach_channel (TpChannelManager *iface,
+ TpExportableChannelFunc callback,
+ gpointer user_data)
+{
+ ExampleEchoImManager *self = EXAMPLE_ECHO_IM_MANAGER (iface);
+ GHashTableIter iter;
+ gpointer handle, channel;
+
+ g_hash_table_iter_init (&iter, self->priv->channels);
+
+ while (g_hash_table_iter_next (&iter, &handle, &channel))
+ {
+ callback (TP_EXPORTABLE_CHANNEL (channel), user_data);
+ }
+}
+
+static void
+channel_closed_cb (ExampleEchoChannel *chan,
+ ExampleEchoImManager *self)
+{
+ tp_channel_manager_emit_channel_closed_for_object (self,
+ TP_EXPORTABLE_CHANNEL (chan));
+
+ if (self->priv->channels != NULL)
+ {
+ TpHandle handle;
+ gboolean really_destroyed;
+
+ g_object_get (chan,
+ "handle", &handle,
+ "channel-destroyed", &really_destroyed,
+ NULL);
+
+ /* Re-announce the channel if it's not yet ready to go away (pending
+ * messages) */
+ if (really_destroyed)
+ {
+ g_hash_table_remove (self->priv->channels,
+ GUINT_TO_POINTER (handle));
+ }
+ else
+ {
+ tp_channel_manager_emit_new_channel (self,
+ TP_EXPORTABLE_CHANNEL (chan), NULL);
+ }
+ }
+}
+
+static ExampleEchoChannel *
+new_channel (ExampleEchoImManager *self,
+ TpHandle handle,
+ TpHandle initiator,
+ gpointer request_token)
+{
+ ExampleEchoChannel *chan;
+ gchar *object_path;
+ GSList *requests = NULL;
+
+ object_path = g_strdup_printf ("%s/EchoChannel%u",
+ self->priv->conn->object_path, handle);
+
+ chan = g_object_new (EXAMPLE_TYPE_ECHO_CHANNEL,
+ "connection", self->priv->conn,
+ "object-path", object_path,
+ "handle", handle,
+ "initiator-handle", initiator,
+ NULL);
+
+ g_free (object_path);
+
+ g_signal_connect (chan, "closed", (GCallback) channel_closed_cb, self);
+
+ g_hash_table_insert (self->priv->channels, GUINT_TO_POINTER (handle), chan);
+
+ if (request_token != NULL)
+ requests = g_slist_prepend (requests, request_token);
+
+ tp_channel_manager_emit_new_channel (self, TP_EXPORTABLE_CHANNEL (chan),
+ requests);
+ g_slist_free (requests);
+
+ return chan;
+}
+
+static const gchar * const fixed_properties[] = {
+ TP_IFACE_CHANNEL ".ChannelType",
+ TP_IFACE_CHANNEL ".TargetHandleType",
+ NULL
+};
+
+static const gchar * const allowed_properties[] = {
+ TP_IFACE_CHANNEL ".TargetHandle",
+ TP_IFACE_CHANNEL ".TargetID",
+ NULL
+};
+
+static void
+example_echo_im_manager_foreach_channel_class (TpChannelManager *manager,
+ TpChannelManagerChannelClassFunc func,
+ gpointer user_data)
+{
+ GHashTable *table = g_hash_table_new_full (g_str_hash, g_str_equal,
+ NULL, (GDestroyNotify) tp_g_value_slice_free);
+ GValue *value;
+
+ value = tp_g_value_slice_new (G_TYPE_STRING);
+ g_value_set_static_string (value, TP_IFACE_CHANNEL_TYPE_TEXT);
+ g_hash_table_insert (table, TP_IFACE_CHANNEL ".ChannelType", value);
+
+ value = tp_g_value_slice_new (G_TYPE_UINT);
+ g_value_set_uint (value, TP_HANDLE_TYPE_CONTACT);
+ g_hash_table_insert (table, TP_IFACE_CHANNEL ".TargetHandleType", value);
+
+ func (manager, table, allowed_properties, user_data);
+
+ g_hash_table_destroy (table);
+}
+
+static gboolean
+example_echo_im_manager_request (ExampleEchoImManager *self,
+ gpointer request_token,
+ GHashTable *request_properties,
+ gboolean require_new)
+{
+ TpHandle handle;
+ ExampleEchoChannel *chan;
+ GError *error = NULL;
+
+ if (tp_strdiff (tp_asv_get_string (request_properties,
+ TP_IFACE_CHANNEL ".ChannelType"),
+ TP_IFACE_CHANNEL_TYPE_TEXT))
+ {
+ return FALSE;
+ }
+
+ if (tp_asv_get_uint32 (request_properties,
+ TP_IFACE_CHANNEL ".TargetHandleType", NULL) != TP_HANDLE_TYPE_CONTACT)
+ {
+ return FALSE;
+ }
+
+ handle = tp_asv_get_uint32 (request_properties,
+ TP_IFACE_CHANNEL ".TargetHandle", NULL);
+ g_assert (handle != 0);
+
+ if (tp_channel_manager_asv_has_unknown_properties (request_properties,
+ fixed_properties, allowed_properties, &error))
+ {
+ goto error;
+ }
+
+ chan = g_hash_table_lookup (self->priv->channels, GUINT_TO_POINTER (handle));
+
+ if (chan == NULL)
+ {
+ chan = new_channel (self, handle, self->priv->conn->self_handle,
+ request_token);
+ }
+ else if (require_new)
+ {
+ g_set_error (&error, TP_ERRORS, TP_ERROR_NOT_AVAILABLE,
+ "An echo channel to contact #%u already exists", handle);
+ goto error;
+ }
+ else
+ {
+ tp_channel_manager_emit_request_already_satisfied (self,
+ request_token, TP_EXPORTABLE_CHANNEL (chan));
+ }
+
+ return TRUE;
+
+error:
+ tp_channel_manager_emit_request_failed (self, request_token,
+ error->domain, error->code, error->message);
+ g_error_free (error);
+ return TRUE;
+}
+
+static gboolean
+example_echo_im_manager_create_channel (TpChannelManager *manager,
+ gpointer request_token,
+ GHashTable *request_properties)
+{
+ return example_echo_im_manager_request (EXAMPLE_ECHO_IM_MANAGER (manager),
+ request_token, request_properties, TRUE);
+}
+
+static gboolean
+example_echo_im_manager_ensure_channel (TpChannelManager *manager,
+ gpointer request_token,
+ GHashTable *request_properties)
+{
+ return example_echo_im_manager_request (EXAMPLE_ECHO_IM_MANAGER (manager),
+ request_token, request_properties, FALSE);
+}
+
+static void
+channel_manager_iface_init (gpointer g_iface,
+ gpointer iface_data G_GNUC_UNUSED)
+{
+ TpChannelManagerIface *iface = g_iface;
+
+ iface->foreach_channel = example_echo_im_manager_foreach_channel;
+ iface->foreach_channel_class = example_echo_im_manager_foreach_channel_class;
+ iface->create_channel = example_echo_im_manager_create_channel;
+ iface->ensure_channel = example_echo_im_manager_ensure_channel;
+ /* In this channel manager, Request has the same semantics as Ensure */
+ iface->request_channel = example_echo_im_manager_ensure_channel;
+}
diff --git a/tests/lib/echo/im-manager.h b/tests/lib/echo/im-manager.h
new file mode 100644
index 0000000..9022ba9
--- /dev/null
+++ b/tests/lib/echo/im-manager.h
@@ -0,0 +1,54 @@
+/*
+ * im-manager.h - header for an example channel manager
+ *
+ * Copyright (C) 2007 Collabora Ltd. <http://www.collabora.co.uk/>
+ * Copyright (C) 2007 Nokia Corporation
+ *
+ * Copying and distribution of this file, with or without modification,
+ * are permitted in any medium without royalty provided the copyright
+ * notice and this notice are preserved.
+ */
+
+#ifndef __EXAMPLE_ECHO_IM_MANAGER_H__
+#define __EXAMPLE_ECHO_IM_MANAGER_H__
+
+#include <glib-object.h>
+
+G_BEGIN_DECLS
+
+typedef struct _ExampleEchoImManager ExampleEchoImManager;
+typedef struct _ExampleEchoImManagerClass ExampleEchoImManagerClass;
+typedef struct _ExampleEchoImManagerPrivate ExampleEchoImManagerPrivate;
+
+struct _ExampleEchoImManagerClass {
+ GObjectClass parent_class;
+};
+
+struct _ExampleEchoImManager {
+ GObject parent;
+
+ ExampleEchoImManagerPrivate *priv;
+};
+
+GType example_echo_im_manager_get_type (void);
+
+/* TYPE MACROS */
+#define EXAMPLE_TYPE_ECHO_IM_MANAGER \
+ (example_echo_im_manager_get_type ())
+#define EXAMPLE_ECHO_IM_MANAGER(obj) \
+ (G_TYPE_CHECK_INSTANCE_CAST((obj), EXAMPLE_TYPE_ECHO_IM_MANAGER, \
+ ExampleEchoImManager))
+#define EXAMPLE_ECHO_IM_MANAGER_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_CAST((klass), EXAMPLE_TYPE_ECHO_IM_MANAGER, \
+ ExampleEchoImManagerClass))
+#define EXAMPLE_IS_ECHO_IM_MANAGER(obj) \
+ (G_TYPE_CHECK_INSTANCE_TYPE((obj), EXAMPLE_TYPE_ECHO_IM_MANAGER))
+#define EXAMPLE_IS_ECHO_IM_MANAGER_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_TYPE((klass), EXAMPLE_TYPE_ECHO_IM_MANAGER))
+#define EXAMPLE_ECHO_IM_MANAGER_GET_CLASS(obj) \
+ (G_TYPE_INSTANCE_GET_CLASS ((obj), EXAMPLE_TYPE_ECHO_IM_MANAGER, \
+ ExampleEchoImManagerClass))
+
+G_END_DECLS
+
+#endif
--
1.5.6.5
More information about the telepathy-commits
mailing list