aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorGuillaume Desmottes <guillaume.desmottes@collabora.co.uk>2011-09-15 17:53:46 +0800
committerGuillaume Desmottes <guillaume.desmottes@collabora.co.uk>2011-09-15 17:53:46 +0800
commit87f3753ef09050949f28ecdc8272579cb9225641 (patch)
treebed270ee1f03aeb298bb3d8cf5ee5ad3a1c6290d
parent4c7ee0a436cea45e4351e857577ddf944d36b2f3 (diff)
parent42ea425116ef9c079fa199039e3ac5ed0bb8e150 (diff)
downloadgsoc2013-empathy-87f3753ef09050949f28ecdc8272579cb9225641.tar
gsoc2013-empathy-87f3753ef09050949f28ecdc8272579cb9225641.tar.gz
gsoc2013-empathy-87f3753ef09050949f28ecdc8272579cb9225641.tar.bz2
gsoc2013-empathy-87f3753ef09050949f28ecdc8272579cb9225641.tar.lz
gsoc2013-empathy-87f3753ef09050949f28ecdc8272579cb9225641.tar.xz
gsoc2013-empathy-87f3753ef09050949f28ecdc8272579cb9225641.tar.zst
gsoc2013-empathy-87f3753ef09050949f28ecdc8272579cb9225641.zip
Merge remote-tracking branch 'pochu/call-reuse-windows-580794'
-rw-r--r--src/empathy-call-factory.c202
-rw-r--r--src/empathy-call-factory.h5
-rw-r--r--src/empathy-call-window.c176
-rw-r--r--src/empathy-call-window.h6
-rw-r--r--src/empathy-call.c81
5 files changed, 370 insertions, 100 deletions
diff --git a/src/empathy-call-factory.c b/src/empathy-call-factory.c
index 9f3fe16ec..ba17270c7 100644
--- a/src/empathy-call-factory.c
+++ b/src/empathy-call-factory.c
@@ -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>
@@ -40,58 +41,55 @@
#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);
}
diff --git a/src/empathy-call-factory.h b/src/empathy-call-factory.h
index 0e5276278..174fbf4bd 100644
--- a/src/empathy-call-factory.h
+++ b/src/empathy-call-factory.h
@@ -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);
diff --git a/src/empathy-call-window.c b/src/empathy-call-window.c
index ccec9f628..33d4a085c 100644
--- a/src/empathy-call-window.c
+++ b/src/empathy-call-window.c
@@ -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);
@@ -1358,6 +1369,105 @@ empathy_call_window_stage_allocation_changed_cb (ClutterActor *stage,
}
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)
{
EmpathyCallWindowPriv *priv;
@@ -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
diff --git a/src/empathy-call-window.h b/src/empathy-call-window.h
index eebb55f69..81cf170ae 100644
--- a/src/empathy-call-window.h
+++ b/src/empathy-call-window.h
@@ -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);
diff --git a/src/empathy-call.c b/src/empathy-call.c
index 1f60217db..1b643dec1 100644
--- a/src/empathy-call.c
+++ b/src/empathy-call.c
@@ -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);