diff options
author | Guillaume Desmottes <guillaume.desmottes@collabora.co.uk> | 2011-09-15 17:53:46 +0800 |
---|---|---|
committer | Guillaume Desmottes <guillaume.desmottes@collabora.co.uk> | 2011-09-15 17:53:46 +0800 |
commit | 87f3753ef09050949f28ecdc8272579cb9225641 (patch) | |
tree | bed270ee1f03aeb298bb3d8cf5ee5ad3a1c6290d | |
parent | 4c7ee0a436cea45e4351e857577ddf944d36b2f3 (diff) | |
parent | 42ea425116ef9c079fa199039e3ac5ed0bb8e150 (diff) | |
download | gsoc2013-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.c | 202 | ||||
-rw-r--r-- | src/empathy-call-factory.h | 5 | ||||
-rw-r--r-- | src/empathy-call-window.c | 176 | ||||
-rw-r--r-- | src/empathy-call-window.h | 6 | ||||
-rw-r--r-- | src/empathy-call.c | 81 |
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); |