2 * empathy-call-window.c - Source for EmpathyCallWindow
3 * Copyright (C) 2008-2009 Collabora Ltd.
4 * @author Sjoerd Simons <sjoerd.simons@collabora.co.uk>
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
27 #include <gdk/gdkkeysyms.h>
30 #include <glib/gi18n.h>
32 #include <telepathy-farsight/channel.h>
34 #include <gst/farsight/fs-element-added-notifier.h>
36 #include <libempathy/empathy-tp-contact-factory.h>
37 #include <libempathy/empathy-call-factory.h>
38 #include <libempathy/empathy-utils.h>
39 #include <libempathy-gtk/empathy-avatar-image.h>
40 #include <libempathy-gtk/empathy-video-widget.h>
41 #include <libempathy-gtk/empathy-audio-src.h>
42 #include <libempathy-gtk/empathy-audio-sink.h>
43 #include <libempathy-gtk/empathy-video-src.h>
44 #include <libempathy-gtk/empathy-ui-utils.h>
45 #include <libempathy-gtk/empathy-sound.h>
47 #include "empathy-call-window.h"
48 #include "empathy-call-window-fullscreen.h"
49 #include "empathy-sidebar.h"
51 #define BUTTON_ID "empathy-call-dtmf-button-id"
53 #define CONTENT_HBOX_BORDER_WIDTH 6
54 #define CONTENT_HBOX_SPACING 3
55 #define CONTENT_HBOX_CHILDREN_PACKING_PADDING 3
57 #define SELF_VIDEO_SECTION_WIDTH 160
58 #define SELF_VIDEO_SECTION_HEIGTH 120
60 /* The avatar's default width and height are set to the same value because we
61 want a square icon. */
62 #define REMOTE_CONTACT_AVATAR_DEFAULT_WIDTH EMPATHY_VIDEO_WIDGET_DEFAULT_HEIGHT
63 #define REMOTE_CONTACT_AVATAR_DEFAULT_HEIGHT EMPATHY_VIDEO_WIDGET_DEFAULT_HEIGHT
65 /* If an video input error occurs, the error message will start with "v4l" */
66 #define VIDEO_INPUT_ERROR_PREFIX "v4l"
68 /* The time interval in milliseconds between 2 outgoing rings */
69 #define MS_BETWEEN_RING 500
71 G_DEFINE_TYPE(EmpathyCallWindow, empathy_call_window, GTK_TYPE_WINDOW)
80 static guint signals[LAST_SIGNAL] = {0};
84 PROP_CALL_HANDLER = 1,
94 /* private structure */
95 typedef struct _EmpathyCallWindowPriv EmpathyCallWindowPriv;
97 struct _EmpathyCallWindowPriv
99 gboolean dispose_has_run;
100 EmpathyCallHandler *handler;
101 EmpathyContact *contact;
106 GtkUIManager *ui_manager;
107 GtkWidget *video_output;
108 GtkWidget *video_preview;
109 GtkWidget *remote_user_avatar_widget;
110 GtkWidget *self_user_avatar_widget;
112 GtkWidget *sidebar_button;
113 GtkWidget *statusbar;
114 GtkWidget *volume_button;
115 GtkWidget *redial_button;
116 GtkWidget *mic_button;
117 GtkWidget *camera_button;
120 GtkAction *show_preview;
121 GtkAction *send_video;
123 GtkAction *menu_fullscreen;
125 /* The frames and boxes that contain self and remote avatar and video
126 input/output. When we redial, we destroy and re-create the boxes */
127 GtkWidget *remote_user_output_frame;
128 GtkWidget *self_user_output_frame;
129 GtkWidget *remote_user_output_hbox;
130 GtkWidget *self_user_output_hbox;
132 /* We keep a reference on the hbox which contains the main content so we can
133 easilly repack everything when toggling fullscreen */
134 GtkWidget *content_hbox;
136 /* This vbox is contained in the content_hbox and it contains the
137 self_user_output_frame and the sidebar button. When toggling fullscreen,
138 it needs to be repacked. We keep a reference on it for easier access. */
141 gulong video_output_motion_handler_id;
142 guint bus_message_source_id;
145 GtkWidget *volume_scale;
146 GtkWidget *volume_progress_bar;
147 GtkAdjustment *audio_input_adj;
149 GtkWidget *dtmf_panel;
151 GstElement *video_input;
152 GstElement *audio_input;
153 GstElement *audio_output;
154 GstElement *pipeline;
155 GstElement *video_tee;
158 GstElement *liveadder;
160 FsElementAddedNotifier *fsnotifier;
167 GtkWidget *video_contrast;
168 GtkWidget *video_brightness;
169 GtkWidget *video_gamma;
172 gboolean call_started;
173 gboolean sending_video;
175 EmpathyCallWindowFullscreen *fullscreen;
176 gboolean is_fullscreen;
178 /* Those fields represent the state of the window before it actually was in
180 gboolean sidebar_was_visible_before_fs;
181 gint original_width_before_fs;
182 gint original_height_before_fs;
185 #define GET_PRIV(o) \
186 (G_TYPE_INSTANCE_GET_PRIVATE ((o), EMPATHY_TYPE_CALL_WINDOW, \
187 EmpathyCallWindowPriv))
189 static void empathy_call_window_realized_cb (GtkWidget *widget,
190 EmpathyCallWindow *window);
192 static gboolean empathy_call_window_delete_cb (GtkWidget *widget,
193 GdkEvent *event, EmpathyCallWindow *window);
195 static gboolean empathy_call_window_state_event_cb (GtkWidget *widget,
196 GdkEventWindowState *event, EmpathyCallWindow *window);
198 static void empathy_call_window_sidebar_toggled_cb (GtkToggleButton *toggle,
199 EmpathyCallWindow *window);
201 static void empathy_call_window_camera_toggled_cb (GtkToggleToolButton *toggle,
202 EmpathyCallWindow *window);
204 static void empathy_call_window_set_send_video (EmpathyCallWindow *window,
207 static void empathy_call_window_send_video_toggled_cb (GtkToggleAction *toggle,
208 EmpathyCallWindow *window);
210 static void empathy_call_window_show_preview_toggled_cb (GtkToggleAction *toggle,
211 EmpathyCallWindow *window);
213 static void empathy_call_window_mic_toggled_cb (
214 GtkToggleToolButton *toggle, EmpathyCallWindow *window);
216 static void empathy_call_window_sidebar_hidden_cb (EmpathySidebar *sidebar,
217 EmpathyCallWindow *window);
219 static void empathy_call_window_sidebar_shown_cb (EmpathySidebar *sidebar,
220 EmpathyCallWindow *window);
222 static void empathy_call_window_hangup_cb (gpointer object,
223 EmpathyCallWindow *window);
225 static void empathy_call_window_fullscreen_cb (gpointer object,
226 EmpathyCallWindow *window);
228 static void empathy_call_window_fullscreen_toggle (EmpathyCallWindow *window);
230 static gboolean empathy_call_window_video_button_press_cb (GtkWidget *video_output,
231 GdkEventButton *event, EmpathyCallWindow *window);
233 static gboolean empathy_call_window_key_press_cb (GtkWidget *video_output,
234 GdkEventKey *event, EmpathyCallWindow *window);
236 static gboolean empathy_call_window_video_output_motion_notify (GtkWidget *widget,
237 GdkEventMotion *event, EmpathyCallWindow *window);
239 static void empathy_call_window_video_menu_popup (EmpathyCallWindow *window,
242 static void empathy_call_window_redial_cb (gpointer object,
243 EmpathyCallWindow *window);
245 static void empathy_call_window_restart_call (EmpathyCallWindow *window);
247 static void empathy_call_window_status_message (EmpathyCallWindow *window,
250 static void empathy_call_window_update_avatars_visibility (EmpathyTpCall *call,
251 EmpathyCallWindow *window);
253 static gboolean empathy_call_window_bus_message (GstBus *bus,
254 GstMessage *message, gpointer user_data);
257 empathy_call_window_volume_changed_cb (GtkScaleButton *button,
258 gdouble value, EmpathyCallWindow *window);
261 empathy_call_window_setup_toolbar (EmpathyCallWindow *self)
263 EmpathyCallWindowPriv *priv = GET_PRIV (self);
264 GtkToolItem *tool_item;
266 /* Add an empty expanded GtkToolItem so the volume button is at the end of
268 tool_item = gtk_tool_item_new ();
269 gtk_tool_item_set_expand (tool_item, TRUE);
270 gtk_widget_show (GTK_WIDGET (tool_item));
271 gtk_toolbar_insert (GTK_TOOLBAR (priv->toolbar), tool_item, -1);
273 priv->volume_button = gtk_volume_button_new ();
274 /* FIXME listen to the audiosinks signals and update the button according to
275 * that, for now starting out at 1.0 and assuming only the app changes the
277 gtk_scale_button_set_value (GTK_SCALE_BUTTON (priv->volume_button), 1.0);
278 g_signal_connect (G_OBJECT (priv->volume_button), "value-changed",
279 G_CALLBACK (empathy_call_window_volume_changed_cb), self);
281 tool_item = gtk_tool_item_new ();
282 gtk_container_add (GTK_CONTAINER (tool_item), priv->volume_button);
283 gtk_widget_show_all (GTK_WIDGET (tool_item));
284 gtk_toolbar_insert (GTK_TOOLBAR (priv->toolbar), tool_item, -1);
288 dtmf_button_pressed_cb (GtkButton *button, EmpathyCallWindow *window)
290 EmpathyCallWindowPriv *priv = GET_PRIV (window);
295 g_object_get (priv->handler, "tp-call", &call, NULL);
297 button_quark = g_quark_from_static_string (BUTTON_ID);
298 event = GPOINTER_TO_UINT (g_object_get_qdata (G_OBJECT (button),
301 empathy_tp_call_start_tone (call, event);
303 g_object_unref (call);
307 dtmf_button_released_cb (GtkButton *button, EmpathyCallWindow *window)
309 EmpathyCallWindowPriv *priv = GET_PRIV (window);
312 g_object_get (priv->handler, "tp-call", &call, NULL);
314 empathy_tp_call_stop_tone (call);
316 g_object_unref (call);
320 empathy_call_window_create_dtmf (EmpathyCallWindow *self)
328 } dtmfbuttons[] = { { "1", TP_DTMF_EVENT_DIGIT_1 },
329 { "2", TP_DTMF_EVENT_DIGIT_2 },
330 { "3", TP_DTMF_EVENT_DIGIT_3 },
331 { "4", TP_DTMF_EVENT_DIGIT_4 },
332 { "5", TP_DTMF_EVENT_DIGIT_5 },
333 { "6", TP_DTMF_EVENT_DIGIT_6 },
334 { "7", TP_DTMF_EVENT_DIGIT_7 },
335 { "8", TP_DTMF_EVENT_DIGIT_8 },
336 { "9", TP_DTMF_EVENT_DIGIT_9 },
337 { "#", TP_DTMF_EVENT_HASH },
338 { "0", TP_DTMF_EVENT_DIGIT_0 },
339 { "*", TP_DTMF_EVENT_ASTERISK },
342 button_quark = g_quark_from_static_string (BUTTON_ID);
344 table = gtk_table_new (4, 3, TRUE);
346 for (i = 0; dtmfbuttons[i].label != NULL; i++)
348 GtkWidget *button = gtk_button_new_with_label (dtmfbuttons[i].label);
349 gtk_table_attach (GTK_TABLE (table), button, i % 3, i % 3 + 1,
350 i/3, i/3 + 1, GTK_EXPAND | GTK_FILL, GTK_EXPAND | GTK_FILL, 1, 1);
352 g_object_set_qdata (G_OBJECT (button), button_quark,
353 GUINT_TO_POINTER (dtmfbuttons[i].event));
355 g_signal_connect (G_OBJECT (button), "pressed",
356 G_CALLBACK (dtmf_button_pressed_cb), self);
357 g_signal_connect (G_OBJECT (button), "released",
358 G_CALLBACK (dtmf_button_released_cb), self);
365 empathy_call_window_create_video_input_add_slider (EmpathyCallWindow *self,
366 gchar *label_text, GtkWidget *bin)
368 GtkWidget *vbox = gtk_vbox_new (FALSE, 2);
369 GtkWidget *scale = gtk_vscale_new_with_range (0, 100, 10);
370 GtkWidget *label = gtk_label_new (label_text);
372 gtk_widget_set_sensitive (scale, FALSE);
374 gtk_container_add (GTK_CONTAINER (bin), vbox);
376 gtk_range_set_inverted (GTK_RANGE (scale), TRUE);
377 gtk_box_pack_start (GTK_BOX (vbox), scale, TRUE, TRUE, 0);
378 gtk_box_pack_start (GTK_BOX (vbox), label, FALSE, FALSE, 0);
384 empathy_call_window_video_contrast_changed_cb (GtkAdjustment *adj,
385 EmpathyCallWindow *self)
388 EmpathyCallWindowPriv *priv = GET_PRIV (self);
390 empathy_video_src_set_channel (priv->video_input,
391 EMPATHY_GST_VIDEO_SRC_CHANNEL_CONTRAST, gtk_adjustment_get_value (adj));
395 empathy_call_window_video_brightness_changed_cb (GtkAdjustment *adj,
396 EmpathyCallWindow *self)
399 EmpathyCallWindowPriv *priv = GET_PRIV (self);
401 empathy_video_src_set_channel (priv->video_input,
402 EMPATHY_GST_VIDEO_SRC_CHANNEL_BRIGHTNESS, gtk_adjustment_get_value (adj));
406 empathy_call_window_video_gamma_changed_cb (GtkAdjustment *adj,
407 EmpathyCallWindow *self)
410 EmpathyCallWindowPriv *priv = GET_PRIV (self);
412 empathy_video_src_set_channel (priv->video_input,
413 EMPATHY_GST_VIDEO_SRC_CHANNEL_GAMMA, gtk_adjustment_get_value (adj));
418 empathy_call_window_create_video_input (EmpathyCallWindow *self)
420 EmpathyCallWindowPriv *priv = GET_PRIV (self);
423 hbox = gtk_hbox_new (TRUE, 3);
425 priv->video_contrast = empathy_call_window_create_video_input_add_slider (
426 self, _("Contrast"), hbox);
428 priv->video_brightness = empathy_call_window_create_video_input_add_slider (
429 self, _("Brightness"), hbox);
431 priv->video_gamma = empathy_call_window_create_video_input_add_slider (
432 self, _("Gamma"), hbox);
438 empathy_call_window_setup_video_input (EmpathyCallWindow *self)
440 EmpathyCallWindowPriv *priv = GET_PRIV (self);
444 supported = empathy_video_src_get_supported_channels (priv->video_input);
446 if (supported & EMPATHY_GST_VIDEO_SRC_SUPPORTS_CONTRAST)
448 adj = gtk_range_get_adjustment (GTK_RANGE (priv->video_contrast));
450 gtk_adjustment_set_value (adj,
451 empathy_video_src_get_channel (priv->video_input,
452 EMPATHY_GST_VIDEO_SRC_CHANNEL_CONTRAST));
454 g_signal_connect (G_OBJECT (adj), "value-changed",
455 G_CALLBACK (empathy_call_window_video_contrast_changed_cb), self);
457 gtk_widget_set_sensitive (priv->video_contrast, TRUE);
460 if (supported & EMPATHY_GST_VIDEO_SRC_SUPPORTS_BRIGHTNESS)
462 adj = gtk_range_get_adjustment (GTK_RANGE (priv->video_brightness));
464 gtk_adjustment_set_value (adj,
465 empathy_video_src_get_channel (priv->video_input,
466 EMPATHY_GST_VIDEO_SRC_CHANNEL_BRIGHTNESS));
468 g_signal_connect (G_OBJECT (adj), "value-changed",
469 G_CALLBACK (empathy_call_window_video_brightness_changed_cb), self);
470 gtk_widget_set_sensitive (priv->video_brightness, TRUE);
473 if (supported & EMPATHY_GST_VIDEO_SRC_SUPPORTS_GAMMA)
475 adj = gtk_range_get_adjustment (GTK_RANGE (priv->video_gamma));
477 gtk_adjustment_set_value (adj,
478 empathy_video_src_get_channel (priv->video_input,
479 EMPATHY_GST_VIDEO_SRC_CHANNEL_GAMMA));
481 g_signal_connect (G_OBJECT (adj), "value-changed",
482 G_CALLBACK (empathy_call_window_video_gamma_changed_cb), self);
483 gtk_widget_set_sensitive (priv->video_gamma, TRUE);
488 empathy_call_window_mic_volume_changed_cb (GtkAdjustment *adj,
489 EmpathyCallWindow *self)
491 EmpathyCallWindowPriv *priv = GET_PRIV (self);
494 if (priv->audio_input == NULL)
497 volume = gtk_adjustment_get_value (adj)/100.0;
499 /* Don't store the volume because of muting */
500 if (volume > 0 || gtk_toggle_tool_button_get_active (
501 GTK_TOGGLE_TOOL_BUTTON (priv->mic_button)))
502 priv->volume = volume;
504 /* Ensure that the toggle button is active if the volume is > 0 and inactive
505 * if it's smaller than 0 */
506 if ((volume > 0) != gtk_toggle_tool_button_get_active (
507 GTK_TOGGLE_TOOL_BUTTON (priv->mic_button)))
508 gtk_toggle_tool_button_set_active (
509 GTK_TOGGLE_TOOL_BUTTON (priv->mic_button), volume > 0);
511 empathy_audio_src_set_volume (EMPATHY_GST_AUDIO_SRC (priv->audio_input),
516 empathy_call_window_audio_input_level_changed_cb (EmpathyGstAudioSrc *src,
517 gdouble level, EmpathyCallWindow *window)
520 EmpathyCallWindowPriv *priv = GET_PRIV (window);
522 value = CLAMP (pow (10, level / 20), 0.0, 1.0);
523 gtk_progress_bar_set_fraction (GTK_PROGRESS_BAR (priv->volume_progress_bar), value);
527 empathy_call_window_create_audio_input (EmpathyCallWindow *self)
529 EmpathyCallWindowPriv *priv = GET_PRIV (self);
530 GtkWidget *hbox, *vbox, *label;
532 hbox = gtk_hbox_new (TRUE, 3);
534 vbox = gtk_vbox_new (FALSE, 3);
535 gtk_box_pack_start (GTK_BOX (hbox), vbox, FALSE, FALSE, 3);
537 priv->volume_scale = gtk_vscale_new_with_range (0, 150, 100);
538 gtk_range_set_inverted (GTK_RANGE (priv->volume_scale), TRUE);
539 label = gtk_label_new (_("Volume"));
541 priv->audio_input_adj = gtk_range_get_adjustment (
542 GTK_RANGE (priv->volume_scale));
543 priv->volume = empathy_audio_src_get_volume (EMPATHY_GST_AUDIO_SRC
544 (priv->audio_input));
545 gtk_adjustment_set_value (priv->audio_input_adj, priv->volume * 100);
547 g_signal_connect (G_OBJECT (priv->audio_input_adj), "value-changed",
548 G_CALLBACK (empathy_call_window_mic_volume_changed_cb), self);
550 gtk_box_pack_start (GTK_BOX (vbox), priv->volume_scale, TRUE, TRUE, 3);
551 gtk_box_pack_start (GTK_BOX (vbox), label, FALSE, FALSE, 3);
553 priv->volume_progress_bar = gtk_progress_bar_new ();
554 gtk_progress_bar_set_orientation (
555 GTK_PROGRESS_BAR (priv->volume_progress_bar), GTK_PROGRESS_BOTTOM_TO_TOP);
556 gtk_progress_bar_set_fraction (GTK_PROGRESS_BAR (priv->volume_progress_bar),
559 gtk_box_pack_start (GTK_BOX (hbox), priv->volume_progress_bar, FALSE, FALSE,
566 empathy_call_window_setup_remote_frame (GstBus *bus, EmpathyCallWindow *self)
568 EmpathyCallWindowPriv *priv = GET_PRIV (self);
570 /* Initializing all the content (UI and output gst elements) related to the
572 priv->remote_user_output_hbox = gtk_hbox_new (FALSE, 0);
574 priv->remote_user_avatar_widget = gtk_image_new ();
575 gtk_box_pack_start (GTK_BOX (priv->remote_user_output_hbox),
576 priv->remote_user_avatar_widget, TRUE, TRUE, 0);
578 priv->video_output = empathy_video_widget_new (bus);
579 gtk_box_pack_start (GTK_BOX (priv->remote_user_output_hbox),
580 priv->video_output, TRUE, TRUE, 0);
582 gtk_widget_add_events (priv->video_output,
583 GDK_BUTTON_PRESS_MASK | GDK_POINTER_MOTION_MASK);
584 g_signal_connect (G_OBJECT (priv->video_output), "button-press-event",
585 G_CALLBACK (empathy_call_window_video_button_press_cb), self);
587 gtk_container_add (GTK_CONTAINER (priv->remote_user_output_frame),
588 priv->remote_user_output_hbox);
590 priv->audio_output = empathy_audio_sink_new ();
591 gst_object_ref (priv->audio_output);
592 gst_object_sink (priv->audio_output);
596 empathy_call_window_setup_self_frame (GstBus *bus, EmpathyCallWindow *self)
598 EmpathyCallWindowPriv *priv = GET_PRIV (self);
600 /* Initializing all the content (UI and input gst elements) related to the
601 self contact, except for the video preview widget. This widget is only
602 initialized when the "show video preview" option is activated */
603 priv->self_user_output_hbox = gtk_hbox_new (FALSE, 0);
605 priv->self_user_avatar_widget = gtk_image_new ();
606 gtk_box_pack_start (GTK_BOX (priv->self_user_output_hbox),
607 priv->self_user_avatar_widget, TRUE, TRUE, 0);
609 gtk_container_add (GTK_CONTAINER (priv->self_user_output_frame),
610 priv->self_user_output_hbox);
612 priv->video_input = empathy_video_src_new ();
613 gst_object_ref (priv->video_input);
614 gst_object_sink (priv->video_input);
616 priv->audio_input = empathy_audio_src_new ();
617 gst_object_ref (priv->audio_input);
618 gst_object_sink (priv->audio_input);
620 g_signal_connect (priv->audio_input, "peak-level-changed",
621 G_CALLBACK (empathy_call_window_audio_input_level_changed_cb), self);
625 empathy_call_window_setup_video_preview (EmpathyCallWindow *window)
627 EmpathyCallWindowPriv *priv = GET_PRIV (window);
629 GstBus *bus = gst_pipeline_get_bus (GST_PIPELINE (priv->pipeline));
631 if (priv->video_preview != NULL)
633 /* Since the video preview and the video tee are initialized and freed
634 at the same time, if one is initialized, then the other one should
636 g_assert (priv->video_tee != NULL);
640 g_assert (priv->video_tee == NULL);
642 priv->video_tee = gst_element_factory_make ("tee", NULL);
643 gst_object_ref (priv->video_tee);
644 gst_object_sink (priv->video_tee);
646 priv->video_preview = empathy_video_widget_new_with_size (bus,
647 SELF_VIDEO_SECTION_WIDTH, SELF_VIDEO_SECTION_HEIGTH);
648 g_object_set (priv->video_preview, "sync", FALSE, "async", TRUE, NULL);
649 gtk_box_pack_start (GTK_BOX (priv->self_user_output_hbox),
650 priv->video_preview, TRUE, TRUE, 0);
652 preview = empathy_video_widget_get_element (
653 EMPATHY_VIDEO_WIDGET (priv->video_preview));
654 gst_bin_add_many (GST_BIN (priv->pipeline), priv->video_input,
655 priv->video_tee, preview, NULL);
656 gst_element_link_many (priv->video_input, priv->video_tee,
659 g_object_unref (bus);
661 gst_element_set_state (preview, GST_STATE_PLAYING);
662 gst_element_set_state (priv->video_input, GST_STATE_PLAYING);
663 gst_element_set_state (priv->video_tee, GST_STATE_PLAYING);
667 empathy_call_window_set_state_connecting (EmpathyCallWindow *window)
669 EmpathyCallWindowPriv *priv = GET_PRIV (window);
671 empathy_call_window_status_message (window, _("Connecting..."));
672 priv->call_state = CONNECTING;
675 empathy_sound_start_playing (GTK_WIDGET (window),
676 EMPATHY_SOUND_PHONE_OUTGOING, MS_BETWEEN_RING);
680 empathy_call_window_init (EmpathyCallWindow *self)
682 EmpathyCallWindowPriv *priv = GET_PRIV (self);
691 GError *error = NULL;
693 filename = empathy_file_lookup ("empathy-call-window.ui", "src");
694 gui = empathy_builder_get_file (filename,
695 "call_window_vbox", &top_vbox,
697 "statusbar", &priv->statusbar,
698 "redial", &priv->redial_button,
699 "microphone", &priv->mic_button,
700 "camera", &priv->camera_button,
701 "toolbar", &priv->toolbar,
702 "send_video", &priv->send_video,
703 "menuredial", &priv->redial,
704 "show_preview", &priv->show_preview,
705 "ui_manager", &priv->ui_manager,
706 "menufullscreen", &priv->menu_fullscreen,
710 empathy_builder_connect (gui, self,
711 "menuhangup", "activate", empathy_call_window_hangup_cb,
712 "hangup", "clicked", empathy_call_window_hangup_cb,
713 "menuredial", "activate", empathy_call_window_redial_cb,
714 "redial", "clicked", empathy_call_window_redial_cb,
715 "microphone", "toggled", empathy_call_window_mic_toggled_cb,
716 "camera", "toggled", empathy_call_window_camera_toggled_cb,
717 "send_video", "toggled", empathy_call_window_send_video_toggled_cb,
718 "show_preview", "toggled", empathy_call_window_show_preview_toggled_cb,
719 "menufullscreen", "activate", empathy_call_window_fullscreen_cb,
722 priv->lock = g_mutex_new ();
724 gtk_container_add (GTK_CONTAINER (self), top_vbox);
726 empathy_call_window_setup_toolbar (self);
728 priv->content_hbox = gtk_hbox_new (FALSE, CONTENT_HBOX_SPACING);
729 gtk_container_set_border_width (GTK_CONTAINER (priv->content_hbox),
730 CONTENT_HBOX_BORDER_WIDTH);
731 gtk_paned_pack1 (GTK_PANED (priv->pane), priv->content_hbox, TRUE, FALSE);
733 priv->pipeline = gst_pipeline_new (NULL);
734 bus = gst_pipeline_get_bus (GST_PIPELINE (priv->pipeline));
735 priv->bus_message_source_id = gst_bus_add_watch (bus,
736 empathy_call_window_bus_message, self);
738 priv->fsnotifier = fs_element_added_notifier_new ();
739 fs_element_added_notifier_add (priv->fsnotifier, GST_BIN (priv->pipeline));
741 keyfile = g_key_file_new ();
742 filename = empathy_file_lookup ("element-properties", "data");
743 if (g_key_file_load_from_file (keyfile, filename, G_KEY_FILE_NONE, &error))
745 fs_element_added_notifier_set_properties_from_keyfile (priv->fsnotifier,
750 g_warning ("Could not load element-properties file: %s", error->message);
751 g_key_file_free (keyfile);
752 g_clear_error (&error);
757 priv->remote_user_output_frame = gtk_frame_new (NULL);
758 gtk_widget_set_size_request (priv->remote_user_output_frame,
759 EMPATHY_VIDEO_WIDGET_DEFAULT_WIDTH, EMPATHY_VIDEO_WIDGET_DEFAULT_HEIGHT);
760 gtk_box_pack_start (GTK_BOX (priv->content_hbox),
761 priv->remote_user_output_frame, TRUE, TRUE,
762 CONTENT_HBOX_CHILDREN_PACKING_PADDING);
763 empathy_call_window_setup_remote_frame (bus, self);
765 priv->self_user_output_frame = gtk_frame_new (NULL);
766 gtk_widget_set_size_request (priv->self_user_output_frame,
767 SELF_VIDEO_SECTION_WIDTH, SELF_VIDEO_SECTION_HEIGTH);
769 priv->vbox = gtk_vbox_new (FALSE, 3);
770 gtk_box_pack_start (GTK_BOX (priv->content_hbox), priv->vbox,
771 FALSE, FALSE, CONTENT_HBOX_CHILDREN_PACKING_PADDING);
772 gtk_box_pack_start (GTK_BOX (priv->vbox), priv->self_user_output_frame, FALSE,
774 empathy_call_window_setup_self_frame (bus, self);
776 g_object_unref (bus);
778 priv->sidebar_button = gtk_toggle_button_new_with_mnemonic (_("_Sidebar"));
779 arrow = gtk_arrow_new (GTK_ARROW_RIGHT, GTK_SHADOW_NONE);
780 g_signal_connect (G_OBJECT (priv->sidebar_button), "toggled",
781 G_CALLBACK (empathy_call_window_sidebar_toggled_cb), self);
783 gtk_button_set_image (GTK_BUTTON (priv->sidebar_button), arrow);
785 h = gtk_hbox_new (FALSE, 3);
786 gtk_box_pack_end (GTK_BOX (priv->vbox), h, FALSE, FALSE, 3);
787 gtk_box_pack_end (GTK_BOX (h), priv->sidebar_button, FALSE, FALSE, 3);
789 priv->sidebar = empathy_sidebar_new ();
790 g_signal_connect (G_OBJECT (priv->sidebar),
791 "hide", G_CALLBACK (empathy_call_window_sidebar_hidden_cb), self);
792 g_signal_connect (G_OBJECT (priv->sidebar),
793 "show", G_CALLBACK (empathy_call_window_sidebar_shown_cb), self);
794 gtk_paned_pack2 (GTK_PANED (priv->pane), priv->sidebar, FALSE, FALSE);
796 priv->dtmf_panel = empathy_call_window_create_dtmf (self);
797 empathy_sidebar_add_page (EMPATHY_SIDEBAR (priv->sidebar), _("Dialpad"),
800 gtk_widget_set_sensitive (priv->dtmf_panel, FALSE);
802 page = empathy_call_window_create_audio_input (self);
803 empathy_sidebar_add_page (EMPATHY_SIDEBAR (priv->sidebar), _("Audio input"),
806 page = empathy_call_window_create_video_input (self);
807 empathy_sidebar_add_page (EMPATHY_SIDEBAR (priv->sidebar), _("Video input"),
810 gtk_widget_show_all (top_vbox);
812 gtk_widget_hide (priv->sidebar);
814 priv->fullscreen = empathy_call_window_fullscreen_new (self);
815 empathy_call_window_fullscreen_set_video_widget (priv->fullscreen, priv->video_output);
816 g_signal_connect (G_OBJECT (priv->fullscreen->leave_fullscreen_button),
817 "clicked", G_CALLBACK (empathy_call_window_fullscreen_cb), self);
819 g_signal_connect (G_OBJECT (self), "realize",
820 G_CALLBACK (empathy_call_window_realized_cb), self);
822 g_signal_connect (G_OBJECT (self), "delete-event",
823 G_CALLBACK (empathy_call_window_delete_cb), self);
825 g_signal_connect (G_OBJECT (self), "window-state-event",
826 G_CALLBACK (empathy_call_window_state_event_cb), self);
828 g_signal_connect (G_OBJECT (self), "key-press-event",
829 G_CALLBACK (empathy_call_window_key_press_cb), self);
831 priv->timer = g_timer_new ();
833 g_object_ref (priv->ui_manager);
834 g_object_unref (gui);
837 /* Instead of specifying a width and a height, we specify only one size. That's
838 because we want a square avatar icon. */
840 init_contact_avatar_with_size (EmpathyContact *contact, GtkWidget *image_widget,
843 GdkPixbuf *pixbuf_avatar = NULL;
847 pixbuf_avatar = empathy_pixbuf_avatar_from_contact_scaled (contact,
851 if (pixbuf_avatar == NULL)
853 pixbuf_avatar = empathy_pixbuf_from_icon_name_sized ("stock_person",
857 gtk_image_set_from_pixbuf (GTK_IMAGE (image_widget), pixbuf_avatar);
861 set_window_title (EmpathyCallWindow *self)
863 EmpathyCallWindowPriv *priv = GET_PRIV (self);
866 tmp = g_strdup_printf (_("Call with %s"),
867 empathy_contact_get_name (priv->contact));
868 gtk_window_set_title (GTK_WINDOW (self), tmp);
873 contact_name_changed_cb (EmpathyContact *contact,
874 GParamSpec *pspec, EmpathyCallWindow *self)
876 set_window_title (self);
880 contact_avatar_changed_cb (EmpathyContact *contact,
881 GParamSpec *pspec, GtkWidget *avatar_widget)
883 init_contact_avatar_with_size (contact, avatar_widget,
884 avatar_widget->allocation.height);
888 empathy_call_window_got_self_contact_cb (EmpathyTpContactFactory *factory,
889 EmpathyContact *contact, const GError *error, gpointer user_data,
890 GObject *weak_object)
892 EmpathyCallWindow *self = EMPATHY_CALL_WINDOW (user_data);
893 EmpathyCallWindowPriv *priv = GET_PRIV (self);
895 init_contact_avatar_with_size (contact, priv->self_user_avatar_widget,
896 MIN (SELF_VIDEO_SECTION_WIDTH, SELF_VIDEO_SECTION_HEIGTH));
898 g_signal_connect (contact, "notify::avatar",
899 G_CALLBACK (contact_avatar_changed_cb), priv->self_user_avatar_widget);
903 empathy_call_window_setup_avatars (EmpathyCallWindow *self,
904 EmpathyCallHandler *handler)
906 EmpathyCallWindowPriv *priv = GET_PRIV (self);
908 g_object_get (handler, "contact", &(priv->contact), NULL);
910 if (priv->contact != NULL)
912 TpConnection *connection;
913 EmpathyTpContactFactory *factory;
915 set_window_title (self);
917 g_signal_connect (priv->contact, "notify::name",
918 G_CALLBACK (contact_name_changed_cb), self);
919 g_signal_connect (priv->contact, "notify::avatar",
920 G_CALLBACK (contact_avatar_changed_cb),
921 priv->remote_user_avatar_widget);
923 /* Retreiving the self avatar */
924 connection = empathy_contact_get_connection (priv->contact);
925 factory = empathy_tp_contact_factory_dup_singleton (connection);
926 empathy_tp_contact_factory_get_from_handle (factory,
927 tp_connection_get_self_handle (connection),
928 empathy_call_window_got_self_contact_cb, self, NULL, NULL);
930 g_object_unref (factory);
934 g_warning ("call handler doesn't have a contact");
935 gtk_window_set_title (GTK_WINDOW (self), _("Call"));
937 /* Since we can't access the remote contact, we can't get a connection
938 to it and can't get the self contact (and its avatar). This means
939 that we have to manually set the self avatar. */
940 init_contact_avatar_with_size (NULL, priv->self_user_avatar_widget,
941 MIN (SELF_VIDEO_SECTION_WIDTH, SELF_VIDEO_SECTION_HEIGTH));
944 init_contact_avatar_with_size (priv->contact,
945 priv->remote_user_avatar_widget, MIN (REMOTE_CONTACT_AVATAR_DEFAULT_WIDTH,
946 REMOTE_CONTACT_AVATAR_DEFAULT_HEIGHT));
948 /* The remote avatar is shown by default and will be hidden when we receive
949 video from the remote side. */
950 gtk_widget_hide (priv->video_output);
951 gtk_widget_show (priv->remote_user_avatar_widget);
955 empathy_call_window_setup_video_preview_visibility (EmpathyCallWindow *self,
956 EmpathyCallHandler *handler)
958 EmpathyCallWindowPriv *priv = GET_PRIV (self);
959 gboolean initial_video = empathy_call_handler_has_initial_video (priv->handler);
961 gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (priv->show_preview),
966 empathy_call_window_constructed (GObject *object)
968 EmpathyCallWindow *self = EMPATHY_CALL_WINDOW (object);
969 EmpathyCallWindowPriv *priv = GET_PRIV (self);
972 g_assert (priv->handler != NULL);
974 g_object_get (priv->handler, "tp-call", &call, NULL);
975 priv->outgoing = (call == NULL);
977 g_object_unref (call);
979 empathy_call_window_setup_avatars (self, priv->handler);
980 empathy_call_window_setup_video_preview_visibility (self, priv->handler);
981 empathy_call_window_set_state_connecting (self);
984 static void empathy_call_window_dispose (GObject *object);
985 static void empathy_call_window_finalize (GObject *object);
988 empathy_call_window_set_property (GObject *object,
989 guint property_id, const GValue *value, GParamSpec *pspec)
991 EmpathyCallWindowPriv *priv = GET_PRIV (object);
995 case PROP_CALL_HANDLER:
996 priv->handler = g_value_dup_object (value);
999 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
1004 empathy_call_window_get_property (GObject *object,
1005 guint property_id, GValue *value, GParamSpec *pspec)
1007 EmpathyCallWindowPriv *priv = GET_PRIV (object);
1009 switch (property_id)
1011 case PROP_CALL_HANDLER:
1012 g_value_set_object (value, priv->handler);
1015 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
1020 empathy_call_window_class_init (
1021 EmpathyCallWindowClass *empathy_call_window_class)
1023 GObjectClass *object_class = G_OBJECT_CLASS (empathy_call_window_class);
1024 GParamSpec *param_spec;
1026 g_type_class_add_private (empathy_call_window_class,
1027 sizeof (EmpathyCallWindowPriv));
1029 object_class->constructed = empathy_call_window_constructed;
1030 object_class->set_property = empathy_call_window_set_property;
1031 object_class->get_property = empathy_call_window_get_property;
1033 object_class->dispose = empathy_call_window_dispose;
1034 object_class->finalize = empathy_call_window_finalize;
1036 param_spec = g_param_spec_object ("handler",
1037 "handler", "The call handler",
1038 EMPATHY_TYPE_CALL_HANDLER,
1039 G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS);
1040 g_object_class_install_property (object_class,
1041 PROP_CALL_HANDLER, param_spec);
1045 empathy_call_window_video_stream_changed_cb (EmpathyTpCall *call,
1046 GParamSpec *property, EmpathyCallWindow *self)
1048 empathy_call_window_update_avatars_visibility (call, self);
1052 empathy_call_window_dispose (GObject *object)
1054 EmpathyTpCall *call;
1055 EmpathyCallWindow *self = EMPATHY_CALL_WINDOW (object);
1056 EmpathyCallWindowPriv *priv = GET_PRIV (self);
1058 if (priv->dispose_has_run)
1061 priv->dispose_has_run = TRUE;
1063 g_object_get (priv->handler, "tp-call", &call, NULL);
1067 g_signal_handlers_disconnect_by_func (call,
1068 empathy_call_window_video_stream_changed_cb, object);
1071 g_object_unref (call);
1073 if (priv->handler != NULL)
1074 g_object_unref (priv->handler);
1075 priv->handler = NULL;
1077 if (priv->pipeline != NULL)
1078 g_object_unref (priv->pipeline);
1079 priv->pipeline = NULL;
1081 if (priv->video_input != NULL)
1082 g_object_unref (priv->video_input);
1083 priv->video_input = NULL;
1085 if (priv->audio_input != NULL)
1086 g_object_unref (priv->audio_input);
1087 priv->audio_input = NULL;
1089 if (priv->audio_output != NULL)
1090 g_object_unref (priv->audio_output);
1091 priv->audio_output = NULL;
1093 if (priv->video_tee != NULL)
1094 g_object_unref (priv->video_tee);
1095 priv->video_tee = NULL;
1097 if (priv->fsnotifier != NULL)
1098 g_object_unref (priv->fsnotifier);
1099 priv->fsnotifier = NULL;
1101 if (priv->timer_id != 0)
1102 g_source_remove (priv->timer_id);
1105 if (priv->ui_manager != NULL)
1106 g_object_unref (priv->ui_manager);
1107 priv->ui_manager = NULL;
1109 if (priv->contact != NULL)
1111 g_signal_handlers_disconnect_by_func (priv->contact,
1112 contact_name_changed_cb, self);
1113 g_object_unref (priv->contact);
1114 priv->contact = NULL;
1117 /* release any references held by the object here */
1118 if (G_OBJECT_CLASS (empathy_call_window_parent_class)->dispose)
1119 G_OBJECT_CLASS (empathy_call_window_parent_class)->dispose (object);
1123 empathy_call_window_finalize (GObject *object)
1125 EmpathyCallWindow *self = EMPATHY_CALL_WINDOW (object);
1126 EmpathyCallWindowPriv *priv = GET_PRIV (self);
1128 if (priv->video_output_motion_handler_id != 0)
1130 g_signal_handler_disconnect (G_OBJECT (priv->video_output),
1131 priv->video_output_motion_handler_id);
1132 priv->video_output_motion_handler_id = 0;
1135 if (priv->bus_message_source_id != 0)
1137 g_source_remove (priv->bus_message_source_id);
1138 priv->bus_message_source_id = 0;
1141 /* free any data held directly by the object here */
1142 g_mutex_free (priv->lock);
1144 g_timer_destroy (priv->timer);
1146 G_OBJECT_CLASS (empathy_call_window_parent_class)->finalize (object);
1151 empathy_call_window_new (EmpathyCallHandler *handler)
1153 return EMPATHY_CALL_WINDOW (
1154 g_object_new (EMPATHY_TYPE_CALL_WINDOW, "handler", handler, NULL));
1158 empathy_call_window_conference_added_cb (EmpathyCallHandler *handler,
1159 GstElement *conference, gpointer user_data)
1161 EmpathyCallWindow *self = EMPATHY_CALL_WINDOW (user_data);
1162 EmpathyCallWindowPriv *priv = GET_PRIV (self);
1164 gst_bin_add (GST_BIN (priv->pipeline), conference);
1166 gst_element_set_state (conference, GST_STATE_PLAYING);
1170 empathy_call_window_request_resource_cb (EmpathyCallHandler *handler,
1171 FsMediaType type, FsStreamDirection direction, gpointer user_data)
1173 EmpathyCallWindow *self = EMPATHY_CALL_WINDOW (user_data);
1174 EmpathyCallWindowPriv *priv = GET_PRIV (self);
1176 if (type != TP_MEDIA_STREAM_TYPE_VIDEO)
1179 if (direction == FS_DIRECTION_RECV)
1182 /* video and direction is send */
1183 return priv->video_input != NULL;
1187 empathy_call_window_reset_pipeline (EmpathyCallWindow *self)
1189 GstStateChangeReturn state_change_return;
1190 EmpathyCallWindowPriv *priv = GET_PRIV (self);
1192 if (priv->pipeline == NULL)
1195 if (priv->bus_message_source_id != 0)
1197 g_source_remove (priv->bus_message_source_id);
1198 priv->bus_message_source_id = 0;
1201 state_change_return = gst_element_set_state (priv->pipeline, GST_STATE_NULL);
1203 if (state_change_return == GST_STATE_CHANGE_SUCCESS ||
1204 state_change_return == GST_STATE_CHANGE_NO_PREROLL)
1206 if (priv->pipeline != NULL)
1207 g_object_unref (priv->pipeline);
1208 priv->pipeline = NULL;
1210 if (priv->video_input != NULL)
1211 g_object_unref (priv->video_input);
1212 priv->video_input = NULL;
1214 if (priv->audio_input != NULL)
1215 g_object_unref (priv->audio_input);
1216 priv->audio_input = NULL;
1218 g_signal_handlers_disconnect_by_func (priv->audio_input_adj,
1219 empathy_call_window_mic_volume_changed_cb, self);
1221 if (priv->audio_output != NULL)
1222 g_object_unref (priv->audio_output);
1223 priv->audio_output = NULL;
1225 if (priv->video_tee != NULL)
1226 g_object_unref (priv->video_tee);
1227 priv->video_tee = NULL;
1229 if (priv->video_preview != NULL)
1230 gtk_widget_destroy (priv->video_preview);
1231 priv->video_preview = NULL;
1233 priv->liveadder = NULL;
1234 priv->funnel = NULL;
1240 g_message ("Error: could not destroy pipeline. Closing call window");
1241 gtk_widget_destroy (GTK_WIDGET (self));
1248 empathy_call_window_disconnected (EmpathyCallWindow *self)
1250 gboolean could_disconnect = FALSE;
1251 EmpathyCallWindowPriv *priv = GET_PRIV (self);
1252 gboolean could_reset_pipeline = empathy_call_window_reset_pipeline (self);
1254 if (priv->call_state == CONNECTING)
1255 empathy_sound_stop (EMPATHY_SOUND_PHONE_OUTGOING);
1257 if (priv->call_state != REDIALING)
1258 priv->call_state = DISCONNECTED;
1260 if (could_reset_pipeline)
1262 gboolean initial_video = empathy_call_handler_has_initial_video (
1264 g_mutex_lock (priv->lock);
1266 g_timer_stop (priv->timer);
1268 if (priv->timer_id != 0)
1269 g_source_remove (priv->timer_id);
1272 g_mutex_unlock (priv->lock);
1274 empathy_call_window_status_message (self, _("Disconnected"));
1276 gtk_action_set_sensitive (priv->redial, TRUE);
1277 gtk_widget_set_sensitive (priv->redial_button, TRUE);
1279 /* Reseting the send_video, camera_buton and mic_button to their
1281 gtk_widget_set_sensitive (priv->camera_button, FALSE);
1282 gtk_widget_set_sensitive (priv->mic_button, FALSE);
1283 gtk_action_set_sensitive (priv->send_video, FALSE);
1284 gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (priv->send_video),
1286 gtk_toggle_tool_button_set_active (
1287 GTK_TOGGLE_TOOL_BUTTON (priv->camera_button), initial_video);
1288 gtk_toggle_tool_button_set_active (
1289 GTK_TOGGLE_TOOL_BUTTON (priv->mic_button), TRUE);
1291 gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (priv->show_preview),
1293 gtk_action_set_sensitive (priv->show_preview, FALSE);
1295 gtk_progress_bar_set_fraction (
1296 GTK_PROGRESS_BAR (priv->volume_progress_bar), 0);
1298 gtk_widget_hide (priv->video_output);
1299 gtk_widget_show (priv->remote_user_avatar_widget);
1301 priv->sending_video = FALSE;
1302 priv->call_started = FALSE;
1304 could_disconnect = TRUE;
1307 return could_disconnect;
1312 empathy_call_window_channel_closed_cb (TfChannel *channel, gpointer user_data)
1314 EmpathyCallWindow *self = EMPATHY_CALL_WINDOW (user_data);
1315 EmpathyCallWindowPriv *priv = GET_PRIV (self);
1317 if (empathy_call_window_disconnected (self) && priv->call_state == REDIALING)
1318 empathy_call_window_restart_call (self);
1321 /* Called with global lock held */
1323 empathy_call_window_get_video_sink_pad (EmpathyCallWindow *self)
1325 EmpathyCallWindowPriv *priv = GET_PRIV (self);
1328 if (priv->funnel == NULL)
1332 output = empathy_video_widget_get_element (EMPATHY_VIDEO_WIDGET
1333 (priv->video_output));
1335 priv->funnel = gst_element_factory_make ("fsfunnel", NULL);
1337 gst_bin_add (GST_BIN (priv->pipeline), priv->funnel);
1338 gst_bin_add (GST_BIN (priv->pipeline), output);
1340 gst_element_link (priv->funnel, output);
1342 gst_element_set_state (priv->funnel, GST_STATE_PLAYING);
1343 gst_element_set_state (output, GST_STATE_PLAYING);
1346 pad = gst_element_get_request_pad (priv->funnel, "sink%d");
1351 /* Called with global lock held */
1353 empathy_call_window_get_audio_sink_pad (EmpathyCallWindow *self)
1355 EmpathyCallWindowPriv *priv = GET_PRIV (self);
1358 if (priv->liveadder == NULL)
1360 priv->liveadder = gst_element_factory_make ("liveadder", NULL);
1362 gst_bin_add (GST_BIN (priv->pipeline), priv->liveadder);
1363 gst_bin_add (GST_BIN (priv->pipeline), priv->audio_output);
1365 gst_element_link (priv->liveadder, priv->audio_output);
1367 gst_element_set_state (priv->liveadder, GST_STATE_PLAYING);
1368 gst_element_set_state (priv->audio_output, GST_STATE_PLAYING);
1371 pad = gst_element_get_request_pad (priv->liveadder, "sink%d");
1377 empathy_call_window_update_timer (gpointer user_data)
1379 EmpathyCallWindow *self = EMPATHY_CALL_WINDOW (user_data);
1380 EmpathyCallWindowPriv *priv = GET_PRIV (self);
1384 time = g_timer_elapsed (priv->timer, NULL);
1386 /* Translators: number of minutes:seconds the caller has been connected */
1387 str = g_strdup_printf (_("Connected — %d:%02dm"), (int) time / 60,
1389 empathy_call_window_status_message (self, str);
1396 empathy_call_window_connected (gpointer user_data)
1398 EmpathyCallWindow *self = EMPATHY_CALL_WINDOW (user_data);
1399 EmpathyCallWindowPriv *priv = GET_PRIV (self);
1400 EmpathyTpCall *call;
1401 gboolean can_send_video;
1403 can_send_video = priv->video_input != NULL && priv->contact != NULL &&
1404 empathy_contact_can_voip_video (priv->contact);
1406 g_object_get (priv->handler, "tp-call", &call, NULL);
1408 g_signal_connect (call, "notify::video-stream",
1409 G_CALLBACK (empathy_call_window_video_stream_changed_cb), self);
1411 if (empathy_tp_call_has_dtmf (call))
1412 gtk_widget_set_sensitive (priv->dtmf_panel, TRUE);
1414 if (priv->video_input == NULL)
1415 empathy_call_window_set_send_video (self, FALSE);
1417 priv->sending_video = can_send_video ?
1418 empathy_tp_call_is_sending_video (call) : FALSE;
1420 gtk_action_set_sensitive (priv->show_preview, TRUE);
1421 gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (priv->show_preview),
1423 || gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (priv->show_preview)));
1424 gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (priv->send_video),
1425 priv->sending_video && priv->video_input != NULL);
1426 gtk_toggle_tool_button_set_active (
1427 GTK_TOGGLE_TOOL_BUTTON (priv->camera_button),
1428 priv->sending_video && priv->video_input != NULL);
1429 gtk_widget_set_sensitive (priv->camera_button, can_send_video);
1430 gtk_action_set_sensitive (priv->send_video, can_send_video);
1432 gtk_action_set_sensitive (priv->redial, FALSE);
1433 gtk_widget_set_sensitive (priv->redial_button, FALSE);
1435 gtk_widget_set_sensitive (priv->mic_button, TRUE);
1437 empathy_call_window_update_avatars_visibility (call, self);
1439 g_object_unref (call);
1441 g_mutex_lock (priv->lock);
1443 priv->timer_id = g_timeout_add_seconds (1,
1444 empathy_call_window_update_timer, self);
1446 g_mutex_unlock (priv->lock);
1448 empathy_call_window_update_timer (self);
1454 /* Called from the streaming thread */
1456 empathy_call_window_src_added_cb (EmpathyCallHandler *handler,
1457 GstPad *src, guint media_type, gpointer user_data)
1459 EmpathyCallWindow *self = EMPATHY_CALL_WINDOW (user_data);
1460 EmpathyCallWindowPriv *priv = GET_PRIV (self);
1464 g_mutex_lock (priv->lock);
1466 if (priv->call_state != CONNECTED)
1468 g_timer_start (priv->timer);
1469 priv->timer_id = g_idle_add (empathy_call_window_connected, self);
1470 priv->call_state = CONNECTED;
1471 empathy_sound_stop (EMPATHY_SOUND_PHONE_OUTGOING);
1476 case TP_MEDIA_STREAM_TYPE_AUDIO:
1477 pad = empathy_call_window_get_audio_sink_pad (self);
1479 case TP_MEDIA_STREAM_TYPE_VIDEO:
1480 gtk_widget_hide (priv->remote_user_avatar_widget);
1481 gtk_widget_show (priv->video_output);
1482 pad = empathy_call_window_get_video_sink_pad (self);
1485 g_assert_not_reached ();
1488 gst_pad_link (src, pad);
1489 gst_object_unref (pad);
1491 g_mutex_unlock (priv->lock);
1494 /* Called from the streaming thread */
1496 empathy_call_window_sink_added_cb (EmpathyCallHandler *handler,
1497 GstPad *sink, guint media_type, gpointer user_data)
1499 EmpathyCallWindow *self = EMPATHY_CALL_WINDOW (user_data);
1500 EmpathyCallWindowPriv *priv = GET_PRIV (self);
1505 case TP_MEDIA_STREAM_TYPE_AUDIO:
1506 gst_bin_add (GST_BIN (priv->pipeline), priv->audio_input);
1508 pad = gst_element_get_static_pad (priv->audio_input, "src");
1509 gst_pad_link (pad, sink);
1511 gst_element_set_state (priv->audio_input, GST_STATE_PLAYING);
1513 case TP_MEDIA_STREAM_TYPE_VIDEO:
1514 if (priv->video_input != NULL)
1516 EmpathyTpCall *call;
1517 g_object_get (priv->handler, "tp-call", &call, NULL);
1519 if (empathy_tp_call_is_sending_video (call))
1521 empathy_call_window_setup_video_preview (self);
1523 gtk_toggle_action_set_active (
1524 GTK_TOGGLE_ACTION (priv->show_preview), TRUE);
1526 if (priv->video_preview != NULL)
1527 gtk_widget_show (priv->video_preview);
1528 gtk_widget_hide (priv->self_user_avatar_widget);
1531 g_object_unref (call);
1533 if (priv->video_tee != NULL)
1535 pad = gst_element_get_request_pad (priv->video_tee, "src%d");
1536 gst_pad_link (pad, sink);
1541 g_assert_not_reached ();
1547 empathy_call_window_remove_video_input (EmpathyCallWindow *self)
1549 EmpathyCallWindowPriv *priv = GET_PRIV (self);
1550 GstElement *preview;
1552 preview = empathy_video_widget_get_element (
1553 EMPATHY_VIDEO_WIDGET (priv->video_preview));
1555 gst_element_set_state (priv->video_input, GST_STATE_NULL);
1556 gst_element_set_state (priv->video_tee, GST_STATE_NULL);
1557 gst_element_set_state (preview, GST_STATE_NULL);
1559 gst_bin_remove_many (GST_BIN (priv->pipeline), priv->video_input,
1560 priv->video_tee, preview, NULL);
1562 g_object_unref (priv->video_input);
1563 priv->video_input = NULL;
1564 g_object_unref (priv->video_tee);
1565 priv->video_tee = NULL;
1566 gtk_widget_destroy (priv->video_preview);
1567 priv->video_preview = NULL;
1569 gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (priv->send_video), FALSE);
1570 gtk_toggle_tool_button_set_active (
1571 GTK_TOGGLE_TOOL_BUTTON (priv->camera_button), FALSE);
1572 gtk_widget_set_sensitive (priv->camera_button, FALSE);
1573 gtk_action_set_sensitive (priv->send_video, FALSE);
1575 gtk_widget_show (priv->self_user_avatar_widget);
1580 empathy_call_window_bus_message (GstBus *bus, GstMessage *message,
1583 EmpathyCallWindow *self = EMPATHY_CALL_WINDOW (user_data);
1584 EmpathyCallWindowPriv *priv = GET_PRIV (self);
1587 empathy_call_handler_bus_message (priv->handler, bus, message);
1589 switch (GST_MESSAGE_TYPE (message))
1591 case GST_MESSAGE_STATE_CHANGED:
1592 if (GST_MESSAGE_SRC (message) == GST_OBJECT (priv->video_input))
1594 gst_message_parse_state_changed (message, NULL, &newstate, NULL);
1595 if (newstate == GST_STATE_PAUSED)
1596 empathy_call_window_setup_video_input (self);
1598 if (GST_MESSAGE_SRC (message) == GST_OBJECT (priv->pipeline) &&
1599 !priv->call_started)
1601 gst_message_parse_state_changed (message, NULL, &newstate, NULL);
1602 if (newstate == GST_STATE_PAUSED)
1604 priv->call_started = TRUE;
1605 empathy_call_handler_start_call (priv->handler);
1606 gst_element_set_state (priv->pipeline, GST_STATE_PLAYING);
1610 case GST_MESSAGE_ERROR:
1612 GError *error = NULL;
1613 GstElement *gst_error;
1616 gst_message_parse_error (message, &error, &debug);
1617 gst_error = GST_ELEMENT (GST_MESSAGE_SRC (message));
1619 g_message ("Element error: %s -- %s\n", error->message, debug);
1621 if (g_str_has_prefix (gst_element_get_name (gst_error),
1622 VIDEO_INPUT_ERROR_PREFIX))
1624 /* Remove the video input and continue */
1625 if (priv->video_input != NULL)
1626 empathy_call_window_remove_video_input (self);
1627 gst_element_set_state (priv->pipeline, GST_STATE_PLAYING);
1631 empathy_call_window_disconnected (self);
1633 g_error_free (error);
1644 empathy_call_window_update_self_avatar_visibility (EmpathyCallWindow *window)
1646 EmpathyCallWindowPriv *priv = GET_PRIV (window);
1648 if (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (priv->show_preview)))
1650 if (priv->video_preview != NULL)
1652 gtk_widget_hide (priv->self_user_avatar_widget);
1653 gtk_widget_show (priv->video_preview);
1657 if (priv->video_preview != NULL)
1658 gtk_widget_hide (priv->video_preview);
1660 gtk_widget_show (priv->self_user_avatar_widget);
1666 empathy_call_window_update_avatars_visibility (EmpathyTpCall *call,
1667 EmpathyCallWindow *window)
1669 EmpathyCallWindowPriv *priv = GET_PRIV (window);
1671 if (empathy_tp_call_is_receiving_video (call))
1673 gtk_widget_hide (priv->remote_user_avatar_widget);
1674 gtk_widget_show (priv->video_output);
1678 gtk_widget_hide (priv->video_output);
1679 gtk_widget_show (priv->remote_user_avatar_widget);
1682 empathy_call_window_update_self_avatar_visibility (window);
1686 empathy_call_window_realized_cb (GtkWidget *widget, EmpathyCallWindow *window)
1688 EmpathyCallWindowPriv *priv = GET_PRIV (window);
1690 g_signal_connect (priv->handler, "conference-added",
1691 G_CALLBACK (empathy_call_window_conference_added_cb), window);
1692 g_signal_connect (priv->handler, "request-resource",
1693 G_CALLBACK (empathy_call_window_request_resource_cb), window);
1694 g_signal_connect (priv->handler, "closed",
1695 G_CALLBACK (empathy_call_window_channel_closed_cb), window);
1696 g_signal_connect (priv->handler, "src-pad-added",
1697 G_CALLBACK (empathy_call_window_src_added_cb), window);
1698 g_signal_connect (priv->handler, "sink-pad-added",
1699 G_CALLBACK (empathy_call_window_sink_added_cb), window);
1701 gst_element_set_state (priv->pipeline, GST_STATE_PAUSED);
1705 empathy_call_window_delete_cb (GtkWidget *widget, GdkEvent*event,
1706 EmpathyCallWindow *window)
1708 EmpathyCallWindowPriv *priv = GET_PRIV (window);
1710 if (priv->pipeline != NULL)
1712 if (priv->bus_message_source_id != 0)
1714 g_source_remove (priv->bus_message_source_id);
1715 priv->bus_message_source_id = 0;
1718 gst_element_set_state (priv->pipeline, GST_STATE_NULL);
1721 if (priv->call_state == CONNECTING)
1722 empathy_sound_stop (EMPATHY_SOUND_PHONE_OUTGOING);
1728 show_controls (EmpathyCallWindow *window, gboolean set_fullscreen)
1731 EmpathyCallWindowPriv *priv = GET_PRIV (window);
1733 menu = gtk_ui_manager_get_widget (priv->ui_manager,
1738 gtk_widget_hide (priv->sidebar);
1739 gtk_widget_hide (menu);
1740 gtk_widget_hide (priv->vbox);
1741 gtk_widget_hide (priv->statusbar);
1742 gtk_widget_hide (priv->toolbar);
1746 if (priv->sidebar_was_visible_before_fs)
1747 gtk_widget_show (priv->sidebar);
1749 gtk_widget_show (menu);
1750 gtk_widget_show (priv->vbox);
1751 gtk_widget_show (priv->statusbar);
1752 gtk_widget_show (priv->toolbar);
1754 gtk_window_resize (GTK_WINDOW (window), priv->original_width_before_fs,
1755 priv->original_height_before_fs);
1760 show_borders (EmpathyCallWindow *window, gboolean set_fullscreen)
1762 EmpathyCallWindowPriv *priv = GET_PRIV (window);
1764 gtk_container_set_border_width (GTK_CONTAINER (priv->content_hbox),
1765 set_fullscreen ? 0 : CONTENT_HBOX_BORDER_WIDTH);
1766 gtk_box_set_spacing (GTK_BOX (priv->content_hbox),
1767 set_fullscreen ? 0 : CONTENT_HBOX_SPACING);
1768 gtk_box_set_child_packing (GTK_BOX (priv->content_hbox),
1769 priv->video_output, TRUE, TRUE,
1770 set_fullscreen ? 0 : CONTENT_HBOX_CHILDREN_PACKING_PADDING,
1772 gtk_box_set_child_packing (GTK_BOX (priv->content_hbox),
1773 priv->vbox, TRUE, TRUE,
1774 set_fullscreen ? 0 : CONTENT_HBOX_CHILDREN_PACKING_PADDING,
1779 empathy_call_window_state_event_cb (GtkWidget *widget,
1780 GdkEventWindowState *event, EmpathyCallWindow *window)
1782 if (event->changed_mask & GDK_WINDOW_STATE_FULLSCREEN)
1784 EmpathyCallWindowPriv *priv = GET_PRIV (window);
1785 gboolean set_fullscreen = event->new_window_state & GDK_WINDOW_STATE_FULLSCREEN;
1789 gboolean sidebar_was_visible;
1790 gint original_width = GTK_WIDGET (window)->allocation.width;
1791 gint original_height = GTK_WIDGET (window)->allocation.height;
1793 g_object_get (priv->sidebar, "visible", &sidebar_was_visible, NULL);
1795 priv->sidebar_was_visible_before_fs = sidebar_was_visible;
1796 priv->original_width_before_fs = original_width;
1797 priv->original_height_before_fs = original_height;
1799 if (priv->video_output_motion_handler_id == 0 &&
1800 priv->video_output != NULL)
1802 priv->video_output_motion_handler_id = g_signal_connect (
1803 G_OBJECT (priv->video_output), "motion-notify-event",
1804 G_CALLBACK (empathy_call_window_video_output_motion_notify), window);
1809 if (priv->video_output_motion_handler_id != 0)
1811 g_signal_handler_disconnect (G_OBJECT (priv->video_output),
1812 priv->video_output_motion_handler_id);
1813 priv->video_output_motion_handler_id = 0;
1817 empathy_call_window_fullscreen_set_fullscreen (priv->fullscreen,
1819 show_controls (window, set_fullscreen);
1820 show_borders (window, set_fullscreen);
1821 gtk_action_set_stock_id (priv->menu_fullscreen,
1822 (set_fullscreen ? "gtk-leave-fullscreen" : "gtk-fullscreen"));
1823 priv->is_fullscreen = set_fullscreen;
1830 empathy_call_window_sidebar_toggled_cb (GtkToggleButton *toggle,
1831 EmpathyCallWindow *window)
1833 EmpathyCallWindowPriv *priv = GET_PRIV (window);
1835 int w,h, handle_size;
1837 w = GTK_WIDGET (window)->allocation.width;
1838 h = GTK_WIDGET (window)->allocation.height;
1840 gtk_widget_style_get (priv->pane, "handle_size", &handle_size, NULL);
1842 if (gtk_toggle_button_get_active (toggle))
1844 arrow = gtk_arrow_new (GTK_ARROW_LEFT, GTK_SHADOW_NONE);
1845 gtk_widget_show (priv->sidebar);
1846 w += priv->sidebar->allocation.width + handle_size;
1850 arrow = gtk_arrow_new (GTK_ARROW_RIGHT, GTK_SHADOW_NONE);
1851 w -= priv->sidebar->allocation.width + handle_size;
1852 gtk_widget_hide (priv->sidebar);
1855 gtk_button_set_image (GTK_BUTTON (priv->sidebar_button), arrow);
1858 gtk_window_resize (GTK_WINDOW (window), w, h);
1862 empathy_call_window_set_send_video (EmpathyCallWindow *window,
1865 EmpathyCallWindowPriv *priv = GET_PRIV (window);
1866 EmpathyTpCall *call;
1868 priv->sending_video = send;
1870 /* When we start sending video, we want to show the video preview by
1874 empathy_call_window_setup_video_preview (window);
1875 gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (priv->show_preview),
1879 g_object_get (priv->handler, "tp-call", &call, NULL);
1880 empathy_tp_call_request_video_stream_direction (call, send);
1881 g_object_unref (call);
1885 empathy_call_window_camera_toggled_cb (GtkToggleToolButton *toggle,
1886 EmpathyCallWindow *window)
1888 EmpathyCallWindowPriv *priv = GET_PRIV (window);
1891 if (priv->call_state != CONNECTED)
1894 active = (gtk_toggle_tool_button_get_active (toggle));
1896 if (priv->sending_video == active)
1899 empathy_call_window_set_send_video (window, active);
1900 gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (priv->send_video), active);
1904 empathy_call_window_send_video_toggled_cb (GtkToggleAction *toggle,
1905 EmpathyCallWindow *window)
1907 EmpathyCallWindowPriv *priv = GET_PRIV (window);
1910 if (priv->call_state != CONNECTED)
1913 active = (gtk_toggle_action_get_active (toggle));
1915 if (priv->sending_video == active)
1918 empathy_call_window_set_send_video (window, active);
1919 gtk_toggle_tool_button_set_active (
1920 GTK_TOGGLE_TOOL_BUTTON (priv->camera_button), active);
1924 empathy_call_window_show_preview_toggled_cb (GtkToggleAction *toggle,
1925 EmpathyCallWindow *window)
1927 gboolean show_preview_toggled;
1928 EmpathyCallWindowPriv *priv = GET_PRIV (window);
1930 show_preview_toggled = gtk_toggle_action_get_active (toggle);
1932 if (show_preview_toggled)
1934 empathy_call_window_setup_video_preview (window);
1935 gtk_widget_show (priv->self_user_output_frame);
1936 empathy_call_window_update_self_avatar_visibility (window);
1940 gtk_widget_hide (priv->self_user_output_frame);
1945 empathy_call_window_mic_toggled_cb (GtkToggleToolButton *toggle,
1946 EmpathyCallWindow *window)
1948 EmpathyCallWindowPriv *priv = GET_PRIV (window);
1951 if (priv->audio_input == NULL)
1954 active = (gtk_toggle_tool_button_get_active (toggle));
1958 empathy_audio_src_set_volume (EMPATHY_GST_AUDIO_SRC (priv->audio_input),
1960 gtk_adjustment_set_value (priv->audio_input_adj, priv->volume * 100);
1964 /* TODO, Instead of setting the input volume to 0 we should probably
1965 * stop sending but this would cause the audio call to drop if both
1966 * sides mute at the same time on certain CMs AFAIK. Need to revisit this
1967 * in the future. GNOME #574574
1969 empathy_audio_src_set_volume (EMPATHY_GST_AUDIO_SRC (priv->audio_input),
1971 gtk_adjustment_set_value (priv->audio_input_adj, 0);
1976 empathy_call_window_sidebar_hidden_cb (EmpathySidebar *sidebar,
1977 EmpathyCallWindow *window)
1979 EmpathyCallWindowPriv *priv = GET_PRIV (window);
1981 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (priv->sidebar_button),
1986 empathy_call_window_sidebar_shown_cb (EmpathySidebar *sidebar,
1987 EmpathyCallWindow *window)
1989 EmpathyCallWindowPriv *priv = GET_PRIV (window);
1991 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (priv->sidebar_button),
1996 empathy_call_window_hangup_cb (gpointer object,
1997 EmpathyCallWindow *window)
1999 if (empathy_call_window_disconnected (window))
2000 gtk_widget_destroy (GTK_WIDGET (window));
2004 empathy_call_window_restart_call (EmpathyCallWindow *window)
2007 EmpathyCallWindowPriv *priv = GET_PRIV (window);
2009 gtk_widget_destroy (priv->remote_user_output_hbox);
2010 gtk_widget_destroy (priv->self_user_output_hbox);
2012 priv->pipeline = gst_pipeline_new (NULL);
2013 bus = gst_pipeline_get_bus (GST_PIPELINE (priv->pipeline));
2014 priv->bus_message_source_id = gst_bus_add_watch (bus,
2015 empathy_call_window_bus_message, window);
2017 empathy_call_window_setup_remote_frame (bus, window);
2018 empathy_call_window_setup_self_frame (bus, window);
2020 g_signal_connect (G_OBJECT (priv->audio_input_adj), "value-changed",
2021 G_CALLBACK (empathy_call_window_mic_volume_changed_cb), window);
2023 /* While the call was disconnected, the input volume might have changed.
2024 * However, since the audio_input source was destroyed, its volume has not
2025 * been updated during that time. That's why we manually update it here */
2026 empathy_call_window_mic_volume_changed_cb (priv->audio_input_adj, window);
2028 g_object_unref (bus);
2030 gtk_widget_show_all (priv->content_hbox);
2032 if (!empathy_call_handler_has_initial_video (priv->handler))
2033 gtk_widget_hide (priv->self_user_output_frame);
2035 priv->outgoing = TRUE;
2036 empathy_call_window_set_state_connecting (window);
2038 priv->call_started = TRUE;
2039 empathy_call_handler_start_call (priv->handler);
2040 empathy_call_window_setup_avatars (window, priv->handler);
2041 gst_element_set_state (priv->pipeline, GST_STATE_PLAYING);
2043 gtk_action_set_sensitive (priv->redial, FALSE);
2044 gtk_widget_set_sensitive (priv->redial_button, FALSE);
2048 empathy_call_window_redial_cb (gpointer object,
2049 EmpathyCallWindow *window)
2051 EmpathyCallWindowPriv *priv = GET_PRIV (window);
2053 if (priv->call_state == CONNECTED)
2054 priv->call_state = REDIALING;
2056 empathy_call_handler_stop_call (priv->handler);
2058 if (priv->call_state != CONNECTED)
2059 empathy_call_window_restart_call (window);
2063 empathy_call_window_fullscreen_cb (gpointer object,
2064 EmpathyCallWindow *window)
2066 empathy_call_window_fullscreen_toggle (window);
2070 empathy_call_window_fullscreen_toggle (EmpathyCallWindow *window)
2072 EmpathyCallWindowPriv *priv = GET_PRIV (window);
2074 if (priv->is_fullscreen)
2075 gtk_window_unfullscreen (GTK_WINDOW (window));
2077 gtk_window_fullscreen (GTK_WINDOW (window));
2081 empathy_call_window_video_button_press_cb (GtkWidget *video_output,
2082 GdkEventButton *event, EmpathyCallWindow *window)
2084 if (event->button == 3 && event->type == GDK_BUTTON_PRESS)
2086 empathy_call_window_video_menu_popup (window, event->button);
2094 empathy_call_window_key_press_cb (GtkWidget *video_output,
2095 GdkEventKey *event, EmpathyCallWindow *window)
2097 EmpathyCallWindowPriv *priv = GET_PRIV (window);
2099 if (priv->is_fullscreen && event->keyval == GDK_Escape)
2101 /* Since we are in fullscreen mode, toggling will bring us back to
2103 empathy_call_window_fullscreen_toggle (window);
2111 empathy_call_window_video_output_motion_notify (GtkWidget *widget,
2112 GdkEventMotion *event, EmpathyCallWindow *window)
2114 EmpathyCallWindowPriv *priv = GET_PRIV (window);
2116 if (priv->is_fullscreen)
2118 empathy_call_window_fullscreen_show_popup (priv->fullscreen);
2125 empathy_call_window_video_menu_popup (EmpathyCallWindow *window,
2129 EmpathyCallWindowPriv *priv = GET_PRIV (window);
2131 menu = gtk_ui_manager_get_widget (priv->ui_manager,
2133 gtk_menu_popup (GTK_MENU (menu), NULL, NULL, NULL, NULL,
2134 button, gtk_get_current_event_time ());
2135 gtk_menu_shell_select_first (GTK_MENU_SHELL (menu), FALSE);
2139 empathy_call_window_status_message (EmpathyCallWindow *window,
2142 EmpathyCallWindowPriv *priv = GET_PRIV (window);
2144 if (priv->context_id == 0)
2146 priv->context_id = gtk_statusbar_get_context_id (
2147 GTK_STATUSBAR (priv->statusbar), "voip call status messages");
2151 gtk_statusbar_pop (GTK_STATUSBAR (priv->statusbar), priv->context_id);
2154 gtk_statusbar_push (GTK_STATUSBAR (priv->statusbar), priv->context_id,
2159 empathy_call_window_volume_changed_cb (GtkScaleButton *button,
2160 gdouble value, EmpathyCallWindow *window)
2162 EmpathyCallWindowPriv *priv = GET_PRIV (window);
2164 empathy_audio_sink_set_volume (EMPATHY_GST_AUDIO_SINK (priv->audio_output),