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 /* translators: Call is a noun and %s is the contact name. This string is used
867 * in the window title */
868 tmp = g_strdup_printf (_("Call with %s"),
869 empathy_contact_get_name (priv->contact));
870 gtk_window_set_title (GTK_WINDOW (self), tmp);
875 contact_name_changed_cb (EmpathyContact *contact,
876 GParamSpec *pspec, EmpathyCallWindow *self)
878 set_window_title (self);
882 contact_avatar_changed_cb (EmpathyContact *contact,
883 GParamSpec *pspec, GtkWidget *avatar_widget)
885 init_contact_avatar_with_size (contact, avatar_widget,
886 avatar_widget->allocation.height);
890 empathy_call_window_got_self_contact_cb (EmpathyTpContactFactory *factory,
891 EmpathyContact *contact, const GError *error, gpointer user_data,
892 GObject *weak_object)
894 EmpathyCallWindow *self = EMPATHY_CALL_WINDOW (user_data);
895 EmpathyCallWindowPriv *priv = GET_PRIV (self);
897 init_contact_avatar_with_size (contact, priv->self_user_avatar_widget,
898 MIN (SELF_VIDEO_SECTION_WIDTH, SELF_VIDEO_SECTION_HEIGTH));
900 g_signal_connect (contact, "notify::avatar",
901 G_CALLBACK (contact_avatar_changed_cb), priv->self_user_avatar_widget);
905 empathy_call_window_setup_avatars (EmpathyCallWindow *self,
906 EmpathyCallHandler *handler)
908 EmpathyCallWindowPriv *priv = GET_PRIV (self);
910 g_object_get (handler, "contact", &(priv->contact), NULL);
912 if (priv->contact != NULL)
914 TpConnection *connection;
915 EmpathyTpContactFactory *factory;
917 set_window_title (self);
919 g_signal_connect (priv->contact, "notify::name",
920 G_CALLBACK (contact_name_changed_cb), self);
921 g_signal_connect (priv->contact, "notify::avatar",
922 G_CALLBACK (contact_avatar_changed_cb),
923 priv->remote_user_avatar_widget);
925 /* Retreiving the self avatar */
926 connection = empathy_contact_get_connection (priv->contact);
927 factory = empathy_tp_contact_factory_dup_singleton (connection);
928 empathy_tp_contact_factory_get_from_handle (factory,
929 tp_connection_get_self_handle (connection),
930 empathy_call_window_got_self_contact_cb, self, NULL, NULL);
932 g_object_unref (factory);
936 g_warning ("call handler doesn't have a contact");
937 /* translators: Call is a noun. This string is used in the window title */
938 gtk_window_set_title (GTK_WINDOW (self), _("Call"));
940 /* Since we can't access the remote contact, we can't get a connection
941 to it and can't get the self contact (and its avatar). This means
942 that we have to manually set the self avatar. */
943 init_contact_avatar_with_size (NULL, priv->self_user_avatar_widget,
944 MIN (SELF_VIDEO_SECTION_WIDTH, SELF_VIDEO_SECTION_HEIGTH));
947 init_contact_avatar_with_size (priv->contact,
948 priv->remote_user_avatar_widget, MIN (REMOTE_CONTACT_AVATAR_DEFAULT_WIDTH,
949 REMOTE_CONTACT_AVATAR_DEFAULT_HEIGHT));
951 /* The remote avatar is shown by default and will be hidden when we receive
952 video from the remote side. */
953 gtk_widget_hide (priv->video_output);
954 gtk_widget_show (priv->remote_user_avatar_widget);
958 empathy_call_window_setup_video_preview_visibility (EmpathyCallWindow *self,
959 EmpathyCallHandler *handler)
961 EmpathyCallWindowPriv *priv = GET_PRIV (self);
962 gboolean initial_video = empathy_call_handler_has_initial_video (priv->handler);
964 gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (priv->show_preview),
969 empathy_call_window_constructed (GObject *object)
971 EmpathyCallWindow *self = EMPATHY_CALL_WINDOW (object);
972 EmpathyCallWindowPriv *priv = GET_PRIV (self);
975 g_assert (priv->handler != NULL);
977 g_object_get (priv->handler, "tp-call", &call, NULL);
978 priv->outgoing = (call == NULL);
980 g_object_unref (call);
982 empathy_call_window_setup_avatars (self, priv->handler);
983 empathy_call_window_setup_video_preview_visibility (self, priv->handler);
984 empathy_call_window_set_state_connecting (self);
987 static void empathy_call_window_dispose (GObject *object);
988 static void empathy_call_window_finalize (GObject *object);
991 empathy_call_window_set_property (GObject *object,
992 guint property_id, const GValue *value, GParamSpec *pspec)
994 EmpathyCallWindowPriv *priv = GET_PRIV (object);
998 case PROP_CALL_HANDLER:
999 priv->handler = g_value_dup_object (value);
1002 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
1007 empathy_call_window_get_property (GObject *object,
1008 guint property_id, GValue *value, GParamSpec *pspec)
1010 EmpathyCallWindowPriv *priv = GET_PRIV (object);
1012 switch (property_id)
1014 case PROP_CALL_HANDLER:
1015 g_value_set_object (value, priv->handler);
1018 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
1023 empathy_call_window_class_init (
1024 EmpathyCallWindowClass *empathy_call_window_class)
1026 GObjectClass *object_class = G_OBJECT_CLASS (empathy_call_window_class);
1027 GParamSpec *param_spec;
1029 g_type_class_add_private (empathy_call_window_class,
1030 sizeof (EmpathyCallWindowPriv));
1032 object_class->constructed = empathy_call_window_constructed;
1033 object_class->set_property = empathy_call_window_set_property;
1034 object_class->get_property = empathy_call_window_get_property;
1036 object_class->dispose = empathy_call_window_dispose;
1037 object_class->finalize = empathy_call_window_finalize;
1039 param_spec = g_param_spec_object ("handler",
1040 "handler", "The call handler",
1041 EMPATHY_TYPE_CALL_HANDLER,
1042 G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS);
1043 g_object_class_install_property (object_class,
1044 PROP_CALL_HANDLER, param_spec);
1048 empathy_call_window_video_stream_changed_cb (EmpathyTpCall *call,
1049 GParamSpec *property, EmpathyCallWindow *self)
1051 empathy_call_window_update_avatars_visibility (call, self);
1055 empathy_call_window_dispose (GObject *object)
1057 EmpathyTpCall *call;
1058 EmpathyCallWindow *self = EMPATHY_CALL_WINDOW (object);
1059 EmpathyCallWindowPriv *priv = GET_PRIV (self);
1061 if (priv->dispose_has_run)
1064 priv->dispose_has_run = TRUE;
1066 g_object_get (priv->handler, "tp-call", &call, NULL);
1070 g_signal_handlers_disconnect_by_func (call,
1071 empathy_call_window_video_stream_changed_cb, object);
1074 g_object_unref (call);
1076 if (priv->handler != NULL)
1077 g_object_unref (priv->handler);
1078 priv->handler = NULL;
1080 if (priv->pipeline != NULL)
1081 g_object_unref (priv->pipeline);
1082 priv->pipeline = NULL;
1084 if (priv->video_input != NULL)
1085 g_object_unref (priv->video_input);
1086 priv->video_input = NULL;
1088 if (priv->audio_input != NULL)
1089 g_object_unref (priv->audio_input);
1090 priv->audio_input = NULL;
1092 if (priv->audio_output != NULL)
1093 g_object_unref (priv->audio_output);
1094 priv->audio_output = NULL;
1096 if (priv->video_tee != NULL)
1097 g_object_unref (priv->video_tee);
1098 priv->video_tee = NULL;
1100 if (priv->fsnotifier != NULL)
1101 g_object_unref (priv->fsnotifier);
1102 priv->fsnotifier = NULL;
1104 if (priv->timer_id != 0)
1105 g_source_remove (priv->timer_id);
1108 if (priv->ui_manager != NULL)
1109 g_object_unref (priv->ui_manager);
1110 priv->ui_manager = NULL;
1112 if (priv->contact != NULL)
1114 g_signal_handlers_disconnect_by_func (priv->contact,
1115 contact_name_changed_cb, self);
1116 g_object_unref (priv->contact);
1117 priv->contact = NULL;
1120 /* release any references held by the object here */
1121 if (G_OBJECT_CLASS (empathy_call_window_parent_class)->dispose)
1122 G_OBJECT_CLASS (empathy_call_window_parent_class)->dispose (object);
1126 empathy_call_window_finalize (GObject *object)
1128 EmpathyCallWindow *self = EMPATHY_CALL_WINDOW (object);
1129 EmpathyCallWindowPriv *priv = GET_PRIV (self);
1131 if (priv->video_output_motion_handler_id != 0)
1133 g_signal_handler_disconnect (G_OBJECT (priv->video_output),
1134 priv->video_output_motion_handler_id);
1135 priv->video_output_motion_handler_id = 0;
1138 if (priv->bus_message_source_id != 0)
1140 g_source_remove (priv->bus_message_source_id);
1141 priv->bus_message_source_id = 0;
1144 /* free any data held directly by the object here */
1145 g_mutex_free (priv->lock);
1147 g_timer_destroy (priv->timer);
1149 G_OBJECT_CLASS (empathy_call_window_parent_class)->finalize (object);
1154 empathy_call_window_new (EmpathyCallHandler *handler)
1156 return EMPATHY_CALL_WINDOW (
1157 g_object_new (EMPATHY_TYPE_CALL_WINDOW, "handler", handler, NULL));
1161 empathy_call_window_conference_added_cb (EmpathyCallHandler *handler,
1162 GstElement *conference, gpointer user_data)
1164 EmpathyCallWindow *self = EMPATHY_CALL_WINDOW (user_data);
1165 EmpathyCallWindowPriv *priv = GET_PRIV (self);
1167 gst_bin_add (GST_BIN (priv->pipeline), conference);
1169 gst_element_set_state (conference, GST_STATE_PLAYING);
1173 empathy_call_window_request_resource_cb (EmpathyCallHandler *handler,
1174 FsMediaType type, FsStreamDirection direction, gpointer user_data)
1176 EmpathyCallWindow *self = EMPATHY_CALL_WINDOW (user_data);
1177 EmpathyCallWindowPriv *priv = GET_PRIV (self);
1179 if (type != TP_MEDIA_STREAM_TYPE_VIDEO)
1182 if (direction == FS_DIRECTION_RECV)
1185 /* video and direction is send */
1186 return priv->video_input != NULL;
1190 empathy_call_window_reset_pipeline (EmpathyCallWindow *self)
1192 GstStateChangeReturn state_change_return;
1193 EmpathyCallWindowPriv *priv = GET_PRIV (self);
1195 if (priv->pipeline == NULL)
1198 if (priv->bus_message_source_id != 0)
1200 g_source_remove (priv->bus_message_source_id);
1201 priv->bus_message_source_id = 0;
1204 state_change_return = gst_element_set_state (priv->pipeline, GST_STATE_NULL);
1206 if (state_change_return == GST_STATE_CHANGE_SUCCESS ||
1207 state_change_return == GST_STATE_CHANGE_NO_PREROLL)
1209 if (priv->pipeline != NULL)
1210 g_object_unref (priv->pipeline);
1211 priv->pipeline = NULL;
1213 if (priv->video_input != NULL)
1214 g_object_unref (priv->video_input);
1215 priv->video_input = NULL;
1217 if (priv->audio_input != NULL)
1218 g_object_unref (priv->audio_input);
1219 priv->audio_input = NULL;
1221 g_signal_handlers_disconnect_by_func (priv->audio_input_adj,
1222 empathy_call_window_mic_volume_changed_cb, self);
1224 if (priv->audio_output != NULL)
1225 g_object_unref (priv->audio_output);
1226 priv->audio_output = NULL;
1228 if (priv->video_tee != NULL)
1229 g_object_unref (priv->video_tee);
1230 priv->video_tee = NULL;
1232 if (priv->video_preview != NULL)
1233 gtk_widget_destroy (priv->video_preview);
1234 priv->video_preview = NULL;
1236 priv->liveadder = NULL;
1237 priv->funnel = NULL;
1243 g_message ("Error: could not destroy pipeline. Closing call window");
1244 gtk_widget_destroy (GTK_WIDGET (self));
1251 empathy_call_window_disconnected (EmpathyCallWindow *self)
1253 gboolean could_disconnect = FALSE;
1254 EmpathyCallWindowPriv *priv = GET_PRIV (self);
1255 gboolean could_reset_pipeline = empathy_call_window_reset_pipeline (self);
1257 if (priv->call_state == CONNECTING)
1258 empathy_sound_stop (EMPATHY_SOUND_PHONE_OUTGOING);
1260 if (priv->call_state != REDIALING)
1261 priv->call_state = DISCONNECTED;
1263 if (could_reset_pipeline)
1265 gboolean initial_video = empathy_call_handler_has_initial_video (
1267 g_mutex_lock (priv->lock);
1269 g_timer_stop (priv->timer);
1271 if (priv->timer_id != 0)
1272 g_source_remove (priv->timer_id);
1275 g_mutex_unlock (priv->lock);
1277 empathy_call_window_status_message (self, _("Disconnected"));
1279 gtk_action_set_sensitive (priv->redial, TRUE);
1280 gtk_widget_set_sensitive (priv->redial_button, TRUE);
1282 /* Reseting the send_video, camera_buton and mic_button to their
1284 gtk_widget_set_sensitive (priv->camera_button, FALSE);
1285 gtk_widget_set_sensitive (priv->mic_button, FALSE);
1286 gtk_action_set_sensitive (priv->send_video, FALSE);
1287 gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (priv->send_video),
1289 gtk_toggle_tool_button_set_active (
1290 GTK_TOGGLE_TOOL_BUTTON (priv->camera_button), initial_video);
1291 gtk_toggle_tool_button_set_active (
1292 GTK_TOGGLE_TOOL_BUTTON (priv->mic_button), TRUE);
1294 gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (priv->show_preview),
1296 gtk_action_set_sensitive (priv->show_preview, FALSE);
1298 gtk_progress_bar_set_fraction (
1299 GTK_PROGRESS_BAR (priv->volume_progress_bar), 0);
1301 gtk_widget_hide (priv->video_output);
1302 gtk_widget_show (priv->remote_user_avatar_widget);
1304 priv->sending_video = FALSE;
1305 priv->call_started = FALSE;
1307 could_disconnect = TRUE;
1310 return could_disconnect;
1315 empathy_call_window_channel_closed_cb (TfChannel *channel, gpointer user_data)
1317 EmpathyCallWindow *self = EMPATHY_CALL_WINDOW (user_data);
1318 EmpathyCallWindowPriv *priv = GET_PRIV (self);
1320 if (empathy_call_window_disconnected (self) && priv->call_state == REDIALING)
1321 empathy_call_window_restart_call (self);
1324 /* Called with global lock held */
1326 empathy_call_window_get_video_sink_pad (EmpathyCallWindow *self)
1328 EmpathyCallWindowPriv *priv = GET_PRIV (self);
1331 if (priv->funnel == NULL)
1335 output = empathy_video_widget_get_element (EMPATHY_VIDEO_WIDGET
1336 (priv->video_output));
1338 priv->funnel = gst_element_factory_make ("fsfunnel", NULL);
1340 gst_bin_add (GST_BIN (priv->pipeline), priv->funnel);
1341 gst_bin_add (GST_BIN (priv->pipeline), output);
1343 gst_element_link (priv->funnel, output);
1345 gst_element_set_state (priv->funnel, GST_STATE_PLAYING);
1346 gst_element_set_state (output, GST_STATE_PLAYING);
1349 pad = gst_element_get_request_pad (priv->funnel, "sink%d");
1354 /* Called with global lock held */
1356 empathy_call_window_get_audio_sink_pad (EmpathyCallWindow *self)
1358 EmpathyCallWindowPriv *priv = GET_PRIV (self);
1361 if (priv->liveadder == NULL)
1363 priv->liveadder = gst_element_factory_make ("liveadder", NULL);
1365 gst_bin_add (GST_BIN (priv->pipeline), priv->liveadder);
1366 gst_bin_add (GST_BIN (priv->pipeline), priv->audio_output);
1368 gst_element_link (priv->liveadder, priv->audio_output);
1370 gst_element_set_state (priv->liveadder, GST_STATE_PLAYING);
1371 gst_element_set_state (priv->audio_output, GST_STATE_PLAYING);
1374 pad = gst_element_get_request_pad (priv->liveadder, "sink%d");
1380 empathy_call_window_update_timer (gpointer user_data)
1382 EmpathyCallWindow *self = EMPATHY_CALL_WINDOW (user_data);
1383 EmpathyCallWindowPriv *priv = GET_PRIV (self);
1387 time = g_timer_elapsed (priv->timer, NULL);
1389 /* Translators: number of minutes:seconds the caller has been connected */
1390 str = g_strdup_printf (_("Connected — %d:%02dm"), (int) time / 60,
1392 empathy_call_window_status_message (self, str);
1399 empathy_call_window_connected (gpointer user_data)
1401 EmpathyCallWindow *self = EMPATHY_CALL_WINDOW (user_data);
1402 EmpathyCallWindowPriv *priv = GET_PRIV (self);
1403 EmpathyTpCall *call;
1404 gboolean can_send_video;
1406 can_send_video = priv->video_input != NULL && priv->contact != NULL &&
1407 empathy_contact_can_voip_video (priv->contact);
1409 g_object_get (priv->handler, "tp-call", &call, NULL);
1411 g_signal_connect (call, "notify::video-stream",
1412 G_CALLBACK (empathy_call_window_video_stream_changed_cb), self);
1414 if (empathy_tp_call_has_dtmf (call))
1415 gtk_widget_set_sensitive (priv->dtmf_panel, TRUE);
1417 if (priv->video_input == NULL)
1418 empathy_call_window_set_send_video (self, FALSE);
1420 priv->sending_video = can_send_video ?
1421 empathy_tp_call_is_sending_video (call) : FALSE;
1423 gtk_action_set_sensitive (priv->show_preview, TRUE);
1424 gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (priv->show_preview),
1426 || gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (priv->show_preview)));
1427 gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (priv->send_video),
1428 priv->sending_video && priv->video_input != NULL);
1429 gtk_toggle_tool_button_set_active (
1430 GTK_TOGGLE_TOOL_BUTTON (priv->camera_button),
1431 priv->sending_video && priv->video_input != NULL);
1432 gtk_widget_set_sensitive (priv->camera_button, can_send_video);
1433 gtk_action_set_sensitive (priv->send_video, can_send_video);
1435 gtk_action_set_sensitive (priv->redial, FALSE);
1436 gtk_widget_set_sensitive (priv->redial_button, FALSE);
1438 gtk_widget_set_sensitive (priv->mic_button, TRUE);
1440 empathy_call_window_update_avatars_visibility (call, self);
1442 g_object_unref (call);
1444 g_mutex_lock (priv->lock);
1446 priv->timer_id = g_timeout_add_seconds (1,
1447 empathy_call_window_update_timer, self);
1449 g_mutex_unlock (priv->lock);
1451 empathy_call_window_update_timer (self);
1457 /* Called from the streaming thread */
1459 empathy_call_window_src_added_cb (EmpathyCallHandler *handler,
1460 GstPad *src, guint media_type, gpointer user_data)
1462 EmpathyCallWindow *self = EMPATHY_CALL_WINDOW (user_data);
1463 EmpathyCallWindowPriv *priv = GET_PRIV (self);
1467 g_mutex_lock (priv->lock);
1469 if (priv->call_state != CONNECTED)
1471 g_timer_start (priv->timer);
1472 priv->timer_id = g_idle_add (empathy_call_window_connected, self);
1473 priv->call_state = CONNECTED;
1474 empathy_sound_stop (EMPATHY_SOUND_PHONE_OUTGOING);
1479 case TP_MEDIA_STREAM_TYPE_AUDIO:
1480 pad = empathy_call_window_get_audio_sink_pad (self);
1482 case TP_MEDIA_STREAM_TYPE_VIDEO:
1483 gtk_widget_hide (priv->remote_user_avatar_widget);
1484 gtk_widget_show (priv->video_output);
1485 pad = empathy_call_window_get_video_sink_pad (self);
1488 g_assert_not_reached ();
1491 gst_pad_link (src, pad);
1492 gst_object_unref (pad);
1494 g_mutex_unlock (priv->lock);
1497 /* Called from the streaming thread */
1499 empathy_call_window_sink_added_cb (EmpathyCallHandler *handler,
1500 GstPad *sink, guint media_type, gpointer user_data)
1502 EmpathyCallWindow *self = EMPATHY_CALL_WINDOW (user_data);
1503 EmpathyCallWindowPriv *priv = GET_PRIV (self);
1508 case TP_MEDIA_STREAM_TYPE_AUDIO:
1509 gst_bin_add (GST_BIN (priv->pipeline), priv->audio_input);
1511 pad = gst_element_get_static_pad (priv->audio_input, "src");
1512 gst_pad_link (pad, sink);
1514 gst_element_set_state (priv->audio_input, GST_STATE_PLAYING);
1516 case TP_MEDIA_STREAM_TYPE_VIDEO:
1517 if (priv->video_input != NULL)
1519 EmpathyTpCall *call;
1520 g_object_get (priv->handler, "tp-call", &call, NULL);
1522 if (empathy_tp_call_is_sending_video (call))
1524 empathy_call_window_setup_video_preview (self);
1526 gtk_toggle_action_set_active (
1527 GTK_TOGGLE_ACTION (priv->show_preview), TRUE);
1529 if (priv->video_preview != NULL)
1530 gtk_widget_show (priv->video_preview);
1531 gtk_widget_hide (priv->self_user_avatar_widget);
1534 g_object_unref (call);
1536 if (priv->video_tee != NULL)
1538 pad = gst_element_get_request_pad (priv->video_tee, "src%d");
1539 gst_pad_link (pad, sink);
1544 g_assert_not_reached ();
1550 empathy_call_window_remove_video_input (EmpathyCallWindow *self)
1552 EmpathyCallWindowPriv *priv = GET_PRIV (self);
1553 GstElement *preview;
1555 preview = empathy_video_widget_get_element (
1556 EMPATHY_VIDEO_WIDGET (priv->video_preview));
1558 gst_element_set_state (priv->video_input, GST_STATE_NULL);
1559 gst_element_set_state (priv->video_tee, GST_STATE_NULL);
1560 gst_element_set_state (preview, GST_STATE_NULL);
1562 gst_bin_remove_many (GST_BIN (priv->pipeline), priv->video_input,
1563 priv->video_tee, preview, NULL);
1565 g_object_unref (priv->video_input);
1566 priv->video_input = NULL;
1567 g_object_unref (priv->video_tee);
1568 priv->video_tee = NULL;
1569 gtk_widget_destroy (priv->video_preview);
1570 priv->video_preview = NULL;
1572 gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (priv->send_video), FALSE);
1573 gtk_toggle_tool_button_set_active (
1574 GTK_TOGGLE_TOOL_BUTTON (priv->camera_button), FALSE);
1575 gtk_widget_set_sensitive (priv->camera_button, FALSE);
1576 gtk_action_set_sensitive (priv->send_video, FALSE);
1578 gtk_widget_show (priv->self_user_avatar_widget);
1583 empathy_call_window_bus_message (GstBus *bus, GstMessage *message,
1586 EmpathyCallWindow *self = EMPATHY_CALL_WINDOW (user_data);
1587 EmpathyCallWindowPriv *priv = GET_PRIV (self);
1590 empathy_call_handler_bus_message (priv->handler, bus, message);
1592 switch (GST_MESSAGE_TYPE (message))
1594 case GST_MESSAGE_STATE_CHANGED:
1595 if (GST_MESSAGE_SRC (message) == GST_OBJECT (priv->video_input))
1597 gst_message_parse_state_changed (message, NULL, &newstate, NULL);
1598 if (newstate == GST_STATE_PAUSED)
1599 empathy_call_window_setup_video_input (self);
1601 if (GST_MESSAGE_SRC (message) == GST_OBJECT (priv->pipeline) &&
1602 !priv->call_started)
1604 gst_message_parse_state_changed (message, NULL, &newstate, NULL);
1605 if (newstate == GST_STATE_PAUSED)
1607 priv->call_started = TRUE;
1608 empathy_call_handler_start_call (priv->handler);
1609 gst_element_set_state (priv->pipeline, GST_STATE_PLAYING);
1613 case GST_MESSAGE_ERROR:
1615 GError *error = NULL;
1616 GstElement *gst_error;
1619 gst_message_parse_error (message, &error, &debug);
1620 gst_error = GST_ELEMENT (GST_MESSAGE_SRC (message));
1622 g_message ("Element error: %s -- %s\n", error->message, debug);
1624 if (g_str_has_prefix (gst_element_get_name (gst_error),
1625 VIDEO_INPUT_ERROR_PREFIX))
1627 /* Remove the video input and continue */
1628 if (priv->video_input != NULL)
1629 empathy_call_window_remove_video_input (self);
1630 gst_element_set_state (priv->pipeline, GST_STATE_PLAYING);
1634 empathy_call_window_disconnected (self);
1636 g_error_free (error);
1647 empathy_call_window_update_self_avatar_visibility (EmpathyCallWindow *window)
1649 EmpathyCallWindowPriv *priv = GET_PRIV (window);
1651 if (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (priv->show_preview)))
1653 if (priv->video_preview != NULL)
1655 gtk_widget_hide (priv->self_user_avatar_widget);
1656 gtk_widget_show (priv->video_preview);
1660 if (priv->video_preview != NULL)
1661 gtk_widget_hide (priv->video_preview);
1663 gtk_widget_show (priv->self_user_avatar_widget);
1669 empathy_call_window_update_avatars_visibility (EmpathyTpCall *call,
1670 EmpathyCallWindow *window)
1672 EmpathyCallWindowPriv *priv = GET_PRIV (window);
1674 if (empathy_tp_call_is_receiving_video (call))
1676 gtk_widget_hide (priv->remote_user_avatar_widget);
1677 gtk_widget_show (priv->video_output);
1681 gtk_widget_hide (priv->video_output);
1682 gtk_widget_show (priv->remote_user_avatar_widget);
1685 empathy_call_window_update_self_avatar_visibility (window);
1689 empathy_call_window_realized_cb (GtkWidget *widget, EmpathyCallWindow *window)
1691 EmpathyCallWindowPriv *priv = GET_PRIV (window);
1693 g_signal_connect (priv->handler, "conference-added",
1694 G_CALLBACK (empathy_call_window_conference_added_cb), window);
1695 g_signal_connect (priv->handler, "request-resource",
1696 G_CALLBACK (empathy_call_window_request_resource_cb), window);
1697 g_signal_connect (priv->handler, "closed",
1698 G_CALLBACK (empathy_call_window_channel_closed_cb), window);
1699 g_signal_connect (priv->handler, "src-pad-added",
1700 G_CALLBACK (empathy_call_window_src_added_cb), window);
1701 g_signal_connect (priv->handler, "sink-pad-added",
1702 G_CALLBACK (empathy_call_window_sink_added_cb), window);
1704 gst_element_set_state (priv->pipeline, GST_STATE_PAUSED);
1708 empathy_call_window_delete_cb (GtkWidget *widget, GdkEvent*event,
1709 EmpathyCallWindow *window)
1711 EmpathyCallWindowPriv *priv = GET_PRIV (window);
1713 if (priv->pipeline != NULL)
1715 if (priv->bus_message_source_id != 0)
1717 g_source_remove (priv->bus_message_source_id);
1718 priv->bus_message_source_id = 0;
1721 gst_element_set_state (priv->pipeline, GST_STATE_NULL);
1724 if (priv->call_state == CONNECTING)
1725 empathy_sound_stop (EMPATHY_SOUND_PHONE_OUTGOING);
1731 show_controls (EmpathyCallWindow *window, gboolean set_fullscreen)
1734 EmpathyCallWindowPriv *priv = GET_PRIV (window);
1736 menu = gtk_ui_manager_get_widget (priv->ui_manager,
1741 gtk_widget_hide (priv->sidebar);
1742 gtk_widget_hide (menu);
1743 gtk_widget_hide (priv->vbox);
1744 gtk_widget_hide (priv->statusbar);
1745 gtk_widget_hide (priv->toolbar);
1749 if (priv->sidebar_was_visible_before_fs)
1750 gtk_widget_show (priv->sidebar);
1752 gtk_widget_show (menu);
1753 gtk_widget_show (priv->vbox);
1754 gtk_widget_show (priv->statusbar);
1755 gtk_widget_show (priv->toolbar);
1757 gtk_window_resize (GTK_WINDOW (window), priv->original_width_before_fs,
1758 priv->original_height_before_fs);
1763 show_borders (EmpathyCallWindow *window, gboolean set_fullscreen)
1765 EmpathyCallWindowPriv *priv = GET_PRIV (window);
1767 gtk_container_set_border_width (GTK_CONTAINER (priv->content_hbox),
1768 set_fullscreen ? 0 : CONTENT_HBOX_BORDER_WIDTH);
1769 gtk_box_set_spacing (GTK_BOX (priv->content_hbox),
1770 set_fullscreen ? 0 : CONTENT_HBOX_SPACING);
1771 gtk_box_set_child_packing (GTK_BOX (priv->content_hbox),
1772 priv->video_output, TRUE, TRUE,
1773 set_fullscreen ? 0 : CONTENT_HBOX_CHILDREN_PACKING_PADDING,
1775 gtk_box_set_child_packing (GTK_BOX (priv->content_hbox),
1776 priv->vbox, TRUE, TRUE,
1777 set_fullscreen ? 0 : CONTENT_HBOX_CHILDREN_PACKING_PADDING,
1782 empathy_call_window_state_event_cb (GtkWidget *widget,
1783 GdkEventWindowState *event, EmpathyCallWindow *window)
1785 if (event->changed_mask & GDK_WINDOW_STATE_FULLSCREEN)
1787 EmpathyCallWindowPriv *priv = GET_PRIV (window);
1788 gboolean set_fullscreen = event->new_window_state & GDK_WINDOW_STATE_FULLSCREEN;
1792 gboolean sidebar_was_visible;
1793 gint original_width = GTK_WIDGET (window)->allocation.width;
1794 gint original_height = GTK_WIDGET (window)->allocation.height;
1796 g_object_get (priv->sidebar, "visible", &sidebar_was_visible, NULL);
1798 priv->sidebar_was_visible_before_fs = sidebar_was_visible;
1799 priv->original_width_before_fs = original_width;
1800 priv->original_height_before_fs = original_height;
1802 if (priv->video_output_motion_handler_id == 0 &&
1803 priv->video_output != NULL)
1805 priv->video_output_motion_handler_id = g_signal_connect (
1806 G_OBJECT (priv->video_output), "motion-notify-event",
1807 G_CALLBACK (empathy_call_window_video_output_motion_notify), window);
1812 if (priv->video_output_motion_handler_id != 0)
1814 g_signal_handler_disconnect (G_OBJECT (priv->video_output),
1815 priv->video_output_motion_handler_id);
1816 priv->video_output_motion_handler_id = 0;
1820 empathy_call_window_fullscreen_set_fullscreen (priv->fullscreen,
1822 show_controls (window, set_fullscreen);
1823 show_borders (window, set_fullscreen);
1824 gtk_action_set_stock_id (priv->menu_fullscreen,
1825 (set_fullscreen ? "gtk-leave-fullscreen" : "gtk-fullscreen"));
1826 priv->is_fullscreen = set_fullscreen;
1833 empathy_call_window_sidebar_toggled_cb (GtkToggleButton *toggle,
1834 EmpathyCallWindow *window)
1836 EmpathyCallWindowPriv *priv = GET_PRIV (window);
1838 int w,h, handle_size;
1840 w = GTK_WIDGET (window)->allocation.width;
1841 h = GTK_WIDGET (window)->allocation.height;
1843 gtk_widget_style_get (priv->pane, "handle_size", &handle_size, NULL);
1845 if (gtk_toggle_button_get_active (toggle))
1847 arrow = gtk_arrow_new (GTK_ARROW_LEFT, GTK_SHADOW_NONE);
1848 gtk_widget_show (priv->sidebar);
1849 w += priv->sidebar->allocation.width + handle_size;
1853 arrow = gtk_arrow_new (GTK_ARROW_RIGHT, GTK_SHADOW_NONE);
1854 w -= priv->sidebar->allocation.width + handle_size;
1855 gtk_widget_hide (priv->sidebar);
1858 gtk_button_set_image (GTK_BUTTON (priv->sidebar_button), arrow);
1861 gtk_window_resize (GTK_WINDOW (window), w, h);
1865 empathy_call_window_set_send_video (EmpathyCallWindow *window,
1868 EmpathyCallWindowPriv *priv = GET_PRIV (window);
1869 EmpathyTpCall *call;
1871 priv->sending_video = send;
1873 /* When we start sending video, we want to show the video preview by
1877 empathy_call_window_setup_video_preview (window);
1878 gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (priv->show_preview),
1882 g_object_get (priv->handler, "tp-call", &call, NULL);
1883 empathy_tp_call_request_video_stream_direction (call, send);
1884 g_object_unref (call);
1888 empathy_call_window_camera_toggled_cb (GtkToggleToolButton *toggle,
1889 EmpathyCallWindow *window)
1891 EmpathyCallWindowPriv *priv = GET_PRIV (window);
1894 if (priv->call_state != CONNECTED)
1897 active = (gtk_toggle_tool_button_get_active (toggle));
1899 if (priv->sending_video == active)
1902 empathy_call_window_set_send_video (window, active);
1903 gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (priv->send_video), active);
1907 empathy_call_window_send_video_toggled_cb (GtkToggleAction *toggle,
1908 EmpathyCallWindow *window)
1910 EmpathyCallWindowPriv *priv = GET_PRIV (window);
1913 if (priv->call_state != CONNECTED)
1916 active = (gtk_toggle_action_get_active (toggle));
1918 if (priv->sending_video == active)
1921 empathy_call_window_set_send_video (window, active);
1922 gtk_toggle_tool_button_set_active (
1923 GTK_TOGGLE_TOOL_BUTTON (priv->camera_button), active);
1927 empathy_call_window_show_preview_toggled_cb (GtkToggleAction *toggle,
1928 EmpathyCallWindow *window)
1930 gboolean show_preview_toggled;
1931 EmpathyCallWindowPriv *priv = GET_PRIV (window);
1933 show_preview_toggled = gtk_toggle_action_get_active (toggle);
1935 if (show_preview_toggled)
1937 empathy_call_window_setup_video_preview (window);
1938 gtk_widget_show (priv->self_user_output_frame);
1939 empathy_call_window_update_self_avatar_visibility (window);
1943 gtk_widget_hide (priv->self_user_output_frame);
1948 empathy_call_window_mic_toggled_cb (GtkToggleToolButton *toggle,
1949 EmpathyCallWindow *window)
1951 EmpathyCallWindowPriv *priv = GET_PRIV (window);
1954 if (priv->audio_input == NULL)
1957 active = (gtk_toggle_tool_button_get_active (toggle));
1961 empathy_audio_src_set_volume (EMPATHY_GST_AUDIO_SRC (priv->audio_input),
1963 gtk_adjustment_set_value (priv->audio_input_adj, priv->volume * 100);
1967 /* TODO, Instead of setting the input volume to 0 we should probably
1968 * stop sending but this would cause the audio call to drop if both
1969 * sides mute at the same time on certain CMs AFAIK. Need to revisit this
1970 * in the future. GNOME #574574
1972 empathy_audio_src_set_volume (EMPATHY_GST_AUDIO_SRC (priv->audio_input),
1974 gtk_adjustment_set_value (priv->audio_input_adj, 0);
1979 empathy_call_window_sidebar_hidden_cb (EmpathySidebar *sidebar,
1980 EmpathyCallWindow *window)
1982 EmpathyCallWindowPriv *priv = GET_PRIV (window);
1984 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (priv->sidebar_button),
1989 empathy_call_window_sidebar_shown_cb (EmpathySidebar *sidebar,
1990 EmpathyCallWindow *window)
1992 EmpathyCallWindowPriv *priv = GET_PRIV (window);
1994 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (priv->sidebar_button),
1999 empathy_call_window_hangup_cb (gpointer object,
2000 EmpathyCallWindow *window)
2002 if (empathy_call_window_disconnected (window))
2003 gtk_widget_destroy (GTK_WIDGET (window));
2007 empathy_call_window_restart_call (EmpathyCallWindow *window)
2010 EmpathyCallWindowPriv *priv = GET_PRIV (window);
2012 gtk_widget_destroy (priv->remote_user_output_hbox);
2013 gtk_widget_destroy (priv->self_user_output_hbox);
2015 priv->pipeline = gst_pipeline_new (NULL);
2016 bus = gst_pipeline_get_bus (GST_PIPELINE (priv->pipeline));
2017 priv->bus_message_source_id = gst_bus_add_watch (bus,
2018 empathy_call_window_bus_message, window);
2020 empathy_call_window_setup_remote_frame (bus, window);
2021 empathy_call_window_setup_self_frame (bus, window);
2023 g_signal_connect (G_OBJECT (priv->audio_input_adj), "value-changed",
2024 G_CALLBACK (empathy_call_window_mic_volume_changed_cb), window);
2026 /* While the call was disconnected, the input volume might have changed.
2027 * However, since the audio_input source was destroyed, its volume has not
2028 * been updated during that time. That's why we manually update it here */
2029 empathy_call_window_mic_volume_changed_cb (priv->audio_input_adj, window);
2031 g_object_unref (bus);
2033 gtk_widget_show_all (priv->content_hbox);
2035 if (!empathy_call_handler_has_initial_video (priv->handler))
2036 gtk_widget_hide (priv->self_user_output_frame);
2038 priv->outgoing = TRUE;
2039 empathy_call_window_set_state_connecting (window);
2041 priv->call_started = TRUE;
2042 empathy_call_handler_start_call (priv->handler);
2043 empathy_call_window_setup_avatars (window, priv->handler);
2044 gst_element_set_state (priv->pipeline, GST_STATE_PLAYING);
2046 gtk_action_set_sensitive (priv->redial, FALSE);
2047 gtk_widget_set_sensitive (priv->redial_button, FALSE);
2051 empathy_call_window_redial_cb (gpointer object,
2052 EmpathyCallWindow *window)
2054 EmpathyCallWindowPriv *priv = GET_PRIV (window);
2056 if (priv->call_state == CONNECTED)
2057 priv->call_state = REDIALING;
2059 empathy_call_handler_stop_call (priv->handler);
2061 if (priv->call_state != CONNECTED)
2062 empathy_call_window_restart_call (window);
2066 empathy_call_window_fullscreen_cb (gpointer object,
2067 EmpathyCallWindow *window)
2069 empathy_call_window_fullscreen_toggle (window);
2073 empathy_call_window_fullscreen_toggle (EmpathyCallWindow *window)
2075 EmpathyCallWindowPriv *priv = GET_PRIV (window);
2077 if (priv->is_fullscreen)
2078 gtk_window_unfullscreen (GTK_WINDOW (window));
2080 gtk_window_fullscreen (GTK_WINDOW (window));
2084 empathy_call_window_video_button_press_cb (GtkWidget *video_output,
2085 GdkEventButton *event, EmpathyCallWindow *window)
2087 if (event->button == 3 && event->type == GDK_BUTTON_PRESS)
2089 empathy_call_window_video_menu_popup (window, event->button);
2097 empathy_call_window_key_press_cb (GtkWidget *video_output,
2098 GdkEventKey *event, EmpathyCallWindow *window)
2100 EmpathyCallWindowPriv *priv = GET_PRIV (window);
2102 if (priv->is_fullscreen && event->keyval == GDK_Escape)
2104 /* Since we are in fullscreen mode, toggling will bring us back to
2106 empathy_call_window_fullscreen_toggle (window);
2114 empathy_call_window_video_output_motion_notify (GtkWidget *widget,
2115 GdkEventMotion *event, EmpathyCallWindow *window)
2117 EmpathyCallWindowPriv *priv = GET_PRIV (window);
2119 if (priv->is_fullscreen)
2121 empathy_call_window_fullscreen_show_popup (priv->fullscreen);
2128 empathy_call_window_video_menu_popup (EmpathyCallWindow *window,
2132 EmpathyCallWindowPriv *priv = GET_PRIV (window);
2134 menu = gtk_ui_manager_get_widget (priv->ui_manager,
2136 gtk_menu_popup (GTK_MENU (menu), NULL, NULL, NULL, NULL,
2137 button, gtk_get_current_event_time ());
2138 gtk_menu_shell_select_first (GTK_MENU_SHELL (menu), FALSE);
2142 empathy_call_window_status_message (EmpathyCallWindow *window,
2145 EmpathyCallWindowPriv *priv = GET_PRIV (window);
2147 if (priv->context_id == 0)
2149 priv->context_id = gtk_statusbar_get_context_id (
2150 GTK_STATUSBAR (priv->statusbar), "voip call status messages");
2154 gtk_statusbar_pop (GTK_STATUSBAR (priv->statusbar), priv->context_id);
2157 gtk_statusbar_push (GTK_STATUSBAR (priv->statusbar), priv->context_id,
2162 empathy_call_window_volume_changed_cb (GtkScaleButton *button,
2163 gdouble value, EmpathyCallWindow *window)
2165 EmpathyCallWindowPriv *priv = GET_PRIV (window);
2167 empathy_audio_sink_set_volume (EMPATHY_GST_AUDIO_SINK (priv->audio_output),