[Telepathy-commits] [telepathy-glib/master] Echo CM: implement TpExportableChannel, respawning, and (#if'd out) Destroyable

Simon McVittie simon.mcvittie at collabora.co.uk
Thu Oct 30 07:05:22 PDT 2008


---
 examples/cm/echo/chan.c    |  140 ++++++++++++++++++++++++++++++++++++++------
 examples/cm/echo/factory.c |   21 +++++--
 2 files changed, 138 insertions(+), 23 deletions(-)

diff --git a/examples/cm/echo/chan.c b/examples/cm/echo/chan.c
index 37f55bb..0a7b938 100644
--- a/examples/cm/echo/chan.c
+++ b/examples/cm/echo/chan.c
@@ -30,7 +30,15 @@ G_DEFINE_TYPE_WITH_CODE (ExampleEchoChannel,
       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_CHANNEL_IFACE, NULL))
+    G_IMPLEMENT_INTERFACE (TP_TYPE_CHANNEL_IFACE, NULL);
+    G_IMPLEMENT_INTERFACE (TP_TYPE_EXPORTABLE_CHANNEL, NULL))
+
+/* FIXME: when supported, add:
+static void channel_iface_init (gpointer iface, gpointer data);
+
+    G_IMPLEMENT_INTERFACE (TP_TYPE_SVC_CHANNEL_INTERFACE_DESTROYABLE,
+      destroyable_iface_init);
+*/
 
 /* type definition stuff */
 
@@ -46,6 +54,8 @@ enum
   PROP_INITIATOR_ID,
   PROP_CONNECTION,
   PROP_INTERFACES,
+  PROP_CHANNEL_DESTROYED,
+  PROP_CHANNEL_PROPERTIES,
   N_PROPS
 };
 
@@ -54,6 +64,7 @@ struct _ExampleEchoChannelPrivate
   TpBaseConnection *conn;
   gchar *object_path;
   TpHandle handle;
+  TpHandle initiator;
 
   /* These are really booleans, but gboolean is signed. Thanks, GLib */
   unsigned closed:1;
@@ -61,6 +72,7 @@ struct _ExampleEchoChannelPrivate
 };
 
 static const char * example_echo_channel_interfaces[] = { NULL };
+/* FIXME: when supported, add TP_IFACE_CHANNEL_INTERFACE_DESTROYABLE */
 
 static void
 example_echo_channel_init (ExampleEchoChannel *self)
@@ -84,6 +96,9 @@ constructor (GType type,
 
   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);
 
@@ -131,27 +146,21 @@ get_property (GObject *object,
         }
       break;
     case PROP_REQUESTED:
-      /* this example CM doesn't yet support other contacts initiating
-       * chats with us, so the only way a channel can exist is if the
-       * user asked for it */
-      g_value_set_boolean (value, TRUE);
+      g_value_set_boolean (value,
+          (self->priv->initiator == self->priv->conn->self_handle));
       break;
     case PROP_INITIATOR_HANDLE:
-      /* this example CM doesn't yet support other contacts initiating
-       * chats with us, so the only way a channel can exist is if the
-       * user asked for it */
-      g_value_set_uint (value, self->priv->conn->self_handle);
+      g_value_set_uint (value, self->priv->initiator);
       break;
     case PROP_INITIATOR_ID:
         {
-          /* this example CM doesn't yet support other contacts initiating
-           * chats with us, so the only way a channel can exist is if the
-           * user asked for it */
           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->conn->self_handle));
+              self->priv->initiator == 0
+                  ? ""
+                  : tp_handle_inspect (contact_repo, self->priv->initiator));
         }
       break;
     case PROP_CONNECTION:
@@ -160,6 +169,22 @@ get_property (GObject *object,
     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;
@@ -186,6 +211,10 @@ set_property (GObject *object,
        */
       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
@@ -212,6 +241,7 @@ dispose (GObject *object)
 
   if (!self->priv->closed)
     {
+      self->priv->closed = TRUE;
       tp_svc_channel_emit_closed (self);
     }
 
@@ -226,6 +256,10 @@ finalize (GObject *object)
       (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);
@@ -274,6 +308,11 @@ example_echo_channel_class_init (ExampleEchoChannelClass *klass)
       "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,
@@ -295,7 +334,7 @@ example_echo_channel_class_init (ExampleEchoChannelClass *klass)
   param_spec = g_param_spec_uint ("initiator-handle", "Initiator's handle",
       "The contact who initiated the channel",
       0, G_MAXUINT32, 0,
-      G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
+      G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
   g_object_class_install_property (object_class, PROP_INITIATOR_HANDLE,
       param_spec);
 
@@ -321,17 +360,55 @@ example_echo_channel_class_init (ExampleEchoChannelClass *klass)
 }
 
 static void
-channel_close (TpSvcChannel *iface,
-               DBusGMethodInvocation *context)
+example_echo_channel_close (ExampleEchoChannel *self)
 {
-  ExampleEchoChannel *self = EXAMPLE_ECHO_CHANNEL (iface);
+  GObject *object = (GObject *) self;
 
   if (!self->priv->closed)
     {
-      self->priv->closed = TRUE;
+      TpHandle first_sender;
+
+      /* The factory 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);
 }
 
@@ -432,3 +509,30 @@ text_iface_init (gpointer iface,
   IMPLEMENT (send);
 #undef IMPLEMENT
 }
+
+/* FIXME: enable this when Destroyable is supported */
+#if 0
+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_return_from_close (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
+}
+#endif
diff --git a/examples/cm/echo/factory.c b/examples/cm/echo/factory.c
index b03f59c..3a934f2 100644
--- a/examples/cm/echo/factory.c
+++ b/examples/cm/echo/factory.c
@@ -173,20 +173,30 @@ example_echo_factory_foreach (TpChannelFactoryIface *iface,
 static void
 channel_closed_cb (ExampleEchoChannel *chan, ExampleEchoFactory *self)
 {
-  TpHandle handle;
-
   if (self->priv->channels != NULL)
     {
+      TpHandle handle;
+      gboolean really_destroyed;
+
       g_object_get (chan,
           "handle", &handle,
+          "channel-destroyed", &really_destroyed,
           NULL);
 
-      g_hash_table_remove (self->priv->channels, GUINT_TO_POINTER (handle));
+      /* 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 (ExampleEchoFactory *self, TpHandle handle)
+new_channel (ExampleEchoFactory *self,
+             TpHandle handle,
+             TpHandle initiator)
 {
   ExampleEchoChannel *chan;
   gchar *object_path;
@@ -198,6 +208,7 @@ new_channel (ExampleEchoFactory *self, TpHandle handle)
       "connection", self->priv->conn,
       "object-path", object_path,
       "handle", handle,
+      "initiator-handle", initiator,
       NULL);
 
   g_free (object_path);
@@ -242,7 +253,7 @@ example_echo_factory_request (TpChannelFactoryIface *iface,
   if (chan == NULL)
     {
       status = TP_CHANNEL_FACTORY_REQUEST_STATUS_CREATED;
-      chan = new_channel (self, handle);
+      chan = new_channel (self, handle, self->priv->conn->self_handle);
     }
 
   g_assert (chan != NULL);
-- 
1.5.6.5




More information about the Telepathy-commits mailing list