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 #define DEBUG_FLAG EMPATHY_DEBUG_VOIP
48 #include <libempathy/empathy-debug.h>
50 #include "empathy-call-window.h"
51 #include "empathy-call-window-fullscreen.h"
52 #include "empathy-sidebar.h"
54 #define BUTTON_ID "empathy-call-dtmf-button-id"
56 #define CONTENT_HBOX_BORDER_WIDTH 6
57 #define CONTENT_HBOX_SPACING 3
58 #define CONTENT_HBOX_CHILDREN_PACKING_PADDING 3
60 #define SELF_VIDEO_SECTION_WIDTH 160
61 #define SELF_VIDEO_SECTION_HEIGTH 120
63 /* The avatar's default width and height are set to the same value because we
64 want a square icon. */
65 #define REMOTE_CONTACT_AVATAR_DEFAULT_WIDTH EMPATHY_VIDEO_WIDGET_DEFAULT_HEIGHT
66 #define REMOTE_CONTACT_AVATAR_DEFAULT_HEIGHT \
67 EMPATHY_VIDEO_WIDGET_DEFAULT_HEIGHT
69 /* If an video input error occurs, the error message will start with "v4l" */
70 #define VIDEO_INPUT_ERROR_PREFIX "v4l"
72 /* The time interval in milliseconds between 2 outgoing rings */
73 #define MS_BETWEEN_RING 500
75 G_DEFINE_TYPE(EmpathyCallWindow, empathy_call_window, GTK_TYPE_WINDOW)
84 static guint signals[LAST_SIGNAL] = {0};
88 PROP_CALL_HANDLER = 1,
98 /* private structure */
99 typedef struct _EmpathyCallWindowPriv EmpathyCallWindowPriv;
101 struct _EmpathyCallWindowPriv
103 gboolean dispose_has_run;
104 EmpathyCallHandler *handler;
105 EmpathyContact *contact;
110 GtkUIManager *ui_manager;
111 GtkWidget *errors_vbox;
112 GtkWidget *video_output;
113 GtkWidget *video_preview;
114 GtkWidget *remote_user_avatar_widget;
115 GtkWidget *self_user_avatar_widget;
117 GtkWidget *sidebar_button;
118 GtkWidget *statusbar;
119 GtkWidget *volume_button;
120 GtkWidget *redial_button;
121 GtkWidget *mic_button;
122 GtkWidget *camera_button;
125 GtkAction *always_show_preview;
126 GtkAction *send_video;
128 GtkAction *menu_fullscreen;
130 /* The frames and boxes that contain self and remote avatar and video
131 input/output. When we redial, we destroy and re-create the boxes */
132 GtkWidget *remote_user_output_frame;
133 GtkWidget *self_user_output_frame;
134 GtkWidget *remote_user_output_hbox;
135 GtkWidget *self_user_output_hbox;
137 /* We keep a reference on the hbox which contains the main content so we can
138 easilly repack everything when toggling fullscreen */
139 GtkWidget *content_hbox;
141 /* This vbox is contained in the content_hbox and it contains the
142 self_user_output_frame and the sidebar button. When toggling fullscreen,
143 it needs to be repacked. We keep a reference on it for easier access. */
146 gulong video_output_motion_handler_id;
147 guint bus_message_source_id;
150 GtkWidget *volume_scale;
151 GtkWidget *volume_progress_bar;
152 GtkAdjustment *audio_input_adj;
154 GtkWidget *dtmf_panel;
156 GstElement *video_input;
157 GstElement *audio_input;
158 GstElement *audio_output;
159 GstElement *pipeline;
160 GstElement *video_tee;
163 GstElement *liveadder;
165 FsElementAddedNotifier *fsnotifier;
172 GtkWidget *video_contrast;
173 GtkWidget *video_brightness;
174 GtkWidget *video_gamma;
177 gboolean call_started;
178 gboolean sending_video;
180 EmpathyCallWindowFullscreen *fullscreen;
181 gboolean is_fullscreen;
183 /* Those fields represent the state of the window before it actually was in
185 gboolean sidebar_was_visible_before_fs;
186 gint original_width_before_fs;
187 gint original_height_before_fs;
190 #define GET_PRIV(o) \
191 (G_TYPE_INSTANCE_GET_PRIVATE ((o), EMPATHY_TYPE_CALL_WINDOW, \
192 EmpathyCallWindowPriv))
194 static void empathy_call_window_realized_cb (GtkWidget *widget,
195 EmpathyCallWindow *window);
197 static gboolean empathy_call_window_delete_cb (GtkWidget *widget,
198 GdkEvent *event, EmpathyCallWindow *window);
200 static gboolean empathy_call_window_state_event_cb (GtkWidget *widget,
201 GdkEventWindowState *event, EmpathyCallWindow *window);
203 static void empathy_call_window_sidebar_toggled_cb (GtkToggleButton *toggle,
204 EmpathyCallWindow *window);
206 static void empathy_call_window_camera_toggled_cb (GtkToggleToolButton *toggle,
207 EmpathyCallWindow *window);
209 static void empathy_call_window_set_send_video (EmpathyCallWindow *window,
212 static void empathy_call_window_send_video_toggled_cb (GtkToggleAction *toggle,
213 EmpathyCallWindow *window);
215 static void empathy_call_window_always_show_preview_toggled_cb (
216 GtkToggleAction *toggle, EmpathyCallWindow *window);
218 static void empathy_call_window_mic_toggled_cb (
219 GtkToggleToolButton *toggle, EmpathyCallWindow *window);
221 static void empathy_call_window_sidebar_hidden_cb (EmpathySidebar *sidebar,
222 EmpathyCallWindow *window);
224 static void empathy_call_window_sidebar_shown_cb (EmpathySidebar *sidebar,
225 EmpathyCallWindow *window);
227 static void empathy_call_window_hangup_cb (gpointer object,
228 EmpathyCallWindow *window);
230 static void empathy_call_window_fullscreen_cb (gpointer object,
231 EmpathyCallWindow *window);
233 static void empathy_call_window_fullscreen_toggle (EmpathyCallWindow *window);
235 static gboolean empathy_call_window_video_button_press_cb (
236 GtkWidget *video_output, GdkEventButton *event, EmpathyCallWindow *window);
238 static gboolean empathy_call_window_key_press_cb (GtkWidget *video_output,
239 GdkEventKey *event, EmpathyCallWindow *window);
241 static gboolean empathy_call_window_video_output_motion_notify (
242 GtkWidget *widget, GdkEventMotion *event, EmpathyCallWindow *window);
244 static void empathy_call_window_video_menu_popup (EmpathyCallWindow *window,
247 static void empathy_call_window_redial_cb (gpointer object,
248 EmpathyCallWindow *window);
250 static void empathy_call_window_restart_call (EmpathyCallWindow *window);
252 static void empathy_call_window_status_message (EmpathyCallWindow *window,
255 static void empathy_call_window_update_avatars_visibility (EmpathyTpCall *call,
256 EmpathyCallWindow *window);
258 static gboolean empathy_call_window_bus_message (GstBus *bus,
259 GstMessage *message, gpointer user_data);
262 empathy_call_window_volume_changed_cb (GtkScaleButton *button,
263 gdouble value, EmpathyCallWindow *window);
266 empathy_call_window_setup_toolbar (EmpathyCallWindow *self)
268 EmpathyCallWindowPriv *priv = GET_PRIV (self);
269 GtkToolItem *tool_item;
271 /* Add an empty expanded GtkToolItem so the volume button is at the end of
273 tool_item = gtk_tool_item_new ();
274 gtk_tool_item_set_expand (tool_item, TRUE);
275 gtk_widget_show (GTK_WIDGET (tool_item));
276 gtk_toolbar_insert (GTK_TOOLBAR (priv->toolbar), tool_item, -1);
278 priv->volume_button = gtk_volume_button_new ();
279 /* FIXME listen to the audiosinks signals and update the button according to
280 * that, for now starting out at 1.0 and assuming only the app changes the
282 gtk_scale_button_set_value (GTK_SCALE_BUTTON (priv->volume_button), 1.0);
283 g_signal_connect (G_OBJECT (priv->volume_button), "value-changed",
284 G_CALLBACK (empathy_call_window_volume_changed_cb), self);
286 tool_item = gtk_tool_item_new ();
287 gtk_container_add (GTK_CONTAINER (tool_item), priv->volume_button);
288 gtk_widget_show_all (GTK_WIDGET (tool_item));
289 gtk_toolbar_insert (GTK_TOOLBAR (priv->toolbar), tool_item, -1);
293 dtmf_button_pressed_cb (GtkButton *button, EmpathyCallWindow *window)
295 EmpathyCallWindowPriv *priv = GET_PRIV (window);
300 g_object_get (priv->handler, "tp-call", &call, NULL);
302 button_quark = g_quark_from_static_string (BUTTON_ID);
303 event = GPOINTER_TO_UINT (g_object_get_qdata (G_OBJECT (button),
306 empathy_tp_call_start_tone (call, event);
308 g_object_unref (call);
312 dtmf_button_released_cb (GtkButton *button, EmpathyCallWindow *window)
314 EmpathyCallWindowPriv *priv = GET_PRIV (window);
317 g_object_get (priv->handler, "tp-call", &call, NULL);
319 empathy_tp_call_stop_tone (call);
321 g_object_unref (call);
325 empathy_call_window_create_dtmf (EmpathyCallWindow *self)
333 } dtmfbuttons[] = { { "1", TP_DTMF_EVENT_DIGIT_1 },
334 { "2", TP_DTMF_EVENT_DIGIT_2 },
335 { "3", TP_DTMF_EVENT_DIGIT_3 },
336 { "4", TP_DTMF_EVENT_DIGIT_4 },
337 { "5", TP_DTMF_EVENT_DIGIT_5 },
338 { "6", TP_DTMF_EVENT_DIGIT_6 },
339 { "7", TP_DTMF_EVENT_DIGIT_7 },
340 { "8", TP_DTMF_EVENT_DIGIT_8 },
341 { "9", TP_DTMF_EVENT_DIGIT_9 },
342 { "#", TP_DTMF_EVENT_HASH },
343 { "0", TP_DTMF_EVENT_DIGIT_0 },
344 { "*", TP_DTMF_EVENT_ASTERISK },
347 button_quark = g_quark_from_static_string (BUTTON_ID);
349 table = gtk_table_new (4, 3, TRUE);
351 for (i = 0; dtmfbuttons[i].label != NULL; i++)
353 GtkWidget *button = gtk_button_new_with_label (dtmfbuttons[i].label);
354 gtk_table_attach (GTK_TABLE (table), button, i % 3, i % 3 + 1,
355 i/3, i/3 + 1, GTK_EXPAND | GTK_FILL, GTK_EXPAND | GTK_FILL, 1, 1);
357 g_object_set_qdata (G_OBJECT (button), button_quark,
358 GUINT_TO_POINTER (dtmfbuttons[i].event));
360 g_signal_connect (G_OBJECT (button), "pressed",
361 G_CALLBACK (dtmf_button_pressed_cb), self);
362 g_signal_connect (G_OBJECT (button), "released",
363 G_CALLBACK (dtmf_button_released_cb), self);
370 empathy_call_window_create_video_input_add_slider (EmpathyCallWindow *self,
371 gchar *label_text, GtkWidget *bin)
373 GtkWidget *vbox = gtk_vbox_new (FALSE, 2);
374 GtkWidget *scale = gtk_vscale_new_with_range (0, 100, 10);
375 GtkWidget *label = gtk_label_new (label_text);
377 gtk_widget_set_sensitive (scale, FALSE);
379 gtk_container_add (GTK_CONTAINER (bin), vbox);
381 gtk_range_set_inverted (GTK_RANGE (scale), TRUE);
382 gtk_box_pack_start (GTK_BOX (vbox), scale, TRUE, TRUE, 0);
383 gtk_box_pack_start (GTK_BOX (vbox), label, FALSE, FALSE, 0);
389 empathy_call_window_video_contrast_changed_cb (GtkAdjustment *adj,
390 EmpathyCallWindow *self)
393 EmpathyCallWindowPriv *priv = GET_PRIV (self);
395 empathy_video_src_set_channel (priv->video_input,
396 EMPATHY_GST_VIDEO_SRC_CHANNEL_CONTRAST, gtk_adjustment_get_value (adj));
400 empathy_call_window_video_brightness_changed_cb (GtkAdjustment *adj,
401 EmpathyCallWindow *self)
404 EmpathyCallWindowPriv *priv = GET_PRIV (self);
406 empathy_video_src_set_channel (priv->video_input,
407 EMPATHY_GST_VIDEO_SRC_CHANNEL_BRIGHTNESS, gtk_adjustment_get_value (adj));
411 empathy_call_window_video_gamma_changed_cb (GtkAdjustment *adj,
412 EmpathyCallWindow *self)
415 EmpathyCallWindowPriv *priv = GET_PRIV (self);
417 empathy_video_src_set_channel (priv->video_input,
418 EMPATHY_GST_VIDEO_SRC_CHANNEL_GAMMA, gtk_adjustment_get_value (adj));
423 empathy_call_window_create_video_input (EmpathyCallWindow *self)
425 EmpathyCallWindowPriv *priv = GET_PRIV (self);
428 hbox = gtk_hbox_new (TRUE, 3);
430 priv->video_contrast = empathy_call_window_create_video_input_add_slider (
431 self, _("Contrast"), hbox);
433 priv->video_brightness = empathy_call_window_create_video_input_add_slider (
434 self, _("Brightness"), hbox);
436 priv->video_gamma = empathy_call_window_create_video_input_add_slider (
437 self, _("Gamma"), hbox);
443 empathy_call_window_setup_video_input (EmpathyCallWindow *self)
445 EmpathyCallWindowPriv *priv = GET_PRIV (self);
449 supported = empathy_video_src_get_supported_channels (priv->video_input);
451 if (supported & EMPATHY_GST_VIDEO_SRC_SUPPORTS_CONTRAST)
453 adj = gtk_range_get_adjustment (GTK_RANGE (priv->video_contrast));
455 gtk_adjustment_set_value (adj,
456 empathy_video_src_get_channel (priv->video_input,
457 EMPATHY_GST_VIDEO_SRC_CHANNEL_CONTRAST));
459 g_signal_connect (G_OBJECT (adj), "value-changed",
460 G_CALLBACK (empathy_call_window_video_contrast_changed_cb), self);
462 gtk_widget_set_sensitive (priv->video_contrast, TRUE);
465 if (supported & EMPATHY_GST_VIDEO_SRC_SUPPORTS_BRIGHTNESS)
467 adj = gtk_range_get_adjustment (GTK_RANGE (priv->video_brightness));
469 gtk_adjustment_set_value (adj,
470 empathy_video_src_get_channel (priv->video_input,
471 EMPATHY_GST_VIDEO_SRC_CHANNEL_BRIGHTNESS));
473 g_signal_connect (G_OBJECT (adj), "value-changed",
474 G_CALLBACK (empathy_call_window_video_brightness_changed_cb), self);
475 gtk_widget_set_sensitive (priv->video_brightness, TRUE);
478 if (supported & EMPATHY_GST_VIDEO_SRC_SUPPORTS_GAMMA)
480 adj = gtk_range_get_adjustment (GTK_RANGE (priv->video_gamma));
482 gtk_adjustment_set_value (adj,
483 empathy_video_src_get_channel (priv->video_input,
484 EMPATHY_GST_VIDEO_SRC_CHANNEL_GAMMA));
486 g_signal_connect (G_OBJECT (adj), "value-changed",
487 G_CALLBACK (empathy_call_window_video_gamma_changed_cb), self);
488 gtk_widget_set_sensitive (priv->video_gamma, TRUE);
493 empathy_call_window_mic_volume_changed_cb (GtkAdjustment *adj,
494 EmpathyCallWindow *self)
496 EmpathyCallWindowPriv *priv = GET_PRIV (self);
499 if (priv->audio_input == NULL)
502 volume = gtk_adjustment_get_value (adj)/100.0;
504 /* Don't store the volume because of muting */
505 if (volume > 0 || gtk_toggle_tool_button_get_active (
506 GTK_TOGGLE_TOOL_BUTTON (priv->mic_button)))
507 priv->volume = volume;
509 /* Ensure that the toggle button is active if the volume is > 0 and inactive
510 * if it's smaller than 0 */
511 if ((volume > 0) != gtk_toggle_tool_button_get_active (
512 GTK_TOGGLE_TOOL_BUTTON (priv->mic_button)))
513 gtk_toggle_tool_button_set_active (
514 GTK_TOGGLE_TOOL_BUTTON (priv->mic_button), volume > 0);
516 empathy_audio_src_set_volume (EMPATHY_GST_AUDIO_SRC (priv->audio_input),
521 empathy_call_window_audio_input_level_changed_cb (EmpathyGstAudioSrc *src,
522 gdouble level, EmpathyCallWindow *window)
525 EmpathyCallWindowPriv *priv = GET_PRIV (window);
527 value = CLAMP (pow (10, level / 20), 0.0, 1.0);
528 gtk_progress_bar_set_fraction (GTK_PROGRESS_BAR (priv->volume_progress_bar),
533 empathy_call_window_create_audio_input (EmpathyCallWindow *self)
535 EmpathyCallWindowPriv *priv = GET_PRIV (self);
536 GtkWidget *hbox, *vbox, *label;
538 hbox = gtk_hbox_new (TRUE, 3);
540 vbox = gtk_vbox_new (FALSE, 3);
541 gtk_box_pack_start (GTK_BOX (hbox), vbox, FALSE, FALSE, 3);
543 priv->volume_scale = gtk_vscale_new_with_range (0, 150, 100);
544 gtk_range_set_inverted (GTK_RANGE (priv->volume_scale), TRUE);
545 label = gtk_label_new (_("Volume"));
547 priv->audio_input_adj = gtk_range_get_adjustment (
548 GTK_RANGE (priv->volume_scale));
549 priv->volume = empathy_audio_src_get_volume (EMPATHY_GST_AUDIO_SRC
550 (priv->audio_input));
551 gtk_adjustment_set_value (priv->audio_input_adj, priv->volume * 100);
553 g_signal_connect (G_OBJECT (priv->audio_input_adj), "value-changed",
554 G_CALLBACK (empathy_call_window_mic_volume_changed_cb), self);
556 gtk_box_pack_start (GTK_BOX (vbox), priv->volume_scale, TRUE, TRUE, 3);
557 gtk_box_pack_start (GTK_BOX (vbox), label, FALSE, FALSE, 3);
559 priv->volume_progress_bar = gtk_progress_bar_new ();
560 gtk_progress_bar_set_orientation (
561 GTK_PROGRESS_BAR (priv->volume_progress_bar),
562 GTK_PROGRESS_BOTTOM_TO_TOP);
563 gtk_progress_bar_set_fraction (GTK_PROGRESS_BAR (priv->volume_progress_bar),
566 gtk_box_pack_start (GTK_BOX (hbox), priv->volume_progress_bar, FALSE, FALSE,
573 empathy_call_window_setup_remote_frame (GstBus *bus, EmpathyCallWindow *self)
575 EmpathyCallWindowPriv *priv = GET_PRIV (self);
577 /* Initializing all the content (UI and output gst elements) related to the
579 priv->remote_user_output_hbox = gtk_hbox_new (FALSE, 0);
581 priv->remote_user_avatar_widget = gtk_image_new ();
582 gtk_box_pack_start (GTK_BOX (priv->remote_user_output_hbox),
583 priv->remote_user_avatar_widget, TRUE, TRUE, 0);
585 priv->video_output = empathy_video_widget_new (bus);
586 gtk_box_pack_start (GTK_BOX (priv->remote_user_output_hbox),
587 priv->video_output, TRUE, TRUE, 0);
589 gtk_widget_add_events (priv->video_output,
590 GDK_BUTTON_PRESS_MASK | GDK_POINTER_MOTION_MASK);
591 g_signal_connect (G_OBJECT (priv->video_output), "button-press-event",
592 G_CALLBACK (empathy_call_window_video_button_press_cb), self);
594 gtk_container_add (GTK_CONTAINER (priv->remote_user_output_frame),
595 priv->remote_user_output_hbox);
597 priv->audio_output = empathy_audio_sink_new ();
598 gst_object_ref (priv->audio_output);
599 gst_object_sink (priv->audio_output);
603 empathy_call_window_setup_self_frame (GstBus *bus, EmpathyCallWindow *self)
605 EmpathyCallWindowPriv *priv = GET_PRIV (self);
607 /* Initializing all the content (UI and input gst elements) related to the
608 self contact, except for the video preview widget. This widget is only
609 initialized when the "show video preview" option is activated */
610 priv->self_user_output_hbox = gtk_hbox_new (FALSE, 0);
612 priv->self_user_avatar_widget = gtk_image_new ();
613 gtk_box_pack_start (GTK_BOX (priv->self_user_output_hbox),
614 priv->self_user_avatar_widget, TRUE, TRUE, 0);
616 gtk_container_add (GTK_CONTAINER (priv->self_user_output_frame),
617 priv->self_user_output_hbox);
619 priv->video_input = empathy_video_src_new ();
620 gst_object_ref (priv->video_input);
621 gst_object_sink (priv->video_input);
623 priv->audio_input = empathy_audio_src_new ();
624 gst_object_ref (priv->audio_input);
625 gst_object_sink (priv->audio_input);
627 empathy_signal_connect_weak (priv->audio_input, "peak-level-changed",
628 G_CALLBACK (empathy_call_window_audio_input_level_changed_cb),
633 empathy_call_window_setup_video_preview (EmpathyCallWindow *window)
635 EmpathyCallWindowPriv *priv = GET_PRIV (window);
637 GstBus *bus = gst_pipeline_get_bus (GST_PIPELINE (priv->pipeline));
639 if (priv->video_preview != NULL)
641 /* Since the video preview and the video tee are initialized and freed
642 at the same time, if one is initialized, then the other one should
644 g_assert (priv->video_tee != NULL);
648 g_assert (priv->video_tee == NULL);
650 priv->video_tee = gst_element_factory_make ("tee", NULL);
651 gst_object_ref (priv->video_tee);
652 gst_object_sink (priv->video_tee);
654 priv->video_preview = empathy_video_widget_new_with_size (bus,
655 SELF_VIDEO_SECTION_WIDTH, SELF_VIDEO_SECTION_HEIGTH);
656 g_object_set (priv->video_preview, "sync", FALSE, "async", TRUE, NULL);
657 gtk_box_pack_start (GTK_BOX (priv->self_user_output_hbox),
658 priv->video_preview, TRUE, TRUE, 0);
660 preview = empathy_video_widget_get_element (
661 EMPATHY_VIDEO_WIDGET (priv->video_preview));
662 gst_bin_add_many (GST_BIN (priv->pipeline), priv->video_input,
663 priv->video_tee, preview, NULL);
664 gst_element_link_many (priv->video_input, priv->video_tee,
667 g_object_unref (bus);
669 gst_element_set_state (preview, GST_STATE_PLAYING);
670 gst_element_set_state (priv->video_input, GST_STATE_PLAYING);
671 gst_element_set_state (priv->video_tee, GST_STATE_PLAYING);
675 display_video_preview (EmpathyCallWindow *self,
678 EmpathyCallWindowPriv *priv = GET_PRIV (self);
682 /* Display the preview and hide the self avatar */
683 DEBUG ("Show video preview");
685 if (priv->video_preview == NULL)
686 empathy_call_window_setup_video_preview (self);
687 gtk_widget_show (priv->video_preview);
688 gtk_widget_hide (priv->self_user_avatar_widget);
692 /* Display the self avatar and hide the preview */
693 DEBUG ("Show self avatar");
695 if (priv->video_preview != NULL)
696 gtk_widget_hide (priv->video_preview);
697 gtk_widget_show (priv->self_user_avatar_widget);
702 empathy_call_window_set_state_connecting (EmpathyCallWindow *window)
704 EmpathyCallWindowPriv *priv = GET_PRIV (window);
706 empathy_call_window_status_message (window, _("Connecting..."));
707 priv->call_state = CONNECTING;
710 empathy_sound_start_playing (GTK_WIDGET (window),
711 EMPATHY_SOUND_PHONE_OUTGOING, MS_BETWEEN_RING);
715 empathy_call_window_init (EmpathyCallWindow *self)
717 EmpathyCallWindowPriv *priv = GET_PRIV (self);
726 GError *error = NULL;
728 filename = empathy_file_lookup ("empathy-call-window.ui", "src");
729 gui = empathy_builder_get_file (filename,
730 "call_window_vbox", &top_vbox,
731 "errors_vbox", &priv->errors_vbox,
733 "statusbar", &priv->statusbar,
734 "redial", &priv->redial_button,
735 "microphone", &priv->mic_button,
736 "camera", &priv->camera_button,
737 "toolbar", &priv->toolbar,
738 "send_video", &priv->send_video,
739 "menuredial", &priv->redial,
740 "always_show_preview", &priv->always_show_preview,
741 "ui_manager", &priv->ui_manager,
742 "menufullscreen", &priv->menu_fullscreen,
746 empathy_builder_connect (gui, self,
747 "menuhangup", "activate", empathy_call_window_hangup_cb,
748 "hangup", "clicked", empathy_call_window_hangup_cb,
749 "menuredial", "activate", empathy_call_window_redial_cb,
750 "redial", "clicked", empathy_call_window_redial_cb,
751 "microphone", "toggled", empathy_call_window_mic_toggled_cb,
752 "camera", "toggled", empathy_call_window_camera_toggled_cb,
753 "send_video", "toggled", empathy_call_window_send_video_toggled_cb,
754 "always_show_preview", "toggled",
755 empathy_call_window_always_show_preview_toggled_cb,
756 "menufullscreen", "activate", empathy_call_window_fullscreen_cb,
759 priv->lock = g_mutex_new ();
761 gtk_container_add (GTK_CONTAINER (self), top_vbox);
763 priv->content_hbox = gtk_hbox_new (FALSE, CONTENT_HBOX_SPACING);
764 gtk_container_set_border_width (GTK_CONTAINER (priv->content_hbox),
765 CONTENT_HBOX_BORDER_WIDTH);
766 gtk_paned_pack1 (GTK_PANED (priv->pane), priv->content_hbox, TRUE, FALSE);
768 priv->pipeline = gst_pipeline_new (NULL);
769 bus = gst_pipeline_get_bus (GST_PIPELINE (priv->pipeline));
770 priv->bus_message_source_id = gst_bus_add_watch (bus,
771 empathy_call_window_bus_message, self);
773 priv->fsnotifier = fs_element_added_notifier_new ();
774 fs_element_added_notifier_add (priv->fsnotifier, GST_BIN (priv->pipeline));
776 keyfile = g_key_file_new ();
777 filename = empathy_file_lookup ("element-properties", "data");
778 if (g_key_file_load_from_file (keyfile, filename, G_KEY_FILE_NONE, &error))
780 fs_element_added_notifier_set_properties_from_keyfile (priv->fsnotifier,
785 g_warning ("Could not load element-properties file: %s", error->message);
786 g_key_file_free (keyfile);
787 g_clear_error (&error);
792 priv->remote_user_output_frame = gtk_frame_new (NULL);
793 gtk_widget_set_size_request (priv->remote_user_output_frame,
794 EMPATHY_VIDEO_WIDGET_DEFAULT_WIDTH, EMPATHY_VIDEO_WIDGET_DEFAULT_HEIGHT);
795 gtk_box_pack_start (GTK_BOX (priv->content_hbox),
796 priv->remote_user_output_frame, TRUE, TRUE,
797 CONTENT_HBOX_CHILDREN_PACKING_PADDING);
798 empathy_call_window_setup_remote_frame (bus, self);
800 priv->self_user_output_frame = gtk_frame_new (NULL);
801 gtk_widget_set_size_request (priv->self_user_output_frame,
802 SELF_VIDEO_SECTION_WIDTH, SELF_VIDEO_SECTION_HEIGTH);
804 priv->vbox = gtk_vbox_new (FALSE, 3);
805 gtk_box_pack_start (GTK_BOX (priv->content_hbox), priv->vbox,
806 FALSE, FALSE, CONTENT_HBOX_CHILDREN_PACKING_PADDING);
807 gtk_box_pack_start (GTK_BOX (priv->vbox), priv->self_user_output_frame,
809 empathy_call_window_setup_self_frame (bus, self);
811 empathy_call_window_setup_toolbar (self);
813 g_object_unref (bus);
815 priv->sidebar_button = gtk_toggle_button_new_with_mnemonic (_("_Sidebar"));
816 arrow = gtk_arrow_new (GTK_ARROW_RIGHT, GTK_SHADOW_NONE);
817 g_signal_connect (G_OBJECT (priv->sidebar_button), "toggled",
818 G_CALLBACK (empathy_call_window_sidebar_toggled_cb), self);
820 gtk_button_set_image (GTK_BUTTON (priv->sidebar_button), arrow);
822 h = gtk_hbox_new (FALSE, 3);
823 gtk_box_pack_end (GTK_BOX (priv->vbox), h, FALSE, FALSE, 3);
824 gtk_box_pack_end (GTK_BOX (h), priv->sidebar_button, FALSE, FALSE, 3);
826 priv->sidebar = empathy_sidebar_new ();
827 g_signal_connect (G_OBJECT (priv->sidebar),
828 "hide", G_CALLBACK (empathy_call_window_sidebar_hidden_cb), self);
829 g_signal_connect (G_OBJECT (priv->sidebar),
830 "show", G_CALLBACK (empathy_call_window_sidebar_shown_cb), self);
831 gtk_paned_pack2 (GTK_PANED (priv->pane), priv->sidebar, FALSE, FALSE);
833 priv->dtmf_panel = empathy_call_window_create_dtmf (self);
834 empathy_sidebar_add_page (EMPATHY_SIDEBAR (priv->sidebar), _("Dialpad"),
837 gtk_widget_set_sensitive (priv->dtmf_panel, FALSE);
839 page = empathy_call_window_create_audio_input (self);
840 empathy_sidebar_add_page (EMPATHY_SIDEBAR (priv->sidebar), _("Audio input"),
843 page = empathy_call_window_create_video_input (self);
844 empathy_sidebar_add_page (EMPATHY_SIDEBAR (priv->sidebar), _("Video input"),
847 gtk_widget_show_all (top_vbox);
849 gtk_widget_hide (priv->sidebar);
851 priv->fullscreen = empathy_call_window_fullscreen_new (self);
852 empathy_call_window_fullscreen_set_video_widget (priv->fullscreen,
854 g_signal_connect (G_OBJECT (priv->fullscreen->leave_fullscreen_button),
855 "clicked", G_CALLBACK (empathy_call_window_fullscreen_cb), self);
857 g_signal_connect (G_OBJECT (self), "realize",
858 G_CALLBACK (empathy_call_window_realized_cb), self);
860 g_signal_connect (G_OBJECT (self), "delete-event",
861 G_CALLBACK (empathy_call_window_delete_cb), self);
863 g_signal_connect (G_OBJECT (self), "window-state-event",
864 G_CALLBACK (empathy_call_window_state_event_cb), self);
866 g_signal_connect (G_OBJECT (self), "key-press-event",
867 G_CALLBACK (empathy_call_window_key_press_cb), self);
869 priv->timer = g_timer_new ();
871 g_object_ref (priv->ui_manager);
872 g_object_unref (gui);
875 /* Instead of specifying a width and a height, we specify only one size. That's
876 because we want a square avatar icon. */
878 init_contact_avatar_with_size (EmpathyContact *contact,
879 GtkWidget *image_widget,
882 GdkPixbuf *pixbuf_avatar = NULL;
886 pixbuf_avatar = empathy_pixbuf_avatar_from_contact_scaled (contact,
890 if (pixbuf_avatar == NULL)
892 pixbuf_avatar = empathy_pixbuf_from_icon_name_sized ("stock_person",
896 gtk_image_set_from_pixbuf (GTK_IMAGE (image_widget), pixbuf_avatar);
900 set_window_title (EmpathyCallWindow *self)
902 EmpathyCallWindowPriv *priv = GET_PRIV (self);
905 /* translators: Call is a noun and %s is the contact name. This string
906 * is used in the window title */
907 tmp = g_strdup_printf (_("Call with %s"),
908 empathy_contact_get_name (priv->contact));
909 gtk_window_set_title (GTK_WINDOW (self), tmp);
914 contact_name_changed_cb (EmpathyContact *contact,
915 GParamSpec *pspec, EmpathyCallWindow *self)
917 set_window_title (self);
921 contact_avatar_changed_cb (EmpathyContact *contact,
922 GParamSpec *pspec, GtkWidget *avatar_widget)
926 size = avatar_widget->allocation.height;
930 /* the widget is not allocated yet, set a default size */
931 size = MIN (REMOTE_CONTACT_AVATAR_DEFAULT_HEIGHT,
932 REMOTE_CONTACT_AVATAR_DEFAULT_WIDTH);
935 init_contact_avatar_with_size (contact, avatar_widget, size);
939 empathy_call_window_got_self_contact_cb (EmpathyTpContactFactory *factory,
940 EmpathyContact *contact, const GError *error, gpointer user_data,
941 GObject *weak_object)
943 EmpathyCallWindow *self = EMPATHY_CALL_WINDOW (user_data);
944 EmpathyCallWindowPriv *priv = GET_PRIV (self);
946 init_contact_avatar_with_size (contact, priv->self_user_avatar_widget,
947 MIN (SELF_VIDEO_SECTION_WIDTH, SELF_VIDEO_SECTION_HEIGTH));
949 g_signal_connect (contact, "notify::avatar",
950 G_CALLBACK (contact_avatar_changed_cb), priv->self_user_avatar_widget);
954 empathy_call_window_setup_avatars (EmpathyCallWindow *self,
955 EmpathyCallHandler *handler)
957 EmpathyCallWindowPriv *priv = GET_PRIV (self);
959 g_object_get (handler, "contact", &(priv->contact), NULL);
961 if (priv->contact != NULL)
963 TpConnection *connection;
964 EmpathyTpContactFactory *factory;
966 set_window_title (self);
968 g_signal_connect (priv->contact, "notify::name",
969 G_CALLBACK (contact_name_changed_cb), self);
970 g_signal_connect (priv->contact, "notify::avatar",
971 G_CALLBACK (contact_avatar_changed_cb),
972 priv->remote_user_avatar_widget);
974 /* Retreiving the self avatar */
975 connection = empathy_contact_get_connection (priv->contact);
976 factory = empathy_tp_contact_factory_dup_singleton (connection);
977 empathy_tp_contact_factory_get_from_handle (factory,
978 tp_connection_get_self_handle (connection),
979 empathy_call_window_got_self_contact_cb, self, NULL, G_OBJECT (self));
981 g_object_unref (factory);
985 g_warning ("call handler doesn't have a contact");
986 /* translators: Call is a noun. This string is used in the window
988 gtk_window_set_title (GTK_WINDOW (self), _("Call"));
990 /* Since we can't access the remote contact, we can't get a connection
991 to it and can't get the self contact (and its avatar). This means
992 that we have to manually set the self avatar. */
993 init_contact_avatar_with_size (NULL, priv->self_user_avatar_widget,
994 MIN (SELF_VIDEO_SECTION_WIDTH, SELF_VIDEO_SECTION_HEIGTH));
997 init_contact_avatar_with_size (priv->contact,
998 priv->remote_user_avatar_widget,
999 MIN (REMOTE_CONTACT_AVATAR_DEFAULT_WIDTH,
1000 REMOTE_CONTACT_AVATAR_DEFAULT_HEIGHT));
1002 /* The remote avatar is shown by default and will be hidden when we receive
1003 video from the remote side. */
1004 gtk_widget_hide (priv->video_output);
1005 gtk_widget_show (priv->remote_user_avatar_widget);
1009 empathy_call_window_constructed (GObject *object)
1011 EmpathyCallWindow *self = EMPATHY_CALL_WINDOW (object);
1012 EmpathyCallWindowPriv *priv = GET_PRIV (self);
1013 EmpathyTpCall *call;
1015 g_assert (priv->handler != NULL);
1017 g_object_get (priv->handler, "tp-call", &call, NULL);
1018 priv->outgoing = (call == NULL);
1020 g_object_unref (call);
1022 empathy_call_window_setup_avatars (self, priv->handler);
1023 empathy_call_window_set_state_connecting (self);
1026 static void empathy_call_window_dispose (GObject *object);
1027 static void empathy_call_window_finalize (GObject *object);
1030 empathy_call_window_set_property (GObject *object,
1031 guint property_id, const GValue *value, GParamSpec *pspec)
1033 EmpathyCallWindowPriv *priv = GET_PRIV (object);
1035 switch (property_id)
1037 case PROP_CALL_HANDLER:
1038 priv->handler = g_value_dup_object (value);
1041 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
1046 empathy_call_window_get_property (GObject *object,
1047 guint property_id, GValue *value, GParamSpec *pspec)
1049 EmpathyCallWindowPriv *priv = GET_PRIV (object);
1051 switch (property_id)
1053 case PROP_CALL_HANDLER:
1054 g_value_set_object (value, priv->handler);
1057 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
1062 empathy_call_window_class_init (
1063 EmpathyCallWindowClass *empathy_call_window_class)
1065 GObjectClass *object_class = G_OBJECT_CLASS (empathy_call_window_class);
1066 GParamSpec *param_spec;
1068 g_type_class_add_private (empathy_call_window_class,
1069 sizeof (EmpathyCallWindowPriv));
1071 object_class->constructed = empathy_call_window_constructed;
1072 object_class->set_property = empathy_call_window_set_property;
1073 object_class->get_property = empathy_call_window_get_property;
1075 object_class->dispose = empathy_call_window_dispose;
1076 object_class->finalize = empathy_call_window_finalize;
1078 param_spec = g_param_spec_object ("handler",
1079 "handler", "The call handler",
1080 EMPATHY_TYPE_CALL_HANDLER,
1081 G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS);
1082 g_object_class_install_property (object_class,
1083 PROP_CALL_HANDLER, param_spec);
1087 empathy_call_window_video_stream_changed_cb (EmpathyTpCall *call,
1088 GParamSpec *property, EmpathyCallWindow *self)
1090 empathy_call_window_update_avatars_visibility (call, self);
1094 empathy_call_window_dispose (GObject *object)
1096 EmpathyTpCall *call;
1097 EmpathyCallWindow *self = EMPATHY_CALL_WINDOW (object);
1098 EmpathyCallWindowPriv *priv = GET_PRIV (self);
1100 if (priv->dispose_has_run)
1103 priv->dispose_has_run = TRUE;
1105 g_object_get (priv->handler, "tp-call", &call, NULL);
1109 g_signal_handlers_disconnect_by_func (call,
1110 empathy_call_window_video_stream_changed_cb, object);
1111 g_object_unref (call);
1114 if (priv->handler != NULL)
1115 g_object_unref (priv->handler);
1116 priv->handler = NULL;
1118 if (priv->pipeline != NULL)
1119 g_object_unref (priv->pipeline);
1120 priv->pipeline = NULL;
1122 if (priv->video_input != NULL)
1123 g_object_unref (priv->video_input);
1124 priv->video_input = NULL;
1126 if (priv->audio_input != NULL)
1127 g_object_unref (priv->audio_input);
1128 priv->audio_input = NULL;
1130 if (priv->audio_output != NULL)
1131 g_object_unref (priv->audio_output);
1132 priv->audio_output = NULL;
1134 if (priv->video_tee != NULL)
1135 g_object_unref (priv->video_tee);
1136 priv->video_tee = NULL;
1138 if (priv->fsnotifier != NULL)
1139 g_object_unref (priv->fsnotifier);
1140 priv->fsnotifier = NULL;
1142 if (priv->timer_id != 0)
1143 g_source_remove (priv->timer_id);
1146 if (priv->ui_manager != NULL)
1147 g_object_unref (priv->ui_manager);
1148 priv->ui_manager = NULL;
1150 if (priv->contact != NULL)
1152 g_signal_handlers_disconnect_by_func (priv->contact,
1153 contact_name_changed_cb, self);
1154 g_object_unref (priv->contact);
1155 priv->contact = NULL;
1158 /* release any references held by the object here */
1159 if (G_OBJECT_CLASS (empathy_call_window_parent_class)->dispose)
1160 G_OBJECT_CLASS (empathy_call_window_parent_class)->dispose (object);
1164 empathy_call_window_finalize (GObject *object)
1166 EmpathyCallWindow *self = EMPATHY_CALL_WINDOW (object);
1167 EmpathyCallWindowPriv *priv = GET_PRIV (self);
1169 if (priv->video_output_motion_handler_id != 0)
1171 g_signal_handler_disconnect (G_OBJECT (priv->video_output),
1172 priv->video_output_motion_handler_id);
1173 priv->video_output_motion_handler_id = 0;
1176 if (priv->bus_message_source_id != 0)
1178 g_source_remove (priv->bus_message_source_id);
1179 priv->bus_message_source_id = 0;
1182 /* free any data held directly by the object here */
1183 g_mutex_free (priv->lock);
1185 g_timer_destroy (priv->timer);
1187 G_OBJECT_CLASS (empathy_call_window_parent_class)->finalize (object);
1192 empathy_call_window_new (EmpathyCallHandler *handler)
1194 return EMPATHY_CALL_WINDOW (
1195 g_object_new (EMPATHY_TYPE_CALL_WINDOW, "handler", handler, NULL));
1199 empathy_call_window_conference_added_cb (EmpathyCallHandler *handler,
1200 GstElement *conference, gpointer user_data)
1202 EmpathyCallWindow *self = EMPATHY_CALL_WINDOW (user_data);
1203 EmpathyCallWindowPriv *priv = GET_PRIV (self);
1205 gst_bin_add (GST_BIN (priv->pipeline), conference);
1207 gst_element_set_state (conference, GST_STATE_PLAYING);
1211 empathy_call_window_request_resource_cb (EmpathyCallHandler *handler,
1212 FsMediaType type, FsStreamDirection direction, gpointer user_data)
1214 EmpathyCallWindow *self = EMPATHY_CALL_WINDOW (user_data);
1215 EmpathyCallWindowPriv *priv = GET_PRIV (self);
1217 if (type != TP_MEDIA_STREAM_TYPE_VIDEO)
1220 if (direction == FS_DIRECTION_RECV)
1223 /* video and direction is send */
1224 return priv->video_input != NULL;
1228 empathy_call_window_reset_pipeline (EmpathyCallWindow *self)
1230 GstStateChangeReturn state_change_return;
1231 EmpathyCallWindowPriv *priv = GET_PRIV (self);
1233 if (priv->pipeline == NULL)
1236 if (priv->bus_message_source_id != 0)
1238 g_source_remove (priv->bus_message_source_id);
1239 priv->bus_message_source_id = 0;
1242 state_change_return = gst_element_set_state (priv->pipeline, GST_STATE_NULL);
1244 if (state_change_return == GST_STATE_CHANGE_SUCCESS ||
1245 state_change_return == GST_STATE_CHANGE_NO_PREROLL)
1247 if (priv->pipeline != NULL)
1248 g_object_unref (priv->pipeline);
1249 priv->pipeline = NULL;
1251 if (priv->video_input != NULL)
1252 g_object_unref (priv->video_input);
1253 priv->video_input = NULL;
1255 if (priv->audio_input != NULL)
1256 g_object_unref (priv->audio_input);
1257 priv->audio_input = NULL;
1259 g_signal_handlers_disconnect_by_func (priv->audio_input_adj,
1260 empathy_call_window_mic_volume_changed_cb, self);
1262 if (priv->audio_output != NULL)
1263 g_object_unref (priv->audio_output);
1264 priv->audio_output = NULL;
1266 if (priv->video_tee != NULL)
1267 g_object_unref (priv->video_tee);
1268 priv->video_tee = NULL;
1270 if (priv->video_preview != NULL)
1271 gtk_widget_destroy (priv->video_preview);
1272 priv->video_preview = NULL;
1274 priv->liveadder = NULL;
1275 priv->funnel = NULL;
1281 g_message ("Error: could not destroy pipeline. Closing call window");
1282 gtk_widget_destroy (GTK_WIDGET (self));
1289 empathy_call_window_disconnected (EmpathyCallWindow *self)
1291 gboolean could_disconnect = FALSE;
1292 EmpathyCallWindowPriv *priv = GET_PRIV (self);
1293 gboolean could_reset_pipeline = empathy_call_window_reset_pipeline (self);
1295 if (priv->call_state == CONNECTING)
1296 empathy_sound_stop (EMPATHY_SOUND_PHONE_OUTGOING);
1298 if (priv->call_state != REDIALING)
1299 priv->call_state = DISCONNECTED;
1301 if (could_reset_pipeline)
1303 gboolean initial_video = empathy_call_handler_has_initial_video (
1305 g_mutex_lock (priv->lock);
1307 g_timer_stop (priv->timer);
1309 if (priv->timer_id != 0)
1310 g_source_remove (priv->timer_id);
1313 g_mutex_unlock (priv->lock);
1315 empathy_call_window_status_message (self, _("Disconnected"));
1317 gtk_action_set_sensitive (priv->redial, TRUE);
1318 gtk_widget_set_sensitive (priv->redial_button, TRUE);
1320 /* Reseting the send_video, camera_buton and mic_button to their
1322 gtk_widget_set_sensitive (priv->camera_button, FALSE);
1323 gtk_widget_set_sensitive (priv->mic_button, FALSE);
1324 gtk_action_set_sensitive (priv->send_video, FALSE);
1325 gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (priv->send_video),
1327 gtk_toggle_tool_button_set_active (
1328 GTK_TOGGLE_TOOL_BUTTON (priv->camera_button), initial_video);
1329 gtk_toggle_tool_button_set_active (
1330 GTK_TOGGLE_TOOL_BUTTON (priv->mic_button), TRUE);
1332 gtk_progress_bar_set_fraction (
1333 GTK_PROGRESS_BAR (priv->volume_progress_bar), 0);
1335 gtk_widget_hide (priv->video_output);
1336 gtk_widget_show (priv->remote_user_avatar_widget);
1338 priv->sending_video = FALSE;
1339 priv->call_started = FALSE;
1341 could_disconnect = TRUE;
1344 return could_disconnect;
1349 empathy_call_window_channel_closed_cb (EmpathyCallHandler *handler,
1352 EmpathyCallWindow *self = EMPATHY_CALL_WINDOW (user_data);
1353 EmpathyCallWindowPriv *priv = GET_PRIV (self);
1355 if (empathy_call_window_disconnected (self) && priv->call_state == REDIALING)
1356 empathy_call_window_restart_call (self);
1361 empathy_call_window_channel_stream_closed_cb (EmpathyCallHandler *handler,
1362 TfStream *stream, gpointer user_data)
1364 EmpathyCallWindow *self = EMPATHY_CALL_WINDOW (user_data);
1365 EmpathyCallWindowPriv *priv = GET_PRIV (self);
1368 g_object_get (stream, "media-type", &media_type, NULL);
1371 * This assumes that there is only one video stream per channel...
1374 if (media_type == TP_MEDIA_STREAM_TYPE_VIDEO)
1376 if (priv->funnel != NULL)
1380 output = empathy_video_widget_get_element (EMPATHY_VIDEO_WIDGET
1381 (priv->video_output));
1383 gst_element_set_state (output, GST_STATE_NULL);
1384 gst_element_set_state (priv->funnel, GST_STATE_NULL);
1386 gst_bin_remove (GST_BIN (priv->pipeline), output);
1387 gst_bin_remove (GST_BIN (priv->pipeline), priv->funnel);
1388 priv->funnel = NULL;
1391 else if (media_type == TP_MEDIA_STREAM_TYPE_AUDIO)
1393 if (priv->liveadder != NULL)
1395 gst_element_set_state (priv->audio_output, GST_STATE_NULL);
1396 gst_element_set_state (priv->liveadder, GST_STATE_NULL);
1398 gst_bin_remove (GST_BIN (priv->pipeline), priv->audio_output);
1399 gst_bin_remove (GST_BIN (priv->pipeline), priv->liveadder);
1400 priv->liveadder = NULL;
1405 /* Called with global lock held */
1407 empathy_call_window_get_video_sink_pad (EmpathyCallWindow *self)
1409 EmpathyCallWindowPriv *priv = GET_PRIV (self);
1412 if (priv->funnel == NULL)
1416 output = empathy_video_widget_get_element (EMPATHY_VIDEO_WIDGET
1417 (priv->video_output));
1419 priv->funnel = gst_element_factory_make ("fsfunnel", NULL);
1421 gst_bin_add (GST_BIN (priv->pipeline), priv->funnel);
1422 gst_bin_add (GST_BIN (priv->pipeline), output);
1424 gst_element_link (priv->funnel, output);
1426 gst_element_set_state (priv->funnel, GST_STATE_PLAYING);
1427 gst_element_set_state (output, GST_STATE_PLAYING);
1430 pad = gst_element_get_request_pad (priv->funnel, "sink%d");
1435 /* Called with global lock held */
1437 empathy_call_window_get_audio_sink_pad (EmpathyCallWindow *self)
1439 EmpathyCallWindowPriv *priv = GET_PRIV (self);
1442 if (priv->liveadder == NULL)
1444 priv->liveadder = gst_element_factory_make ("liveadder", NULL);
1446 gst_bin_add (GST_BIN (priv->pipeline), priv->liveadder);
1447 gst_bin_add (GST_BIN (priv->pipeline), priv->audio_output);
1449 gst_element_link (priv->liveadder, priv->audio_output);
1451 gst_element_set_state (priv->liveadder, GST_STATE_PLAYING);
1452 gst_element_set_state (priv->audio_output, GST_STATE_PLAYING);
1455 pad = gst_element_get_request_pad (priv->liveadder, "sink%d");
1461 empathy_call_window_update_timer (gpointer user_data)
1463 EmpathyCallWindow *self = EMPATHY_CALL_WINDOW (user_data);
1464 EmpathyCallWindowPriv *priv = GET_PRIV (self);
1468 time_ = g_timer_elapsed (priv->timer, NULL);
1470 /* Translators: number of minutes:seconds the caller has been connected */
1471 str = g_strdup_printf (_("Connected — %d:%02dm"), (int) time_ / 60,
1473 empathy_call_window_status_message (self, str);
1480 display_error (EmpathyCallWindow *self,
1481 EmpathyTpCall *call,
1485 const gchar *details)
1487 EmpathyCallWindowPriv *priv = GET_PRIV (self);
1488 GtkWidget *info_bar;
1489 GtkWidget *content_area;
1496 /* Create info bar */
1497 info_bar = gtk_info_bar_new_with_buttons (GTK_STOCK_CLOSE, GTK_RESPONSE_CLOSE,
1500 gtk_info_bar_set_message_type (GTK_INFO_BAR (info_bar), GTK_MESSAGE_WARNING);
1502 content_area = gtk_info_bar_get_content_area (GTK_INFO_BAR (info_bar));
1504 /* hbox containing the image and the messages vbox */
1505 hbox = gtk_hbox_new (FALSE, 3);
1506 gtk_container_add (GTK_CONTAINER (content_area), hbox);
1509 image = gtk_image_new_from_icon_name (img, GTK_ICON_SIZE_DIALOG);
1510 gtk_box_pack_start (GTK_BOX (hbox), image, FALSE, FALSE, 0);
1512 /* vbox containing the main message and the details expander */
1513 vbox = gtk_vbox_new (FALSE, 3);
1514 gtk_box_pack_start (GTK_BOX (hbox), vbox, TRUE, TRUE, 0);
1517 txt = g_strdup_printf ("<b>%s</b>\n%s", title, desc);
1519 label = gtk_label_new (NULL);
1520 gtk_label_set_markup (GTK_LABEL (label), txt);
1521 gtk_label_set_line_wrap (GTK_LABEL (label), TRUE);
1522 gtk_misc_set_alignment (GTK_MISC (label), 0, 0);
1525 gtk_box_pack_start (GTK_BOX (vbox), label, TRUE, TRUE, 0);
1528 if (details != NULL)
1530 GtkWidget *expander;
1532 expander = gtk_expander_new (_("Technical Details"));
1534 txt = g_strdup_printf ("<i>%s</i>", details);
1536 label = gtk_label_new (NULL);
1537 gtk_label_set_markup (GTK_LABEL (label), txt);
1538 gtk_label_set_line_wrap (GTK_LABEL (label), TRUE);
1539 gtk_misc_set_alignment (GTK_MISC (label), 0, 0);
1542 gtk_container_add (GTK_CONTAINER (expander), label);
1543 gtk_box_pack_start (GTK_BOX (vbox), expander, TRUE, TRUE, 0);
1546 g_signal_connect (info_bar, "response",
1547 G_CALLBACK (gtk_widget_destroy), NULL);
1549 gtk_box_pack_start (GTK_BOX (priv->errors_vbox), info_bar,
1550 FALSE, FALSE, CONTENT_HBOX_CHILDREN_PACKING_PADDING);
1551 gtk_widget_show_all (info_bar);
1555 media_stream_error_to_txt (EmpathyCallWindow *self,
1556 EmpathyTpCall *call,
1558 TpMediaStreamError error)
1560 EmpathyCallWindowPriv *priv = GET_PRIV (self);
1567 case TP_MEDIA_STREAM_ERROR_CODEC_NEGOTIATION_FAILED:
1569 return g_strdup_printf (
1570 _("%s's software does not understand any of the audio formats "
1571 "supported by your computer"),
1572 empathy_contact_get_name (priv->contact));
1574 return g_strdup_printf (
1575 _("%s's software does not understand any of the video formats "
1576 "supported by your computer"),
1577 empathy_contact_get_name (priv->contact));
1579 case TP_MEDIA_STREAM_ERROR_CONNECTION_FAILED:
1580 return g_strdup_printf (
1581 _("Can't establish a connection to %s. "
1582 "One of you might be on a network that does not allow "
1583 "direct connections."),
1584 empathy_contact_get_name (priv->contact));
1586 case TP_MEDIA_STREAM_ERROR_NETWORK_ERROR:
1587 return g_strdup (_("There was a failure on the network"));
1589 case TP_MEDIA_STREAM_ERROR_NO_CODECS:
1591 return g_strdup (_("The audio formats necessary for this call "
1592 "are not installed on your computer"));
1594 return g_strdup (_("The video formats necessary for this call "
1595 "are not installed on your computer"));
1597 case TP_MEDIA_STREAM_ERROR_INVALID_CM_BEHAVIOR:
1598 cm = empathy_tp_call_get_connection_manager (call);
1600 url = g_strdup_printf ("http://bugs.freedesktop.org/enter_bug.cgi?"
1601 "product=Telepathy&component=%s", cm);
1603 result = g_strdup_printf (
1604 _("Something not expected happened in a Telepathy component. "
1605 "Please <a href=\"%s\">report this bug</a> and attach "
1606 "logs gathered from the 'Debug' window in the Help menu."), url);
1611 case TP_MEDIA_STREAM_ERROR_MEDIA_ERROR:
1612 return g_strdup (_("There was a failure in the call engine"));
1620 empathy_call_window_stream_error (EmpathyCallWindow *self,
1621 EmpathyTpCall *call,
1630 desc = media_stream_error_to_txt (self, call, audio, code);
1633 /* No description, use the error message. That's not great as it's not
1634 * localized but it's better than nothing. */
1635 display_error (self, call, icon, title, msg, NULL);
1639 display_error (self, call, icon, title, desc, msg);
1645 empathy_call_window_audio_stream_error (EmpathyTpCall *call,
1648 EmpathyCallWindow *self)
1650 empathy_call_window_stream_error (self, call, TRUE, code, msg,
1651 "gnome-stock-mic", _("Can't establish audio stream"));
1655 empathy_call_window_video_stream_error (EmpathyTpCall *call,
1658 EmpathyCallWindow *self)
1660 empathy_call_window_stream_error (self, call, FALSE, code, msg,
1661 "camera-web", _("Can't establish video stream"));
1665 empathy_call_window_connected (gpointer user_data)
1667 EmpathyCallWindow *self = EMPATHY_CALL_WINDOW (user_data);
1668 EmpathyCallWindowPriv *priv = GET_PRIV (self);
1669 EmpathyTpCall *call;
1670 gboolean can_send_video;
1672 empathy_sound_stop (EMPATHY_SOUND_PHONE_OUTGOING);
1674 can_send_video = priv->video_input != NULL && priv->contact != NULL &&
1675 empathy_contact_can_voip_video (priv->contact);
1677 g_object_get (priv->handler, "tp-call", &call, NULL);
1679 g_signal_connect (call, "notify::video-stream",
1680 G_CALLBACK (empathy_call_window_video_stream_changed_cb), self);
1682 if (empathy_tp_call_has_dtmf (call))
1683 gtk_widget_set_sensitive (priv->dtmf_panel, TRUE);
1685 if (priv->video_input == NULL)
1686 empathy_call_window_set_send_video (self, FALSE);
1688 priv->sending_video = can_send_video ?
1689 empathy_tp_call_is_sending_video (call) : FALSE;
1691 gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (priv->send_video),
1692 priv->sending_video && priv->video_input != NULL);
1693 gtk_toggle_tool_button_set_active (
1694 GTK_TOGGLE_TOOL_BUTTON (priv->camera_button),
1695 priv->sending_video && priv->video_input != NULL);
1696 gtk_widget_set_sensitive (priv->camera_button, can_send_video);
1697 gtk_action_set_sensitive (priv->send_video, can_send_video);
1699 gtk_action_set_sensitive (priv->redial, FALSE);
1700 gtk_widget_set_sensitive (priv->redial_button, FALSE);
1702 gtk_widget_set_sensitive (priv->mic_button, TRUE);
1704 empathy_call_window_update_avatars_visibility (call, self);
1706 g_object_unref (call);
1708 g_mutex_lock (priv->lock);
1710 priv->timer_id = g_timeout_add_seconds (1,
1711 empathy_call_window_update_timer, self);
1713 g_mutex_unlock (priv->lock);
1715 empathy_call_window_update_timer (self);
1721 /* Called from the streaming thread */
1723 empathy_call_window_src_added_cb (EmpathyCallHandler *handler,
1724 GstPad *src, guint media_type, gpointer user_data)
1726 EmpathyCallWindow *self = EMPATHY_CALL_WINDOW (user_data);
1727 EmpathyCallWindowPriv *priv = GET_PRIV (self);
1731 g_mutex_lock (priv->lock);
1733 if (priv->call_state != CONNECTED)
1735 g_timer_start (priv->timer);
1736 priv->timer_id = g_idle_add (empathy_call_window_connected, self);
1737 priv->call_state = CONNECTED;
1742 case TP_MEDIA_STREAM_TYPE_AUDIO:
1743 pad = empathy_call_window_get_audio_sink_pad (self);
1745 case TP_MEDIA_STREAM_TYPE_VIDEO:
1746 gtk_widget_hide (priv->remote_user_avatar_widget);
1747 gtk_widget_show (priv->video_output);
1748 pad = empathy_call_window_get_video_sink_pad (self);
1751 g_assert_not_reached ();
1754 gst_pad_link (src, pad);
1755 gst_object_unref (pad);
1757 g_mutex_unlock (priv->lock);
1760 /* Called from the streaming thread */
1762 empathy_call_window_sink_added_cb (EmpathyCallHandler *handler,
1763 GstPad *sink, guint media_type, gpointer user_data)
1765 EmpathyCallWindow *self = EMPATHY_CALL_WINDOW (user_data);
1766 EmpathyCallWindowPriv *priv = GET_PRIV (self);
1771 case TP_MEDIA_STREAM_TYPE_AUDIO:
1772 gst_bin_add (GST_BIN (priv->pipeline), priv->audio_input);
1774 pad = gst_element_get_static_pad (priv->audio_input, "src");
1775 gst_pad_link (pad, sink);
1777 gst_element_set_state (priv->audio_input, GST_STATE_PLAYING);
1779 case TP_MEDIA_STREAM_TYPE_VIDEO:
1780 if (priv->video_input != NULL)
1782 EmpathyTpCall *call;
1783 g_object_get (priv->handler, "tp-call", &call, NULL);
1785 if (empathy_tp_call_is_sending_video (call))
1787 display_video_preview (self, TRUE);
1790 g_object_unref (call);
1792 if (priv->video_tee != NULL)
1794 pad = gst_element_get_request_pad (priv->video_tee, "src%d");
1795 gst_pad_link (pad, sink);
1800 g_assert_not_reached ();
1806 empathy_call_window_remove_video_input (EmpathyCallWindow *self)
1808 EmpathyCallWindowPriv *priv = GET_PRIV (self);
1809 GstElement *preview;
1811 preview = empathy_video_widget_get_element (
1812 EMPATHY_VIDEO_WIDGET (priv->video_preview));
1814 gst_element_set_state (priv->video_input, GST_STATE_NULL);
1815 gst_element_set_state (priv->video_tee, GST_STATE_NULL);
1816 gst_element_set_state (preview, GST_STATE_NULL);
1818 gst_bin_remove_many (GST_BIN (priv->pipeline), priv->video_input,
1819 priv->video_tee, preview, NULL);
1821 g_object_unref (priv->video_input);
1822 priv->video_input = NULL;
1823 g_object_unref (priv->video_tee);
1824 priv->video_tee = NULL;
1825 gtk_widget_destroy (priv->video_preview);
1826 priv->video_preview = NULL;
1828 gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (priv->send_video), FALSE);
1829 gtk_toggle_tool_button_set_active (
1830 GTK_TOGGLE_TOOL_BUTTON (priv->camera_button), FALSE);
1831 gtk_widget_set_sensitive (priv->camera_button, FALSE);
1832 gtk_action_set_sensitive (priv->send_video, FALSE);
1834 gtk_widget_show (priv->self_user_avatar_widget);
1839 empathy_call_window_bus_message (GstBus *bus, GstMessage *message,
1842 EmpathyCallWindow *self = EMPATHY_CALL_WINDOW (user_data);
1843 EmpathyCallWindowPriv *priv = GET_PRIV (self);
1846 empathy_call_handler_bus_message (priv->handler, bus, message);
1848 switch (GST_MESSAGE_TYPE (message))
1850 case GST_MESSAGE_STATE_CHANGED:
1851 if (GST_MESSAGE_SRC (message) == GST_OBJECT (priv->video_input))
1853 gst_message_parse_state_changed (message, NULL, &newstate, NULL);
1854 if (newstate == GST_STATE_PAUSED)
1855 empathy_call_window_setup_video_input (self);
1857 if (GST_MESSAGE_SRC (message) == GST_OBJECT (priv->pipeline) &&
1858 !priv->call_started)
1860 gst_message_parse_state_changed (message, NULL, &newstate, NULL);
1861 if (newstate == GST_STATE_PAUSED)
1863 priv->call_started = TRUE;
1864 empathy_call_handler_start_call (priv->handler);
1865 gst_element_set_state (priv->pipeline, GST_STATE_PLAYING);
1869 case GST_MESSAGE_ERROR:
1871 GError *error = NULL;
1872 GstElement *gst_error;
1875 gst_message_parse_error (message, &error, &debug);
1876 gst_error = GST_ELEMENT (GST_MESSAGE_SRC (message));
1878 g_message ("Element error: %s -- %s\n", error->message, debug);
1880 if (g_str_has_prefix (gst_element_get_name (gst_error),
1881 VIDEO_INPUT_ERROR_PREFIX))
1883 /* Remove the video input and continue */
1884 if (priv->video_input != NULL)
1885 empathy_call_window_remove_video_input (self);
1886 gst_element_set_state (priv->pipeline, GST_STATE_PLAYING);
1890 empathy_call_window_disconnected (self);
1892 g_error_free (error);
1903 empathy_call_window_update_avatars_visibility (EmpathyTpCall *call,
1904 EmpathyCallWindow *window)
1906 EmpathyCallWindowPriv *priv = GET_PRIV (window);
1908 if (empathy_tp_call_is_receiving_video (call))
1910 gtk_widget_hide (priv->remote_user_avatar_widget);
1911 gtk_widget_show (priv->video_output);
1915 gtk_widget_hide (priv->video_output);
1916 gtk_widget_show (priv->remote_user_avatar_widget);
1921 call_handler_notify_tp_call_cb (EmpathyCallHandler *handler,
1923 EmpathyCallWindow *self)
1925 EmpathyCallWindowPriv *priv = GET_PRIV (self);
1926 EmpathyTpCall *call;
1928 g_object_get (priv->handler, "tp-call", &call, NULL);
1932 empathy_signal_connect_weak (call, "audio-stream-error",
1933 G_CALLBACK (empathy_call_window_audio_stream_error), G_OBJECT (self));
1934 empathy_signal_connect_weak (call, "video-stream-error",
1935 G_CALLBACK (empathy_call_window_video_stream_error), G_OBJECT (self));
1937 g_object_unref (call);
1941 empathy_call_window_realized_cb (GtkWidget *widget, EmpathyCallWindow *window)
1943 EmpathyCallWindowPriv *priv = GET_PRIV (window);
1944 EmpathyTpCall *call;
1946 g_signal_connect (priv->handler, "conference-added",
1947 G_CALLBACK (empathy_call_window_conference_added_cb), window);
1948 g_signal_connect (priv->handler, "request-resource",
1949 G_CALLBACK (empathy_call_window_request_resource_cb), window);
1950 g_signal_connect (priv->handler, "closed",
1951 G_CALLBACK (empathy_call_window_channel_closed_cb), window);
1952 g_signal_connect (priv->handler, "src-pad-added",
1953 G_CALLBACK (empathy_call_window_src_added_cb), window);
1954 g_signal_connect (priv->handler, "sink-pad-added",
1955 G_CALLBACK (empathy_call_window_sink_added_cb), window);
1956 g_signal_connect (priv->handler, "stream-closed",
1957 G_CALLBACK (empathy_call_window_channel_stream_closed_cb), window);
1959 g_object_get (priv->handler, "tp-call", &call, NULL);
1962 empathy_signal_connect_weak (call, "audio-stream-error",
1963 G_CALLBACK (empathy_call_window_audio_stream_error), G_OBJECT (window));
1964 empathy_signal_connect_weak (call, "video-stream-error",
1965 G_CALLBACK (empathy_call_window_video_stream_error), G_OBJECT (window));
1967 g_object_unref (call);
1971 /* tp-call doesn't exist yet, we'll connect signals once it has been
1973 g_signal_connect (priv->handler, "notify::tp-call",
1974 G_CALLBACK (call_handler_notify_tp_call_cb), window);
1977 gst_element_set_state (priv->pipeline, GST_STATE_PAUSED);
1981 empathy_call_window_delete_cb (GtkWidget *widget, GdkEvent*event,
1982 EmpathyCallWindow *window)
1984 EmpathyCallWindowPriv *priv = GET_PRIV (window);
1986 if (priv->pipeline != NULL)
1988 if (priv->bus_message_source_id != 0)
1990 g_source_remove (priv->bus_message_source_id);
1991 priv->bus_message_source_id = 0;
1994 gst_element_set_state (priv->pipeline, GST_STATE_NULL);
1997 if (priv->call_state == CONNECTING)
1998 empathy_sound_stop (EMPATHY_SOUND_PHONE_OUTGOING);
2004 show_controls (EmpathyCallWindow *window, gboolean set_fullscreen)
2007 EmpathyCallWindowPriv *priv = GET_PRIV (window);
2009 menu = gtk_ui_manager_get_widget (priv->ui_manager,
2014 gtk_widget_hide (priv->sidebar);
2015 gtk_widget_hide (menu);
2016 gtk_widget_hide (priv->vbox);
2017 gtk_widget_hide (priv->statusbar);
2018 gtk_widget_hide (priv->toolbar);
2022 if (priv->sidebar_was_visible_before_fs)
2023 gtk_widget_show (priv->sidebar);
2025 gtk_widget_show (menu);
2026 gtk_widget_show (priv->vbox);
2027 gtk_widget_show (priv->statusbar);
2028 gtk_widget_show (priv->toolbar);
2030 gtk_window_resize (GTK_WINDOW (window), priv->original_width_before_fs,
2031 priv->original_height_before_fs);
2036 show_borders (EmpathyCallWindow *window, gboolean set_fullscreen)
2038 EmpathyCallWindowPriv *priv = GET_PRIV (window);
2040 gtk_container_set_border_width (GTK_CONTAINER (priv->content_hbox),
2041 set_fullscreen ? 0 : CONTENT_HBOX_BORDER_WIDTH);
2042 gtk_box_set_spacing (GTK_BOX (priv->content_hbox),
2043 set_fullscreen ? 0 : CONTENT_HBOX_SPACING);
2044 gtk_box_set_child_packing (GTK_BOX (priv->content_hbox),
2045 priv->video_output, TRUE, TRUE,
2046 set_fullscreen ? 0 : CONTENT_HBOX_CHILDREN_PACKING_PADDING,
2048 gtk_box_set_child_packing (GTK_BOX (priv->content_hbox),
2049 priv->vbox, TRUE, TRUE,
2050 set_fullscreen ? 0 : CONTENT_HBOX_CHILDREN_PACKING_PADDING,
2055 empathy_call_window_state_event_cb (GtkWidget *widget,
2056 GdkEventWindowState *event, EmpathyCallWindow *window)
2058 if (event->changed_mask & GDK_WINDOW_STATE_FULLSCREEN)
2060 EmpathyCallWindowPriv *priv = GET_PRIV (window);
2061 gboolean set_fullscreen = event->new_window_state &
2062 GDK_WINDOW_STATE_FULLSCREEN;
2066 gboolean sidebar_was_visible;
2067 GtkAllocation allocation;
2068 gint original_width, original_height;
2070 gtk_widget_get_allocation (GTK_WIDGET (window), &allocation);
2071 original_width = allocation.width;
2072 original_height = allocation.height;
2074 g_object_get (priv->sidebar, "visible", &sidebar_was_visible, NULL);
2076 priv->sidebar_was_visible_before_fs = sidebar_was_visible;
2077 priv->original_width_before_fs = original_width;
2078 priv->original_height_before_fs = original_height;
2080 if (priv->video_output_motion_handler_id == 0 &&
2081 priv->video_output != NULL)
2083 priv->video_output_motion_handler_id = g_signal_connect (
2084 G_OBJECT (priv->video_output), "motion-notify-event",
2085 G_CALLBACK (empathy_call_window_video_output_motion_notify),
2091 if (priv->video_output_motion_handler_id != 0)
2093 g_signal_handler_disconnect (G_OBJECT (priv->video_output),
2094 priv->video_output_motion_handler_id);
2095 priv->video_output_motion_handler_id = 0;
2099 empathy_call_window_fullscreen_set_fullscreen (priv->fullscreen,
2101 show_controls (window, set_fullscreen);
2102 show_borders (window, set_fullscreen);
2103 gtk_action_set_stock_id (priv->menu_fullscreen,
2104 (set_fullscreen ? "gtk-leave-fullscreen" : "gtk-fullscreen"));
2105 priv->is_fullscreen = set_fullscreen;
2112 empathy_call_window_sidebar_toggled_cb (GtkToggleButton *toggle,
2113 EmpathyCallWindow *window)
2115 EmpathyCallWindowPriv *priv = GET_PRIV (window);
2117 int w, h, handle_size;
2118 GtkAllocation allocation, sidebar_allocation;
2120 gtk_widget_get_allocation (GTK_WIDGET (window), &allocation);
2121 w = allocation.width;
2122 h = allocation.height;
2124 gtk_widget_style_get (priv->pane, "handle_size", &handle_size, NULL);
2126 gtk_widget_get_allocation (priv->sidebar, &sidebar_allocation);
2127 if (gtk_toggle_button_get_active (toggle))
2129 arrow = gtk_arrow_new (GTK_ARROW_LEFT, GTK_SHADOW_NONE);
2130 gtk_widget_show (priv->sidebar);
2131 w += sidebar_allocation.width + handle_size;
2135 arrow = gtk_arrow_new (GTK_ARROW_RIGHT, GTK_SHADOW_NONE);
2136 w -= sidebar_allocation.width + handle_size;
2137 gtk_widget_hide (priv->sidebar);
2140 gtk_button_set_image (GTK_BUTTON (priv->sidebar_button), arrow);
2143 gtk_window_resize (GTK_WINDOW (window), w, h);
2147 empathy_call_window_set_send_video (EmpathyCallWindow *window,
2150 EmpathyCallWindowPriv *priv = GET_PRIV (window);
2151 EmpathyTpCall *call;
2153 priv->sending_video = send;
2155 /* When we start sending video, we want to show the video preview by
2159 display_video_preview (window, TRUE);
2162 g_object_get (priv->handler, "tp-call", &call, NULL);
2163 empathy_tp_call_request_video_stream_direction (call, send);
2164 g_object_unref (call);
2168 empathy_call_window_camera_toggled_cb (GtkToggleToolButton *toggle,
2169 EmpathyCallWindow *window)
2171 EmpathyCallWindowPriv *priv = GET_PRIV (window);
2174 if (priv->call_state != CONNECTED)
2177 active = (gtk_toggle_tool_button_get_active (toggle));
2179 if (priv->sending_video == active)
2182 empathy_call_window_set_send_video (window, active);
2183 gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (priv->send_video), active);
2187 empathy_call_window_send_video_toggled_cb (GtkToggleAction *toggle,
2188 EmpathyCallWindow *window)
2190 EmpathyCallWindowPriv *priv = GET_PRIV (window);
2193 if (priv->call_state != CONNECTED)
2196 active = (gtk_toggle_action_get_active (toggle));
2198 if (priv->sending_video == active)
2201 empathy_call_window_set_send_video (window, active);
2202 gtk_toggle_tool_button_set_active (
2203 GTK_TOGGLE_TOOL_BUTTON (priv->camera_button), active);
2207 empathy_call_window_always_show_preview_toggled_cb (GtkToggleAction *toggle,
2208 EmpathyCallWindow *window)
2210 EmpathyCallWindowPriv *priv = GET_PRIV (window);
2212 if (gtk_toggle_action_get_active (toggle))
2214 display_video_preview (window, TRUE);
2218 /* disable preview if we are not sending */
2219 if (!priv->sending_video)
2220 display_video_preview (window, FALSE);
2225 empathy_call_window_mic_toggled_cb (GtkToggleToolButton *toggle,
2226 EmpathyCallWindow *window)
2228 EmpathyCallWindowPriv *priv = GET_PRIV (window);
2231 if (priv->audio_input == NULL)
2234 active = (gtk_toggle_tool_button_get_active (toggle));
2238 empathy_audio_src_set_volume (EMPATHY_GST_AUDIO_SRC (priv->audio_input),
2240 gtk_adjustment_set_value (priv->audio_input_adj, priv->volume * 100);
2244 /* TODO, Instead of setting the input volume to 0 we should probably
2245 * stop sending but this would cause the audio call to drop if both
2246 * sides mute at the same time on certain CMs AFAIK. Need to revisit this
2247 * in the future. GNOME #574574
2249 empathy_audio_src_set_volume (EMPATHY_GST_AUDIO_SRC (priv->audio_input),
2251 gtk_adjustment_set_value (priv->audio_input_adj, 0);
2256 empathy_call_window_sidebar_hidden_cb (EmpathySidebar *sidebar,
2257 EmpathyCallWindow *window)
2259 EmpathyCallWindowPriv *priv = GET_PRIV (window);
2261 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (priv->sidebar_button),
2266 empathy_call_window_sidebar_shown_cb (EmpathySidebar *sidebar,
2267 EmpathyCallWindow *window)
2269 EmpathyCallWindowPriv *priv = GET_PRIV (window);
2271 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (priv->sidebar_button),
2276 empathy_call_window_hangup_cb (gpointer object,
2277 EmpathyCallWindow *window)
2279 if (empathy_call_window_disconnected (window))
2280 gtk_widget_destroy (GTK_WIDGET (window));
2284 empathy_call_window_restart_call (EmpathyCallWindow *window)
2287 EmpathyCallWindowPriv *priv = GET_PRIV (window);
2289 gtk_widget_destroy (priv->remote_user_output_hbox);
2290 gtk_widget_destroy (priv->self_user_output_hbox);
2292 priv->pipeline = gst_pipeline_new (NULL);
2293 bus = gst_pipeline_get_bus (GST_PIPELINE (priv->pipeline));
2294 priv->bus_message_source_id = gst_bus_add_watch (bus,
2295 empathy_call_window_bus_message, window);
2297 empathy_call_window_setup_remote_frame (bus, window);
2298 empathy_call_window_setup_self_frame (bus, window);
2300 g_signal_connect (G_OBJECT (priv->audio_input_adj), "value-changed",
2301 G_CALLBACK (empathy_call_window_mic_volume_changed_cb), window);
2303 /* While the call was disconnected, the input volume might have changed.
2304 * However, since the audio_input source was destroyed, its volume has not
2305 * been updated during that time. That's why we manually update it here */
2306 empathy_call_window_mic_volume_changed_cb (priv->audio_input_adj, window);
2308 g_object_unref (bus);
2310 gtk_widget_show_all (priv->content_hbox);
2312 priv->outgoing = TRUE;
2313 empathy_call_window_set_state_connecting (window);
2315 priv->call_started = TRUE;
2316 empathy_call_handler_start_call (priv->handler);
2317 empathy_call_window_setup_avatars (window, priv->handler);
2318 gst_element_set_state (priv->pipeline, GST_STATE_PLAYING);
2320 gtk_action_set_sensitive (priv->redial, FALSE);
2321 gtk_widget_set_sensitive (priv->redial_button, FALSE);
2325 empathy_call_window_redial_cb (gpointer object,
2326 EmpathyCallWindow *window)
2328 EmpathyCallWindowPriv *priv = GET_PRIV (window);
2330 if (priv->call_state == CONNECTED)
2331 priv->call_state = REDIALING;
2333 empathy_call_handler_stop_call (priv->handler);
2335 if (priv->call_state != CONNECTED)
2336 empathy_call_window_restart_call (window);
2340 empathy_call_window_fullscreen_cb (gpointer object,
2341 EmpathyCallWindow *window)
2343 empathy_call_window_fullscreen_toggle (window);
2347 empathy_call_window_fullscreen_toggle (EmpathyCallWindow *window)
2349 EmpathyCallWindowPriv *priv = GET_PRIV (window);
2351 if (priv->is_fullscreen)
2352 gtk_window_unfullscreen (GTK_WINDOW (window));
2354 gtk_window_fullscreen (GTK_WINDOW (window));
2358 empathy_call_window_video_button_press_cb (GtkWidget *video_output,
2359 GdkEventButton *event, EmpathyCallWindow *window)
2361 if (event->button == 3 && event->type == GDK_BUTTON_PRESS)
2363 empathy_call_window_video_menu_popup (window, event->button);
2371 empathy_call_window_key_press_cb (GtkWidget *video_output,
2372 GdkEventKey *event, EmpathyCallWindow *window)
2374 EmpathyCallWindowPriv *priv = GET_PRIV (window);
2376 if (priv->is_fullscreen && event->keyval == GDK_Escape)
2378 /* Since we are in fullscreen mode, toggling will bring us back to
2380 empathy_call_window_fullscreen_toggle (window);
2388 empathy_call_window_video_output_motion_notify (GtkWidget *widget,
2389 GdkEventMotion *event, EmpathyCallWindow *window)
2391 EmpathyCallWindowPriv *priv = GET_PRIV (window);
2393 if (priv->is_fullscreen)
2395 empathy_call_window_fullscreen_show_popup (priv->fullscreen);
2402 empathy_call_window_video_menu_popup (EmpathyCallWindow *window,
2406 EmpathyCallWindowPriv *priv = GET_PRIV (window);
2408 menu = gtk_ui_manager_get_widget (priv->ui_manager,
2410 gtk_menu_popup (GTK_MENU (menu), NULL, NULL, NULL, NULL,
2411 button, gtk_get_current_event_time ());
2412 gtk_menu_shell_select_first (GTK_MENU_SHELL (menu), FALSE);
2416 empathy_call_window_status_message (EmpathyCallWindow *window,
2419 EmpathyCallWindowPriv *priv = GET_PRIV (window);
2421 if (priv->context_id == 0)
2423 priv->context_id = gtk_statusbar_get_context_id (
2424 GTK_STATUSBAR (priv->statusbar), "voip call status messages");
2428 gtk_statusbar_pop (GTK_STATUSBAR (priv->statusbar), priv->context_id);
2431 gtk_statusbar_push (GTK_STATUSBAR (priv->statusbar), priv->context_id,
2436 empathy_call_window_volume_changed_cb (GtkScaleButton *button,
2437 gdouble value, EmpathyCallWindow *window)
2439 EmpathyCallWindowPriv *priv = GET_PRIV (window);
2441 if (priv->audio_output == NULL)
2444 empathy_audio_sink_set_volume (EMPATHY_GST_AUDIO_SINK (priv->audio_output),