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)
1136 g_source_remove (priv->bus_message_source_id);
1138 /* free any data held directly by the object here */
1139 g_mutex_free (priv->lock);
1141 g_timer_destroy (priv->timer);
1143 G_OBJECT_CLASS (empathy_call_window_parent_class)->finalize (object);
1148 empathy_call_window_new (EmpathyCallHandler *handler)
1150 return EMPATHY_CALL_WINDOW (
1151 g_object_new (EMPATHY_TYPE_CALL_WINDOW, "handler", handler, NULL));
1155 empathy_call_window_conference_added_cb (EmpathyCallHandler *handler,
1156 GstElement *conference, gpointer user_data)
1158 EmpathyCallWindow *self = EMPATHY_CALL_WINDOW (user_data);
1159 EmpathyCallWindowPriv *priv = GET_PRIV (self);
1161 gst_bin_add (GST_BIN (priv->pipeline), conference);
1163 gst_element_set_state (conference, GST_STATE_PLAYING);
1167 empathy_call_window_request_resource_cb (EmpathyCallHandler *handler,
1168 FsMediaType type, FsStreamDirection direction, gpointer user_data)
1170 EmpathyCallWindow *self = EMPATHY_CALL_WINDOW (user_data);
1171 EmpathyCallWindowPriv *priv = GET_PRIV (self);
1173 if (type != TP_MEDIA_STREAM_TYPE_VIDEO)
1176 if (direction == FS_DIRECTION_RECV)
1179 /* video and direction is send */
1180 return priv->video_input != NULL;
1184 empathy_call_window_reset_pipeline (EmpathyCallWindow *self)
1186 GstStateChangeReturn state_change_return;
1187 EmpathyCallWindowPriv *priv = GET_PRIV (self);
1189 if (priv->pipeline == NULL)
1192 if (priv->bus_message_source_id != 0)
1193 g_source_remove (priv->bus_message_source_id);
1194 state_change_return = gst_element_set_state (priv->pipeline, GST_STATE_NULL);
1196 if (state_change_return == GST_STATE_CHANGE_SUCCESS ||
1197 state_change_return == GST_STATE_CHANGE_NO_PREROLL)
1199 if (priv->pipeline != NULL)
1200 g_object_unref (priv->pipeline);
1201 priv->pipeline = NULL;
1203 if (priv->video_input != NULL)
1204 g_object_unref (priv->video_input);
1205 priv->video_input = NULL;
1207 if (priv->audio_input != NULL)
1208 g_object_unref (priv->audio_input);
1209 priv->audio_input = NULL;
1211 g_signal_handlers_disconnect_by_func (priv->audio_input_adj,
1212 empathy_call_window_mic_volume_changed_cb, self);
1214 if (priv->audio_output != NULL)
1215 g_object_unref (priv->audio_output);
1216 priv->audio_output = NULL;
1218 if (priv->video_tee != NULL)
1219 g_object_unref (priv->video_tee);
1220 priv->video_tee = NULL;
1222 if (priv->video_preview != NULL)
1223 gtk_widget_destroy (priv->video_preview);
1224 priv->video_preview = NULL;
1226 priv->liveadder = NULL;
1227 priv->funnel = NULL;
1233 g_message ("Error: could not destroy pipeline. Closing call window");
1234 gtk_widget_destroy (GTK_WIDGET (self));
1241 empathy_call_window_disconnected (EmpathyCallWindow *self)
1243 gboolean could_disconnect = FALSE;
1244 EmpathyCallWindowPriv *priv = GET_PRIV (self);
1245 gboolean could_reset_pipeline = empathy_call_window_reset_pipeline (self);
1247 if (priv->call_state == CONNECTING)
1248 empathy_sound_stop (EMPATHY_SOUND_PHONE_OUTGOING);
1250 if (priv->call_state != REDIALING)
1251 priv->call_state = DISCONNECTED;
1253 if (could_reset_pipeline)
1255 gboolean initial_video = empathy_call_handler_has_initial_video (
1257 g_mutex_lock (priv->lock);
1259 g_timer_stop (priv->timer);
1261 if (priv->timer_id != 0)
1262 g_source_remove (priv->timer_id);
1265 g_mutex_unlock (priv->lock);
1267 empathy_call_window_status_message (self, _("Disconnected"));
1269 gtk_action_set_sensitive (priv->redial, TRUE);
1270 gtk_widget_set_sensitive (priv->redial_button, TRUE);
1272 /* Reseting the send_video, camera_buton and mic_button to their
1274 gtk_widget_set_sensitive (priv->camera_button, FALSE);
1275 gtk_widget_set_sensitive (priv->mic_button, FALSE);
1276 gtk_action_set_sensitive (priv->send_video, FALSE);
1277 gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (priv->send_video),
1279 gtk_toggle_tool_button_set_active (
1280 GTK_TOGGLE_TOOL_BUTTON (priv->camera_button), initial_video);
1281 gtk_toggle_tool_button_set_active (
1282 GTK_TOGGLE_TOOL_BUTTON (priv->mic_button), TRUE);
1284 gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (priv->show_preview),
1286 gtk_action_set_sensitive (priv->show_preview, FALSE);
1288 gtk_progress_bar_set_fraction (
1289 GTK_PROGRESS_BAR (priv->volume_progress_bar), 0);
1291 gtk_widget_hide (priv->video_output);
1292 gtk_widget_show (priv->remote_user_avatar_widget);
1294 priv->sending_video = FALSE;
1295 priv->call_started = FALSE;
1297 could_disconnect = TRUE;
1300 return could_disconnect;
1305 empathy_call_window_channel_closed_cb (TfChannel *channel, gpointer user_data)
1307 EmpathyCallWindow *self = EMPATHY_CALL_WINDOW (user_data);
1308 EmpathyCallWindowPriv *priv = GET_PRIV (self);
1310 if (empathy_call_window_disconnected (self) && priv->call_state == REDIALING)
1311 empathy_call_window_restart_call (self);
1314 /* Called with global lock held */
1316 empathy_call_window_get_video_sink_pad (EmpathyCallWindow *self)
1318 EmpathyCallWindowPriv *priv = GET_PRIV (self);
1321 if (priv->funnel == NULL)
1325 output = empathy_video_widget_get_element (EMPATHY_VIDEO_WIDGET
1326 (priv->video_output));
1328 priv->funnel = gst_element_factory_make ("fsfunnel", NULL);
1330 gst_bin_add (GST_BIN (priv->pipeline), priv->funnel);
1331 gst_bin_add (GST_BIN (priv->pipeline), output);
1333 gst_element_link (priv->funnel, output);
1335 gst_element_set_state (priv->funnel, GST_STATE_PLAYING);
1336 gst_element_set_state (output, GST_STATE_PLAYING);
1339 pad = gst_element_get_request_pad (priv->funnel, "sink%d");
1344 /* Called with global lock held */
1346 empathy_call_window_get_audio_sink_pad (EmpathyCallWindow *self)
1348 EmpathyCallWindowPriv *priv = GET_PRIV (self);
1351 if (priv->liveadder == NULL)
1353 priv->liveadder = gst_element_factory_make ("liveadder", NULL);
1355 gst_bin_add (GST_BIN (priv->pipeline), priv->liveadder);
1356 gst_bin_add (GST_BIN (priv->pipeline), priv->audio_output);
1358 gst_element_link (priv->liveadder, priv->audio_output);
1360 gst_element_set_state (priv->liveadder, GST_STATE_PLAYING);
1361 gst_element_set_state (priv->audio_output, GST_STATE_PLAYING);
1364 pad = gst_element_get_request_pad (priv->liveadder, "sink%d");
1370 empathy_call_window_update_timer (gpointer user_data)
1372 EmpathyCallWindow *self = EMPATHY_CALL_WINDOW (user_data);
1373 EmpathyCallWindowPriv *priv = GET_PRIV (self);
1377 time = g_timer_elapsed (priv->timer, NULL);
1379 /* Translators: number of minutes:seconds the caller has been connected */
1380 str = g_strdup_printf (_("Connected — %d:%02dm"), (int) time / 60,
1382 empathy_call_window_status_message (self, str);
1389 empathy_call_window_connected (gpointer user_data)
1391 EmpathyCallWindow *self = EMPATHY_CALL_WINDOW (user_data);
1392 EmpathyCallWindowPriv *priv = GET_PRIV (self);
1393 EmpathyTpCall *call;
1394 gboolean can_send_video;
1396 can_send_video = priv->video_input != NULL && priv->contact != NULL &&
1397 empathy_contact_can_voip_video (priv->contact);
1399 g_object_get (priv->handler, "tp-call", &call, NULL);
1401 g_signal_connect (call, "notify::video-stream",
1402 G_CALLBACK (empathy_call_window_video_stream_changed_cb), self);
1404 if (empathy_tp_call_has_dtmf (call))
1405 gtk_widget_set_sensitive (priv->dtmf_panel, TRUE);
1407 if (priv->video_input == NULL)
1408 empathy_call_window_set_send_video (self, FALSE);
1410 priv->sending_video = can_send_video ?
1411 empathy_tp_call_is_sending_video (call) : FALSE;
1413 gtk_action_set_sensitive (priv->show_preview, TRUE);
1414 gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (priv->show_preview),
1416 || gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (priv->show_preview)));
1417 gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (priv->send_video),
1418 priv->sending_video && priv->video_input != NULL);
1419 gtk_toggle_tool_button_set_active (
1420 GTK_TOGGLE_TOOL_BUTTON (priv->camera_button),
1421 priv->sending_video && priv->video_input != NULL);
1422 gtk_widget_set_sensitive (priv->camera_button, can_send_video);
1423 gtk_action_set_sensitive (priv->send_video, can_send_video);
1425 gtk_action_set_sensitive (priv->redial, FALSE);
1426 gtk_widget_set_sensitive (priv->redial_button, FALSE);
1428 gtk_widget_set_sensitive (priv->mic_button, TRUE);
1430 empathy_call_window_update_avatars_visibility (call, self);
1432 g_object_unref (call);
1434 g_mutex_lock (priv->lock);
1436 priv->timer_id = g_timeout_add_seconds (1,
1437 empathy_call_window_update_timer, self);
1439 g_mutex_unlock (priv->lock);
1441 empathy_call_window_update_timer (self);
1447 /* Called from the streaming thread */
1449 empathy_call_window_src_added_cb (EmpathyCallHandler *handler,
1450 GstPad *src, guint media_type, gpointer user_data)
1452 EmpathyCallWindow *self = EMPATHY_CALL_WINDOW (user_data);
1453 EmpathyCallWindowPriv *priv = GET_PRIV (self);
1457 g_mutex_lock (priv->lock);
1459 if (priv->call_state != CONNECTED)
1461 g_timer_start (priv->timer);
1462 priv->timer_id = g_idle_add (empathy_call_window_connected, self);
1463 priv->call_state = CONNECTED;
1464 empathy_sound_stop (EMPATHY_SOUND_PHONE_OUTGOING);
1469 case TP_MEDIA_STREAM_TYPE_AUDIO:
1470 pad = empathy_call_window_get_audio_sink_pad (self);
1472 case TP_MEDIA_STREAM_TYPE_VIDEO:
1473 gtk_widget_hide (priv->remote_user_avatar_widget);
1474 gtk_widget_show (priv->video_output);
1475 pad = empathy_call_window_get_video_sink_pad (self);
1478 g_assert_not_reached ();
1481 gst_pad_link (src, pad);
1482 gst_object_unref (pad);
1484 g_mutex_unlock (priv->lock);
1487 /* Called from the streaming thread */
1489 empathy_call_window_sink_added_cb (EmpathyCallHandler *handler,
1490 GstPad *sink, guint media_type, gpointer user_data)
1492 EmpathyCallWindow *self = EMPATHY_CALL_WINDOW (user_data);
1493 EmpathyCallWindowPriv *priv = GET_PRIV (self);
1498 case TP_MEDIA_STREAM_TYPE_AUDIO:
1499 gst_bin_add (GST_BIN (priv->pipeline), priv->audio_input);
1501 pad = gst_element_get_static_pad (priv->audio_input, "src");
1502 gst_pad_link (pad, sink);
1504 gst_element_set_state (priv->audio_input, GST_STATE_PLAYING);
1506 case TP_MEDIA_STREAM_TYPE_VIDEO:
1507 if (priv->video_input != NULL)
1509 EmpathyTpCall *call;
1510 g_object_get (priv->handler, "tp-call", &call, NULL);
1512 if (empathy_tp_call_is_sending_video (call))
1514 empathy_call_window_setup_video_preview (self);
1516 gtk_toggle_action_set_active (
1517 GTK_TOGGLE_ACTION (priv->show_preview), TRUE);
1519 if (priv->video_preview != NULL)
1520 gtk_widget_show (priv->video_preview);
1521 gtk_widget_hide (priv->self_user_avatar_widget);
1524 g_object_unref (call);
1526 if (priv->video_tee != NULL)
1528 pad = gst_element_get_request_pad (priv->video_tee, "src%d");
1529 gst_pad_link (pad, sink);
1534 g_assert_not_reached ();
1540 empathy_call_window_remove_video_input (EmpathyCallWindow *self)
1542 EmpathyCallWindowPriv *priv = GET_PRIV (self);
1543 GstElement *preview;
1545 preview = empathy_video_widget_get_element (
1546 EMPATHY_VIDEO_WIDGET (priv->video_preview));
1548 gst_element_set_state (priv->video_input, GST_STATE_NULL);
1549 gst_element_set_state (priv->video_tee, GST_STATE_NULL);
1550 gst_element_set_state (preview, GST_STATE_NULL);
1552 gst_bin_remove_many (GST_BIN (priv->pipeline), priv->video_input,
1553 priv->video_tee, preview, NULL);
1555 g_object_unref (priv->video_input);
1556 priv->video_input = NULL;
1557 g_object_unref (priv->video_tee);
1558 priv->video_tee = NULL;
1559 gtk_widget_destroy (priv->video_preview);
1560 priv->video_preview = NULL;
1562 gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (priv->send_video), FALSE);
1563 gtk_toggle_tool_button_set_active (
1564 GTK_TOGGLE_TOOL_BUTTON (priv->camera_button), FALSE);
1565 gtk_widget_set_sensitive (priv->camera_button, FALSE);
1566 gtk_action_set_sensitive (priv->send_video, FALSE);
1568 gtk_widget_show (priv->self_user_avatar_widget);
1573 empathy_call_window_bus_message (GstBus *bus, GstMessage *message,
1576 EmpathyCallWindow *self = EMPATHY_CALL_WINDOW (user_data);
1577 EmpathyCallWindowPriv *priv = GET_PRIV (self);
1580 empathy_call_handler_bus_message (priv->handler, bus, message);
1582 switch (GST_MESSAGE_TYPE (message))
1584 case GST_MESSAGE_STATE_CHANGED:
1585 if (GST_MESSAGE_SRC (message) == GST_OBJECT (priv->video_input))
1587 gst_message_parse_state_changed (message, NULL, &newstate, NULL);
1588 if (newstate == GST_STATE_PAUSED)
1589 empathy_call_window_setup_video_input (self);
1591 if (GST_MESSAGE_SRC (message) == GST_OBJECT (priv->pipeline) &&
1592 !priv->call_started)
1594 gst_message_parse_state_changed (message, NULL, &newstate, NULL);
1595 if (newstate == GST_STATE_PAUSED)
1597 priv->call_started = TRUE;
1598 empathy_call_handler_start_call (priv->handler);
1599 gst_element_set_state (priv->pipeline, GST_STATE_PLAYING);
1603 case GST_MESSAGE_ERROR:
1605 GError *error = NULL;
1606 GstElement *gst_error;
1609 gst_message_parse_error (message, &error, &debug);
1610 gst_error = GST_ELEMENT (GST_MESSAGE_SRC (message));
1612 g_message ("Element error: %s -- %s\n", error->message, debug);
1614 if (g_str_has_prefix (gst_element_get_name (gst_error),
1615 VIDEO_INPUT_ERROR_PREFIX))
1617 /* Remove the video input and continue */
1618 if (priv->video_input != NULL)
1619 empathy_call_window_remove_video_input (self);
1620 gst_element_set_state (priv->pipeline, GST_STATE_PLAYING);
1624 empathy_call_window_disconnected (self);
1626 g_error_free (error);
1637 empathy_call_window_update_self_avatar_visibility (EmpathyCallWindow *window)
1639 EmpathyCallWindowPriv *priv = GET_PRIV (window);
1641 if (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (priv->show_preview)))
1643 if (priv->video_preview != NULL)
1645 gtk_widget_hide (priv->self_user_avatar_widget);
1646 gtk_widget_show (priv->video_preview);
1650 if (priv->video_preview != NULL)
1651 gtk_widget_hide (priv->video_preview);
1653 gtk_widget_show (priv->self_user_avatar_widget);
1659 empathy_call_window_update_avatars_visibility (EmpathyTpCall *call,
1660 EmpathyCallWindow *window)
1662 EmpathyCallWindowPriv *priv = GET_PRIV (window);
1664 if (empathy_tp_call_is_receiving_video (call))
1666 gtk_widget_hide (priv->remote_user_avatar_widget);
1667 gtk_widget_show (priv->video_output);
1671 gtk_widget_hide (priv->video_output);
1672 gtk_widget_show (priv->remote_user_avatar_widget);
1675 empathy_call_window_update_self_avatar_visibility (window);
1679 empathy_call_window_realized_cb (GtkWidget *widget, EmpathyCallWindow *window)
1681 EmpathyCallWindowPriv *priv = GET_PRIV (window);
1683 g_signal_connect (priv->handler, "conference-added",
1684 G_CALLBACK (empathy_call_window_conference_added_cb), window);
1685 g_signal_connect (priv->handler, "request-resource",
1686 G_CALLBACK (empathy_call_window_request_resource_cb), window);
1687 g_signal_connect (priv->handler, "closed",
1688 G_CALLBACK (empathy_call_window_channel_closed_cb), window);
1689 g_signal_connect (priv->handler, "src-pad-added",
1690 G_CALLBACK (empathy_call_window_src_added_cb), window);
1691 g_signal_connect (priv->handler, "sink-pad-added",
1692 G_CALLBACK (empathy_call_window_sink_added_cb), window);
1694 gst_element_set_state (priv->pipeline, GST_STATE_PAUSED);
1698 empathy_call_window_delete_cb (GtkWidget *widget, GdkEvent*event,
1699 EmpathyCallWindow *window)
1701 EmpathyCallWindowPriv *priv = GET_PRIV (window);
1703 if (priv->pipeline != NULL)
1705 if (priv->bus_message_source_id != 0)
1706 g_source_remove (priv->bus_message_source_id);
1708 gst_element_set_state (priv->pipeline, GST_STATE_NULL);
1711 if (priv->call_state == CONNECTING)
1712 empathy_sound_stop (EMPATHY_SOUND_PHONE_OUTGOING);
1718 show_controls (EmpathyCallWindow *window, gboolean set_fullscreen)
1721 EmpathyCallWindowPriv *priv = GET_PRIV (window);
1723 menu = gtk_ui_manager_get_widget (priv->ui_manager,
1728 gtk_widget_hide (priv->sidebar);
1729 gtk_widget_hide (menu);
1730 gtk_widget_hide (priv->vbox);
1731 gtk_widget_hide (priv->statusbar);
1732 gtk_widget_hide (priv->toolbar);
1736 if (priv->sidebar_was_visible_before_fs)
1737 gtk_widget_show (priv->sidebar);
1739 gtk_widget_show (menu);
1740 gtk_widget_show (priv->vbox);
1741 gtk_widget_show (priv->statusbar);
1742 gtk_widget_show (priv->toolbar);
1744 gtk_window_resize (GTK_WINDOW (window), priv->original_width_before_fs,
1745 priv->original_height_before_fs);
1750 show_borders (EmpathyCallWindow *window, gboolean set_fullscreen)
1752 EmpathyCallWindowPriv *priv = GET_PRIV (window);
1754 gtk_container_set_border_width (GTK_CONTAINER (priv->content_hbox),
1755 set_fullscreen ? 0 : CONTENT_HBOX_BORDER_WIDTH);
1756 gtk_box_set_spacing (GTK_BOX (priv->content_hbox),
1757 set_fullscreen ? 0 : CONTENT_HBOX_SPACING);
1758 gtk_box_set_child_packing (GTK_BOX (priv->content_hbox),
1759 priv->video_output, TRUE, TRUE,
1760 set_fullscreen ? 0 : CONTENT_HBOX_CHILDREN_PACKING_PADDING,
1762 gtk_box_set_child_packing (GTK_BOX (priv->content_hbox),
1763 priv->vbox, TRUE, TRUE,
1764 set_fullscreen ? 0 : CONTENT_HBOX_CHILDREN_PACKING_PADDING,
1769 empathy_call_window_state_event_cb (GtkWidget *widget,
1770 GdkEventWindowState *event, EmpathyCallWindow *window)
1772 if (event->changed_mask & GDK_WINDOW_STATE_FULLSCREEN)
1774 EmpathyCallWindowPriv *priv = GET_PRIV (window);
1775 gboolean set_fullscreen = event->new_window_state & GDK_WINDOW_STATE_FULLSCREEN;
1779 gboolean sidebar_was_visible;
1780 gint original_width = GTK_WIDGET (window)->allocation.width;
1781 gint original_height = GTK_WIDGET (window)->allocation.height;
1783 g_object_get (priv->sidebar, "visible", &sidebar_was_visible, NULL);
1785 priv->sidebar_was_visible_before_fs = sidebar_was_visible;
1786 priv->original_width_before_fs = original_width;
1787 priv->original_height_before_fs = original_height;
1789 if (priv->video_output_motion_handler_id == 0 &&
1790 priv->video_output != NULL)
1792 priv->video_output_motion_handler_id = g_signal_connect (
1793 G_OBJECT (priv->video_output), "motion-notify-event",
1794 G_CALLBACK (empathy_call_window_video_output_motion_notify), window);
1799 if (priv->video_output_motion_handler_id != 0)
1801 g_signal_handler_disconnect (G_OBJECT (priv->video_output),
1802 priv->video_output_motion_handler_id);
1803 priv->video_output_motion_handler_id = 0;
1807 empathy_call_window_fullscreen_set_fullscreen (priv->fullscreen,
1809 show_controls (window, set_fullscreen);
1810 show_borders (window, set_fullscreen);
1811 gtk_action_set_stock_id (priv->menu_fullscreen,
1812 (set_fullscreen ? "gtk-leave-fullscreen" : "gtk-fullscreen"));
1813 priv->is_fullscreen = set_fullscreen;
1820 empathy_call_window_sidebar_toggled_cb (GtkToggleButton *toggle,
1821 EmpathyCallWindow *window)
1823 EmpathyCallWindowPriv *priv = GET_PRIV (window);
1825 int w,h, handle_size;
1827 w = GTK_WIDGET (window)->allocation.width;
1828 h = GTK_WIDGET (window)->allocation.height;
1830 gtk_widget_style_get (priv->pane, "handle_size", &handle_size, NULL);
1832 if (gtk_toggle_button_get_active (toggle))
1834 arrow = gtk_arrow_new (GTK_ARROW_LEFT, GTK_SHADOW_NONE);
1835 gtk_widget_show (priv->sidebar);
1836 w += priv->sidebar->allocation.width + handle_size;
1840 arrow = gtk_arrow_new (GTK_ARROW_RIGHT, GTK_SHADOW_NONE);
1841 w -= priv->sidebar->allocation.width + handle_size;
1842 gtk_widget_hide (priv->sidebar);
1845 gtk_button_set_image (GTK_BUTTON (priv->sidebar_button), arrow);
1848 gtk_window_resize (GTK_WINDOW (window), w, h);
1852 empathy_call_window_set_send_video (EmpathyCallWindow *window,
1855 EmpathyCallWindowPriv *priv = GET_PRIV (window);
1856 EmpathyTpCall *call;
1858 priv->sending_video = send;
1860 /* When we start sending video, we want to show the video preview by
1864 empathy_call_window_setup_video_preview (window);
1865 gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (priv->show_preview),
1869 g_object_get (priv->handler, "tp-call", &call, NULL);
1870 empathy_tp_call_request_video_stream_direction (call, send);
1871 g_object_unref (call);
1875 empathy_call_window_camera_toggled_cb (GtkToggleToolButton *toggle,
1876 EmpathyCallWindow *window)
1878 EmpathyCallWindowPriv *priv = GET_PRIV (window);
1881 if (priv->call_state != CONNECTED)
1884 active = (gtk_toggle_tool_button_get_active (toggle));
1886 if (priv->sending_video == active)
1889 empathy_call_window_set_send_video (window, active);
1890 gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (priv->send_video), active);
1894 empathy_call_window_send_video_toggled_cb (GtkToggleAction *toggle,
1895 EmpathyCallWindow *window)
1897 EmpathyCallWindowPriv *priv = GET_PRIV (window);
1900 if (priv->call_state != CONNECTED)
1903 active = (gtk_toggle_action_get_active (toggle));
1905 if (priv->sending_video == active)
1908 empathy_call_window_set_send_video (window, active);
1909 gtk_toggle_tool_button_set_active (
1910 GTK_TOGGLE_TOOL_BUTTON (priv->camera_button), active);
1914 empathy_call_window_show_preview_toggled_cb (GtkToggleAction *toggle,
1915 EmpathyCallWindow *window)
1917 gboolean show_preview_toggled;
1918 EmpathyCallWindowPriv *priv = GET_PRIV (window);
1920 show_preview_toggled = gtk_toggle_action_get_active (toggle);
1922 if (show_preview_toggled)
1924 empathy_call_window_setup_video_preview (window);
1925 gtk_widget_show (priv->self_user_output_frame);
1926 empathy_call_window_update_self_avatar_visibility (window);
1930 gtk_widget_hide (priv->self_user_output_frame);
1935 empathy_call_window_mic_toggled_cb (GtkToggleToolButton *toggle,
1936 EmpathyCallWindow *window)
1938 EmpathyCallWindowPriv *priv = GET_PRIV (window);
1941 if (priv->audio_input == NULL)
1944 active = (gtk_toggle_tool_button_get_active (toggle));
1948 empathy_audio_src_set_volume (EMPATHY_GST_AUDIO_SRC (priv->audio_input),
1950 gtk_adjustment_set_value (priv->audio_input_adj, priv->volume * 100);
1954 /* TODO, Instead of setting the input volume to 0 we should probably
1955 * stop sending but this would cause the audio call to drop if both
1956 * sides mute at the same time on certain CMs AFAIK. Need to revisit this
1957 * in the future. GNOME #574574
1959 empathy_audio_src_set_volume (EMPATHY_GST_AUDIO_SRC (priv->audio_input),
1961 gtk_adjustment_set_value (priv->audio_input_adj, 0);
1966 empathy_call_window_sidebar_hidden_cb (EmpathySidebar *sidebar,
1967 EmpathyCallWindow *window)
1969 EmpathyCallWindowPriv *priv = GET_PRIV (window);
1971 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (priv->sidebar_button),
1976 empathy_call_window_sidebar_shown_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_hangup_cb (gpointer object,
1987 EmpathyCallWindow *window)
1989 if (empathy_call_window_disconnected (window))
1990 gtk_widget_destroy (GTK_WIDGET (window));
1994 empathy_call_window_restart_call (EmpathyCallWindow *window)
1997 EmpathyCallWindowPriv *priv = GET_PRIV (window);
1999 gtk_widget_destroy (priv->remote_user_output_hbox);
2000 gtk_widget_destroy (priv->self_user_output_hbox);
2002 priv->pipeline = gst_pipeline_new (NULL);
2003 bus = gst_pipeline_get_bus (GST_PIPELINE (priv->pipeline));
2004 priv->bus_message_source_id = gst_bus_add_watch (bus,
2005 empathy_call_window_bus_message, window);
2007 empathy_call_window_setup_remote_frame (bus, window);
2008 empathy_call_window_setup_self_frame (bus, window);
2010 g_signal_connect (G_OBJECT (priv->audio_input_adj), "value-changed",
2011 G_CALLBACK (empathy_call_window_mic_volume_changed_cb), window);
2013 /* While the call was disconnected, the input volume might have changed.
2014 * However, since the audio_input source was destroyed, its volume has not
2015 * been updated during that time. That's why we manually update it here */
2016 empathy_call_window_mic_volume_changed_cb (priv->audio_input_adj, window);
2018 g_object_unref (bus);
2020 gtk_widget_show_all (priv->content_hbox);
2022 if (!empathy_call_handler_has_initial_video (priv->handler))
2023 gtk_widget_hide (priv->self_user_output_frame);
2025 priv->outgoing = TRUE;
2026 empathy_call_window_set_state_connecting (window);
2028 priv->call_started = TRUE;
2029 empathy_call_handler_start_call (priv->handler);
2030 empathy_call_window_setup_avatars (window, priv->handler);
2031 gst_element_set_state (priv->pipeline, GST_STATE_PLAYING);
2033 gtk_action_set_sensitive (priv->redial, FALSE);
2034 gtk_widget_set_sensitive (priv->redial_button, FALSE);
2038 empathy_call_window_redial_cb (gpointer object,
2039 EmpathyCallWindow *window)
2041 EmpathyCallWindowPriv *priv = GET_PRIV (window);
2043 if (priv->call_state == CONNECTED)
2044 priv->call_state = REDIALING;
2046 empathy_call_handler_stop_call (priv->handler);
2048 if (priv->call_state != CONNECTED)
2049 empathy_call_window_restart_call (window);
2053 empathy_call_window_fullscreen_cb (gpointer object,
2054 EmpathyCallWindow *window)
2056 empathy_call_window_fullscreen_toggle (window);
2060 empathy_call_window_fullscreen_toggle (EmpathyCallWindow *window)
2062 EmpathyCallWindowPriv *priv = GET_PRIV (window);
2064 if (priv->is_fullscreen)
2065 gtk_window_unfullscreen (GTK_WINDOW (window));
2067 gtk_window_fullscreen (GTK_WINDOW (window));
2071 empathy_call_window_video_button_press_cb (GtkWidget *video_output,
2072 GdkEventButton *event, EmpathyCallWindow *window)
2074 if (event->button == 3 && event->type == GDK_BUTTON_PRESS)
2076 empathy_call_window_video_menu_popup (window, event->button);
2084 empathy_call_window_key_press_cb (GtkWidget *video_output,
2085 GdkEventKey *event, EmpathyCallWindow *window)
2087 EmpathyCallWindowPriv *priv = GET_PRIV (window);
2089 if (priv->is_fullscreen && event->keyval == GDK_Escape)
2091 /* Since we are in fullscreen mode, toggling will bring us back to
2093 empathy_call_window_fullscreen_toggle (window);
2101 empathy_call_window_video_output_motion_notify (GtkWidget *widget,
2102 GdkEventMotion *event, EmpathyCallWindow *window)
2104 EmpathyCallWindowPriv *priv = GET_PRIV (window);
2106 if (priv->is_fullscreen)
2108 empathy_call_window_fullscreen_show_popup (priv->fullscreen);
2115 empathy_call_window_video_menu_popup (EmpathyCallWindow *window,
2119 EmpathyCallWindowPriv *priv = GET_PRIV (window);
2121 menu = gtk_ui_manager_get_widget (priv->ui_manager,
2123 gtk_menu_popup (GTK_MENU (menu), NULL, NULL, NULL, NULL,
2124 button, gtk_get_current_event_time ());
2125 gtk_menu_shell_select_first (GTK_MENU_SHELL (menu), FALSE);
2129 empathy_call_window_status_message (EmpathyCallWindow *window,
2132 EmpathyCallWindowPriv *priv = GET_PRIV (window);
2134 if (priv->context_id == 0)
2136 priv->context_id = gtk_statusbar_get_context_id (
2137 GTK_STATUSBAR (priv->statusbar), "voip call status messages");
2141 gtk_statusbar_pop (GTK_STATUSBAR (priv->statusbar), priv->context_id);
2144 gtk_statusbar_push (GTK_STATUSBAR (priv->statusbar), priv->context_id,
2149 empathy_call_window_volume_changed_cb (GtkScaleButton *button,
2150 gdouble value, EmpathyCallWindow *window)
2152 EmpathyCallWindowPriv *priv = GET_PRIV (window);
2154 empathy_audio_sink_set_volume (EMPATHY_GST_AUDIO_SINK (priv->audio_output),