]> git.0d.be Git - empathy.git/commitdiff
Merge remote-tracking branch 'pochu/call-reuse-windows-580794'
authorGuillaume Desmottes <guillaume.desmottes@collabora.co.uk>
Thu, 15 Sep 2011 09:53:46 +0000 (11:53 +0200)
committerGuillaume Desmottes <guillaume.desmottes@collabora.co.uk>
Thu, 15 Sep 2011 09:53:46 +0000 (11:53 +0200)
src/empathy-call-factory.c
src/empathy-call-factory.h
src/empathy-call-window.c
src/empathy-call-window.h
src/empathy-call.c

index 9f3fe16ec88aead02b59f1d406975f927d16d3bb..ba17270c7036eb3bf7fc2c606eb0898e8a83bebd 100644 (file)
@@ -29,6 +29,7 @@
 
 #include <telepathy-yell/telepathy-yell.h>
 
+#include <libempathy/empathy-client-factory.h>
 #include <libempathy/empathy-request-util.h>
 #include <libempathy/empathy-tp-contact-factory.h>
 #include <libempathy/empathy-utils.h>
 #define DEBUG_FLAG EMPATHY_DEBUG_VOIP
 #include <libempathy/empathy-debug.h>
 
-G_DEFINE_TYPE(EmpathyCallFactory, empathy_call_factory, G_TYPE_OBJECT)
+G_DEFINE_TYPE(EmpathyCallFactory, empathy_call_factory, TP_TYPE_BASE_CLIENT)
 
-static void handle_channels_cb (TpSimpleHandler *handler,
+static void handle_channels (TpBaseClient *client,
     TpAccount *account,
     TpConnection *connection,
     GList *channels,
     GList *requests_satisfied,
     gint64 user_action_time,
-    TpHandleChannelsContext *context,
-    gpointer user_data);
+    TpHandleChannelsContext *context);
+
+static void approve_channels (TpBaseClient *client,
+    TpAccount *account,
+    TpConnection *connection,
+    GList *channels,
+    TpChannelDispatchOperation *dispatch_operation,
+    TpAddDispatchOperationContext *context);
 
 /* signal enum */
 enum
 {
     NEW_CALL_HANDLER,
+    INCOMING_CALL,
     LAST_SIGNAL
 };
 
 static guint signals[LAST_SIGNAL] = {0};
 
-/* private structure */
-typedef struct {
-  TpBaseClient *handler;
-  gboolean dispose_has_run;
-} EmpathyCallFactoryPriv;
-
-#define GET_PRIV(obj) EMPATHY_GET_PRIV (obj, EmpathyCallFactory)
-
 static GObject *call_factory = NULL;
 
 static void
 empathy_call_factory_init (EmpathyCallFactory *obj)
 {
-  EmpathyCallFactoryPriv *priv = G_TYPE_INSTANCE_GET_PRIVATE (obj,
-    EMPATHY_TYPE_CALL_FACTORY, EmpathyCallFactoryPriv);
-  TpAccountManager *am;
-
-  obj->priv = priv;
-
-  am = tp_account_manager_dup ();
+  TpBaseClient *client = (TpBaseClient *) obj;
 
-  priv->handler = tp_simple_handler_new_with_am (am, FALSE, FALSE,
-      EMPATHY_CALL_BUS_NAME_SUFFIX, FALSE, handle_channels_cb, obj, NULL);
+  tp_base_client_take_approver_filter (client, tp_asv_new (
+        TP_PROP_CHANNEL_CHANNEL_TYPE, G_TYPE_STRING,
+          TPY_IFACE_CHANNEL_TYPE_CALL,
+        TP_PROP_CHANNEL_TARGET_HANDLE_TYPE,
+          G_TYPE_UINT, TP_HANDLE_TYPE_CONTACT,
+        NULL));
 
-  tp_base_client_take_handler_filter (priv->handler, tp_asv_new (
+  tp_base_client_take_handler_filter (client, tp_asv_new (
         TP_PROP_CHANNEL_CHANNEL_TYPE, G_TYPE_STRING,
           TPY_IFACE_CHANNEL_TYPE_CALL,
         TP_PROP_CHANNEL_TARGET_HANDLE_TYPE,
           G_TYPE_UINT, TP_HANDLE_TYPE_CONTACT,
         NULL));
 
-  tp_base_client_take_handler_filter (priv->handler, tp_asv_new (
+  tp_base_client_take_handler_filter (client, tp_asv_new (
         TP_PROP_CHANNEL_CHANNEL_TYPE, G_TYPE_STRING,
           TPY_IFACE_CHANNEL_TYPE_CALL,
         TP_PROP_CHANNEL_TARGET_HANDLE_TYPE,
@@ -99,7 +97,7 @@ empathy_call_factory_init (EmpathyCallFactory *obj)
         TPY_PROP_CHANNEL_TYPE_CALL_INITIAL_AUDIO, G_TYPE_BOOLEAN, TRUE,
         NULL));
 
-  tp_base_client_take_handler_filter (priv->handler, tp_asv_new (
+  tp_base_client_take_handler_filter (client, tp_asv_new (
         TP_PROP_CHANNEL_CHANNEL_TYPE, G_TYPE_STRING,
           TPY_IFACE_CHANNEL_TYPE_CALL,
         TP_PROP_CHANNEL_TARGET_HANDLE_TYPE,
@@ -107,13 +105,11 @@ empathy_call_factory_init (EmpathyCallFactory *obj)
         TPY_PROP_CHANNEL_TYPE_CALL_INITIAL_VIDEO, G_TYPE_BOOLEAN, TRUE,
         NULL));
 
-  tp_base_client_add_handler_capabilities_varargs (priv->handler,
+  tp_base_client_add_handler_capabilities_varargs (client,
     "org.freedesktop.Telepathy.Channel.Interface.MediaSignalling/ice-udp",
     "org.freedesktop.Telepathy.Channel.Interface.MediaSignalling/gtalk-p2p",
     "org.freedesktop.Telepathy.Channel.Interface.MediaSignalling/video/h264",
     NULL);
-
-  g_object_unref (am);
 }
 
 static GObject *
@@ -130,59 +126,59 @@ empathy_call_factory_constructor (GType type, guint n_construct_params,
 }
 
 static void
-empathy_call_factory_finalize (GObject *object)
-{
-  /* free any data held directly by the object here */
-
-  if (G_OBJECT_CLASS (empathy_call_factory_parent_class)->finalize)
-    G_OBJECT_CLASS (empathy_call_factory_parent_class)->finalize (object);
-}
-
-static void
-empathy_call_factory_dispose (GObject *object)
-{
-  EmpathyCallFactoryPriv *priv = GET_PRIV (object);
-
-  if (priv->dispose_has_run)
-    return;
-
-  priv->dispose_has_run = TRUE;
-
-  tp_clear_object (&priv->handler);
-
-  if (G_OBJECT_CLASS (empathy_call_factory_parent_class)->dispose)
-    G_OBJECT_CLASS (empathy_call_factory_parent_class)->dispose (object);
-}
-
-static void
-empathy_call_factory_class_init (
-  EmpathyCallFactoryClass *empathy_call_factory_class)
+empathy_call_factory_class_init (EmpathyCallFactoryClass *klass)
 {
-  GObjectClass *object_class = G_OBJECT_CLASS (empathy_call_factory_class);
-
-  g_type_class_add_private (empathy_call_factory_class,
-    sizeof (EmpathyCallFactoryPriv));
+  GObjectClass *object_class = G_OBJECT_CLASS (klass);
+  TpBaseClientClass *base_clt_cls = TP_BASE_CLIENT_CLASS (klass);
 
   object_class->constructor = empathy_call_factory_constructor;
-  object_class->dispose = empathy_call_factory_dispose;
-  object_class->finalize = empathy_call_factory_finalize;
+
+  base_clt_cls->handle_channels = handle_channels;
+  base_clt_cls->add_dispatch_operation = approve_channels;
 
   signals[NEW_CALL_HANDLER] =
     g_signal_new ("new-call-handler",
-      G_TYPE_FROM_CLASS (empathy_call_factory_class),
+      G_TYPE_FROM_CLASS (klass),
       G_SIGNAL_RUN_LAST, 0,
       NULL, NULL,
       _src_marshal_VOID__OBJECT_BOOLEAN,
       G_TYPE_NONE,
       2, EMPATHY_TYPE_CALL_HANDLER, G_TYPE_BOOLEAN);
+
+  signals[INCOMING_CALL] =
+    g_signal_new ("incoming-call",
+      G_TYPE_FROM_CLASS (klass),
+      G_SIGNAL_RUN_LAST, 0,
+      NULL, NULL,
+      _src_marshal_BOOLEAN__UINT_OBJECT_OBJECT_OBJECT,
+      G_TYPE_BOOLEAN,
+      4, G_TYPE_UINT, TPY_TYPE_CALL_CHANNEL,
+      TP_TYPE_CHANNEL_DISPATCH_OPERATION,
+      TP_TYPE_ADD_DISPATCH_OPERATION_CONTEXT);
 }
 
 EmpathyCallFactory *
 empathy_call_factory_initialise (void)
 {
+  EmpathyCallFactory *self;
+  EmpathyClientFactory *factory;
+  TpAccountManager *am;
+
   g_return_val_if_fail (call_factory == NULL, NULL);
 
-  return EMPATHY_CALL_FACTORY (g_object_new (EMPATHY_TYPE_CALL_FACTORY, NULL));
+  am = tp_account_manager_dup ();
+  factory = empathy_client_factory_dup ();
+
+  self = EMPATHY_CALL_FACTORY (g_object_new (EMPATHY_TYPE_CALL_FACTORY,
+      "account-manager", am,
+      "factory", factory,
+      "name", EMPATHY_CALL_BUS_NAME_SUFFIX,
+      NULL));
+
+  g_object_unref (am);
+  g_object_unref (factory);
+
+  return self;
 }
 
 EmpathyCallFactory *
@@ -254,16 +250,15 @@ call_channel_ready_cb (TpyCallChannel *call,
 
 
 static void
-handle_channels_cb (TpSimpleHandler *handler,
+handle_channels (TpBaseClient *client,
     TpAccount *account,
     TpConnection *connection,
     GList *channels,
     GList *requests_satisfied,
     gint64 user_action_time,
-    TpHandleChannelsContext *context,
-    gpointer user_data)
+    TpHandleChannelsContext *context)
 {
-  EmpathyCallFactory *self = user_data;
+  EmpathyCallFactory *self = EMPATHY_CALL_FACTORY (client);
   GList *l;
 
   for (l = channels; l != NULL; l = g_list_next (l))
@@ -298,11 +293,80 @@ handle_channels_cb (TpSimpleHandler *handler,
   tp_handle_channels_context_accept (context);
 }
 
+static TpyCallChannel *
+find_call_channel (GList *channels)
+{
+  GList *l;
+
+  for (l = channels; l != NULL; l = g_list_next (l))
+    {
+      TpChannel *channel = l->data;
+      GQuark channel_type;
+
+      if (tp_proxy_get_invalidated (channel) != NULL)
+        continue;
+
+      channel_type = tp_channel_get_channel_type_id (channel);
+
+      if (channel_type == TPY_IFACE_QUARK_CHANNEL_TYPE_CALL)
+        return TPY_CALL_CHANNEL (channel);
+    }
+
+  return NULL;
+}
+
+static void
+approve_channels (TpBaseClient *client,
+    TpAccount *account,
+    TpConnection *connection,
+    GList *channels,
+    TpChannelDispatchOperation *dispatch_operation,
+    TpAddDispatchOperationContext *context)
+{
+  EmpathyCallFactory *self = EMPATHY_CALL_FACTORY (client);
+  TpyCallChannel *channel;
+  guint handle;
+  GError error = { TP_ERRORS, TP_ERROR_INVALID_ARGUMENT, "" };
+  gboolean handled = FALSE;
+
+  channel = find_call_channel (channels);
+
+  if (channel == NULL)
+    {
+      DEBUG ("Failed to find the main channel; ignoring");
+      error.message = "Unknown channel";
+      goto out;
+    }
+
+  handle = tp_channel_get_handle (TP_CHANNEL (channel), NULL);
+
+  if (handle == 0)
+    {
+      DEBUG ("Unknown handle, ignoring");
+      error.code = TP_ERROR_INVALID_HANDLE;
+      error.message = "Unknown handle";
+      goto out;
+    }
+
+  g_signal_emit (self, signals[INCOMING_CALL], 0,
+      handle, channel, dispatch_operation, context,
+      &handled);
+
+  if (handled)
+    return;
+
+  /* There was no call window so the context wasn't handled. */
+  DEBUG ("Call with a contact for which there's no existing "
+    "call window, ignoring");
+  error.message = "No call window with this contact";
+
+ out:
+  tp_add_dispatch_operation_context_fail (context, &error);
+}
+
 gboolean
 empathy_call_factory_register (EmpathyCallFactory *self,
     GError **error)
 {
-  EmpathyCallFactoryPriv *priv = GET_PRIV (self);
-
-  return tp_base_client_register (priv->handler, error);
+  return tp_base_client_register (TP_BASE_CLIENT (self), error);
 }
index 0e5276278b1a97382f4dfebe249f12634039ac31..174fbf4bd6134faa34c442e0bca87fc3200b53d3 100644 (file)
@@ -29,12 +29,11 @@ typedef struct _EmpathyCallFactory EmpathyCallFactory;
 typedef struct _EmpathyCallFactoryClass EmpathyCallFactoryClass;
 
 struct _EmpathyCallFactoryClass {
-    GObjectClass parent_class;
+    TpBaseClientClass parent_class;
 };
 
 struct _EmpathyCallFactory {
-    GObject parent;
-    gpointer priv;
+    TpBaseClient parent;
 };
 
 GType empathy_call_factory_get_type (void);
index ccec9f628c9243c38a18b8de46bca436f16a0830..33d4a085c6b12988a25208a2e180b5695be5919f 100644 (file)
@@ -43,6 +43,7 @@
 #include <libempathy/empathy-camera-monitor.h>
 #include <libempathy/empathy-gsettings.h>
 #include <libempathy/empathy-tp-contact-factory.h>
+#include <libempathy/empathy-request-util.h>
 #include <libempathy/empathy-utils.h>
 
 #include <libempathy-gtk/empathy-avatar-image.h>
@@ -103,11 +104,12 @@ enum {
 };
 
 typedef enum {
-  CONNECTING,
-  CONNECTED,
-  HELD,
-  DISCONNECTED,
-  REDIALING
+  RINGING,       /* Incoming call */
+  CONNECTING,    /* Outgoing call */
+  CONNECTED,     /* Connected */
+  HELD,          /* Connected, but on hold */
+  DISCONNECTED,  /* Disconnected */
+  REDIALING      /* Redialing (special case of CONNECTING) */
 } CallState;
 
 typedef enum {
@@ -184,6 +186,13 @@ struct _EmpathyCallWindowPriv
      easilly repack everything when toggling fullscreen */
   GtkWidget *content_hbox;
 
+  /* These are used to accept or reject an incoming call when the status
+     is RINGING. */
+  GtkWidget *incoming_call_dialog;
+  TpyCallChannel *pending_channel;
+  TpChannelDispatchOperation *pending_cdo;
+  TpAddDispatchOperationContext *pending_context;
+
   gulong video_output_motion_handler_id;
   guint bus_message_source_id;
 
@@ -293,6 +302,8 @@ static gboolean empathy_call_window_video_output_motion_notify (
 static void empathy_call_window_video_menu_popup (EmpathyCallWindow *window,
   guint button);
 
+static void empathy_call_window_connect_handler (EmpathyCallWindow *self);
+
 static void empathy_call_window_dialpad_cb (GtkToggleToolButton *button,
   EmpathyCallWindow *window);
 
@@ -1357,6 +1368,105 @@ empathy_call_window_stage_allocation_changed_cb (ClutterActor *stage,
       FLOATING_TOOLBAR_SPACING - FLOATING_TOOLBAR_HEIGHT);
 }
 
+static void
+empathy_call_window_incoming_call_response_cb (GtkDialog *dialog,
+    gint response_id,
+    EmpathyCallWindow *self)
+{
+  switch (response_id)
+    {
+      case GTK_RESPONSE_ACCEPT:
+        tp_channel_dispatch_operation_handle_with_async (
+            self->priv->pending_cdo, EMPATHY_CALL_BUS_NAME, NULL, NULL);
+
+        tp_clear_object (&self->priv->pending_cdo);
+        tp_clear_object (&self->priv->pending_channel);
+        tp_clear_object (&self->priv->pending_context);
+
+        break;
+      case GTK_RESPONSE_CANCEL:
+        tp_channel_dispatch_operation_close_channels_async (
+            self->priv->pending_cdo, NULL, NULL);
+
+        empathy_call_window_status_message (self, _("Disconnected"));
+        self->priv->call_state = DISCONNECTED;
+        break;
+      default:
+        g_warn_if_reached ();
+    }
+}
+
+static void
+empathy_call_window_set_state_ringing (EmpathyCallWindow *self)
+{
+  gboolean video;
+
+  g_assert (self->priv->call_state != CONNECTED);
+
+  video = tpy_call_channel_has_initial_video (self->priv->pending_channel);
+
+  empathy_call_window_status_message (self, _("Incoming call"));
+  self->priv->call_state = RINGING;
+
+  self->priv->incoming_call_dialog = gtk_message_dialog_new (
+      GTK_WINDOW (self), GTK_DIALOG_MODAL,
+      GTK_MESSAGE_QUESTION, GTK_BUTTONS_NONE,
+      video ? _("Incoming video call from %s") : _("Incoming call from %s"),
+      empathy_contact_get_alias (self->priv->contact));
+
+  gtk_dialog_add_buttons (GTK_DIALOG (self->priv->incoming_call_dialog),
+      _("Reject"), GTK_RESPONSE_CANCEL,
+      _("Answer"), GTK_RESPONSE_ACCEPT,
+      NULL);
+
+  g_signal_connect (self->priv->incoming_call_dialog, "response",
+      G_CALLBACK (empathy_call_window_incoming_call_response_cb), self);
+  gtk_widget_show (self->priv->incoming_call_dialog);
+}
+
+static void
+empathy_call_window_cdo_invalidated_cb (TpProxy *channel,
+    guint domain,
+    gint code,
+    gchar *message,
+    EmpathyCallWindow *self)
+{
+  tp_clear_object (&self->priv->pending_cdo);
+  tp_clear_object (&self->priv->pending_channel);
+  tp_clear_object (&self->priv->pending_context);
+
+  /* We don't know if the incoming call has been accepted or not, so we
+   * assume it hasn't and if it has, we'll set the proper status when
+   * we get the new handler. */
+  empathy_call_window_status_message (self, _("Disconnected"));
+  self->priv->call_state = DISCONNECTED;
+
+  gtk_widget_destroy (self->priv->incoming_call_dialog);
+  self->priv->incoming_call_dialog = NULL;
+}
+
+void
+empathy_call_window_start_ringing (EmpathyCallWindow *self,
+    TpyCallChannel *channel,
+    TpChannelDispatchOperation *dispatch_operation,
+    TpAddDispatchOperationContext *context)
+{
+  g_assert (self->priv->pending_channel == NULL);
+  g_assert (self->priv->pending_context == NULL);
+  g_assert (self->priv->pending_cdo == NULL);
+
+  /* Start ringing and delay until the user answers or hangs. */
+  self->priv->pending_channel = g_object_ref (channel);
+  self->priv->pending_context = g_object_ref (context);
+  self->priv->pending_cdo = g_object_ref (dispatch_operation);
+
+  g_signal_connect (self->priv->pending_cdo, "invalidated",
+      G_CALLBACK (empathy_call_window_cdo_invalidated_cb), self);
+
+  empathy_call_window_set_state_ringing (self);
+  tp_add_dispatch_operation_context_accept (context);
+}
+
 static void
 empathy_call_window_init (EmpathyCallWindow *self)
 {
@@ -2199,6 +2309,20 @@ empathy_call_window_new (EmpathyCallHandler *handler)
     g_object_new (EMPATHY_TYPE_CALL_WINDOW, "handler", handler, NULL));
 }
 
+void
+empathy_call_window_present (EmpathyCallWindow *self,
+    EmpathyCallHandler *handler)
+{
+  g_return_if_fail (EMPATHY_IS_CALL_HANDLER (handler));
+
+  tp_clear_object (&self->priv->handler);
+  self->priv->handler = g_object_ref (handler);
+  empathy_call_window_connect_handler (self);
+
+  empathy_window_present (GTK_WINDOW (self));
+  empathy_call_window_restart_call (self);
+}
+
 static void
 empathy_call_window_conference_added_cb (EmpathyCallHandler *handler,
   GstElement *conference, gpointer user_data)
@@ -3272,35 +3396,30 @@ call_handler_notify_call_cb (EmpathyCallHandler *handler,
 }
 
 static void
-empathy_call_window_realized_cb (GtkWidget *widget, EmpathyCallWindow *window)
+empathy_call_window_connect_handler (EmpathyCallWindow *self)
 {
-  EmpathyCallWindowPriv *priv = GET_PRIV (window);
+  EmpathyCallWindowPriv *priv = GET_PRIV (self);
   TpyCallChannel *call;
-  gint width;
-
-  /* Make the hangup button twice as wide */
-  width = gtk_widget_get_allocated_width (priv->hangup_button);
-  gtk_widget_set_size_request (priv->hangup_button, width * 2, -1);
 
   g_signal_connect (priv->handler, "state-changed",
-    G_CALLBACK (empathy_call_window_state_changed_cb), window);
+    G_CALLBACK (empathy_call_window_state_changed_cb), self);
   g_signal_connect (priv->handler, "conference-added",
-    G_CALLBACK (empathy_call_window_conference_added_cb), window);
+    G_CALLBACK (empathy_call_window_conference_added_cb), self);
   g_signal_connect (priv->handler, "conference-removed",
-    G_CALLBACK (empathy_call_window_conference_removed_cb), window);
+    G_CALLBACK (empathy_call_window_conference_removed_cb), self);
   g_signal_connect (priv->handler, "closed",
-    G_CALLBACK (empathy_call_window_channel_closed_cb), window);
+    G_CALLBACK (empathy_call_window_channel_closed_cb), self);
   g_signal_connect (priv->handler, "src-pad-added",
-    G_CALLBACK (empathy_call_window_src_added_cb), window);
+    G_CALLBACK (empathy_call_window_src_added_cb), self);
   g_signal_connect (priv->handler, "sink-pad-added",
-    G_CALLBACK (empathy_call_window_sink_added_cb), window);
+    G_CALLBACK (empathy_call_window_sink_added_cb), self);
   g_signal_connect (priv->handler, "sink-pad-removed",
-    G_CALLBACK (empathy_call_window_sink_removed_cb), window);
+    G_CALLBACK (empathy_call_window_sink_removed_cb), self);
 
   g_object_get (priv->handler, "call-channel", &call, NULL);
   if (call != NULL)
     {
-      call_handler_notify_call_cb (priv->handler, NULL, window);
+      call_handler_notify_call_cb (priv->handler, NULL, self);
       g_object_unref (call);
     }
   else
@@ -3308,10 +3427,23 @@ empathy_call_window_realized_cb (GtkWidget *widget, EmpathyCallWindow *window)
       /* call-channel doesn't exist yet, we'll connect signals once it has been
        * set */
       g_signal_connect (priv->handler, "notify::call-channel",
-        G_CALLBACK (call_handler_notify_call_cb), window);
+        G_CALLBACK (call_handler_notify_call_cb), self);
     }
+}
+
+static void
+empathy_call_window_realized_cb (GtkWidget *widget,
+    EmpathyCallWindow *self)
+{
+  gint width;
+
+  /* Make the hangup button twice as wide */
+  width = gtk_widget_get_allocated_width (self->priv->hangup_button);
+  gtk_widget_set_size_request (self->priv->hangup_button, width * 2, -1);
+
+  empathy_call_window_connect_handler (self);
 
-  gst_element_set_state (priv->pipeline, GST_STATE_PAUSED);
+  gst_element_set_state (self->priv->pipeline, GST_STATE_PAUSED);
 }
 
 static gboolean
index eebb55f69e051a55402cb7a0fcd0d0d0562e58d7..81cf170aef42669797c1459c2efaba5e929958a6 100644 (file)
@@ -63,6 +63,12 @@ GType empathy_call_window_get_type (void);
     EmpathyCallWindowClass))
 
 EmpathyCallWindow *empathy_call_window_new (EmpathyCallHandler *handler);
+void empathy_call_window_present (EmpathyCallWindow *window,
+  EmpathyCallHandler *handler);
+void empathy_call_window_start_ringing (EmpathyCallWindow *self,
+  TpyCallChannel *channel,
+  TpChannelDispatchOperation *dispatch_operation,
+  TpAddDispatchOperationContext *context);
 
 GtkUIManager *empathy_call_window_get_ui_manager (EmpathyCallWindow *window);
 
index 1f60217db662134a60ab23fce0f3d4da74cdf136..1b643dec11980d73ebe69a9ad56d5c969e167afa 100644 (file)
@@ -34,6 +34,8 @@
 
 #include <telepathy-yell/telepathy-yell.h>
 
+#include <libempathy/empathy-client-factory.h>
+
 #include <libempathy-gtk/empathy-ui-utils.h>
 
 #include "empathy-call-window.h"
@@ -55,6 +57,55 @@ static gboolean use_timer = TRUE;
 
 static EmpathyCallFactory *call_factory = NULL;
 
+/* An EmpathyContact -> EmpathyCallWindow hash table for all existing
+ * Call windows. We own a ref on the EmpathyContacts. */
+static GHashTable *call_windows;
+
+static void
+call_window_destroyed_cb (GtkWidget *window,
+    EmpathyContact *contact)
+{
+  g_hash_table_remove (call_windows, contact);
+
+  g_application_release (G_APPLICATION (app));
+}
+
+static gboolean
+find_window_for_handle (gpointer key,
+    gpointer value,
+    gpointer user_data)
+{
+  EmpathyContact *contact = key;
+  guint handle = GPOINTER_TO_UINT (user_data);
+
+  if (handle == empathy_contact_get_handle (contact))
+    return TRUE;
+
+  return FALSE;
+}
+
+static gboolean
+incoming_call_cb (EmpathyCallFactory *factory,
+    guint handle,
+    TpyCallChannel *channel,
+    TpChannelDispatchOperation *dispatch_operation,
+    TpAddDispatchOperationContext *context,
+    gpointer user_data)
+{
+  EmpathyCallWindow *window = g_hash_table_find (call_windows,
+      find_window_for_handle, GUINT_TO_POINTER (handle));
+
+  if (window != NULL)
+    {
+      /* The window takes care of accepting or rejecting the context. */
+      empathy_call_window_start_ringing (window,
+          channel, dispatch_operation, context);
+      return TRUE;
+    }
+
+  return FALSE;
+}
+
 static void
 new_call_handler_cb (EmpathyCallFactory *factory,
     EmpathyCallHandler *handler,
@@ -62,17 +113,29 @@ new_call_handler_cb (EmpathyCallFactory *factory,
     gpointer user_data)
 {
   EmpathyCallWindow *window;
+  EmpathyContact *contact;
 
-  DEBUG ("Create a new call window");
+  DEBUG ("Show the call window");
 
-  window = empathy_call_window_new (handler);
+  g_object_get (handler, "target-contact", &contact, NULL);
 
-  g_application_hold (G_APPLICATION (app));
+  window = g_hash_table_lookup (call_windows, contact);
 
-  g_signal_connect_swapped (window, "destroy",
-      G_CALLBACK (g_application_release), app);
+  if (window != NULL)
+    {
+      empathy_call_window_present (window, handler);
+    }
+  else
+    {
+      window = empathy_call_window_new (handler);
 
-  gtk_widget_show (GTK_WIDGET (window));
+      g_hash_table_insert (call_windows, g_object_ref (contact), window);
+      g_application_hold (G_APPLICATION (app));
+      g_signal_connect (window, "destroy",
+          G_CALLBACK (call_window_destroyed_cb), contact);
+
+      gtk_widget_show (GTK_WIDGET (window));
+    }
 }
 
 static void
@@ -96,6 +159,8 @@ activate_cb (GApplication *application)
 
   g_signal_connect (G_OBJECT (call_factory), "new-call-handler",
       G_CALLBACK (new_call_handler_cb), NULL);
+  g_signal_connect (G_OBJECT (call_factory), "incoming-call",
+      G_CALLBACK (incoming_call_cb), NULL);
 
   if (!empathy_call_factory_register (call_factory, &error))
     {
@@ -172,6 +237,9 @@ main (int argc,
       use_timer = FALSE;
     }
 
+  call_windows = g_hash_table_new_full (g_direct_hash, g_direct_equal,
+      g_object_unref, NULL);
+
   /* the inactivity timeout can only be set while the application is held */
   g_application_hold (G_APPLICATION (app));
   g_application_set_inactivity_timeout (G_APPLICATION (app), TIMEOUT * 1000);
@@ -179,6 +247,7 @@ main (int argc,
 
   retval = g_application_run (G_APPLICATION (app), argc, argv);
 
+  g_hash_table_unref (call_windows);
   g_object_unref (app);
   tp_clear_object (&call_factory);