[Telepathy-commits] [telepathy-glib/master] Add the beginnings of a StreamedMedia demo connection manager

Simon McVittie simon.mcvittie at collabora.co.uk
Tue Mar 10 04:32:29 PDT 2009


---
 configure.ac                              |    1 +
 examples/cm/Makefile.am                   |    1 +
 examples/cm/callable/Makefile.am          |   66 ++++
 examples/cm/callable/conn.c               |  383 ++++++++++++++++++++
 examples/cm/callable/conn.h               |   78 ++++
 examples/cm/callable/connection-manager.c |  125 +++++++
 examples/cm/callable/connection-manager.h |   73 ++++
 examples/cm/callable/main.c               |   54 +++
 examples/cm/callable/manager-file.py      |   19 +
 examples/cm/callable/media-channel.c      |  546 +++++++++++++++++++++++++++++
 examples/cm/callable/media-channel.h      |   74 ++++
 examples/cm/callable/media-manager.c      |  400 +++++++++++++++++++++
 examples/cm/callable/media-manager.h      |   71 ++++
 13 files changed, 1891 insertions(+), 0 deletions(-)
 create mode 100644 examples/cm/callable/Makefile.am
 create mode 100644 examples/cm/callable/conn.c
 create mode 100644 examples/cm/callable/conn.h
 create mode 100644 examples/cm/callable/connection-manager.c
 create mode 100644 examples/cm/callable/connection-manager.h
 create mode 100644 examples/cm/callable/main.c
 create mode 100644 examples/cm/callable/manager-file.py
 create mode 100644 examples/cm/callable/media-channel.c
 create mode 100644 examples/cm/callable/media-channel.h
 create mode 100644 examples/cm/callable/media-manager.c
 create mode 100644 examples/cm/callable/media-manager.h

diff --git a/configure.ac b/configure.ac
index 2a6118a..8840c58 100644
--- a/configure.ac
+++ b/configure.ac
@@ -200,6 +200,7 @@ AC_OUTPUT( Makefile \
 	   examples/Makefile \
 	   examples/client/Makefile \
 	   examples/cm/Makefile \
+	   examples/cm/callable/Makefile \
 	   examples/cm/channelspecific/Makefile \
 	   examples/cm/contactlist/Makefile \
 	   examples/cm/echo/Makefile \
diff --git a/examples/cm/Makefile.am b/examples/cm/Makefile.am
index 2da3990..5cae88c 100644
--- a/examples/cm/Makefile.am
+++ b/examples/cm/Makefile.am
@@ -1,4 +1,5 @@
 SUBDIRS = \
+    callable \
     channelspecific \
     contactlist \
     echo \
diff --git a/examples/cm/callable/Makefile.am b/examples/cm/callable/Makefile.am
new file mode 100644
index 0000000..a60ef40
--- /dev/null
+++ b/examples/cm/callable/Makefile.am
@@ -0,0 +1,66 @@
+# Example connection manager with audio/video calls.
+
+EXAMPLES = telepathy-example-cm-callable
+noinst_LTLIBRARIES = libexample-cm-callable.la
+
+if INSTALL_EXAMPLES
+libexec_PROGRAMS = $(EXAMPLES)
+else
+noinst_PROGRAMS = $(EXAMPLES)
+endif
+
+libexample_cm_callable_la_SOURCES = \
+    conn.c \
+    conn.h \
+    connection-manager.c \
+    connection-manager.h \
+    media-channel.c \
+    media-channel.h \
+    media-manager.c \
+    media-manager.h
+
+# In an external project you'd use $(TP_GLIB_LIBS) (obtained from
+# pkg-config via autoconf) instead of the .la path
+libexample_cm_callable_la_LIBADD = \
+    $(GLIB_LIBS) \
+    $(DBUS_LIBS) \
+    $(top_builddir)/telepathy-glib/libtelepathy-glib.la
+
+telepathy_example_cm_callable_SOURCES = \
+    main.c
+
+telepathy_example_cm_callable_LDADD = \
+    $(noinst_LTLIBRARIES)
+
+AM_CFLAGS = \
+    $(ERROR_CFLAGS) \
+    $(DBUS_CFLAGS) \
+    $(GLIB_CFLAGS) \
+    $(TP_GLIB_CFLAGS)
+
+EXTRA_DIST = manager-file.py
+
+servicedir = ${datadir}/dbus-1/services
+
+if INSTALL_EXAMPLES
+service_DATA = _gen/org.freedesktop.Telepathy.ConnectionManager.example_callable.service
+$(service_DATA): %: Makefile
+	$(mkdir_p) _gen
+	{ echo "[D-BUS Service]" && \
+	echo "Name=org.freedesktop.Telepathy.ConnectionManager.example_callable" && \
+	echo "Exec=${libexecdir}/telepathy-example-cm-callable"; } > $@
+
+managerdir = ${datadir}/telepathy/managers
+manager_DATA = _gen/example_callable.manager
+endif
+
+_gen/example_callable.manager _gen/param-spec-struct.h: \
+		manager-file.py $(top_srcdir)/tools/manager-file.py
+	$(mkdir_p) _gen
+	$(PYTHON) $(top_srcdir)/tools/manager-file.py $(srcdir)/manager-file.py _gen
+
+BUILT_SOURCES = _gen/param-spec-struct.h
+CLEANFILES = $(BUILT_SOURCES)
+
+clean-local:
+	rm -rf _gen
diff --git a/examples/cm/callable/conn.c b/examples/cm/callable/conn.c
new file mode 100644
index 0000000..e9d6e43
--- /dev/null
+++ b/examples/cm/callable/conn.c
@@ -0,0 +1,383 @@
+/*
+ * conn.c - an example connection
+ *
+ * Copyright © 2007-2009 Collabora Ltd. <http://www.collabora.co.uk/>
+ * Copyright © 2007-2009 Nokia Corporation
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+
+#include "conn.h"
+
+#include <string.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/handle-repo-static.h>
+#include <telepathy-glib/interfaces.h>
+
+#include "media-manager.h"
+
+G_DEFINE_TYPE_WITH_CODE (ExampleCallableConnection,
+    example_callable_connection,
+    TP_TYPE_BASE_CONNECTION,
+    G_IMPLEMENT_INTERFACE (TP_TYPE_SVC_CONNECTION_INTERFACE_CONTACTS,
+      tp_contacts_mixin_iface_init);
+    G_IMPLEMENT_INTERFACE (TP_TYPE_SVC_CONNECTION_INTERFACE_PRESENCE,
+      tp_presence_mixin_iface_init);
+    G_IMPLEMENT_INTERFACE (TP_TYPE_SVC_CONNECTION_INTERFACE_SIMPLE_PRESENCE,
+      tp_presence_mixin_simple_presence_iface_init))
+
+enum
+{
+  PROP_ACCOUNT = 1,
+  N_PROPS
+};
+
+struct _ExampleCallableConnectionPrivate
+{
+  gchar *account;
+  gboolean away;
+  gchar *presence_message;
+};
+
+static void
+example_callable_connection_init (ExampleCallableConnection *self)
+{
+  self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self,
+      EXAMPLE_TYPE_CALLABLE_CONNECTION,
+      ExampleCallableConnectionPrivate);
+  self->priv->away = FALSE;
+  self->priv->presence_message = g_strdup ("");
+}
+
+static void
+get_property (GObject *object,
+              guint property_id,
+              GValue *value,
+              GParamSpec *spec)
+{
+  ExampleCallableConnection *self = EXAMPLE_CALLABLE_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)
+{
+  ExampleCallableConnection *self = EXAMPLE_CALLABLE_CONNECTION (object);
+
+  switch (property_id)
+    {
+    case PROP_ACCOUNT:
+      g_free (self->priv->account);
+      self->priv->account = g_value_dup_string (value);
+      break;
+
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, spec);
+    }
+}
+
+static void
+finalize (GObject *object)
+{
+  ExampleCallableConnection *self = EXAMPLE_CALLABLE_CONNECTION (object);
+
+  tp_contacts_mixin_finalize (object);
+  g_free (self->priv->account);
+  g_free (self->priv->presence_message);
+
+  G_OBJECT_CLASS (example_callable_connection_parent_class)->finalize (object);
+}
+
+static gchar *
+get_unique_connection_name (TpBaseConnection *conn)
+{
+  ExampleCallableConnection *self = EXAMPLE_CALLABLE_CONNECTION (conn);
+
+  return g_strdup_printf ("%s@%p", self->priv->account, self);
+}
+
+gchar *
+example_callable_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,
+          "Contact ID must not be empty");
+      return NULL;
+    }
+
+  return g_utf8_normalize (id, -1, G_NORMALIZE_ALL_COMPOSE);
+}
+
+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_callable_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_CALLABLE_MEDIA_MANAGER,
+        "connection", conn,
+        NULL));
+
+  return ret;
+}
+
+static gboolean
+start_connecting (TpBaseConnection *conn,
+                  GError **error)
+{
+  ExampleCallableConnection *self = EXAMPLE_CALLABLE_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, error);
+
+  if (conn->self_handle == 0)
+    return FALSE;
+
+  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
+constructed (GObject *object)
+{
+  TpBaseConnection *base = TP_BASE_CONNECTION (object);
+  void (*chain_up) (GObject *) =
+    G_OBJECT_CLASS (example_callable_connection_parent_class)->constructed;
+
+  if (chain_up != NULL)
+    chain_up (object);
+
+  tp_contacts_mixin_init (object,
+      G_STRUCT_OFFSET (ExampleCallableConnection, contacts_mixin));
+  tp_base_connection_register_with_contacts_mixin (base);
+
+  tp_presence_mixin_init (object,
+      G_STRUCT_OFFSET (ExampleCallableConnection, presence_mixin));
+  tp_presence_mixin_simple_presence_register_with_contacts_mixin (object);
+}
+
+static gboolean
+status_available (GObject *object,
+                  guint index_)
+{
+  TpBaseConnection *base = TP_BASE_CONNECTION (object);
+
+  if (base->status != TP_CONNECTION_STATUS_CONNECTED)
+    return FALSE;
+
+  return TRUE;
+}
+
+static GHashTable *
+get_contact_statuses (GObject *object,
+                      const GArray *contacts,
+                      GError **error)
+{
+  ExampleCallableConnection *self =
+    EXAMPLE_CALLABLE_CONNECTION (object);
+  TpBaseConnection *base = TP_BASE_CONNECTION (object);
+  guint i;
+  GHashTable *result = g_hash_table_new_full (g_direct_hash, g_direct_equal,
+      NULL, (GDestroyNotify) tp_presence_status_free);
+
+  for (i = 0; i < contacts->len; i++)
+    {
+      TpHandle contact = g_array_index (contacts, guint, i);
+      ExampleCallablePresence presence;
+      GHashTable *parameters;
+
+      parameters = g_hash_table_new_full (g_str_hash,
+          g_str_equal, NULL, (GDestroyNotify) tp_g_value_slice_free);
+
+      /* we know our own status from the connection; for this example CM,
+       * everyone else's status is assumed to be "available" */
+      if (contact == base->self_handle)
+        {
+          presence = (self->priv->away ? EXAMPLE_CALLABLE_PRESENCE_AWAY
+              : EXAMPLE_CALLABLE_PRESENCE_AVAILABLE);
+
+          if (self->priv->presence_message[0] != '\0')
+            g_hash_table_insert (parameters, "message",
+                tp_g_value_slice_new_string (self->priv->presence_message));
+        }
+      else
+        {
+          presence = EXAMPLE_CALLABLE_PRESENCE_AVAILABLE;
+        }
+
+      g_hash_table_insert (result, GUINT_TO_POINTER (contact),
+          tp_presence_status_new (presence, parameters));
+      g_hash_table_destroy (parameters);
+    }
+
+  return result;
+}
+
+static gboolean
+set_own_status (GObject *object,
+                const TpPresenceStatus *status,
+                GError **error)
+{
+  ExampleCallableConnection *self =
+    EXAMPLE_CALLABLE_CONNECTION (object);
+  TpBaseConnection *base = TP_BASE_CONNECTION (object);
+  GHashTable *presences;
+  const gchar *message = "";
+
+  if (status->optional_arguments != NULL)
+    {
+      GValue *v = g_hash_table_lookup (status->optional_arguments, "message");
+
+      if (v != NULL && G_VALUE_HOLDS_STRING (v))
+        {
+          message = g_value_get_string (v);
+
+          if (message == NULL)
+            message = "";
+        }
+    }
+
+  if (status->index == EXAMPLE_CALLABLE_PRESENCE_AWAY)
+    {
+      if (self->priv->away && !tp_strdiff (message,
+            self->priv->presence_message))
+        return TRUE;
+
+      self->priv->away = TRUE;
+    }
+  else
+    {
+      if (!self->priv->away && !tp_strdiff (message,
+            self->priv->presence_message))
+        return TRUE;
+
+      self->priv->away = FALSE;
+    }
+
+  g_free (self->priv->presence_message);
+  self->priv->presence_message = g_strdup (message);
+
+  presences = g_hash_table_new_full (g_direct_hash, g_direct_equal,
+      NULL, NULL);
+  g_hash_table_insert (presences, GUINT_TO_POINTER (base->self_handle),
+      (gpointer) status);
+  tp_presence_mixin_emit_presence_update (object, presences);
+  g_hash_table_destroy (presences);
+  return TRUE;
+}
+
+static const TpPresenceStatusOptionalArgumentSpec can_have_message[] = {
+      { "message", "s", NULL, NULL },
+      { NULL }
+};
+
+/* Must be kept in sync with ExampleCallablePresence enum in header */
+static const TpPresenceStatusSpec presence_statuses[] = {
+      { "offline", TP_CONNECTION_PRESENCE_TYPE_OFFLINE, FALSE, NULL },
+      { "unknown", TP_CONNECTION_PRESENCE_TYPE_UNKNOWN, FALSE, NULL },
+      { "error", TP_CONNECTION_PRESENCE_TYPE_ERROR, FALSE, NULL },
+      { "away", TP_CONNECTION_PRESENCE_TYPE_AWAY, TRUE, can_have_message },
+      { "available", TP_CONNECTION_PRESENCE_TYPE_AVAILABLE, TRUE,
+        can_have_message },
+      { NULL }
+};
+
+static void
+example_callable_connection_class_init (
+    ExampleCallableConnectionClass *klass)
+{
+  static const gchar *interfaces_always_present[] = {
+      TP_IFACE_CONNECTION_INTERFACE_CONTACTS,
+      TP_IFACE_CONNECTION_INTERFACE_PRESENCE,
+      TP_IFACE_CONNECTION_INTERFACE_REQUESTS,
+      TP_IFACE_CONNECTION_INTERFACE_SIMPLE_PRESENCE,
+      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->constructed = constructed;
+  object_class->finalize = finalize;
+  g_type_class_add_private (klass,
+      sizeof (ExampleCallableConnectionPrivate));
+
+  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_STRINGS);
+  g_object_class_install_property (object_class, PROP_ACCOUNT, param_spec);
+
+  tp_contacts_mixin_class_init (object_class,
+      G_STRUCT_OFFSET (ExampleCallableConnectionClass, contacts_mixin));
+  tp_presence_mixin_class_init (object_class,
+      G_STRUCT_OFFSET (ExampleCallableConnectionClass, presence_mixin),
+      status_available, get_contact_statuses, set_own_status,
+      presence_statuses);
+  tp_presence_mixin_simple_presence_init_dbus_properties (object_class);
+}
diff --git a/examples/cm/callable/conn.h b/examples/cm/callable/conn.h
new file mode 100644
index 0000000..f3d4690
--- /dev/null
+++ b/examples/cm/callable/conn.h
@@ -0,0 +1,78 @@
+/*
+ * conn.h - header for an example connection
+ *
+ * Copyright © 2007-2009 Collabora Ltd. <http://www.collabora.co.uk/>
+ * Copyright © 2007-2009 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_CALLABLE_CONN_H__
+#define __EXAMPLE_CALLABLE_CONN_H__
+
+#include <glib-object.h>
+#include <telepathy-glib/base-connection.h>
+#include <telepathy-glib/contacts-mixin.h>
+#include <telepathy-glib/presence-mixin.h>
+
+G_BEGIN_DECLS
+
+typedef struct _ExampleCallableConnection ExampleCallableConnection;
+typedef struct _ExampleCallableConnectionPrivate
+    ExampleCallableConnectionPrivate;
+
+typedef struct _ExampleCallableConnectionClass ExampleCallableConnectionClass;
+typedef struct _ExampleCallableConnectionClassPrivate
+    ExampleCallableConnectionClassPrivate;
+
+struct _ExampleCallableConnectionClass {
+    TpBaseConnectionClass parent_class;
+    TpPresenceMixinClass presence_mixin;
+    TpContactsMixinClass contacts_mixin;
+
+    ExampleCallableConnectionClassPrivate *priv;
+};
+
+struct _ExampleCallableConnection {
+    TpBaseConnection parent;
+    TpPresenceMixin presence_mixin;
+    TpContactsMixin contacts_mixin;
+
+    ExampleCallableConnectionPrivate *priv;
+};
+
+GType example_callable_connection_get_type (void);
+
+#define EXAMPLE_TYPE_CALLABLE_CONNECTION \
+  (example_callable_connection_get_type ())
+#define EXAMPLE_CALLABLE_CONNECTION(obj) \
+  (G_TYPE_CHECK_INSTANCE_CAST((obj), EXAMPLE_TYPE_CALLABLE_CONNECTION, \
+                              ExampleCallableConnection))
+#define EXAMPLE_CALLABLE_CONNECTION_CLASS(klass) \
+  (G_TYPE_CHECK_CLASS_CAST((klass), EXAMPLE_TYPE_CALLABLE_CONNECTION, \
+                           ExampleCallableConnectionClass))
+#define EXAMPLE_IS_CALLABLE_CONNECTION(obj) \
+  (G_TYPE_CHECK_INSTANCE_TYPE((obj), EXAMPLE_TYPE_CALLABLE_CONNECTION))
+#define EXAMPLE_IS_CALLABLE_CONNECTION_CLASS(klass) \
+  (G_TYPE_CHECK_CLASS_TYPE((klass), EXAMPLE_TYPE_CALLABLE_CONNECTION))
+#define EXAMPLE_CALLABLE_CONNECTION_GET_CLASS(obj) \
+  (G_TYPE_INSTANCE_GET_CLASS ((obj), EXAMPLE_TYPE_CALLABLE_CONNECTION, \
+                              ExampleCallableConnectionClass))
+
+gchar *example_callable_normalize_contact (TpHandleRepoIface *repo,
+    const gchar *id, gpointer context, GError **error);
+
+/* Must be kept in sync with the array presence_statuses in conn.c */
+typedef enum {
+    EXAMPLE_CALLABLE_PRESENCE_OFFLINE = 0,
+    EXAMPLE_CALLABLE_PRESENCE_UNKNOWN,
+    EXAMPLE_CALLABLE_PRESENCE_ERROR,
+    EXAMPLE_CALLABLE_PRESENCE_AWAY,
+    EXAMPLE_CALLABLE_PRESENCE_AVAILABLE
+} ExampleCallablePresence;
+
+G_END_DECLS
+
+#endif
diff --git a/examples/cm/callable/connection-manager.c b/examples/cm/callable/connection-manager.c
new file mode 100644
index 0000000..768cc1c
--- /dev/null
+++ b/examples/cm/callable/connection-manager.c
@@ -0,0 +1,125 @@
+/*
+ * manager.c - an example connection manager
+ *
+ * Copyright © 2007-2009 Collabora Ltd. <http://www.collabora.co.uk/>
+ * Copyright © 2007-2009 Nokia Corporation
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+
+#include "connection-manager.h"
+
+#include <dbus/dbus-glib.h>
+
+#include <telepathy-glib/dbus.h>
+#include <telepathy-glib/errors.h>
+
+#include "conn.h"
+
+G_DEFINE_TYPE (ExampleCallableConnectionManager,
+    example_callable_connection_manager,
+    TP_TYPE_BASE_CONNECTION_MANAGER)
+
+struct _ExampleCallableConnectionManagerPrivate
+{
+  int dummy;
+};
+
+static void
+example_callable_connection_manager_init (
+    ExampleCallableConnectionManager *self)
+{
+  self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self,
+      EXAMPLE_TYPE_CALLABLE_CONNECTION_MANAGER,
+      ExampleCallableConnectionManagerPrivate);
+}
+
+typedef struct {
+    gchar *account;
+} ExampleParams;
+
+static gboolean
+account_param_filter (const TpCMParamSpec *paramspec,
+                      GValue *value,
+                      GError **error)
+{
+  const gchar *id = g_value_get_string (value);
+
+  g_value_take_string (value,
+      example_callable_normalize_contact (NULL, id, NULL, error));
+
+  if (g_value_get_string (value) == NULL)
+    return FALSE;
+
+  return TRUE;
+}
+
+#include "_gen/param-spec-struct.h"
+
+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_callable_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;
+  ExampleCallableConnection *conn;
+
+  conn = EXAMPLE_CALLABLE_CONNECTION
+      (g_object_new (EXAMPLE_TYPE_CALLABLE_CONNECTION,
+          "account", params->account,
+          "protocol", proto,
+          NULL));
+
+  return (TpBaseConnection *) conn;
+}
+
+static void
+example_callable_connection_manager_class_init (
+    ExampleCallableConnectionManagerClass *klass)
+{
+  TpBaseConnectionManagerClass *base_class =
+      (TpBaseConnectionManagerClass *) klass;
+
+  g_type_class_add_private (klass,
+      sizeof (ExampleCallableConnectionManagerPrivate));
+
+  base_class->new_connection = new_connection;
+  base_class->cm_dbus_name = "example_callable";
+  base_class->protocol_params = example_protocols;
+}
diff --git a/examples/cm/callable/connection-manager.h b/examples/cm/callable/connection-manager.h
new file mode 100644
index 0000000..5d854cb
--- /dev/null
+++ b/examples/cm/callable/connection-manager.h
@@ -0,0 +1,73 @@
+/*
+ * manager.h - header for an example connection manager
+ *
+ * Copyright © 2007-2009 Collabora Ltd. <http://www.collabora.co.uk/>
+ * Copyright © 2007-2009 Nokia Corporation
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+
+#ifndef __EXAMPLE_CALLABLE_CONNECTION_MANAGER_H__
+#define __EXAMPLE_CALLABLE_CONNECTION_MANAGER_H__
+
+#include <glib-object.h>
+#include <telepathy-glib/base-connection-manager.h>
+
+G_BEGIN_DECLS
+
+typedef struct _ExampleCallableConnectionManager
+    ExampleCallableConnectionManager;
+typedef struct _ExampleCallableConnectionManagerPrivate
+    ExampleCallableConnectionManagerPrivate;
+
+typedef struct _ExampleCallableConnectionManagerClass
+    ExampleCallableConnectionManagerClass;
+typedef struct _ExampleCallableConnectionManagerClassPrivate
+    ExampleCallableConnectionManagerClassPrivate;
+
+struct _ExampleCallableConnectionManagerClass {
+    TpBaseConnectionManagerClass parent_class;
+
+    ExampleCallableConnectionManagerClassPrivate *priv;
+};
+
+struct _ExampleCallableConnectionManager {
+    TpBaseConnectionManager parent;
+
+    ExampleCallableConnectionManagerPrivate *priv;
+};
+
+GType example_callable_connection_manager_get_type (void);
+
+/* TYPE MACROS */
+#define EXAMPLE_TYPE_CALLABLE_CONNECTION_MANAGER \
+  (example_callable_connection_manager_get_type ())
+#define EXAMPLE_CALLABLE_CONNECTION_MANAGER(obj) \
+  (G_TYPE_CHECK_INSTANCE_CAST((obj), EXAMPLE_TYPE_CALLABLE_CONNECTION_MANAGER, \
+                              ExampleCallableConnectionManager))
+#define EXAMPLE_CALLABLE_CONNECTION_MANAGER_CLASS(klass) \
+  (G_TYPE_CHECK_CLASS_CAST((klass), EXAMPLE_TYPE_CALLABLE_CONNECTION_MANAGER, \
+                           ExampleCallableConnectionManagerClass))
+#define EXAMPLE_IS_CALLABLE_CONNECTION_MANAGER(obj) \
+  (G_TYPE_CHECK_INSTANCE_TYPE((obj), EXAMPLE_TYPE_CALLABLE_CONNECTION_MANAGER))
+#define EXAMPLE_IS_CALLABLE_CONNECTION_MANAGER_CLASS(klass) \
+  (G_TYPE_CHECK_CLASS_TYPE((klass), EXAMPLE_TYPE_CALLABLE_CONNECTION_MANAGER))
+#define EXAMPLE_CALLABLE_CONNECTION_MANAGER_GET_CLASS(obj) \
+  (G_TYPE_INSTANCE_GET_CLASS ((obj), EXAMPLE_TYPE_CALLABLE_CONNECTION_MANAGER, \
+                              ExampleCallableConnectionManagerClass))
+
+G_END_DECLS
+
+#endif
diff --git a/examples/cm/callable/main.c b/examples/cm/callable/main.c
new file mode 100644
index 0000000..ea0b171
--- /dev/null
+++ b/examples/cm/callable/main.c
@@ -0,0 +1,54 @@
+/*
+ * main.c - entry point for an example Telepathy connection manager
+ *
+ * Copyright © 2007-2009 Collabora Ltd. <http://www.collabora.co.uk/>
+ * Copyright © 2007-2009 Nokia Corporation
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+
+#include "config.h"
+
+#include <telepathy-glib/debug.h>
+#include <telepathy-glib/run.h>
+
+#include "connection-manager.h"
+
+static TpBaseConnectionManager *
+construct_cm (void)
+{
+  return (TpBaseConnectionManager *) g_object_new (
+      EXAMPLE_TYPE_CALLABLE_CONNECTION_MANAGER,
+      NULL);
+}
+
+int
+main (int argc,
+      char **argv)
+{
+#ifdef ENABLE_DEBUG
+  tp_debug_divert_messages (g_getenv ("EXAMPLE_CM_LOGFILE"));
+  tp_debug_set_flags (g_getenv ("EXAMPLE_DEBUG"));
+
+  if (g_getenv ("EXAMPLE_TIMING") != NULL)
+    g_log_set_default_handler (tp_debug_timestamped_log_handler, NULL);
+
+  if (g_getenv ("EXAMPLE_PERSIST") != NULL)
+    tp_debug_set_persistent (TRUE);
+#endif
+
+  return tp_run_connection_manager ("telepathy-example-cm-callable",
+      VERSION, construct_cm, argc, argv);
+}
diff --git a/examples/cm/callable/manager-file.py b/examples/cm/callable/manager-file.py
new file mode 100644
index 0000000..6811e3d
--- /dev/null
+++ b/examples/cm/callable/manager-file.py
@@ -0,0 +1,19 @@
+# Input for tools/manager-file.py
+
+MANAGER = 'example_callable'
+PARAMS = {
+        'example' : {
+            'account': {
+                'dtype': 's',
+                'flags': 'required register',
+                'filter': 'account_param_filter',
+                # 'filter_data': 'NULL',
+                # 'default': ...,
+                # 'struct_field': '...',
+                # 'setter_data': 'NULL',
+                },
+            },
+        }
+STRUCTS = {
+        'example': 'ExampleParams'
+        }
diff --git a/examples/cm/callable/media-channel.c b/examples/cm/callable/media-channel.c
new file mode 100644
index 0000000..5e13b3c
--- /dev/null
+++ b/examples/cm/callable/media-channel.c
@@ -0,0 +1,546 @@
+/*
+ * media-channel.c - an example 1-1 streamed media call.
+ *
+ * For simplicity, this channel emulates a device with its own
+ * audio/video user interface, like a video-equipped form of the phones
+ * manipulated by telepathy-snom or gnome-phone-manager.
+ *
+ * As a result, this channel does not have the MediaSignalling interface, and
+ * clients should not attempt to do their own streaming using
+ * telepathy-farsight, telepathy-stream-engine or maemo-stream-engine.
+ *
+ * In practice, nearly all connection managers also have the MediaSignalling
+ * interface on their streamed media channels. Usage for those CMs is the
+ * same, except that whichever client is the primary handler for the channel
+ * should also hand the channel over to telepathy-farsight or
+ * telepathy-stream-engine to implement the actual streaming.
+ *
+ * Copyright © 2007-2009 Collabora Ltd. <http://www.collabora.co.uk/>
+ * Copyright © 2007-2009 Nokia Corporation
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+
+#include "media-channel.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 media_iface_init (gpointer iface, gpointer data);
+static void channel_iface_init (gpointer iface, gpointer data);
+
+G_DEFINE_TYPE_WITH_CODE (ExampleCallableMediaChannel,
+    example_callable_media_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_STREAMED_MEDIA,
+      media_iface_init);
+    G_IMPLEMENT_INTERFACE (TP_TYPE_SVC_CHANNEL_INTERFACE_GROUP,
+      tp_group_mixin_iface_init);
+    G_IMPLEMENT_INTERFACE (TP_TYPE_CHANNEL_IFACE, NULL);
+    G_IMPLEMENT_INTERFACE (TP_TYPE_EXPORTABLE_CHANNEL, NULL))
+
+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 _ExampleCallableMediaChannelPrivate
+{
+  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_callable_media_channel_interfaces[] = {
+    TP_IFACE_CHANNEL_INTERFACE_GROUP,
+    NULL
+};
+
+static void
+example_callable_media_channel_init (ExampleCallableMediaChannel *self)
+{
+  self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self,
+      EXAMPLE_TYPE_CALLABLE_MEDIA_CHANNEL,
+      ExampleCallableMediaChannelPrivate);
+}
+
+static void
+constructed (GObject *object)
+{
+  void (*chain_up) (GObject *) =
+      ((GObjectClass *) example_callable_media_channel_parent_class)->constructed;
+  ExampleCallableMediaChannel *self = EXAMPLE_CALLABLE_MEDIA_CHANNEL (object);
+  TpHandleRepoIface *contact_repo = tp_base_connection_get_handles
+      (self->priv->conn, TP_HANDLE_TYPE_CONTACT);
+  DBusGConnection *bus;
+
+  if (chain_up != NULL)
+    chain_up (object);
+
+  tp_handle_ref (contact_repo, self->priv->handle);
+  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_group_mixin_init (object,
+      G_STRUCT_OFFSET (ExampleCallableMediaChannel, group),
+      contact_repo, self->priv->conn->self_handle);
+
+  /* FIXME: correct flags */
+  tp_group_mixin_change_flags (object,
+      TP_CHANNEL_GROUP_FLAG_CHANNEL_SPECIFIC_HANDLES |
+      TP_CHANNEL_GROUP_FLAG_PROPERTIES,
+      0);
+}
+
+static void
+get_property (GObject *object,
+              guint property_id,
+              GValue *value,
+              GParamSpec *pspec)
+{
+  ExampleCallableMediaChannel *self = EXAMPLE_CALLABLE_MEDIA_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_STREAMED_MEDIA);
+      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,
+              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_callable_media_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)
+{
+  ExampleCallableMediaChannel *self = EXAMPLE_CALLABLE_MEDIA_CHANNEL (object);
+
+  switch (property_id)
+    {
+    case PROP_OBJECT_PATH:
+      g_assert (self->priv->object_path == NULL);
+      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
+example_callable_media_channel_close (ExampleCallableMediaChannel *self)
+{
+  if (!self->priv->closed)
+    {
+      self->priv->closed = TRUE;
+      tp_svc_channel_emit_closed (self);
+    }
+}
+
+static void
+dispose (GObject *object)
+{
+  ExampleCallableMediaChannel *self = EXAMPLE_CALLABLE_MEDIA_CHANNEL (object);
+
+  if (self->priv->disposed)
+    return;
+
+  self->priv->disposed = TRUE;
+
+  example_callable_media_channel_close (self);
+
+  ((GObjectClass *) example_callable_media_channel_parent_class)->dispose (object);
+}
+
+static void
+finalize (GObject *object)
+{
+  ExampleCallableMediaChannel *self = EXAMPLE_CALLABLE_MEDIA_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);
+  tp_handle_unref (contact_handles, self->priv->initiator);
+
+  g_free (self->priv->object_path);
+
+  tp_group_mixin_finalize (object);
+
+  ((GObjectClass *) example_callable_media_channel_parent_class)->finalize (object);
+}
+
+static gboolean
+add_member (GObject *object,
+            TpHandle member,
+            const gchar *message,
+            GError **error)
+{
+  return TRUE;
+}
+
+static gboolean
+remove_member_with_reason (GObject *object,
+                           TpHandle member,
+                           const gchar *message,
+                           guint reason,
+                           GError **error)
+{
+  return TRUE;
+}
+
+static void
+example_callable_media_channel_class_init (ExampleCallableMediaChannelClass *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 (ExampleCallableMediaChannelPrivate));
+
+  object_class->constructed = constructed;
+  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);
+
+  klass->dbus_properties_class.interfaces = prop_interfaces;
+  tp_dbus_properties_mixin_class_init (object_class,
+      G_STRUCT_OFFSET (ExampleCallableMediaChannelClass,
+        dbus_properties_class));
+
+  tp_group_mixin_class_init (object_class,
+      G_STRUCT_OFFSET (ExampleCallableMediaChannelClass, group_class),
+      add_member,
+      NULL);
+  tp_group_mixin_class_set_remove_with_reason_func (object_class,
+      remove_member_with_reason);
+  tp_group_mixin_init_dbus_properties (object_class);
+}
+
+static void
+channel_close (TpSvcChannel *iface,
+               DBusGMethodInvocation *context)
+{
+  ExampleCallableMediaChannel *self = EXAMPLE_CALLABLE_MEDIA_CHANNEL (iface);
+
+  example_callable_media_channel_close (self);
+  tp_svc_channel_return_from_close (context);
+}
+
+static void
+channel_get_channel_type (TpSvcChannel *iface G_GNUC_UNUSED,
+                          DBusGMethodInvocation *context)
+{
+  tp_svc_channel_return_from_get_channel_type (context,
+      TP_IFACE_CHANNEL_TYPE_STREAMED_MEDIA);
+}
+
+static void
+channel_get_handle (TpSvcChannel *iface,
+                    DBusGMethodInvocation *context)
+{
+  ExampleCallableMediaChannel *self = EXAMPLE_CALLABLE_MEDIA_CHANNEL (iface);
+
+  tp_svc_channel_return_from_get_handle (context, TP_HANDLE_TYPE_CONTACT,
+      self->priv->handle);
+}
+
+static void
+channel_get_interfaces (TpSvcChannel *iface G_GNUC_UNUSED,
+                        DBusGMethodInvocation *context)
+{
+  tp_svc_channel_return_from_get_interfaces (context,
+      example_callable_media_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
+media_list_streams (TpSvcChannelTypeStreamedMedia *iface,
+                    DBusGMethodInvocation *context)
+{
+  ExampleCallableMediaChannel *self = EXAMPLE_CALLABLE_MEDIA_CHANNEL (iface);
+  GPtrArray *array = g_ptr_array_sized_new (0);
+
+  /* FIXME */
+  (void) self;
+
+  tp_svc_channel_type_streamed_media_return_from_list_streams (context,
+      array);
+  g_ptr_array_free (array, TRUE);
+}
+
+static void
+media_remove_streams (TpSvcChannelTypeStreamedMedia *iface,
+                      const GArray *stream_ids,
+                      DBusGMethodInvocation *context)
+{
+  ExampleCallableMediaChannel *self = EXAMPLE_CALLABLE_MEDIA_CHANNEL (iface);
+
+  /* FIXME */
+  (void) self;
+
+  tp_svc_channel_type_streamed_media_return_from_remove_streams (context);
+}
+
+static void
+media_request_stream_direction (TpSvcChannelTypeStreamedMedia *iface,
+                                guint stream_id,
+                                guint stream_direction,
+                                DBusGMethodInvocation *context)
+{
+  ExampleCallableMediaChannel *self = EXAMPLE_CALLABLE_MEDIA_CHANNEL (iface);
+
+  /* FIXME */
+  (void) self;
+
+  tp_svc_channel_type_streamed_media_return_from_request_stream_direction (
+      context);
+}
+
+static void
+media_request_streams (TpSvcChannelTypeStreamedMedia *iface,
+                       guint contact_handle,
+                       const GArray *media_types,
+                       DBusGMethodInvocation *context)
+{
+  ExampleCallableMediaChannel *self = EXAMPLE_CALLABLE_MEDIA_CHANNEL (iface);
+  GPtrArray *array = g_ptr_array_sized_new (0);
+
+  /* FIXME */
+  (void) self;
+
+  tp_svc_channel_type_streamed_media_return_from_request_streams (context,
+      array);
+  g_ptr_array_free (array, TRUE);
+}
+
+static void
+media_iface_init (gpointer iface,
+                  gpointer data)
+{
+  TpSvcChannelTypeStreamedMediaClass *klass = iface;
+
+#define IMPLEMENT(x) \
+  tp_svc_channel_type_streamed_media_implement_##x (klass, media_##x)
+  IMPLEMENT (list_streams);
+  IMPLEMENT (remove_streams);
+  IMPLEMENT (request_stream_direction);
+  IMPLEMENT (request_streams);
+#undef IMPLEMENT
+}
diff --git a/examples/cm/callable/media-channel.h b/examples/cm/callable/media-channel.h
new file mode 100644
index 0000000..428370d
--- /dev/null
+++ b/examples/cm/callable/media-channel.h
@@ -0,0 +1,74 @@
+/*
+ * media-channel.h - header for an example channel
+ *
+ * Copyright © 2007-2009 Collabora Ltd. <http://www.collabora.co.uk/>
+ * Copyright © 2007-2009 Nokia Corporation
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+
+#ifndef __EXAMPLE_CALLABLE_MEDIA_CHANNEL_H__
+#define __EXAMPLE_CALLABLE_MEDIA_CHANNEL_H__
+
+#include <glib-object.h>
+#include <telepathy-glib/group-mixin.h>
+
+G_BEGIN_DECLS
+
+typedef struct _ExampleCallableMediaChannel ExampleCallableMediaChannel;
+typedef struct _ExampleCallableMediaChannelPrivate
+    ExampleCallableMediaChannelPrivate;
+
+typedef struct _ExampleCallableMediaChannelClass
+    ExampleCallableMediaChannelClass;
+typedef struct _ExampleCallableMediaChannelClassPrivate
+    ExampleCallableMediaChannelClassPrivate;
+
+GType example_callable_media_channel_get_type (void);
+
+#define EXAMPLE_TYPE_CALLABLE_MEDIA_CHANNEL \
+  (example_callable_media_channel_get_type ())
+#define EXAMPLE_CALLABLE_MEDIA_CHANNEL(obj) \
+  (G_TYPE_CHECK_INSTANCE_CAST ((obj), EXAMPLE_TYPE_CALLABLE_MEDIA_CHANNEL, \
+                               ExampleCallableMediaChannel))
+#define EXAMPLE_CALLABLE_MEDIA_CHANNEL_CLASS(klass) \
+  (G_TYPE_CHECK_CLASS_CAST ((klass), EXAMPLE_TYPE_CALLABLE_MEDIA_CHANNEL, \
+                            ExampleCallableMediaChannelClass))
+#define EXAMPLE_IS_CALLABLE_MEDIA_CHANNEL(obj) \
+  (G_TYPE_CHECK_INSTANCE_TYPE ((obj), EXAMPLE_TYPE_CALLABLE_MEDIA_CHANNEL))
+#define EXAMPLE_IS_CALLABLE_MEDIA_CHANNEL_CLASS(klass) \
+  (G_TYPE_CHECK_CLASS_TYPE ((klass), EXAMPLE_TYPE_CALLABLE_MEDIA_CHANNEL))
+#define EXAMPLE_CALLABLE_MEDIA_CHANNEL_GET_CLASS(obj) \
+  (G_TYPE_INSTANCE_GET_CLASS ((obj), EXAMPLE_TYPE_CALLABLE_MEDIA_CHANNEL, \
+                              ExampleCallableMediaChannelClass))
+
+struct _ExampleCallableMediaChannelClass {
+    GObjectClass parent_class;
+    TpGroupMixinClass group_class;
+    TpDBusPropertiesMixinClass dbus_properties_class;
+
+    ExampleCallableMediaChannelClassPrivate *priv;
+};
+
+struct _ExampleCallableMediaChannel {
+    GObject parent;
+    TpGroupMixin group;
+
+    ExampleCallableMediaChannelPrivate *priv;
+};
+
+G_END_DECLS
+
+#endif
diff --git a/examples/cm/callable/media-manager.c b/examples/cm/callable/media-manager.c
new file mode 100644
index 0000000..453bb47
--- /dev/null
+++ b/examples/cm/callable/media-manager.c
@@ -0,0 +1,400 @@
+/*
+ * media-manager.c - an example channel manager for StreamedMedia calls.
+ * This channel manager emulates a protocol like XMPP Jingle, where you can
+ * make several simultaneous calls to the same or different contacts.
+ *
+ * Copyright © 2007-2009 Collabora Ltd. <http://www.collabora.co.uk/>
+ * Copyright © 2007-2009 Nokia Corporation
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+
+#include "media-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 "media-channel.h"
+
+static void channel_manager_iface_init (gpointer, gpointer);
+
+G_DEFINE_TYPE_WITH_CODE (ExampleCallableMediaManager,
+    example_callable_media_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 _ExampleCallableMediaManagerPrivate
+{
+  TpBaseConnection *conn;
+
+  /* List of ExampleCallableMediaChannel */
+  GList *channels;
+
+  /* Next channel will be ("MediaChannel%u", next_channel_index) */
+  guint next_channel_index;
+
+  gulong status_changed_id;
+};
+
+static void
+example_callable_media_manager_init (ExampleCallableMediaManager *self)
+{
+  self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self,
+      EXAMPLE_TYPE_CALLABLE_MEDIA_MANAGER,
+      ExampleCallableMediaManagerPrivate);
+
+  self->priv->conn = NULL;
+  self->priv->channels = NULL;
+  self->priv->status_changed_id = 0;
+}
+
+static void
+example_callable_media_manager_close_all (ExampleCallableMediaManager *self)
+{
+  if (self->priv->channels != NULL)
+    {
+      GList *tmp = self->priv->channels;
+
+      self->priv->channels = NULL;
+
+      g_list_foreach (tmp, (GFunc) g_object_unref, NULL);
+      g_list_free (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
+dispose (GObject *object)
+{
+  ExampleCallableMediaManager *self = EXAMPLE_CALLABLE_MEDIA_MANAGER (object);
+
+  example_callable_media_manager_close_all (self);
+  g_assert (self->priv->channels == NULL);
+
+  ((GObjectClass *) example_callable_media_manager_parent_class)->dispose (
+    object);
+}
+
+static void
+get_property (GObject *object,
+              guint property_id,
+              GValue *value,
+              GParamSpec *pspec)
+{
+  ExampleCallableMediaManager *self = EXAMPLE_CALLABLE_MEDIA_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)
+{
+  ExampleCallableMediaManager *self = EXAMPLE_CALLABLE_MEDIA_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,
+                   ExampleCallableMediaManager *self)
+{
+  switch (status)
+    {
+    case TP_CONNECTION_STATUS_DISCONNECTED:
+        {
+          example_callable_media_manager_close_all (self);
+        }
+      break;
+
+    default:
+      break;
+    }
+}
+
+static void
+constructed (GObject *object)
+{
+  ExampleCallableMediaManager *self = EXAMPLE_CALLABLE_MEDIA_MANAGER (object);
+  void (*chain_up) (GObject *) =
+      ((GObjectClass *) example_callable_media_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_callable_media_manager_class_init (
+    ExampleCallableMediaManagerClass *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_STRINGS);
+  g_object_class_install_property (object_class, PROP_CONNECTION, param_spec);
+
+  g_type_class_add_private (klass,
+      sizeof (ExampleCallableMediaManagerPrivate));
+}
+
+static void
+example_callable_media_manager_foreach_channel (
+    TpChannelManager *iface,
+    TpExportableChannelFunc callback,
+    gpointer user_data)
+{
+  ExampleCallableMediaManager *self = EXAMPLE_CALLABLE_MEDIA_MANAGER (iface);
+
+  g_list_foreach (self->priv->channels, (GFunc) callback, user_data);
+}
+
+static void
+channel_closed_cb (ExampleCallableMediaChannel *chan,
+                   ExampleCallableMediaManager *self)
+{
+  tp_channel_manager_emit_channel_closed_for_object (self,
+      TP_EXPORTABLE_CHANNEL (chan));
+
+  if (self->priv->channels != NULL)
+    {
+      self->priv->channels = g_list_remove_all (self->priv->channels, chan);
+    }
+}
+
+static ExampleCallableMediaChannel *
+new_channel (ExampleCallableMediaManager *self,
+             TpHandle handle,
+             TpHandle initiator,
+             gpointer request_token)
+{
+  ExampleCallableMediaChannel *chan;
+  gchar *object_path;
+  GSList *requests = NULL;
+
+  /* FIXME: This could potentially wrap around, but only after 4 billion
+   * calls, which is probably plenty. */
+  object_path = g_strdup_printf ("%s/MediaChannel%u",
+      self->priv->conn->object_path, self->priv->next_channel_index++);
+
+  chan = g_object_new (EXAMPLE_TYPE_CALLABLE_MEDIA_CHANNEL,
+      "connection", self->priv->conn,
+      "object-path", object_path,
+      "handle", handle,
+      "initiator-handle", initiator,
+      NULL);
+
+  g_free (object_path);
+
+  g_signal_connect (chan, "closed", G_CALLBACK (channel_closed_cb), self);
+
+  self->priv->channels = g_list_prepend (self->priv->channels, 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_callable_media_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);
+
+  g_hash_table_insert (table, TP_IFACE_CHANNEL ".ChannelType",
+      tp_g_value_slice_new_static_string (
+        TP_IFACE_CHANNEL_TYPE_STREAMED_MEDIA));
+
+  g_hash_table_insert (table, TP_IFACE_CHANNEL ".TargetHandleType",
+      tp_g_value_slice_new_uint (TP_HANDLE_TYPE_CONTACT));
+
+  func (manager, table, allowed_properties, user_data);
+
+  g_hash_table_destroy (table);
+}
+
+static gboolean
+example_callable_media_manager_request (ExampleCallableMediaManager *self,
+                                        gpointer request_token,
+                                        GHashTable *request_properties,
+                                        gboolean require_new)
+{
+  TpHandle handle;
+  GError *error = NULL;
+
+  if (tp_strdiff (tp_asv_get_string (request_properties,
+          TP_IFACE_CHANNEL ".ChannelType"),
+      TP_IFACE_CHANNEL_TYPE_STREAMED_MEDIA))
+    {
+      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;
+    }
+
+  if (!require_new)
+    {
+      /* see if we're already calling that handle */
+      const GList *link;
+
+      for (link = self->priv->channels; link != NULL; link = link->next)
+        {
+          guint its_handle;
+
+          g_object_get (link->data,
+              "handle", &its_handle,
+              NULL);
+
+          if (its_handle == handle)
+            {
+              tp_channel_manager_emit_request_already_satisfied (self,
+                  request_token, TP_EXPORTABLE_CHANNEL (link->data));
+              return TRUE;
+            }
+        }
+    }
+
+  new_channel (self, handle, self->priv->conn->self_handle,
+      request_token);
+  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_callable_media_manager_create_channel (TpChannelManager *manager,
+                                               gpointer request_token,
+                                               GHashTable *request_properties)
+{
+    return example_callable_media_manager_request (
+        EXAMPLE_CALLABLE_MEDIA_MANAGER (manager),
+        request_token, request_properties, TRUE);
+}
+
+static gboolean
+example_callable_media_manager_ensure_channel (TpChannelManager *manager,
+                                               gpointer request_token,
+                                               GHashTable *request_properties)
+{
+    return example_callable_media_manager_request (
+        EXAMPLE_CALLABLE_MEDIA_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_callable_media_manager_foreach_channel;
+  iface->foreach_channel_class =
+    example_callable_media_manager_foreach_channel_class;
+  iface->create_channel = example_callable_media_manager_create_channel;
+  iface->ensure_channel = example_callable_media_manager_ensure_channel;
+  /* In this channel manager, Request has the same semantics as Create
+   * (this matches telepathy-gabble's behaviour) */
+  iface->request_channel = example_callable_media_manager_create_channel;
+}
diff --git a/examples/cm/callable/media-manager.h b/examples/cm/callable/media-manager.h
new file mode 100644
index 0000000..5be239e
--- /dev/null
+++ b/examples/cm/callable/media-manager.h
@@ -0,0 +1,71 @@
+/*
+ * media-manager.h - header for an example channel manager
+ *
+ * Copyright © 2007-2009 Collabora Ltd. <http://www.collabora.co.uk/>
+ * Copyright © 2007-2009 Nokia Corporation
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+
+#ifndef __EXAMPLE_CALLABLE_MEDIA_MANAGER_H__
+#define __EXAMPLE_CALLABLE_MEDIA_MANAGER_H__
+
+#include <glib-object.h>
+
+G_BEGIN_DECLS
+
+typedef struct _ExampleCallableMediaManager ExampleCallableMediaManager;
+typedef struct _ExampleCallableMediaManagerPrivate
+    ExampleCallableMediaManagerPrivate;
+
+typedef struct _ExampleCallableMediaManagerClass
+    ExampleCallableMediaManagerClass;
+typedef struct _ExampleCallableMediaManagerClassPrivate
+    ExampleCallableMediaManagerClassPrivate;
+
+struct _ExampleCallableMediaManagerClass {
+    GObjectClass parent_class;
+
+    ExampleCallableMediaManagerClassPrivate *priv;
+};
+
+struct _ExampleCallableMediaManager {
+    GObject parent;
+
+    ExampleCallableMediaManagerPrivate *priv;
+};
+
+GType example_callable_media_manager_get_type (void);
+
+/* TYPE MACROS */
+#define EXAMPLE_TYPE_CALLABLE_MEDIA_MANAGER \
+  (example_callable_media_manager_get_type ())
+#define EXAMPLE_CALLABLE_MEDIA_MANAGER(obj) \
+  (G_TYPE_CHECK_INSTANCE_CAST((obj), EXAMPLE_TYPE_CALLABLE_MEDIA_MANAGER, \
+                              ExampleCallableMediaManager))
+#define EXAMPLE_CALLABLE_MEDIA_MANAGER_CLASS(klass) \
+  (G_TYPE_CHECK_CLASS_CAST((klass), EXAMPLE_TYPE_CALLABLE_MEDIA_MANAGER, \
+                           ExampleCallableMediaManagerClass))
+#define EXAMPLE_IS_CALLABLE_MEDIA_MANAGER(obj) \
+  (G_TYPE_CHECK_INSTANCE_TYPE((obj), EXAMPLE_TYPE_CALLABLE_MEDIA_MANAGER))
+#define EXAMPLE_IS_CALLABLE_MEDIA_MANAGER_CLASS(klass) \
+  (G_TYPE_CHECK_CLASS_TYPE((klass), EXAMPLE_TYPE_CALLABLE_MEDIA_MANAGER))
+#define EXAMPLE_CALLABLE_MEDIA_MANAGER_GET_CLASS(obj) \
+  (G_TYPE_INSTANCE_GET_CLASS ((obj), EXAMPLE_TYPE_CALLABLE_MEDIA_MANAGER, \
+                              ExampleCallableMediaManagerClass))
+
+G_END_DECLS
+
+#endif
-- 
1.5.6.5




More information about the telepathy-commits mailing list