2 * empathy-call-window.c - Source for EmpathyCallWindow
3 * Copyright (C) 2008-2011 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 <clutter/clutter.h>
33 #include <clutter-gtk/clutter-gtk.h>
34 #include <clutter-gst/clutter-gst.h>
36 #include <telepathy-glib/util.h>
37 #include <telepathy-farstream/telepathy-farstream.h>
38 #include <telepathy-glib/util.h>
40 #include <gst/farsight/fs-element-added-notifier.h>
41 #include <gst/farsight/fs-utils.h>
43 #include <libempathy/empathy-camera-monitor.h>
44 #include <libempathy/empathy-tp-contact-factory.h>
45 #include <libempathy/empathy-utils.h>
46 #include <libempathy-gtk/empathy-avatar-image.h>
47 #include <libempathy-gtk/empathy-ui-utils.h>
48 #include <libempathy-gtk/empathy-sound-manager.h>
49 #include <libempathy-gtk/empathy-geometry.h>
50 #include <libempathy-gtk/empathy-images.h>
52 #define DEBUG_FLAG EMPATHY_DEBUG_VOIP
53 #include <libempathy/empathy-debug.h>
55 #include "empathy-call-window.h"
56 #include "empathy-call-window-fullscreen.h"
57 #include "empathy-call-factory.h"
58 #include "empathy-video-widget.h"
59 #include "empathy-audio-src.h"
60 #include "empathy-audio-sink.h"
61 #include "empathy-video-src.h"
62 #include "ev-sidebar.h"
63 #include "empathy-mic-menu.h"
65 #define CONTENT_HBOX_BORDER_WIDTH 6
66 #define CONTENT_HBOX_SPACING 3
67 #define CONTENT_HBOX_CHILDREN_PACKING_PADDING 3
69 #define SELF_VIDEO_SECTION_WIDTH 120
70 #define SELF_VIDEO_SECTION_HEIGTH 90
72 /* The avatar's default width and height are set to the same value because we
73 want a square icon. */
74 #define REMOTE_CONTACT_AVATAR_DEFAULT_WIDTH EMPATHY_VIDEO_WIDGET_DEFAULT_HEIGHT
75 #define REMOTE_CONTACT_AVATAR_DEFAULT_HEIGHT \
76 EMPATHY_VIDEO_WIDGET_DEFAULT_HEIGHT
78 #define SMALL_TOOLBAR_SIZE 36
80 /* If an video input error occurs, the error message will start with "v4l" */
81 #define VIDEO_INPUT_ERROR_PREFIX "v4l"
83 /* The time interval in milliseconds between 2 outgoing rings */
84 #define MS_BETWEEN_RING 500
86 G_DEFINE_TYPE(EmpathyCallWindow, empathy_call_window, GTK_TYPE_WINDOW)
89 PROP_CALL_HANDLER = 1,
101 CAMERA_STATE_OFF = 0,
105 struct _EmpathyCallWindowPriv
107 gboolean dispose_has_run;
108 EmpathyCallHandler *handler;
110 EmpathyContact *contact;
112 EmpathyCameraMonitor *camera_monitor;
117 GtkUIManager *ui_manager;
118 GtkWidget *errors_vbox;
119 /* widget displays the video received from the remote user. This widget is
120 * alive only during call. */
121 ClutterActor *video_output;
122 ClutterActor *video_preview;
123 ClutterActor *preview_hidden_button;
124 GtkWidget *video_container;
125 GtkWidget *remote_user_avatar_widget;
126 GtkWidget *remote_user_avatar_toolbar;
127 GtkWidget *remote_user_name_toolbar;
129 GtkWidget *status_label;
130 GtkWidget *hangup_button;
131 GtkWidget *audio_call_button;
132 GtkWidget *video_call_button;
133 GtkWidget *mic_button;
134 GtkWidget *camera_button;
135 GtkWidget *dialpad_button;
137 GtkWidget *bottom_toolbar;
139 GtkAction *menu_sidebar;
140 GtkAction *menu_fullscreen;
142 /* The box that contains self and remote avatar and video
143 input/output. When we redial, we destroy and re-create the box */
144 ClutterActor *video_box;
145 ClutterLayoutManager *video_layout;
147 /* We keep a reference on the hbox which contains the main content so we can
148 easilly repack everything when toggling fullscreen */
149 GtkWidget *content_hbox;
151 gulong video_output_motion_handler_id;
152 guint bus_message_source_id;
155 GtkWidget *volume_scale;
156 GtkWidget *volume_progress_bar;
157 GtkAdjustment *audio_input_adj;
159 GtkWidget *dtmf_panel;
162 GtkWidget *details_vbox;
163 GtkWidget *vcodec_encoding_label;
164 GtkWidget *acodec_encoding_label;
165 GtkWidget *vcodec_decoding_label;
166 GtkWidget *acodec_decoding_label;
168 GtkWidget *audio_remote_candidate_label;
169 GtkWidget *audio_local_candidate_label;
170 GtkWidget *video_remote_candidate_label;
171 GtkWidget *video_local_candidate_label;
172 GtkWidget *video_remote_candidate_info_img;
173 GtkWidget *video_local_candidate_info_img;
174 GtkWidget *audio_remote_candidate_info_img;
175 GtkWidget *audio_local_candidate_info_img;
177 GstElement *video_input;
178 GstElement *video_preview_sink;
179 GstElement *video_output_sink;
180 GstElement *audio_input;
181 GstElement *audio_output;
182 GstElement *pipeline;
183 GstElement *video_tee;
192 GtkWidget *video_contrast;
193 GtkWidget *video_brightness;
194 GtkWidget *video_gamma;
197 gboolean call_started;
198 gboolean sending_video;
199 CameraState camera_state;
201 EmpathyCallWindowFullscreen *fullscreen;
202 gboolean is_fullscreen;
207 /* Those fields represent the state of the window before it actually was in
209 gboolean sidebar_was_visible_before_fs;
210 gint original_width_before_fs;
211 gint original_height_before_fs;
213 gint x, y, w, h, sidebar_width;
216 /* TRUE if the call should be started when the pipeline is playing */
217 gboolean start_call_when_playing;
218 /* TRUE if we requested to set the pipeline in the playing state */
219 gboolean pipeline_playing;
221 EmpathySoundManager *sound_mgr;
223 EmpathyMicMenu *mic_menu;
226 #define GET_PRIV(o) (EMPATHY_CALL_WINDOW (o)->priv)
228 static void empathy_call_window_realized_cb (GtkWidget *widget,
229 EmpathyCallWindow *window);
231 static gboolean empathy_call_window_delete_cb (GtkWidget *widget,
232 GdkEvent *event, EmpathyCallWindow *window);
234 static gboolean empathy_call_window_state_event_cb (GtkWidget *widget,
235 GdkEventWindowState *event, EmpathyCallWindow *window);
237 static void empathy_call_window_set_send_video (EmpathyCallWindow *window,
240 static void empathy_call_window_mic_toggled_cb (
241 GtkToggleToolButton *toggle, EmpathyCallWindow *window);
243 static void empathy_call_window_sidebar_cb (GtkToggleAction *menu,
244 EmpathyCallWindow *self);
246 static void empathy_call_window_sidebar_hidden_cb (EvSidebar *sidebar,
247 EmpathyCallWindow *window);
249 static void empathy_call_window_sidebar_shown_cb (EvSidebar *sidebar,
250 EmpathyCallWindow *window);
252 static void empathy_call_window_sidebar_changed_cb (EvSidebar *sidebar,
254 EmpathyCallWindow *window);
256 static void empathy_call_window_hangup_cb (gpointer object,
257 EmpathyCallWindow *window);
259 static void empathy_call_window_fullscreen_cb (gpointer object,
260 EmpathyCallWindow *window);
262 static void empathy_call_window_fullscreen_toggle (EmpathyCallWindow *window);
264 static gboolean empathy_call_window_video_button_press_cb (
265 GtkWidget *video_output, GdkEventButton *event, EmpathyCallWindow *window);
267 static gboolean empathy_call_window_key_press_cb (GtkWidget *video_output,
268 GdkEventKey *event, EmpathyCallWindow *window);
270 static gboolean empathy_call_window_video_output_motion_notify (
271 GtkWidget *widget, GdkEventMotion *event, EmpathyCallWindow *window);
273 static void empathy_call_window_video_menu_popup (EmpathyCallWindow *window,
276 static void empathy_call_window_dialpad_cb (GtkToggleToolButton *button,
277 EmpathyCallWindow *window);
279 static void empathy_call_window_restart_call (EmpathyCallWindow *window);
281 static void empathy_call_window_status_message (EmpathyCallWindow *window,
284 static gboolean empathy_call_window_bus_message (GstBus *bus,
285 GstMessage *message, gpointer user_data);
288 empathy_call_window_volume_changed_cb (GtkScaleButton *button,
289 gdouble value, EmpathyCallWindow *window);
292 empathy_call_window_show_hangup_button (EmpathyCallWindow *self,
295 gtk_widget_set_visible (self->priv->hangup_button, show);
296 gtk_widget_set_visible (self->priv->audio_call_button, !show);
297 gtk_widget_set_visible (self->priv->video_call_button, !show);
301 empathy_call_window_audio_call_cb (GtkToggleToolButton *button,
302 EmpathyCallWindow *self)
304 g_object_set (self->priv->handler, "initial-video", FALSE, NULL);
305 empathy_call_window_restart_call (self);
309 empathy_call_window_video_call_cb (GtkToggleToolButton *button,
310 EmpathyCallWindow *self)
312 empathy_call_window_set_send_video (self, CAMERA_STATE_ON);
313 g_object_set (self->priv->handler, "initial-video", TRUE, NULL);
314 empathy_call_window_restart_call (self);
318 dtmf_button_pressed_cb (GtkButton *button, EmpathyCallWindow *window)
320 EmpathyCallWindowPriv *priv = GET_PRIV (window);
321 TpyCallChannel *call;
325 g_object_get (priv->handler, "call-channel", &call, NULL);
327 button_quark = g_quark_from_static_string (EMPATHY_DTMF_BUTTON_ID);
328 event = GPOINTER_TO_UINT (g_object_get_qdata (G_OBJECT (button),
331 tpy_call_channel_dtmf_start_tone (call, event);
333 g_object_unref (call);
337 dtmf_button_released_cb (GtkButton *button, EmpathyCallWindow *window)
339 EmpathyCallWindowPriv *priv = GET_PRIV (window);
340 TpyCallChannel *call;
342 g_object_get (priv->handler, "call-channel", &call, NULL);
344 tpy_call_channel_dtmf_stop_tone (call);
346 g_object_unref (call);
350 empathy_call_window_create_video_input_add_slider (EmpathyCallWindow *self,
351 gchar *label_text, GtkWidget *bin)
353 GtkWidget *vbox = gtk_vbox_new (FALSE, 2);
354 GtkWidget *scale = gtk_vscale_new_with_range (0, 100, 10);
355 GtkWidget *label = gtk_label_new (label_text);
357 gtk_widget_set_sensitive (scale, FALSE);
359 gtk_container_add (GTK_CONTAINER (bin), vbox);
361 gtk_range_set_inverted (GTK_RANGE (scale), TRUE);
362 gtk_box_pack_start (GTK_BOX (vbox), scale, TRUE, TRUE, 0);
363 gtk_box_pack_start (GTK_BOX (vbox), label, FALSE, FALSE, 0);
369 empathy_call_window_video_contrast_changed_cb (GtkAdjustment *adj,
370 EmpathyCallWindow *self)
373 EmpathyCallWindowPriv *priv = GET_PRIV (self);
375 empathy_video_src_set_channel (priv->video_input,
376 EMPATHY_GST_VIDEO_SRC_CHANNEL_CONTRAST, gtk_adjustment_get_value (adj));
380 empathy_call_window_video_brightness_changed_cb (GtkAdjustment *adj,
381 EmpathyCallWindow *self)
384 EmpathyCallWindowPriv *priv = GET_PRIV (self);
386 empathy_video_src_set_channel (priv->video_input,
387 EMPATHY_GST_VIDEO_SRC_CHANNEL_BRIGHTNESS, gtk_adjustment_get_value (adj));
391 empathy_call_window_video_gamma_changed_cb (GtkAdjustment *adj,
392 EmpathyCallWindow *self)
395 EmpathyCallWindowPriv *priv = GET_PRIV (self);
397 empathy_video_src_set_channel (priv->video_input,
398 EMPATHY_GST_VIDEO_SRC_CHANNEL_GAMMA, gtk_adjustment_get_value (adj));
403 empathy_call_window_create_video_input (EmpathyCallWindow *self)
405 EmpathyCallWindowPriv *priv = GET_PRIV (self);
408 hbox = gtk_hbox_new (TRUE, 3);
410 priv->video_contrast = empathy_call_window_create_video_input_add_slider (
411 self, _("Contrast"), hbox);
413 priv->video_brightness = empathy_call_window_create_video_input_add_slider (
414 self, _("Brightness"), hbox);
416 priv->video_gamma = empathy_call_window_create_video_input_add_slider (
417 self, _("Gamma"), hbox);
423 empathy_call_window_setup_video_input (EmpathyCallWindow *self)
425 EmpathyCallWindowPriv *priv = GET_PRIV (self);
429 supported = empathy_video_src_get_supported_channels (priv->video_input);
431 if (supported & EMPATHY_GST_VIDEO_SRC_SUPPORTS_CONTRAST)
433 adj = gtk_range_get_adjustment (GTK_RANGE (priv->video_contrast));
435 gtk_adjustment_set_value (adj,
436 empathy_video_src_get_channel (priv->video_input,
437 EMPATHY_GST_VIDEO_SRC_CHANNEL_CONTRAST));
439 g_signal_connect (G_OBJECT (adj), "value-changed",
440 G_CALLBACK (empathy_call_window_video_contrast_changed_cb), self);
442 gtk_widget_set_sensitive (priv->video_contrast, TRUE);
445 if (supported & EMPATHY_GST_VIDEO_SRC_SUPPORTS_BRIGHTNESS)
447 adj = gtk_range_get_adjustment (GTK_RANGE (priv->video_brightness));
449 gtk_adjustment_set_value (adj,
450 empathy_video_src_get_channel (priv->video_input,
451 EMPATHY_GST_VIDEO_SRC_CHANNEL_BRIGHTNESS));
453 g_signal_connect (G_OBJECT (adj), "value-changed",
454 G_CALLBACK (empathy_call_window_video_brightness_changed_cb), self);
455 gtk_widget_set_sensitive (priv->video_brightness, TRUE);
458 if (supported & EMPATHY_GST_VIDEO_SRC_SUPPORTS_GAMMA)
460 adj = gtk_range_get_adjustment (GTK_RANGE (priv->video_gamma));
462 gtk_adjustment_set_value (adj,
463 empathy_video_src_get_channel (priv->video_input,
464 EMPATHY_GST_VIDEO_SRC_CHANNEL_GAMMA));
466 g_signal_connect (G_OBJECT (adj), "value-changed",
467 G_CALLBACK (empathy_call_window_video_gamma_changed_cb), self);
468 gtk_widget_set_sensitive (priv->video_gamma, TRUE);
473 empathy_call_window_mic_volume_changed_cb (GtkAdjustment *adj,
474 EmpathyCallWindow *self)
476 EmpathyCallWindowPriv *priv = GET_PRIV (self);
479 volume = gtk_adjustment_get_value (adj)/100.0;
481 /* Don't store the volume because of muting */
482 if (volume > 0 || gtk_toggle_tool_button_get_active (
483 GTK_TOGGLE_TOOL_BUTTON (priv->mic_button)))
484 priv->volume = volume;
486 /* Ensure that the toggle button is active if the volume is > 0 and inactive
487 * if it's smaller than 0 */
488 if ((volume > 0) != gtk_toggle_tool_button_get_active (
489 GTK_TOGGLE_TOOL_BUTTON (priv->mic_button)))
490 gtk_toggle_tool_button_set_active (
491 GTK_TOGGLE_TOOL_BUTTON (priv->mic_button), volume > 0);
493 empathy_audio_src_set_volume (EMPATHY_GST_AUDIO_SRC (priv->audio_input),
498 empathy_call_window_audio_input_level_changed_cb (EmpathyGstAudioSrc *src,
499 gdouble level, EmpathyCallWindow *window)
502 EmpathyCallWindowPriv *priv = GET_PRIV (window);
504 value = CLAMP (pow (10, level / 20), 0.0, 1.0);
505 gtk_progress_bar_set_fraction (GTK_PROGRESS_BAR (priv->volume_progress_bar),
510 empathy_call_window_create_audio_input (EmpathyCallWindow *self)
512 EmpathyCallWindowPriv *priv = GET_PRIV (self);
513 GtkWidget *hbox, *vbox, *label;
515 hbox = gtk_hbox_new (TRUE, 3);
517 vbox = gtk_vbox_new (FALSE, 3);
518 gtk_box_pack_start (GTK_BOX (hbox), vbox, FALSE, FALSE, 3);
520 priv->volume_scale = gtk_vscale_new_with_range (0, 150, 100);
521 gtk_range_set_inverted (GTK_RANGE (priv->volume_scale), TRUE);
522 label = gtk_label_new (_("Volume"));
524 priv->audio_input_adj = gtk_range_get_adjustment (
525 GTK_RANGE (priv->volume_scale));
526 priv->volume = empathy_audio_src_get_volume (EMPATHY_GST_AUDIO_SRC
527 (priv->audio_input));
528 gtk_adjustment_set_value (priv->audio_input_adj, priv->volume * 100);
530 g_signal_connect (G_OBJECT (priv->audio_input_adj), "value-changed",
531 G_CALLBACK (empathy_call_window_mic_volume_changed_cb), self);
533 gtk_box_pack_start (GTK_BOX (vbox), priv->volume_scale, TRUE, TRUE, 3);
534 gtk_box_pack_start (GTK_BOX (vbox), label, FALSE, FALSE, 3);
536 priv->volume_progress_bar = gtk_progress_bar_new ();
538 gtk_orientable_set_orientation (GTK_ORIENTABLE (priv->volume_progress_bar),
539 GTK_ORIENTATION_VERTICAL);
541 gtk_progress_bar_set_fraction (GTK_PROGRESS_BAR (priv->volume_progress_bar),
544 gtk_box_pack_start (GTK_BOX (hbox), priv->volume_progress_bar, FALSE, FALSE,
551 empathy_call_window_show_video_output (EmpathyCallWindow *self,
554 if (self->priv->video_output != NULL)
555 g_object_set (self->priv->video_output, "visible", show, NULL);
557 gtk_widget_set_visible (self->priv->remote_user_avatar_widget, !show);
561 create_video_output_widget (EmpathyCallWindow *self)
563 EmpathyCallWindowPriv *priv = GET_PRIV (self);
565 g_assert (priv->video_output == NULL);
566 g_assert (priv->pipeline != NULL);
568 priv->video_output = clutter_texture_new ();
570 clutter_texture_set_keep_aspect_ratio (CLUTTER_TEXTURE (priv->video_output),
573 priv->video_output_sink = clutter_gst_video_sink_new (
574 CLUTTER_TEXTURE (priv->video_output));
576 clutter_container_add_actor (CLUTTER_CONTAINER (priv->video_box),
579 gtk_widget_add_events (priv->video_container,
580 GDK_BUTTON_PRESS_MASK | GDK_POINTER_MOTION_MASK);
581 g_signal_connect (G_OBJECT (priv->video_container), "button-press-event",
582 G_CALLBACK (empathy_call_window_video_button_press_cb), self);
586 create_video_input (EmpathyCallWindow *self)
588 EmpathyCallWindowPriv *priv = GET_PRIV (self);
590 g_assert (priv->video_input == NULL);
591 priv->video_input = empathy_video_src_new ();
592 gst_object_ref (priv->video_input);
593 gst_object_sink (priv->video_input);
597 create_audio_input (EmpathyCallWindow *self)
599 EmpathyCallWindowPriv *priv = GET_PRIV (self);
601 g_assert (priv->audio_input == NULL);
602 priv->audio_input = empathy_audio_src_new ();
603 gst_object_ref (priv->audio_input);
604 gst_object_sink (priv->audio_input);
606 tp_g_signal_connect_object (priv->audio_input, "peak-level-changed",
607 G_CALLBACK (empathy_call_window_audio_input_level_changed_cb),
612 add_video_preview_to_pipeline (EmpathyCallWindow *self)
614 EmpathyCallWindowPriv *priv = GET_PRIV (self);
617 g_assert (priv->video_preview != NULL);
618 g_assert (priv->pipeline != NULL);
619 g_assert (priv->video_input != NULL);
620 g_assert (priv->video_tee != NULL);
622 preview = priv->video_preview_sink;
624 if (!gst_bin_add (GST_BIN (priv->pipeline), priv->video_input))
626 g_warning ("Could not add video input to pipeline");
630 if (!gst_bin_add (GST_BIN (priv->pipeline), preview))
632 g_warning ("Could not add video preview to pipeline");
636 if (!gst_element_link (priv->video_input, priv->video_tee))
638 g_warning ("Could not link video input to video tee");
642 if (!gst_element_link (priv->video_tee, preview))
644 g_warning ("Could not link video tee to video preview");
650 empathy_call_window_disable_camera_cb (GtkAction *action,
651 EmpathyCallWindow *self)
653 clutter_actor_destroy (self->priv->preview_hidden_button);
655 gtk_toggle_tool_button_set_active (
656 GTK_TOGGLE_TOOL_BUTTON (self->priv->camera_button), FALSE);
660 empathy_call_window_minimise_camera_cb (GtkAction *action,
661 EmpathyCallWindow *self)
663 clutter_actor_hide (self->priv->video_preview);
664 clutter_actor_show (self->priv->preview_hidden_button);
668 empathy_call_window_maximise_camera_cb (GtkAction *action,
669 EmpathyCallWindow *self)
671 clutter_actor_show (self->priv->video_preview);
672 clutter_actor_hide (self->priv->preview_hidden_button);
676 empathy_call_window_preview_button_clicked_cb (GtkButton *button,
677 EmpathyCallWindow *self)
681 menu = gtk_ui_manager_get_widget (self->priv->ui_manager,
683 gtk_menu_popup (GTK_MENU (menu), NULL, NULL, NULL, NULL,
684 0, gtk_get_current_event_time ());
685 gtk_menu_shell_select_first (GTK_MENU_SHELL (menu), FALSE);
689 empathy_call_window_preview_hidden_button_clicked_cb (GtkButton *button,
690 EmpathyCallWindow *self)
694 menu = gtk_ui_manager_get_widget (self->priv->ui_manager,
695 "/preview-hidden-menu");
696 gtk_menu_popup (GTK_MENU (menu), NULL, NULL, NULL, NULL,
697 0, gtk_get_current_event_time ());
698 gtk_menu_shell_select_first (GTK_MENU_SHELL (menu), FALSE);
702 create_video_preview (EmpathyCallWindow *self)
704 EmpathyCallWindowPriv *priv = GET_PRIV (self);
705 ClutterLayoutManager *layout, *layout_center;
706 ClutterActor *preview;
711 g_assert (priv->video_preview == NULL);
713 preview = clutter_texture_new ();
714 clutter_actor_set_size (preview,
715 SELF_VIDEO_SECTION_WIDTH, SELF_VIDEO_SECTION_HEIGTH);
716 priv->video_preview_sink = clutter_gst_video_sink_new (
717 CLUTTER_TEXTURE (preview));
719 /* Flip the video preview */
720 clutter_actor_set_rotation (preview,
723 SELF_VIDEO_SECTION_WIDTH * 0.5,
727 /* Add a little offset to the video preview */
728 layout = clutter_bin_layout_new (CLUTTER_BIN_ALIGNMENT_END,
729 CLUTTER_BIN_ALIGNMENT_START);
730 priv->video_preview = clutter_box_new (layout);
731 clutter_actor_set_size (priv->video_preview,
732 SELF_VIDEO_SECTION_WIDTH + 10, SELF_VIDEO_SECTION_HEIGTH + 10);
734 layout_center = clutter_bin_layout_new (CLUTTER_BIN_ALIGNMENT_CENTER,
735 CLUTTER_BIN_ALIGNMENT_CENTER);
736 box = clutter_box_new (layout_center);
737 clutter_actor_set_size (box,
738 SELF_VIDEO_SECTION_WIDTH, SELF_VIDEO_SECTION_HEIGTH);
740 clutter_container_add_actor (CLUTTER_CONTAINER (box), preview);
741 clutter_container_add_actor (CLUTTER_CONTAINER (priv->video_preview), box);
743 g_object_set (priv->video_preview_sink,
748 /* Translators: this is an "Info" label. It should be as short
750 button = gtk_button_new_with_label (_("i"));
751 b = gtk_clutter_actor_new_with_contents (button);
753 clutter_bin_layout_add (CLUTTER_BIN_LAYOUT (layout_center),
755 CLUTTER_BIN_ALIGNMENT_END,
756 CLUTTER_BIN_ALIGNMENT_END);
758 g_signal_connect (button, "clicked",
759 G_CALLBACK (empathy_call_window_preview_button_clicked_cb),
762 /* Translators: this is an "Info" label. It should be as short
764 button = gtk_button_new_with_label (_("i"));
765 priv->preview_hidden_button =
766 gtk_clutter_actor_new_with_contents (button);
768 clutter_bin_layout_add (CLUTTER_BIN_LAYOUT (priv->video_layout),
769 priv->preview_hidden_button,
770 CLUTTER_BIN_ALIGNMENT_START,
771 CLUTTER_BIN_ALIGNMENT_END);
773 clutter_actor_hide (priv->preview_hidden_button);
775 g_signal_connect (button, "clicked",
776 G_CALLBACK (empathy_call_window_preview_hidden_button_clicked_cb),
779 clutter_bin_layout_add (CLUTTER_BIN_LAYOUT (priv->video_layout),
781 CLUTTER_BIN_ALIGNMENT_START,
782 CLUTTER_BIN_ALIGNMENT_END);
786 play_camera (EmpathyCallWindow *window,
789 EmpathyCallWindowPriv *priv = GET_PRIV (window);
793 if (priv->video_preview == NULL)
795 create_video_preview (window);
796 add_video_preview_to_pipeline (window);
800 state = GST_STATE_PLAYING;
802 state = GST_STATE_NULL;
804 preview = priv->video_preview_sink;
806 gst_element_set_state (preview, state);
807 gst_element_set_state (priv->video_input, state);
808 gst_element_set_state (priv->video_tee, state);
812 display_video_preview (EmpathyCallWindow *self,
815 EmpathyCallWindowPriv *priv = GET_PRIV (self);
819 /* Display the video preview */
820 DEBUG ("Show video preview");
822 play_camera (self, TRUE);
823 clutter_actor_show (priv->video_preview);
827 /* Hide the video preview */
828 DEBUG ("Hide video preview");
830 if (priv->video_preview != NULL)
832 clutter_actor_hide (priv->video_preview);
833 play_camera (self, FALSE);
839 empathy_call_window_set_state_connecting (EmpathyCallWindow *window)
841 EmpathyCallWindowPriv *priv = GET_PRIV (window);
843 empathy_call_window_status_message (window, _("Connecting…"));
844 priv->call_state = CONNECTING;
847 empathy_sound_manager_start_playing (priv->sound_mgr, GTK_WIDGET (window),
848 EMPATHY_SOUND_PHONE_OUTGOING, MS_BETWEEN_RING);
852 disable_camera (EmpathyCallWindow *self)
854 EmpathyCallWindowPriv *priv = GET_PRIV (self);
856 if (priv->camera_state == CAMERA_STATE_OFF)
859 DEBUG ("Disable camera");
861 display_video_preview (self, FALSE);
863 if (priv->camera_state == CAMERA_STATE_ON)
864 empathy_call_window_set_send_video (self, CAMERA_STATE_OFF);
866 priv->camera_state = CAMERA_STATE_OFF;
870 enable_camera (EmpathyCallWindow *self)
872 EmpathyCallWindowPriv *priv = GET_PRIV (self);
874 if (priv->camera_state == CAMERA_STATE_ON)
877 if (priv->video_input == NULL)
879 DEBUG ("Can't enable camera, no input");
883 DEBUG ("Enable camera");
885 empathy_call_window_set_send_video (self, CAMERA_STATE_ON);
887 priv->camera_state = CAMERA_STATE_ON;
891 empathy_call_window_camera_toggled_cb (GtkToggleToolButton *toggle,
892 EmpathyCallWindow *self)
894 if (gtk_toggle_tool_button_get_active (toggle))
895 enable_camera (self);
897 disable_camera (self);
901 create_pipeline (EmpathyCallWindow *self)
903 EmpathyCallWindowPriv *priv = GET_PRIV (self);
906 g_assert (priv->pipeline == NULL);
908 priv->pipeline = gst_pipeline_new (NULL);
909 priv->pipeline_playing = FALSE;
911 priv->video_tee = gst_element_factory_make ("tee", NULL);
912 gst_object_ref (priv->video_tee);
913 gst_object_sink (priv->video_tee);
915 gst_bin_add (GST_BIN (priv->pipeline), priv->video_tee);
917 bus = gst_pipeline_get_bus (GST_PIPELINE (priv->pipeline));
918 priv->bus_message_source_id = gst_bus_add_watch (bus,
919 empathy_call_window_bus_message, self);
921 g_object_unref (bus);
925 empathy_call_window_configure_event_cb (GtkWidget *widget,
927 EmpathyCallWindow *self)
929 GdkWindow *gdk_window;
930 GdkWindowState window_state;
932 gtk_window_get_position (GTK_WINDOW (self), &self->priv->x, &self->priv->y);
933 gtk_window_get_size (GTK_WINDOW (self), &self->priv->w, &self->priv->h);
935 gtk_widget_get_preferred_width (self->priv->sidebar,
936 &self->priv->sidebar_width, NULL);
938 gdk_window = gtk_widget_get_window (widget);
939 window_state = gdk_window_get_state (gdk_window);
940 self->priv->maximized = (window_state & GDK_WINDOW_STATE_MAXIMIZED);
946 empathy_call_window_destroyed_cb (GtkWidget *object,
947 EmpathyCallWindow *self)
949 if (gtk_widget_get_visible (self->priv->sidebar))
951 /* Save the geometry as if the sidebar was hidden. */
952 empathy_geometry_save_values (GTK_WINDOW (self),
953 self->priv->x, self->priv->y,
954 self->priv->w - self->priv->sidebar_width, self->priv->h,
955 self->priv->maximized);
960 empathy_call_window_init (EmpathyCallWindow *self)
962 EmpathyCallWindowPriv *priv;
968 ClutterConstraint *size_constraint;
969 ClutterActor *remote_avatar;
970 GtkStyleContext *context;
974 priv = self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self,
975 EMPATHY_TYPE_CALL_WINDOW, EmpathyCallWindowPriv);
977 filename = empathy_file_lookup ("empathy-call-window.ui", "src");
978 gui = empathy_builder_get_file (filename,
979 "call_window_vbox", &top_vbox,
980 "errors_vbox", &priv->errors_vbox,
982 "remote_user_name_toolbar", &priv->remote_user_name_toolbar,
983 "remote_user_avatar_toolbar", &priv->remote_user_avatar_toolbar,
984 "status_label", &priv->status_label,
985 "audiocall", &priv->audio_call_button,
986 "videocall", &priv->video_call_button,
987 "microphone", &priv->mic_button,
988 "camera", &priv->camera_button,
989 "hangup", &priv->hangup_button,
990 "dialpad", &priv->dialpad_button,
991 "toolbar", &priv->toolbar,
992 "bottom_toolbar", &priv->bottom_toolbar,
993 "menusidebar", &priv->menu_sidebar,
994 "ui_manager", &priv->ui_manager,
995 "menufullscreen", &priv->menu_fullscreen,
996 "details_vbox", &priv->details_vbox,
997 "vcodec_encoding_label", &priv->vcodec_encoding_label,
998 "acodec_encoding_label", &priv->acodec_encoding_label,
999 "acodec_decoding_label", &priv->acodec_decoding_label,
1000 "vcodec_decoding_label", &priv->vcodec_decoding_label,
1001 "audio_remote_candidate_label", &priv->audio_remote_candidate_label,
1002 "audio_local_candidate_label", &priv->audio_local_candidate_label,
1003 "video_remote_candidate_label", &priv->video_remote_candidate_label,
1004 "video_local_candidate_label", &priv->video_local_candidate_label,
1005 "video_remote_candidate_info_img", &priv->video_remote_candidate_info_img,
1006 "video_local_candidate_info_img", &priv->video_local_candidate_info_img,
1007 "audio_remote_candidate_info_img", &priv->audio_remote_candidate_info_img,
1008 "audio_local_candidate_info_img", &priv->audio_local_candidate_info_img,
1012 empathy_builder_connect (gui, self,
1013 "menuhangup", "activate", empathy_call_window_hangup_cb,
1014 "hangup", "clicked", empathy_call_window_hangup_cb,
1015 "audiocall", "clicked", empathy_call_window_audio_call_cb,
1016 "videocall", "clicked", empathy_call_window_video_call_cb,
1017 "menusidebar", "toggled", empathy_call_window_sidebar_cb,
1018 "volume", "value-changed", empathy_call_window_volume_changed_cb,
1019 "microphone", "toggled", empathy_call_window_mic_toggled_cb,
1020 "camera", "toggled", empathy_call_window_camera_toggled_cb,
1021 "dialpad", "toggled", empathy_call_window_dialpad_cb,
1022 "menufullscreen", "activate", empathy_call_window_fullscreen_cb,
1023 "menupreviewdisable", "activate", empathy_call_window_disable_camera_cb,
1024 "menupreviewminimise", "activate", empathy_call_window_minimise_camera_cb,
1025 "menupreviewmaximise", "activate", empathy_call_window_maximise_camera_cb,
1028 gtk_action_set_sensitive (priv->menu_fullscreen, FALSE);
1030 priv->camera_monitor = empathy_camera_monitor_dup_singleton ();
1032 g_object_bind_property (priv->camera_monitor, "available",
1033 priv->camera_button, "sensitive",
1034 G_BINDING_SYNC_CREATE);
1036 priv->lock = g_mutex_new ();
1038 gtk_container_add (GTK_CONTAINER (self), top_vbox);
1040 priv->content_hbox = gtk_hbox_new (FALSE, CONTENT_HBOX_SPACING);
1041 gtk_container_set_border_width (GTK_CONTAINER (priv->content_hbox),
1042 CONTENT_HBOX_BORDER_WIDTH);
1043 gtk_paned_pack1 (GTK_PANED (priv->pane), priv->content_hbox, TRUE, FALSE);
1045 /* avatar/video box */
1046 priv->video_layout = clutter_bin_layout_new (CLUTTER_BIN_ALIGNMENT_CENTER,
1047 CLUTTER_BIN_ALIGNMENT_CENTER);
1049 priv->video_box = clutter_box_new (priv->video_layout);
1051 priv->video_container = gtk_clutter_embed_new ();
1053 /* Set the background color to that of the rest of the window */
1054 context = gtk_widget_get_style_context (priv->content_hbox);
1055 gtk_style_context_get_background_color (context,
1056 GTK_STATE_FLAG_NORMAL, &rgba);
1057 bg.red = CLAMP (rgba.red * 255.0, 0, 255);
1058 bg.green = CLAMP (rgba.green * 255.0, 0, 255);
1059 bg.blue = CLAMP (rgba.blue * 255.0, 0, 255);
1060 bg.alpha = CLAMP (rgba.alpha * 255.0, 0, 255);
1061 clutter_stage_set_color (
1062 CLUTTER_STAGE (gtk_clutter_embed_get_stage (
1063 GTK_CLUTTER_EMBED (priv->video_container))),
1066 clutter_container_add (
1067 CLUTTER_CONTAINER (gtk_clutter_embed_get_stage (
1068 GTK_CLUTTER_EMBED (priv->video_container))),
1072 size_constraint = clutter_bind_constraint_new (
1073 gtk_clutter_embed_get_stage (GTK_CLUTTER_EMBED (priv->video_container)),
1074 CLUTTER_BIND_SIZE, 0);
1075 clutter_actor_add_constraint (priv->video_box, size_constraint);
1077 priv->remote_user_avatar_widget = gtk_image_new ();
1078 remote_avatar = gtk_clutter_actor_new_with_contents (
1079 priv->remote_user_avatar_widget);
1081 clutter_container_add_actor (CLUTTER_CONTAINER (priv->video_box),
1084 gtk_box_pack_start (GTK_BOX (priv->content_hbox),
1085 priv->video_container, TRUE, TRUE,
1086 CONTENT_HBOX_CHILDREN_PACKING_PADDING);
1088 create_pipeline (self);
1089 create_video_output_widget (self);
1090 create_audio_input (self);
1091 create_video_input (self);
1093 /* The call will be started as soon the pipeline is playing */
1094 priv->start_call_when_playing = TRUE;
1096 priv->sidebar = ev_sidebar_new ();
1097 g_signal_connect (G_OBJECT (priv->sidebar),
1098 "hide", G_CALLBACK (empathy_call_window_sidebar_hidden_cb), self);
1099 g_signal_connect (G_OBJECT (priv->sidebar),
1100 "show", G_CALLBACK (empathy_call_window_sidebar_shown_cb), self);
1101 g_signal_connect (G_OBJECT (priv->sidebar), "changed",
1102 G_CALLBACK (empathy_call_window_sidebar_changed_cb), self);
1103 gtk_paned_pack2 (GTK_PANED (priv->pane), priv->sidebar, FALSE, FALSE);
1105 page = empathy_call_window_create_audio_input (self);
1106 ev_sidebar_add_page (EV_SIDEBAR (priv->sidebar), "audio-input",
1107 _("Audio input"), page);
1109 page = empathy_call_window_create_video_input (self);
1110 ev_sidebar_add_page (EV_SIDEBAR (priv->sidebar), "video-input",
1111 _("Video input"), page);
1113 priv->dtmf_panel = empathy_create_dtmf_dialpad (G_OBJECT (self),
1114 G_CALLBACK (dtmf_button_pressed_cb),
1115 G_CALLBACK (dtmf_button_released_cb));
1116 ev_sidebar_add_page (EV_SIDEBAR (priv->sidebar), "dialpad",
1117 _("Dialpad"), priv->dtmf_panel);
1119 gtk_widget_set_sensitive (priv->dtmf_panel, FALSE);
1121 /* Put the details vbox in a scroll window as it can require a lot of
1122 * horizontal space. */
1123 scroll = gtk_scrolled_window_new (NULL, NULL);
1124 gtk_scrolled_window_add_with_viewport (GTK_SCROLLED_WINDOW (scroll),
1125 priv->details_vbox);
1127 ev_sidebar_add_page (EV_SIDEBAR (priv->sidebar), "details", _("Details"),
1130 gtk_widget_show_all (top_vbox);
1132 gtk_widget_hide (priv->sidebar);
1134 priv->fullscreen = empathy_call_window_fullscreen_new (self);
1136 empathy_call_window_fullscreen_set_video_widget (priv->fullscreen,
1137 priv->video_container);
1139 g_signal_connect (G_OBJECT (priv->fullscreen->leave_fullscreen_button),
1140 "clicked", G_CALLBACK (empathy_call_window_fullscreen_cb), self);
1142 g_signal_connect (G_OBJECT (self), "realize",
1143 G_CALLBACK (empathy_call_window_realized_cb), self);
1145 g_signal_connect (G_OBJECT (self), "delete-event",
1146 G_CALLBACK (empathy_call_window_delete_cb), self);
1148 g_signal_connect (G_OBJECT (self), "window-state-event",
1149 G_CALLBACK (empathy_call_window_state_event_cb), self);
1151 g_signal_connect (G_OBJECT (self), "key-press-event",
1152 G_CALLBACK (empathy_call_window_key_press_cb), self);
1154 priv->timer = g_timer_new ();
1156 g_object_ref (priv->ui_manager);
1157 g_object_unref (gui);
1159 priv->sound_mgr = empathy_sound_manager_dup_singleton ();
1160 priv->mic_menu = empathy_mic_menu_new (self);
1162 empathy_call_window_show_hangup_button (self, TRUE);
1164 empathy_geometry_bind (GTK_WINDOW (self), "call-window");
1165 /* These signals are used to track the window position and save it
1166 * when the window is destroyed. We need to do this as we don't want
1167 * the window geometry to be saved with the sidebar taken into account. */
1168 g_signal_connect (self, "destroy",
1169 G_CALLBACK (empathy_call_window_destroyed_cb), self);
1170 g_signal_connect (self, "configure-event",
1171 G_CALLBACK (empathy_call_window_configure_event_cb), self);
1172 g_signal_connect (self, "window-state-event",
1173 G_CALLBACK (empathy_call_window_configure_event_cb), self);
1176 /* Instead of specifying a width and a height, we specify only one size. That's
1177 because we want a square avatar icon. */
1179 init_contact_avatar_with_size (EmpathyContact *contact,
1180 GtkWidget *image_widget,
1183 GdkPixbuf *pixbuf_avatar = NULL;
1185 if (contact != NULL)
1187 pixbuf_avatar = empathy_pixbuf_avatar_from_contact_scaled (contact,
1191 if (pixbuf_avatar == NULL)
1193 pixbuf_avatar = empathy_pixbuf_from_icon_name_sized (
1194 EMPATHY_IMAGE_AVATAR_DEFAULT, size);
1197 gtk_image_set_from_pixbuf (GTK_IMAGE (image_widget), pixbuf_avatar);
1199 if (pixbuf_avatar != NULL)
1200 g_object_unref (pixbuf_avatar);
1204 set_window_title (EmpathyCallWindow *self)
1206 EmpathyCallWindowPriv *priv = GET_PRIV (self);
1209 if (priv->contact != NULL)
1211 /* translators: Call is a noun and %s is the contact name. This string
1212 * is used in the window title */
1213 tmp = g_strdup_printf (_("Call with %s"),
1214 empathy_contact_get_alias (priv->contact));
1215 gtk_window_set_title (GTK_WINDOW (self), tmp);
1220 gtk_window_set_title (GTK_WINDOW (self), _("Call with %d participants"));
1225 set_remote_user_name (EmpathyCallWindow *self,
1226 EmpathyContact *contact)
1228 const gchar *alias = empathy_contact_get_alias (contact);
1229 const gchar *status = empathy_contact_get_status (contact);
1232 label = g_strdup_printf ("%s\n<small>%s</small>", alias, status);
1233 gtk_label_set_markup (GTK_LABEL (self->priv->remote_user_name_toolbar),
1239 contact_name_changed_cb (EmpathyContact *contact,
1241 EmpathyCallWindow *self)
1243 set_window_title (self);
1244 set_remote_user_name (self, contact);
1248 contact_presence_changed_cb (EmpathyContact *contact,
1250 EmpathyCallWindow *self)
1252 set_remote_user_name (self, contact);
1256 contact_avatar_changed_cb (EmpathyContact *contact,
1258 EmpathyCallWindow *self)
1261 GtkAllocation allocation;
1262 GtkWidget *avatar_widget;
1264 avatar_widget = self->priv->remote_user_avatar_widget;
1266 gtk_widget_get_allocation (avatar_widget, &allocation);
1267 size = allocation.height;
1271 /* the widget is not allocated yet, set a default size */
1272 size = MIN (REMOTE_CONTACT_AVATAR_DEFAULT_HEIGHT,
1273 REMOTE_CONTACT_AVATAR_DEFAULT_WIDTH);
1276 init_contact_avatar_with_size (contact, avatar_widget, size);
1278 avatar_widget = self->priv->remote_user_avatar_toolbar;
1280 gtk_widget_get_allocation (avatar_widget, &allocation);
1281 size = allocation.height;
1285 /* the widget is not allocated yet, set a default size */
1286 size = SMALL_TOOLBAR_SIZE;
1289 init_contact_avatar_with_size (contact, avatar_widget, size);
1293 empathy_call_window_setup_avatars (EmpathyCallWindow *self,
1294 EmpathyCallHandler *handler)
1296 EmpathyCallWindowPriv *priv = GET_PRIV (self);
1298 g_signal_connect (priv->contact, "notify::name",
1299 G_CALLBACK (contact_name_changed_cb), self);
1300 g_signal_connect (priv->contact, "notify::avatar",
1301 G_CALLBACK (contact_avatar_changed_cb), self);
1302 /* FIXME: There's no EmpathyContact::presence yet */
1303 g_signal_connect (priv->contact, "notify::presence",
1304 G_CALLBACK (contact_presence_changed_cb), self);
1306 set_window_title (self);
1307 set_remote_user_name (self, priv->contact);
1309 init_contact_avatar_with_size (priv->contact,
1310 priv->remote_user_avatar_widget,
1311 MIN (REMOTE_CONTACT_AVATAR_DEFAULT_WIDTH,
1312 REMOTE_CONTACT_AVATAR_DEFAULT_HEIGHT));
1314 init_contact_avatar_with_size (priv->contact,
1315 priv->remote_user_avatar_toolbar,
1316 SMALL_TOOLBAR_SIZE);
1318 /* The remote avatar is shown by default and will be hidden when we receive
1319 video from the remote side. */
1320 clutter_actor_hide (priv->video_output);
1321 gtk_widget_show (priv->remote_user_avatar_widget);
1325 update_send_codec (EmpathyCallWindow *self,
1328 EmpathyCallWindowPriv *priv = GET_PRIV (self);
1335 codec = empathy_call_handler_get_send_audio_codec (priv->handler);
1336 widget = priv->acodec_encoding_label;
1340 codec = empathy_call_handler_get_send_video_codec (priv->handler);
1341 widget = priv->vcodec_encoding_label;
1347 tmp = g_strdup_printf ("%s/%u", codec->encoding_name, codec->clock_rate);
1348 gtk_label_set_text (GTK_LABEL (widget), tmp);
1353 send_audio_codec_notify_cb (GObject *object,
1357 EmpathyCallWindow *self = user_data;
1359 update_send_codec (self, TRUE);
1363 send_video_codec_notify_cb (GObject *object,
1367 EmpathyCallWindow *self = user_data;
1369 update_send_codec (self, FALSE);
1373 update_recv_codec (EmpathyCallWindow *self,
1376 EmpathyCallWindowPriv *priv = GET_PRIV (self);
1379 GString *str = NULL;
1383 codecs = empathy_call_handler_get_recv_audio_codecs (priv->handler);
1384 widget = priv->acodec_decoding_label;
1388 codecs = empathy_call_handler_get_recv_video_codecs (priv->handler);
1389 widget = priv->vcodec_decoding_label;
1395 for (l = codecs; l != NULL; l = g_list_next (l))
1397 FsCodec *codec = l->data;
1400 str = g_string_new (NULL);
1402 g_string_append (str, ", ");
1404 g_string_append_printf (str, "%s/%u", codec->encoding_name,
1408 gtk_label_set_text (GTK_LABEL (widget), str->str);
1409 g_string_free (str, TRUE);
1413 recv_audio_codecs_notify_cb (GObject *object,
1417 EmpathyCallWindow *self = user_data;
1419 update_recv_codec (self, TRUE);
1423 recv_video_codecs_notify_cb (GObject *object,
1427 EmpathyCallWindow *self = user_data;
1429 update_recv_codec (self, FALSE);
1432 static const gchar *
1433 candidate_type_to_str (FsCandidate *candidate)
1435 switch (candidate->type)
1437 case FS_CANDIDATE_TYPE_HOST:
1439 case FS_CANDIDATE_TYPE_SRFLX:
1440 return "server reflexive";
1441 case FS_CANDIDATE_TYPE_PRFLX:
1442 return "peer reflexive";
1443 case FS_CANDIDATE_TYPE_RELAY:
1445 case FS_CANDIDATE_TYPE_MULTICAST:
1452 static const gchar *
1453 candidate_type_to_desc (FsCandidate *candidate)
1455 switch (candidate->type)
1457 case FS_CANDIDATE_TYPE_HOST:
1458 return _("The IP address as seen by the machine");
1459 case FS_CANDIDATE_TYPE_SRFLX:
1460 return _("The IP address as seen by a server on the Internet");
1461 case FS_CANDIDATE_TYPE_PRFLX:
1462 return _("The IP address of the peer as seen by the other side");
1463 case FS_CANDIDATE_TYPE_RELAY:
1464 return _("The IP address of a relay server");
1465 case FS_CANDIDATE_TYPE_MULTICAST:
1466 return _("The IP address of the multicast group");
1473 update_candidat_widget (EmpathyCallWindow *self,
1476 FsCandidate *candidate)
1480 g_assert (candidate != NULL);
1481 str = g_strdup_printf ("%s %u (%s)", candidate->ip,
1482 candidate->port, candidate_type_to_str (candidate));
1484 gtk_label_set_text (GTK_LABEL (label), str);
1485 gtk_widget_set_tooltip_text (img, candidate_type_to_desc (candidate));
1491 candidates_changed_cb (GObject *object,
1493 EmpathyCallWindow *self)
1495 EmpathyCallWindowPriv *priv = GET_PRIV (self);
1496 FsCandidate *candidate = NULL;
1498 if (type == FS_MEDIA_TYPE_VIDEO)
1500 /* Update remote candidate */
1501 candidate = empathy_call_handler_get_video_remote_candidate (
1504 update_candidat_widget (self, priv->video_remote_candidate_label,
1505 priv->video_remote_candidate_info_img, candidate);
1507 /* Update local candidate */
1508 candidate = empathy_call_handler_get_video_local_candidate (
1511 update_candidat_widget (self, priv->video_local_candidate_label,
1512 priv->video_local_candidate_info_img, candidate);
1516 /* Update remote candidate */
1517 candidate = empathy_call_handler_get_audio_remote_candidate (
1520 update_candidat_widget (self, priv->audio_remote_candidate_label,
1521 priv->audio_remote_candidate_info_img, candidate);
1523 /* Update local candidate */
1524 candidate = empathy_call_handler_get_audio_local_candidate (
1527 update_candidat_widget (self, priv->audio_local_candidate_label,
1528 priv->audio_local_candidate_info_img, candidate);
1533 empathy_call_window_constructed (GObject *object)
1535 EmpathyCallWindow *self = EMPATHY_CALL_WINDOW (object);
1536 EmpathyCallWindowPriv *priv = GET_PRIV (self);
1537 TpyCallChannel *call;
1539 g_assert (priv->handler != NULL);
1541 g_object_get (priv->handler, "call-channel", &call, NULL);
1542 priv->outgoing = (call == NULL);
1544 g_object_unref (call);
1546 g_object_get (priv->handler, "target-contact", &priv->contact, NULL);
1547 g_assert (priv->contact != NULL);
1549 empathy_call_window_setup_avatars (self, priv->handler);
1550 empathy_call_window_set_state_connecting (self);
1552 if (!empathy_call_handler_has_initial_video (priv->handler))
1554 gtk_toggle_tool_button_set_active (
1555 GTK_TOGGLE_TOOL_BUTTON (priv->camera_button), FALSE);
1557 /* If call has InitialVideo, the preview will be started once the call has
1558 * been started (start_call()). */
1560 update_send_codec (self, TRUE);
1561 update_send_codec (self, FALSE);
1562 update_recv_codec (self, TRUE);
1563 update_recv_codec (self, FALSE);
1565 tp_g_signal_connect_object (priv->handler, "notify::send-audio-codec",
1566 G_CALLBACK (send_audio_codec_notify_cb), self, 0);
1567 tp_g_signal_connect_object (priv->handler, "notify::send-video-codec",
1568 G_CALLBACK (send_video_codec_notify_cb), self, 0);
1569 tp_g_signal_connect_object (priv->handler, "notify::recv-audio-codecs",
1570 G_CALLBACK (recv_audio_codecs_notify_cb), self, 0);
1571 tp_g_signal_connect_object (priv->handler, "notify::recv-video-codecs",
1572 G_CALLBACK (recv_video_codecs_notify_cb), self, 0);
1574 tp_g_signal_connect_object (priv->handler, "candidates-changed",
1575 G_CALLBACK (candidates_changed_cb), self, 0);
1578 static void empathy_call_window_dispose (GObject *object);
1579 static void empathy_call_window_finalize (GObject *object);
1582 empathy_call_window_set_property (GObject *object,
1583 guint property_id, const GValue *value, GParamSpec *pspec)
1585 EmpathyCallWindowPriv *priv = GET_PRIV (object);
1587 switch (property_id)
1589 case PROP_CALL_HANDLER:
1590 priv->handler = g_value_dup_object (value);
1593 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
1598 empathy_call_window_get_property (GObject *object,
1599 guint property_id, GValue *value, GParamSpec *pspec)
1601 EmpathyCallWindowPriv *priv = GET_PRIV (object);
1603 switch (property_id)
1605 case PROP_CALL_HANDLER:
1606 g_value_set_object (value, priv->handler);
1609 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
1614 empathy_call_window_class_init (
1615 EmpathyCallWindowClass *empathy_call_window_class)
1617 GObjectClass *object_class = G_OBJECT_CLASS (empathy_call_window_class);
1618 GParamSpec *param_spec;
1620 g_type_class_add_private (empathy_call_window_class,
1621 sizeof (EmpathyCallWindowPriv));
1623 object_class->constructed = empathy_call_window_constructed;
1624 object_class->set_property = empathy_call_window_set_property;
1625 object_class->get_property = empathy_call_window_get_property;
1627 object_class->dispose = empathy_call_window_dispose;
1628 object_class->finalize = empathy_call_window_finalize;
1630 param_spec = g_param_spec_object ("handler",
1631 "handler", "The call handler",
1632 EMPATHY_TYPE_CALL_HANDLER,
1633 G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS);
1634 g_object_class_install_property (object_class,
1635 PROP_CALL_HANDLER, param_spec);
1639 empathy_call_window_dispose (GObject *object)
1641 EmpathyCallWindow *self = EMPATHY_CALL_WINDOW (object);
1642 EmpathyCallWindowPriv *priv = GET_PRIV (self);
1644 if (priv->dispose_has_run)
1647 priv->dispose_has_run = TRUE;
1649 if (priv->handler != NULL)
1651 empathy_call_handler_stop_call (priv->handler);
1652 tp_clear_object (&priv->handler);
1655 if (priv->bus_message_source_id != 0)
1657 g_source_remove (priv->bus_message_source_id);
1658 priv->bus_message_source_id = 0;
1661 if (priv->got_video_src > 0)
1663 g_source_remove (priv->got_video_src);
1664 priv->got_video_src = 0;
1667 tp_clear_object (&priv->pipeline);
1668 tp_clear_object (&priv->video_input);
1669 tp_clear_object (&priv->audio_input);
1670 tp_clear_object (&priv->video_tee);
1671 tp_clear_object (&priv->ui_manager);
1672 tp_clear_object (&priv->fullscreen);
1673 tp_clear_object (&priv->camera_monitor);
1675 g_list_free_full (priv->notifiers, g_object_unref);
1677 if (priv->timer_id != 0)
1678 g_source_remove (priv->timer_id);
1681 if (priv->contact != NULL)
1683 g_signal_handlers_disconnect_by_func (priv->contact,
1684 contact_name_changed_cb, self);
1685 priv->contact = NULL;
1689 tp_clear_object (&priv->sound_mgr);
1691 tp_clear_object (&priv->mic_menu);
1693 G_OBJECT_CLASS (empathy_call_window_parent_class)->dispose (object);
1697 disconnect_video_output_motion_handler (EmpathyCallWindow *self)
1699 EmpathyCallWindowPriv *priv = GET_PRIV (self);
1701 if (priv->video_output_motion_handler_id != 0)
1703 g_signal_handler_disconnect (G_OBJECT (priv->video_container),
1704 priv->video_output_motion_handler_id);
1705 priv->video_output_motion_handler_id = 0;
1710 empathy_call_window_finalize (GObject *object)
1712 EmpathyCallWindow *self = EMPATHY_CALL_WINDOW (object);
1713 EmpathyCallWindowPriv *priv = GET_PRIV (self);
1715 disconnect_video_output_motion_handler (self);
1717 /* free any data held directly by the object here */
1718 g_mutex_free (priv->lock);
1720 g_timer_destroy (priv->timer);
1722 G_OBJECT_CLASS (empathy_call_window_parent_class)->finalize (object);
1727 empathy_call_window_new (EmpathyCallHandler *handler)
1729 return EMPATHY_CALL_WINDOW (
1730 g_object_new (EMPATHY_TYPE_CALL_WINDOW, "handler", handler, NULL));
1734 empathy_call_window_conference_added_cb (EmpathyCallHandler *handler,
1735 GstElement *conference, gpointer user_data)
1737 EmpathyCallWindow *self = EMPATHY_CALL_WINDOW (user_data);
1738 EmpathyCallWindowPriv *priv = GET_PRIV (self);
1739 FsElementAddedNotifier *notifier;
1742 DEBUG ("Conference added");
1744 /* Add notifier to set the various element properties as needed */
1745 notifier = fs_element_added_notifier_new ();
1746 keyfile = fs_utils_get_default_element_properties (conference);
1748 if (keyfile != NULL)
1749 fs_element_added_notifier_set_properties_from_keyfile (notifier, keyfile);
1751 fs_element_added_notifier_add (notifier, GST_BIN (priv->pipeline));
1753 priv->notifiers = g_list_prepend (priv->notifiers, notifier);
1755 gst_bin_add (GST_BIN (priv->pipeline), conference);
1756 gst_element_set_state (conference, GST_STATE_PLAYING);
1760 empathy_call_window_conference_removed_cb (EmpathyCallHandler *handler,
1761 GstElement *conference, gpointer user_data)
1763 EmpathyCallWindow *self = EMPATHY_CALL_WINDOW (user_data);
1764 EmpathyCallWindowPriv *priv = GET_PRIV (self);
1766 gst_bin_remove (GST_BIN (priv->pipeline), conference);
1767 gst_element_set_state (conference, GST_STATE_NULL);
1771 empathy_call_window_reset_pipeline (EmpathyCallWindow *self)
1773 GstStateChangeReturn state_change_return;
1774 EmpathyCallWindowPriv *priv = GET_PRIV (self);
1776 if (priv->pipeline == NULL)
1779 if (priv->bus_message_source_id != 0)
1781 g_source_remove (priv->bus_message_source_id);
1782 priv->bus_message_source_id = 0;
1785 state_change_return = gst_element_set_state (priv->pipeline, GST_STATE_NULL);
1787 if (state_change_return == GST_STATE_CHANGE_SUCCESS ||
1788 state_change_return == GST_STATE_CHANGE_NO_PREROLL)
1790 if (priv->pipeline != NULL)
1791 g_object_unref (priv->pipeline);
1792 priv->pipeline = NULL;
1794 g_signal_handlers_disconnect_by_func (priv->audio_input_adj,
1795 empathy_call_window_mic_volume_changed_cb, self);
1797 if (priv->audio_output != NULL)
1798 g_object_unref (priv->audio_output);
1799 priv->audio_output = NULL;
1801 if (priv->video_tee != NULL)
1802 g_object_unref (priv->video_tee);
1803 priv->video_tee = NULL;
1805 if (priv->video_preview != NULL)
1806 clutter_actor_destroy (priv->video_preview);
1807 priv->video_preview = NULL;
1809 priv->funnel = NULL;
1811 create_pipeline (self);
1812 /* Call will be started when user will hit the 'redial' button */
1813 priv->start_call_when_playing = FALSE;
1814 gst_element_set_state (priv->pipeline, GST_STATE_PAUSED);
1820 g_message ("Error: could not destroy pipeline. Closing call window");
1821 gtk_widget_destroy (GTK_WIDGET (self));
1828 reset_details_pane (EmpathyCallWindow *self)
1830 EmpathyCallWindowPriv *priv = GET_PRIV (self);
1832 gtk_label_set_text (GTK_LABEL (priv->vcodec_encoding_label), _("Unknown"));
1833 gtk_label_set_text (GTK_LABEL (priv->acodec_encoding_label), _("Unknown"));
1834 gtk_label_set_text (GTK_LABEL (priv->vcodec_decoding_label), _("Unknown"));
1835 gtk_label_set_text (GTK_LABEL (priv->acodec_decoding_label), _("Unknown"));
1839 empathy_call_window_disconnected (EmpathyCallWindow *self,
1842 gboolean could_disconnect = FALSE;
1843 EmpathyCallWindowPriv *priv = GET_PRIV (self);
1844 gboolean could_reset_pipeline;
1846 /* Leave full screen mode if needed */
1847 gtk_window_unfullscreen (GTK_WINDOW (self));
1849 gtk_action_set_sensitive (priv->menu_fullscreen, FALSE);
1850 gtk_widget_set_sensitive (priv->dtmf_panel, FALSE);
1852 could_reset_pipeline = empathy_call_window_reset_pipeline (self);
1854 if (priv->call_state == CONNECTING)
1855 empathy_sound_manager_stop (priv->sound_mgr, EMPATHY_SOUND_PHONE_OUTGOING);
1857 if (priv->call_state != REDIALING)
1858 priv->call_state = DISCONNECTED;
1860 if (could_reset_pipeline)
1862 g_mutex_lock (priv->lock);
1864 g_timer_stop (priv->timer);
1866 if (priv->timer_id != 0)
1867 g_source_remove (priv->timer_id);
1870 g_mutex_unlock (priv->lock);
1873 /* We are about to destroy the window, no need to update it or create
1874 * a video preview */
1877 empathy_call_window_status_message (self, _("Disconnected"));
1879 empathy_call_window_show_hangup_button (self, FALSE);
1881 /* Unsensitive the camera and mic button */
1882 gtk_widget_set_sensitive (priv->camera_button, FALSE);
1883 gtk_widget_set_sensitive (priv->mic_button, FALSE);
1885 /* Be sure that the mic button is enabled */
1886 gtk_toggle_tool_button_set_active (
1887 GTK_TOGGLE_TOOL_BUTTON (priv->mic_button), TRUE);
1889 if (priv->camera_state == CAMERA_STATE_ON)
1891 /* Restart the preview with the new pipeline. */
1892 display_video_preview (self, TRUE);
1895 gtk_progress_bar_set_fraction (
1896 GTK_PROGRESS_BAR (priv->volume_progress_bar), 0);
1898 /* destroy the video output; it will be recreated when we'll redial */
1899 disconnect_video_output_motion_handler (self);
1900 if (priv->video_output != NULL)
1901 clutter_actor_destroy (priv->video_output);
1902 priv->video_output = NULL;
1903 if (priv->got_video_src > 0)
1905 g_source_remove (priv->got_video_src);
1906 priv->got_video_src = 0;
1909 gtk_widget_show (priv->remote_user_avatar_widget);
1911 reset_details_pane (self);
1913 priv->sending_video = FALSE;
1914 priv->call_started = FALSE;
1916 could_disconnect = TRUE;
1918 /* TODO: display the self avatar of the preview (depends if the "Always
1919 * Show Video Preview" is enabled or not) */
1922 return could_disconnect;
1927 empathy_call_window_channel_closed_cb (EmpathyCallHandler *handler,
1930 EmpathyCallWindow *self = EMPATHY_CALL_WINDOW (user_data);
1931 EmpathyCallWindowPriv *priv = GET_PRIV (self);
1933 if (empathy_call_window_disconnected (self, TRUE) &&
1934 priv->call_state == REDIALING)
1935 empathy_call_window_restart_call (self);
1939 empathy_call_window_sink_removed_cb (EmpathyCallHandler *handler,
1941 FsMediaType media_type,
1942 EmpathyCallWindow *self)
1944 EmpathyCallWindowPriv *priv = GET_PRIV (self);
1946 DEBUG ("removing content");
1949 * This assumes that there is only one video stream per channel...
1952 if ((guint) media_type == FS_MEDIA_TYPE_VIDEO)
1954 if (priv->funnel != NULL)
1958 output = priv->video_output_sink;
1960 gst_element_set_state (output, GST_STATE_NULL);
1961 gst_element_set_state (priv->funnel, GST_STATE_NULL);
1963 gst_bin_remove (GST_BIN (priv->pipeline), output);
1964 gst_bin_remove (GST_BIN (priv->pipeline), priv->funnel);
1965 priv->funnel = NULL;
1969 else if (media_type == FS_MEDIA_TYPE_AUDIO)
1971 if (priv->audio_output != NULL)
1973 gst_element_set_state (priv->audio_output, GST_STATE_NULL);
1975 gst_bin_remove (GST_BIN (priv->pipeline), priv->audio_output);
1976 priv->audio_output = NULL;
1984 /* Called with global lock held */
1986 empathy_call_window_get_video_sink_pad (EmpathyCallWindow *self)
1988 EmpathyCallWindowPriv *priv = GET_PRIV (self);
1992 if (priv->funnel == NULL)
1994 output = priv->video_output_sink;
1996 priv->funnel = gst_element_factory_make ("fsfunnel", NULL);
2000 g_warning ("Could not create fsfunnel");
2004 if (!gst_bin_add (GST_BIN (priv->pipeline), priv->funnel))
2006 gst_object_unref (priv->funnel);
2007 priv->funnel = NULL;
2008 g_warning ("Could not add funnel to pipeline");
2012 if (!gst_bin_add (GST_BIN (priv->pipeline), output))
2014 g_warning ("Could not add the video output widget to the pipeline");
2018 if (!gst_element_link (priv->funnel, output))
2020 g_warning ("Could not link output sink to funnel");
2021 goto error_output_added;
2024 if (gst_element_set_state (output, GST_STATE_PLAYING) == GST_STATE_CHANGE_FAILURE)
2026 g_warning ("Could not start video sink");
2027 goto error_output_added;
2030 if (gst_element_set_state (priv->funnel, GST_STATE_PLAYING) == GST_STATE_CHANGE_FAILURE)
2032 g_warning ("Could not start funnel");
2033 goto error_output_added;
2037 pad = gst_element_get_request_pad (priv->funnel, "sink%d");
2040 g_warning ("Could not get request pad from funnel");
2047 gst_element_set_locked_state (priv->funnel, TRUE);
2048 gst_element_set_locked_state (output, TRUE);
2050 gst_element_set_state (priv->funnel, GST_STATE_NULL);
2051 gst_element_set_state (output, GST_STATE_NULL);
2053 gst_bin_remove (GST_BIN (priv->pipeline), output);
2054 gst_element_set_locked_state (output, FALSE);
2058 gst_bin_remove (GST_BIN (priv->pipeline), priv->funnel);
2059 priv->funnel = NULL;
2064 /* Called with global lock held */
2066 empathy_call_window_get_audio_sink_pad (EmpathyCallWindow *self)
2068 EmpathyCallWindowPriv *priv = GET_PRIV (self);
2070 GstPadTemplate *template;
2072 if (priv->audio_output == NULL)
2074 priv->audio_output = empathy_audio_sink_new ();
2075 g_object_ref_sink (priv->audio_output);
2077 if (!gst_bin_add (GST_BIN (priv->pipeline), priv->audio_output))
2079 g_warning ("Could not add audio sink to pipeline");
2080 g_object_unref (priv->audio_output);
2081 goto error_add_output;
2084 if (gst_element_set_state (priv->audio_output, GST_STATE_PLAYING) == GST_STATE_CHANGE_FAILURE)
2086 g_warning ("Could not start audio sink");
2091 template = gst_element_class_get_pad_template (
2092 GST_ELEMENT_GET_CLASS (priv->audio_output), "sink%d");
2094 pad = gst_element_request_pad (priv->audio_output,
2095 template, NULL, NULL);
2099 g_warning ("Could not get sink pad from sink");
2106 gst_element_set_locked_state (priv->audio_output, TRUE);
2107 gst_element_set_state (priv->audio_output, GST_STATE_NULL);
2108 gst_bin_remove (GST_BIN (priv->pipeline), priv->audio_output);
2109 priv->audio_output = NULL;
2117 empathy_call_window_update_timer (gpointer user_data)
2119 EmpathyCallWindow *self = EMPATHY_CALL_WINDOW (user_data);
2120 EmpathyCallWindowPriv *priv = GET_PRIV (self);
2124 time_ = g_timer_elapsed (priv->timer, NULL);
2126 /* Translators: 'status - minutes:seconds' the caller has been connected */
2127 str = g_strdup_printf (_("%s — %d:%02dm"),
2128 priv->call_state == HELD ? _("On hold") : _("Connected"),
2129 (int) time_ / 60, (int) time_ % 60);
2130 empathy_call_window_status_message (self, str);
2138 display_error (EmpathyCallWindow *self,
2139 TpyCallChannel *call,
2143 const gchar *details)
2145 EmpathyCallWindowPriv *priv = GET_PRIV (self);
2146 GtkWidget *info_bar;
2147 GtkWidget *content_area;
2154 /* Create info bar */
2155 info_bar = gtk_info_bar_new_with_buttons (GTK_STOCK_CLOSE, GTK_RESPONSE_CLOSE,
2158 gtk_info_bar_set_message_type (GTK_INFO_BAR (info_bar), GTK_MESSAGE_WARNING);
2160 content_area = gtk_info_bar_get_content_area (GTK_INFO_BAR (info_bar));
2162 /* hbox containing the image and the messages vbox */
2163 hbox = gtk_hbox_new (FALSE, 3);
2164 gtk_container_add (GTK_CONTAINER (content_area), hbox);
2167 image = gtk_image_new_from_icon_name (img, GTK_ICON_SIZE_DIALOG);
2168 gtk_box_pack_start (GTK_BOX (hbox), image, FALSE, FALSE, 0);
2170 /* vbox containing the main message and the details expander */
2171 vbox = gtk_vbox_new (FALSE, 3);
2172 gtk_box_pack_start (GTK_BOX (hbox), vbox, TRUE, TRUE, 0);
2175 txt = g_strdup_printf ("<b>%s</b>\n%s", title, desc);
2177 label = gtk_label_new (NULL);
2178 gtk_label_set_markup (GTK_LABEL (label), txt);
2179 gtk_label_set_line_wrap (GTK_LABEL (label), TRUE);
2180 gtk_misc_set_alignment (GTK_MISC (label), 0, 0);
2183 gtk_box_pack_start (GTK_BOX (vbox), label, TRUE, TRUE, 0);
2186 if (details != NULL)
2188 GtkWidget *expander;
2190 expander = gtk_expander_new (_("Technical Details"));
2192 txt = g_strdup_printf ("<i>%s</i>", details);
2194 label = gtk_label_new (NULL);
2195 gtk_label_set_markup (GTK_LABEL (label), txt);
2196 gtk_label_set_line_wrap (GTK_LABEL (label), TRUE);
2197 gtk_misc_set_alignment (GTK_MISC (label), 0, 0);
2200 gtk_container_add (GTK_CONTAINER (expander), label);
2201 gtk_box_pack_start (GTK_BOX (vbox), expander, TRUE, TRUE, 0);
2204 g_signal_connect (info_bar, "response",
2205 G_CALLBACK (gtk_widget_destroy), NULL);
2207 gtk_box_pack_start (GTK_BOX (priv->errors_vbox), info_bar,
2208 FALSE, FALSE, CONTENT_HBOX_CHILDREN_PACKING_PADDING);
2209 gtk_widget_show_all (info_bar);
2213 media_stream_error_to_txt (EmpathyCallWindow *self,
2214 TpyCallChannel *call,
2216 TpMediaStreamError error)
2218 EmpathyCallWindowPriv *priv = GET_PRIV (self);
2219 const gchar *cm = NULL;
2225 case TP_MEDIA_STREAM_ERROR_CODEC_NEGOTIATION_FAILED:
2227 return g_strdup_printf (
2228 _("%s's software does not understand any of the audio formats "
2229 "supported by your computer"),
2230 empathy_contact_get_alias (priv->contact));
2232 return g_strdup_printf (
2233 _("%s's software does not understand any of the video formats "
2234 "supported by your computer"),
2235 empathy_contact_get_alias (priv->contact));
2237 case TP_MEDIA_STREAM_ERROR_CONNECTION_FAILED:
2238 return g_strdup_printf (
2239 _("Can't establish a connection to %s. "
2240 "One of you might be on a network that does not allow "
2241 "direct connections."),
2242 empathy_contact_get_alias (priv->contact));
2244 case TP_MEDIA_STREAM_ERROR_NETWORK_ERROR:
2245 return g_strdup (_("There was a failure on the network"));
2247 case TP_MEDIA_STREAM_ERROR_NO_CODECS:
2249 return g_strdup (_("The audio formats necessary for this call "
2250 "are not installed on your computer"));
2252 return g_strdup (_("The video formats necessary for this call "
2253 "are not installed on your computer"));
2255 case TP_MEDIA_STREAM_ERROR_INVALID_CM_BEHAVIOR:
2256 tp_connection_parse_object_path (
2257 tp_channel_borrow_connection (TP_CHANNEL (call)),
2260 url = g_strdup_printf ("http://bugs.freedesktop.org/enter_bug.cgi?"
2261 "product=Telepathy&component=%s", cm);
2263 result = g_strdup_printf (
2264 _("Something unexpected happened in a Telepathy component. "
2265 "Please <a href=\"%s\">report this bug</a> and attach "
2266 "logs gathered from the 'Debug' window in the Help menu."), url);
2272 case TP_MEDIA_STREAM_ERROR_MEDIA_ERROR:
2273 return g_strdup (_("There was a failure in the call engine"));
2275 case TP_MEDIA_STREAM_ERROR_EOS:
2276 return g_strdup (_("The end of the stream was reached"));
2278 case TP_MEDIA_STREAM_ERROR_UNKNOWN:
2285 empathy_call_window_stream_error (EmpathyCallWindow *self,
2286 TpyCallChannel *call,
2295 desc = media_stream_error_to_txt (self, call, audio, code);
2298 /* No description, use the error message. That's not great as it's not
2299 * localized but it's better than nothing. */
2300 display_error (self, call, icon, title, msg, NULL);
2304 display_error (self, call, icon, title, desc, msg);
2310 empathy_call_window_audio_stream_error (TpyCallChannel *call,
2313 EmpathyCallWindow *self)
2315 empathy_call_window_stream_error (self, call, TRUE, code, msg,
2316 "gnome-stock-mic", _("Can't establish audio stream"));
2320 empathy_call_window_video_stream_error (TpyCallChannel *call,
2323 EmpathyCallWindow *self)
2325 empathy_call_window_stream_error (self, call, FALSE, code, msg,
2326 "camera-web", _("Can't establish video stream"));
2331 empathy_call_window_state_changed_cb (EmpathyCallHandler *handler,
2333 EmpathyCallWindow *self)
2335 EmpathyCallWindowPriv *priv = GET_PRIV (self);
2336 TpyCallChannel *call;
2337 gboolean can_send_video;
2339 if (state != TPY_CALL_STATE_ACCEPTED)
2342 if (priv->call_state == CONNECTED)
2345 g_timer_start (priv->timer);
2346 priv->call_state = CONNECTED;
2348 empathy_sound_manager_stop (priv->sound_mgr, EMPATHY_SOUND_PHONE_OUTGOING);
2350 can_send_video = priv->video_input != NULL &&
2351 empathy_contact_can_voip_video (priv->contact) &&
2352 empathy_camera_monitor_get_available (priv->camera_monitor);
2354 g_object_get (priv->handler, "call-channel", &call, NULL);
2356 if (tpy_call_channel_has_dtmf (call))
2357 gtk_widget_set_sensitive (priv->dtmf_panel, TRUE);
2359 if (priv->video_input == NULL)
2360 empathy_call_window_set_send_video (self, CAMERA_STATE_OFF);
2362 gtk_widget_set_sensitive (priv->camera_button, can_send_video);
2364 empathy_call_window_show_hangup_button (self, TRUE);
2366 gtk_widget_set_sensitive (priv->mic_button, TRUE);
2368 clutter_actor_hide (priv->video_output);
2369 gtk_widget_show (priv->remote_user_avatar_widget);
2371 g_object_unref (call);
2373 g_mutex_lock (priv->lock);
2375 priv->timer_id = g_timeout_add_seconds (1,
2376 empathy_call_window_update_timer, self);
2378 g_mutex_unlock (priv->lock);
2380 empathy_call_window_update_timer (self);
2382 gtk_action_set_sensitive (priv->menu_fullscreen, TRUE);
2386 empathy_call_window_show_video_output_cb (gpointer user_data)
2388 EmpathyCallWindow *self = EMPATHY_CALL_WINDOW (user_data);
2390 if (self->priv->video_output != NULL)
2392 gtk_widget_hide (self->priv->remote_user_avatar_widget);
2393 clutter_actor_show (self->priv->video_output);
2400 empathy_call_window_check_video_cb (gpointer data)
2402 EmpathyCallWindow *self = data;
2404 if (self->priv->got_video)
2406 self->priv->got_video = FALSE;
2410 /* No video in the last N seconds, display the remote avatar */
2411 empathy_call_window_show_video_output (self, FALSE);
2416 /* Called from the streaming thread */
2418 empathy_call_window_video_probe_cb (GstPad *pad,
2419 GstMiniObject *mini_obj,
2420 EmpathyCallWindow *self)
2423 if (GST_IS_EVENT (mini_obj))
2426 if (G_UNLIKELY (!self->priv->got_video))
2428 /* show the remote video */
2429 g_idle_add_full (G_PRIORITY_DEFAULT_IDLE,
2430 empathy_call_window_show_video_output_cb,
2431 g_object_ref (self), g_object_unref);
2433 self->priv->got_video = TRUE;
2439 /* Called from the streaming thread */
2441 empathy_call_window_src_added_cb (EmpathyCallHandler *handler,
2442 GstPad *src, guint media_type, gpointer user_data)
2444 EmpathyCallWindow *self = EMPATHY_CALL_WINDOW (user_data);
2445 EmpathyCallWindowPriv *priv = GET_PRIV (self);
2446 gboolean retval = FALSE;
2450 g_mutex_lock (priv->lock);
2454 case TP_MEDIA_STREAM_TYPE_AUDIO:
2455 pad = empathy_call_window_get_audio_sink_pad (self);
2457 case TP_MEDIA_STREAM_TYPE_VIDEO:
2458 g_idle_add (empathy_call_window_show_video_output_cb, self);
2459 pad = empathy_call_window_get_video_sink_pad (self);
2461 gst_pad_add_data_probe (src,
2462 G_CALLBACK (empathy_call_window_video_probe_cb), self);
2463 if (priv->got_video_src > 0)
2464 g_source_remove (priv->got_video_src);
2465 priv->got_video_src = g_timeout_add_seconds (5,
2466 empathy_call_window_check_video_cb, self);
2469 g_assert_not_reached ();
2475 if (GST_PAD_LINK_FAILED (gst_pad_link (src, pad)))
2476 g_warning ("Could not link %s sink pad",
2477 media_type == TP_MEDIA_STREAM_TYPE_AUDIO ? "audio" : "video");
2481 gst_object_unref (pad);
2485 /* If no sink could be linked, try to add fakesink to prevent the whole call
2490 GstElement *fakesink = gst_element_factory_make ("fakesink", NULL);
2492 if (gst_bin_add (GST_BIN (priv->pipeline), fakesink))
2494 GstPad *sinkpad = gst_element_get_static_pad (fakesink, "sink");
2495 if (gst_element_set_state (fakesink, GST_STATE_PLAYING) == GST_STATE_CHANGE_FAILURE ||
2496 GST_PAD_LINK_FAILED (gst_pad_link (src, sinkpad)))
2498 gst_element_set_locked_state (fakesink, TRUE);
2499 gst_element_set_state (fakesink, GST_STATE_NULL);
2500 gst_bin_remove (GST_BIN (priv->pipeline), fakesink);
2504 DEBUG ("Could not link real sink, linked fakesink instead");
2506 gst_object_unref (sinkpad);
2510 gst_object_unref (fakesink);
2515 g_mutex_unlock (priv->lock);
2521 empathy_call_window_sink_added_cb (EmpathyCallHandler *handler,
2522 GstPad *sink, FsMediaType media_type, gpointer user_data)
2524 EmpathyCallWindow *self = EMPATHY_CALL_WINDOW (user_data);
2525 EmpathyCallWindowPriv *priv = GET_PRIV (self);
2527 gboolean retval = FALSE;
2531 case FS_MEDIA_TYPE_AUDIO:
2532 if (!gst_bin_add (GST_BIN (priv->pipeline), priv->audio_input))
2534 g_warning ("Could not add audio source to pipeline");
2538 pad = gst_element_get_static_pad (priv->audio_input, "src");
2541 gst_bin_remove (GST_BIN (priv->pipeline), priv->audio_input);
2542 g_warning ("Could not get source pad from audio source");
2546 if (GST_PAD_LINK_FAILED (gst_pad_link (pad, sink)))
2548 gst_bin_remove (GST_BIN (priv->pipeline), priv->audio_input);
2549 g_warning ("Could not link audio source to farsight");
2553 if (gst_element_set_state (priv->audio_input, GST_STATE_PLAYING) == GST_STATE_CHANGE_FAILURE)
2555 g_warning ("Could not start audio source");
2556 gst_element_set_state (priv->audio_input, GST_STATE_NULL);
2557 gst_bin_remove (GST_BIN (priv->pipeline), priv->audio_input);
2563 case FS_MEDIA_TYPE_VIDEO:
2564 if (priv->video_tee != NULL)
2566 pad = gst_element_get_request_pad (priv->video_tee, "src%d");
2567 if (GST_PAD_LINK_FAILED (gst_pad_link (pad, sink)))
2569 g_warning ("Could not link video source input pipeline");
2572 gst_object_unref (pad);
2578 g_assert_not_reached ();
2585 empathy_call_window_remove_video_input (EmpathyCallWindow *self)
2587 EmpathyCallWindowPriv *priv = GET_PRIV (self);
2588 GstElement *preview;
2590 disable_camera (self);
2592 DEBUG ("remove video input");
2593 preview = priv->video_preview_sink;
2595 gst_element_set_state (priv->video_input, GST_STATE_NULL);
2596 gst_element_set_state (priv->video_tee, GST_STATE_NULL);
2597 gst_element_set_state (preview, GST_STATE_NULL);
2599 gst_bin_remove_many (GST_BIN (priv->pipeline), priv->video_input,
2602 g_object_unref (priv->video_input);
2603 priv->video_input = NULL;
2604 g_object_unref (priv->video_tee);
2605 priv->video_tee = NULL;
2606 clutter_actor_destroy (priv->video_preview);
2607 priv->video_preview = NULL;
2609 gtk_widget_set_sensitive (priv->camera_button, FALSE);
2613 start_call (EmpathyCallWindow *self)
2615 EmpathyCallWindowPriv *priv = GET_PRIV (self);
2617 priv->call_started = TRUE;
2618 empathy_call_handler_start_call (priv->handler,
2619 gtk_get_current_event_time ());
2621 if (empathy_call_handler_has_initial_video (priv->handler))
2623 TpyCallChannel *call;
2626 g_object_get (priv->handler, "call-channel", &call, NULL);
2627 s = tpy_call_channel_get_video_state (call);
2629 if (s == TPY_SENDING_STATE_PENDING_SEND ||
2630 s == TPY_SENDING_STATE_SENDING)
2632 /* Enable 'send video' buttons and display the preview */
2633 gtk_toggle_tool_button_set_active (
2634 GTK_TOGGLE_TOOL_BUTTON (priv->camera_button), TRUE);
2638 gtk_toggle_tool_button_set_active (
2639 GTK_TOGGLE_TOOL_BUTTON (priv->camera_button), FALSE);
2641 if (priv->video_preview == NULL)
2643 create_video_preview (self);
2644 add_video_preview_to_pipeline (self);
2648 g_object_unref (call);
2653 empathy_call_window_bus_message (GstBus *bus, GstMessage *message,
2656 EmpathyCallWindow *self = EMPATHY_CALL_WINDOW (user_data);
2657 EmpathyCallWindowPriv *priv = GET_PRIV (self);
2660 empathy_call_handler_bus_message (priv->handler, bus, message);
2662 switch (GST_MESSAGE_TYPE (message))
2664 case GST_MESSAGE_STATE_CHANGED:
2665 if (GST_MESSAGE_SRC (message) == GST_OBJECT (priv->video_input))
2667 gst_message_parse_state_changed (message, NULL, &newstate, NULL);
2668 if (newstate == GST_STATE_PAUSED)
2669 empathy_call_window_setup_video_input (self);
2671 if (GST_MESSAGE_SRC (message) == GST_OBJECT (priv->pipeline) &&
2672 !priv->call_started)
2674 gst_message_parse_state_changed (message, NULL, &newstate, NULL);
2675 if (newstate == GST_STATE_PAUSED)
2677 gst_element_set_state (priv->pipeline, GST_STATE_PLAYING);
2678 priv->pipeline_playing = TRUE;
2680 if (priv->start_call_when_playing)
2685 case GST_MESSAGE_ERROR:
2687 GError *error = NULL;
2688 GstElement *gst_error;
2691 gst_message_parse_error (message, &error, &debug);
2692 gst_error = GST_ELEMENT (GST_MESSAGE_SRC (message));
2694 g_message ("Element error: %s -- %s\n", error->message, debug);
2696 if (g_str_has_prefix (gst_element_get_name (gst_error),
2697 VIDEO_INPUT_ERROR_PREFIX))
2699 /* Remove the video input and continue */
2700 if (priv->video_input != NULL)
2701 empathy_call_window_remove_video_input (self);
2702 gst_element_set_state (priv->pipeline, GST_STATE_PLAYING);
2706 empathy_call_window_disconnected (self, TRUE);
2708 g_error_free (error);
2711 case GST_MESSAGE_UNKNOWN:
2712 case GST_MESSAGE_EOS:
2713 case GST_MESSAGE_WARNING:
2714 case GST_MESSAGE_INFO:
2715 case GST_MESSAGE_TAG:
2716 case GST_MESSAGE_BUFFERING:
2717 case GST_MESSAGE_STATE_DIRTY:
2718 case GST_MESSAGE_STEP_DONE:
2719 case GST_MESSAGE_CLOCK_PROVIDE:
2720 case GST_MESSAGE_CLOCK_LOST:
2721 case GST_MESSAGE_NEW_CLOCK:
2722 case GST_MESSAGE_STRUCTURE_CHANGE:
2723 case GST_MESSAGE_STREAM_STATUS:
2724 case GST_MESSAGE_APPLICATION:
2725 case GST_MESSAGE_ELEMENT:
2726 case GST_MESSAGE_SEGMENT_START:
2727 case GST_MESSAGE_SEGMENT_DONE:
2728 case GST_MESSAGE_DURATION:
2729 case GST_MESSAGE_ANY:
2738 empathy_call_window_members_changed_cb (TpyCallChannel *call,
2739 GHashTable *members,
2740 EmpathyCallWindow *self)
2742 EmpathyCallWindowPriv *priv = GET_PRIV (self);
2743 GHashTableIter iter;
2744 gpointer key, value;
2745 gboolean held = FALSE;
2747 g_hash_table_iter_init (&iter, members);
2748 while (g_hash_table_iter_next (&iter, &key, &value))
2750 if (GPOINTER_TO_INT (value) & TPY_CALL_MEMBER_FLAG_HELD)
2752 /* This assumes this is a 1-1 call, otherwise one participant
2753 * putting the call on hold wouldn't mean the call is on hold
2761 priv->call_state = HELD;
2762 else if (priv->call_state == HELD)
2763 priv->call_state = CONNECTED;
2767 call_handler_notify_call_cb (EmpathyCallHandler *handler,
2769 EmpathyCallWindow *self)
2771 EmpathyCallWindowPriv *priv = GET_PRIV (self);
2772 TpyCallChannel *call;
2774 g_object_get (priv->handler, "call-channel", &call, NULL);
2779 tp_g_signal_connect_object (call, "audio-stream-error",
2780 G_CALLBACK (empathy_call_window_audio_stream_error), self, 0);
2781 tp_g_signal_connect_object (call, "video-stream-error",
2782 G_CALLBACK (empathy_call_window_video_stream_error), self, 0);
2785 tp_g_signal_connect_object (call, "members-changed",
2786 G_CALLBACK (empathy_call_window_members_changed_cb), self, 0);
2788 g_object_unref (call);
2792 empathy_call_window_realized_cb (GtkWidget *widget, EmpathyCallWindow *window)
2794 EmpathyCallWindowPriv *priv = GET_PRIV (window);
2795 TpyCallChannel *call;
2798 /* Make the hangup button twice as wide */
2799 width = gtk_widget_get_allocated_width (priv->hangup_button);
2800 gtk_widget_set_size_request (priv->hangup_button, width * 2, -1);
2802 g_signal_connect (priv->handler, "state-changed",
2803 G_CALLBACK (empathy_call_window_state_changed_cb), window);
2804 g_signal_connect (priv->handler, "conference-added",
2805 G_CALLBACK (empathy_call_window_conference_added_cb), window);
2806 g_signal_connect (priv->handler, "conference-removed",
2807 G_CALLBACK (empathy_call_window_conference_removed_cb), window);
2808 g_signal_connect (priv->handler, "closed",
2809 G_CALLBACK (empathy_call_window_channel_closed_cb), window);
2810 g_signal_connect (priv->handler, "src-pad-added",
2811 G_CALLBACK (empathy_call_window_src_added_cb), window);
2812 g_signal_connect (priv->handler, "sink-pad-added",
2813 G_CALLBACK (empathy_call_window_sink_added_cb), window);
2814 g_signal_connect (priv->handler, "sink-pad-removed",
2815 G_CALLBACK (empathy_call_window_sink_removed_cb), window);
2817 g_object_get (priv->handler, "call-channel", &call, NULL);
2820 call_handler_notify_call_cb (priv->handler, NULL, window);
2821 g_object_unref (call);
2825 /* call-channel doesn't exist yet, we'll connect signals once it has been
2827 g_signal_connect (priv->handler, "notify::call-channel",
2828 G_CALLBACK (call_handler_notify_call_cb), window);
2831 gst_element_set_state (priv->pipeline, GST_STATE_PAUSED);
2835 empathy_call_window_delete_cb (GtkWidget *widget, GdkEvent*event,
2836 EmpathyCallWindow *window)
2838 EmpathyCallWindowPriv *priv = GET_PRIV (window);
2840 if (priv->pipeline != NULL)
2842 if (priv->bus_message_source_id != 0)
2844 g_source_remove (priv->bus_message_source_id);
2845 priv->bus_message_source_id = 0;
2848 gst_element_set_state (priv->pipeline, GST_STATE_NULL);
2851 if (priv->call_state == CONNECTING)
2852 empathy_sound_manager_stop (priv->sound_mgr, EMPATHY_SOUND_PHONE_OUTGOING);
2858 show_controls (EmpathyCallWindow *window, gboolean set_fullscreen)
2861 EmpathyCallWindowPriv *priv = GET_PRIV (window);
2863 menu = gtk_ui_manager_get_widget (priv->ui_manager,
2868 gtk_widget_hide (priv->sidebar);
2869 gtk_widget_hide (menu);
2870 gtk_widget_hide (priv->toolbar);
2874 if (priv->sidebar_was_visible_before_fs)
2875 gtk_widget_show (priv->sidebar);
2877 gtk_widget_show (menu);
2878 gtk_widget_show (priv->toolbar);
2880 gtk_window_resize (GTK_WINDOW (window), priv->original_width_before_fs,
2881 priv->original_height_before_fs);
2886 show_borders (EmpathyCallWindow *window, gboolean set_fullscreen)
2888 EmpathyCallWindowPriv *priv = GET_PRIV (window);
2890 gtk_container_set_border_width (GTK_CONTAINER (priv->content_hbox),
2891 set_fullscreen ? 0 : CONTENT_HBOX_BORDER_WIDTH);
2892 gtk_box_set_spacing (GTK_BOX (priv->content_hbox),
2893 set_fullscreen ? 0 : CONTENT_HBOX_SPACING);
2895 if (priv->video_output != NULL)
2898 gtk_box_set_child_packing (GTK_BOX (priv->content_hbox),
2899 priv->video_output, TRUE, TRUE,
2900 set_fullscreen ? 0 : CONTENT_HBOX_CHILDREN_PACKING_PADDING,
2907 empathy_call_window_state_event_cb (GtkWidget *widget,
2908 GdkEventWindowState *event, EmpathyCallWindow *window)
2910 if (event->changed_mask & GDK_WINDOW_STATE_FULLSCREEN)
2912 EmpathyCallWindowPriv *priv = GET_PRIV (window);
2913 gboolean set_fullscreen = event->new_window_state &
2914 GDK_WINDOW_STATE_FULLSCREEN;
2918 gboolean sidebar_was_visible;
2919 GtkAllocation allocation;
2920 gint original_width, original_height;
2922 gtk_widget_get_allocation (GTK_WIDGET (window), &allocation);
2923 original_width = allocation.width;
2924 original_height = allocation.height;
2926 g_object_get (priv->sidebar, "visible", &sidebar_was_visible, NULL);
2928 priv->sidebar_was_visible_before_fs = sidebar_was_visible;
2929 priv->original_width_before_fs = original_width;
2930 priv->original_height_before_fs = original_height;
2932 if (priv->video_output_motion_handler_id == 0 &&
2933 priv->video_output != NULL)
2935 priv->video_output_motion_handler_id = g_signal_connect (
2936 G_OBJECT (priv->video_container), "motion-notify-event",
2937 G_CALLBACK (empathy_call_window_video_output_motion_notify),
2943 disconnect_video_output_motion_handler (window);
2946 empathy_call_window_fullscreen_set_fullscreen (priv->fullscreen,
2948 show_controls (window, set_fullscreen);
2949 show_borders (window, set_fullscreen);
2950 gtk_action_set_stock_id (priv->menu_fullscreen,
2951 (set_fullscreen ? "gtk-leave-fullscreen" : "gtk-fullscreen"));
2952 priv->is_fullscreen = set_fullscreen;
2959 empathy_call_window_update_sidebar_buttons (EmpathyCallWindow *window,
2962 EmpathyCallWindowPriv *priv = GET_PRIV (window);
2964 /* Update dialpad button */
2965 g_signal_handlers_block_by_func (priv->dialpad_button,
2966 empathy_call_window_dialpad_cb, window);
2967 gtk_toggle_tool_button_set_active (
2968 GTK_TOGGLE_TOOL_BUTTON (priv->dialpad_button),
2970 g_signal_handlers_unblock_by_func (priv->dialpad_button,
2971 empathy_call_window_dialpad_cb, window);
2973 /* Update sidebar menu */
2974 g_signal_handlers_block_by_func (priv->menu_sidebar,
2975 empathy_call_window_sidebar_cb, window);
2976 gtk_toggle_action_set_active (
2977 GTK_TOGGLE_ACTION (priv->menu_sidebar),
2978 gtk_widget_get_visible (priv->sidebar));
2979 g_signal_handlers_unblock_by_func (priv->menu_sidebar,
2980 empathy_call_window_sidebar_cb, window);
2984 empathy_call_window_show_sidebar (EmpathyCallWindow *window,
2987 EmpathyCallWindowPriv *priv = GET_PRIV (window);
2988 int w, h, sidebar_width, handle_size;
2989 GtkAllocation allocation;
2991 gboolean dialpad_shown;
2993 gtk_widget_get_allocation (GTK_WIDGET (window), &allocation);
2994 w = allocation.width;
2995 h = allocation.height;
2997 gtk_widget_style_get (priv->pane, "handle_size", &handle_size, NULL);
2999 gtk_widget_get_preferred_width (priv->sidebar, &sidebar_width, NULL);
3003 gtk_widget_show (priv->sidebar);
3004 w += sidebar_width + handle_size;
3008 w -= sidebar_width + handle_size;
3009 gtk_widget_hide (priv->sidebar);
3013 gtk_window_resize (GTK_WINDOW (window), w, h);
3015 /* Update the 'Dialpad' menu */
3016 page = ev_sidebar_get_current_page (EV_SIDEBAR (priv->sidebar));
3017 dialpad_shown = active && !tp_strdiff (page, "dialpad");
3020 empathy_call_window_update_sidebar_buttons (window, dialpad_shown);
3024 empathy_call_window_set_send_video (EmpathyCallWindow *window,
3027 EmpathyCallWindowPriv *priv = GET_PRIV (window);
3028 TpyCallChannel *call;
3030 priv->sending_video = (state == CAMERA_STATE_ON);
3032 if (state == CAMERA_STATE_ON)
3034 /* When we start sending video, we want to show the video preview by
3036 display_video_preview (window, TRUE);
3040 display_video_preview (window, FALSE);
3043 if (priv->call_state != CONNECTED)
3046 g_object_get (priv->handler, "call-channel", &call, NULL);
3047 DEBUG ("%s sending video", priv->sending_video ? "start": "stop");
3048 tpy_call_channel_send_video (call, priv->sending_video);
3049 g_object_unref (call);
3053 empathy_call_window_mic_toggled_cb (GtkToggleToolButton *toggle,
3054 EmpathyCallWindow *window)
3056 EmpathyCallWindowPriv *priv = GET_PRIV (window);
3059 active = (gtk_toggle_tool_button_get_active (toggle));
3063 empathy_audio_src_set_volume (EMPATHY_GST_AUDIO_SRC (priv->audio_input),
3065 gtk_adjustment_set_value (priv->audio_input_adj, priv->volume * 100);
3069 /* TODO, Instead of setting the input volume to 0 we should probably
3070 * stop sending but this would cause the audio call to drop if both
3071 * sides mute at the same time on certain CMs AFAIK. Need to revisit this
3072 * in the future. GNOME #574574
3074 empathy_audio_src_set_volume (EMPATHY_GST_AUDIO_SRC (priv->audio_input),
3076 gtk_adjustment_set_value (priv->audio_input_adj, 0);
3081 empathy_call_window_sidebar_hidden_cb (EvSidebar *sidebar,
3082 EmpathyCallWindow *window)
3084 empathy_call_window_show_sidebar (window, FALSE);
3088 empathy_call_window_sidebar_shown_cb (EvSidebar *sidebar,
3089 EmpathyCallWindow *window)
3091 empathy_call_window_show_sidebar (window, TRUE);
3095 empathy_call_window_sidebar_changed_cb (EvSidebar *sidebar,
3097 EmpathyCallWindow *window)
3099 empathy_call_window_update_sidebar_buttons (window,
3100 !tp_strdiff (page, "dialpad"));
3104 empathy_call_window_hangup_cb (gpointer object,
3105 EmpathyCallWindow *window)
3107 EmpathyCallWindowPriv *priv = GET_PRIV (window);
3109 empathy_call_handler_stop_call (priv->handler);
3111 if (empathy_call_window_disconnected (window, FALSE))
3112 gtk_widget_destroy (GTK_WIDGET (window));
3116 empathy_call_window_restart_call (EmpathyCallWindow *window)
3118 EmpathyCallWindowPriv *priv = GET_PRIV (window);
3120 /* Remove error info bars */
3121 gtk_container_forall (GTK_CONTAINER (priv->errors_vbox),
3122 (GtkCallback) gtk_widget_destroy, NULL);
3124 create_video_output_widget (window);
3126 g_signal_connect (G_OBJECT (priv->audio_input_adj), "value-changed",
3127 G_CALLBACK (empathy_call_window_mic_volume_changed_cb), window);
3129 /* While the call was disconnected, the input volume might have changed.
3130 * However, since the audio_input source was destroyed, its volume has not
3131 * been updated during that time. That's why we manually update it here */
3132 empathy_call_window_mic_volume_changed_cb (priv->audio_input_adj, window);
3134 priv->outgoing = TRUE;
3135 empathy_call_window_set_state_connecting (window);
3137 if (priv->pipeline_playing)
3138 start_call (window);
3140 /* call will be started when the pipeline is ready */
3141 priv->start_call_when_playing = TRUE;
3143 empathy_call_window_setup_avatars (window, priv->handler);
3145 empathy_call_window_show_hangup_button (window, TRUE);
3149 empathy_call_window_dialpad_cb (GtkToggleToolButton *button,
3150 EmpathyCallWindow *window)
3152 EmpathyCallWindowPriv *priv = GET_PRIV (window);
3155 active = gtk_toggle_tool_button_get_active (button);
3158 ev_sidebar_set_current_page (EV_SIDEBAR (priv->sidebar), "dialpad");
3160 empathy_call_window_show_sidebar (window, active);
3164 empathy_call_window_sidebar_cb (GtkToggleAction *menu,
3165 EmpathyCallWindow *self)
3167 empathy_call_window_show_sidebar (self,
3168 gtk_toggle_action_get_active (menu));
3172 empathy_call_window_fullscreen_cb (gpointer object,
3173 EmpathyCallWindow *window)
3175 empathy_call_window_fullscreen_toggle (window);
3179 empathy_call_window_fullscreen_toggle (EmpathyCallWindow *window)
3181 EmpathyCallWindowPriv *priv = GET_PRIV (window);
3183 if (priv->is_fullscreen)
3184 gtk_window_unfullscreen (GTK_WINDOW (window));
3186 gtk_window_fullscreen (GTK_WINDOW (window));
3190 empathy_call_window_video_button_press_cb (GtkWidget *video_preview,
3191 GdkEventButton *event, EmpathyCallWindow *window)
3193 if (event->button == 3 && event->type == GDK_BUTTON_PRESS)
3195 empathy_call_window_video_menu_popup (window, event->button);
3203 empathy_call_window_key_press_cb (GtkWidget *video_output,
3204 GdkEventKey *event, EmpathyCallWindow *window)
3206 EmpathyCallWindowPriv *priv = GET_PRIV (window);
3208 if (priv->is_fullscreen && event->keyval == GDK_KEY_Escape)
3210 /* Since we are in fullscreen mode, toggling will bring us back to
3212 empathy_call_window_fullscreen_toggle (window);
3220 empathy_call_window_video_output_motion_notify (GtkWidget *widget,
3221 GdkEventMotion *event, EmpathyCallWindow *window)
3223 EmpathyCallWindowPriv *priv = GET_PRIV (window);
3225 if (priv->is_fullscreen)
3227 empathy_call_window_fullscreen_show_popup (priv->fullscreen);
3234 empathy_call_window_video_menu_popup (EmpathyCallWindow *window,
3238 EmpathyCallWindowPriv *priv = GET_PRIV (window);
3240 menu = gtk_ui_manager_get_widget (priv->ui_manager,
3242 gtk_menu_popup (GTK_MENU (menu), NULL, NULL, NULL, NULL,
3243 button, gtk_get_current_event_time ());
3244 gtk_menu_shell_select_first (GTK_MENU_SHELL (menu), FALSE);
3248 empathy_call_window_status_message (EmpathyCallWindow *self,
3251 gtk_label_set_label (GTK_LABEL (self->priv->status_label), message);
3255 empathy_call_window_volume_changed_cb (GtkScaleButton *button,
3256 gdouble value, EmpathyCallWindow *window)
3258 EmpathyCallWindowPriv *priv = GET_PRIV (window);
3260 if (priv->audio_output == NULL)
3263 empathy_audio_sink_set_volume (EMPATHY_GST_AUDIO_SINK (priv->audio_output),
3268 empathy_call_window_get_ui_manager (EmpathyCallWindow *window)
3270 EmpathyCallWindowPriv *priv = GET_PRIV (window);
3272 return priv->ui_manager;
3275 EmpathyGstAudioSrc *
3276 empathy_call_window_get_audio_src (EmpathyCallWindow *window)
3278 EmpathyCallWindowPriv *priv = GET_PRIV (window);
3280 return (EmpathyGstAudioSrc *) priv->audio_input;