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);
1025 if (empathy_call_handler_has_initial_video (priv->handler))
1027 /* Enable 'send video' buttons and display the preview */
1028 gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (priv->send_video), TRUE);
1029 gtk_toggle_tool_button_set_active (
1030 GTK_TOGGLE_TOOL_BUTTON (priv->camera_button), TRUE);
1032 display_video_preview (self, TRUE);
1036 static void empathy_call_window_dispose (GObject *object);
1037 static void empathy_call_window_finalize (GObject *object);
1040 empathy_call_window_set_property (GObject *object,
1041 guint property_id, const GValue *value, GParamSpec *pspec)
1043 EmpathyCallWindowPriv *priv = GET_PRIV (object);
1045 switch (property_id)
1047 case PROP_CALL_HANDLER:
1048 priv->handler = g_value_dup_object (value);
1051 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
1056 empathy_call_window_get_property (GObject *object,
1057 guint property_id, GValue *value, GParamSpec *pspec)
1059 EmpathyCallWindowPriv *priv = GET_PRIV (object);
1061 switch (property_id)
1063 case PROP_CALL_HANDLER:
1064 g_value_set_object (value, priv->handler);
1067 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
1072 empathy_call_window_class_init (
1073 EmpathyCallWindowClass *empathy_call_window_class)
1075 GObjectClass *object_class = G_OBJECT_CLASS (empathy_call_window_class);
1076 GParamSpec *param_spec;
1078 g_type_class_add_private (empathy_call_window_class,
1079 sizeof (EmpathyCallWindowPriv));
1081 object_class->constructed = empathy_call_window_constructed;
1082 object_class->set_property = empathy_call_window_set_property;
1083 object_class->get_property = empathy_call_window_get_property;
1085 object_class->dispose = empathy_call_window_dispose;
1086 object_class->finalize = empathy_call_window_finalize;
1088 param_spec = g_param_spec_object ("handler",
1089 "handler", "The call handler",
1090 EMPATHY_TYPE_CALL_HANDLER,
1091 G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS);
1092 g_object_class_install_property (object_class,
1093 PROP_CALL_HANDLER, param_spec);
1097 empathy_call_window_video_stream_changed_cb (EmpathyTpCall *call,
1098 GParamSpec *property, EmpathyCallWindow *self)
1100 empathy_call_window_update_avatars_visibility (call, self);
1104 empathy_call_window_dispose (GObject *object)
1106 EmpathyTpCall *call;
1107 EmpathyCallWindow *self = EMPATHY_CALL_WINDOW (object);
1108 EmpathyCallWindowPriv *priv = GET_PRIV (self);
1110 if (priv->dispose_has_run)
1113 priv->dispose_has_run = TRUE;
1115 g_object_get (priv->handler, "tp-call", &call, NULL);
1119 g_signal_handlers_disconnect_by_func (call,
1120 empathy_call_window_video_stream_changed_cb, object);
1121 g_object_unref (call);
1124 if (priv->handler != NULL)
1125 g_object_unref (priv->handler);
1126 priv->handler = NULL;
1128 if (priv->pipeline != NULL)
1129 g_object_unref (priv->pipeline);
1130 priv->pipeline = NULL;
1132 if (priv->video_input != NULL)
1133 g_object_unref (priv->video_input);
1134 priv->video_input = NULL;
1136 if (priv->audio_input != NULL)
1137 g_object_unref (priv->audio_input);
1138 priv->audio_input = NULL;
1140 if (priv->audio_output != NULL)
1141 g_object_unref (priv->audio_output);
1142 priv->audio_output = NULL;
1144 if (priv->video_tee != NULL)
1145 g_object_unref (priv->video_tee);
1146 priv->video_tee = NULL;
1148 if (priv->fsnotifier != NULL)
1149 g_object_unref (priv->fsnotifier);
1150 priv->fsnotifier = NULL;
1152 if (priv->timer_id != 0)
1153 g_source_remove (priv->timer_id);
1156 if (priv->ui_manager != NULL)
1157 g_object_unref (priv->ui_manager);
1158 priv->ui_manager = NULL;
1160 if (priv->contact != NULL)
1162 g_signal_handlers_disconnect_by_func (priv->contact,
1163 contact_name_changed_cb, self);
1164 g_object_unref (priv->contact);
1165 priv->contact = NULL;
1168 /* release any references held by the object here */
1169 if (G_OBJECT_CLASS (empathy_call_window_parent_class)->dispose)
1170 G_OBJECT_CLASS (empathy_call_window_parent_class)->dispose (object);
1174 empathy_call_window_finalize (GObject *object)
1176 EmpathyCallWindow *self = EMPATHY_CALL_WINDOW (object);
1177 EmpathyCallWindowPriv *priv = GET_PRIV (self);
1179 if (priv->video_output_motion_handler_id != 0)
1181 g_signal_handler_disconnect (G_OBJECT (priv->video_output),
1182 priv->video_output_motion_handler_id);
1183 priv->video_output_motion_handler_id = 0;
1186 if (priv->bus_message_source_id != 0)
1188 g_source_remove (priv->bus_message_source_id);
1189 priv->bus_message_source_id = 0;
1192 /* free any data held directly by the object here */
1193 g_mutex_free (priv->lock);
1195 g_timer_destroy (priv->timer);
1197 G_OBJECT_CLASS (empathy_call_window_parent_class)->finalize (object);
1202 empathy_call_window_new (EmpathyCallHandler *handler)
1204 return EMPATHY_CALL_WINDOW (
1205 g_object_new (EMPATHY_TYPE_CALL_WINDOW, "handler", handler, NULL));
1209 empathy_call_window_conference_added_cb (EmpathyCallHandler *handler,
1210 GstElement *conference, gpointer user_data)
1212 EmpathyCallWindow *self = EMPATHY_CALL_WINDOW (user_data);
1213 EmpathyCallWindowPriv *priv = GET_PRIV (self);
1215 gst_bin_add (GST_BIN (priv->pipeline), conference);
1217 gst_element_set_state (conference, GST_STATE_PLAYING);
1221 empathy_call_window_request_resource_cb (EmpathyCallHandler *handler,
1222 FsMediaType type, FsStreamDirection direction, gpointer user_data)
1224 EmpathyCallWindow *self = EMPATHY_CALL_WINDOW (user_data);
1225 EmpathyCallWindowPriv *priv = GET_PRIV (self);
1227 if (type != TP_MEDIA_STREAM_TYPE_VIDEO)
1230 if (direction == FS_DIRECTION_RECV)
1233 /* video and direction is send */
1234 return priv->video_input != NULL;
1238 empathy_call_window_reset_pipeline (EmpathyCallWindow *self)
1240 GstStateChangeReturn state_change_return;
1241 EmpathyCallWindowPriv *priv = GET_PRIV (self);
1243 if (priv->pipeline == NULL)
1246 if (priv->bus_message_source_id != 0)
1248 g_source_remove (priv->bus_message_source_id);
1249 priv->bus_message_source_id = 0;
1252 state_change_return = gst_element_set_state (priv->pipeline, GST_STATE_NULL);
1254 if (state_change_return == GST_STATE_CHANGE_SUCCESS ||
1255 state_change_return == GST_STATE_CHANGE_NO_PREROLL)
1257 if (priv->pipeline != NULL)
1258 g_object_unref (priv->pipeline);
1259 priv->pipeline = NULL;
1261 if (priv->video_input != NULL)
1262 g_object_unref (priv->video_input);
1263 priv->video_input = NULL;
1265 if (priv->audio_input != NULL)
1266 g_object_unref (priv->audio_input);
1267 priv->audio_input = NULL;
1269 g_signal_handlers_disconnect_by_func (priv->audio_input_adj,
1270 empathy_call_window_mic_volume_changed_cb, self);
1272 if (priv->audio_output != NULL)
1273 g_object_unref (priv->audio_output);
1274 priv->audio_output = NULL;
1276 if (priv->video_tee != NULL)
1277 g_object_unref (priv->video_tee);
1278 priv->video_tee = NULL;
1280 if (priv->video_preview != NULL)
1281 gtk_widget_destroy (priv->video_preview);
1282 priv->video_preview = NULL;
1284 priv->liveadder = NULL;
1285 priv->funnel = NULL;
1291 g_message ("Error: could not destroy pipeline. Closing call window");
1292 gtk_widget_destroy (GTK_WIDGET (self));
1299 empathy_call_window_disconnected (EmpathyCallWindow *self)
1301 gboolean could_disconnect = FALSE;
1302 EmpathyCallWindowPriv *priv = GET_PRIV (self);
1303 gboolean could_reset_pipeline = empathy_call_window_reset_pipeline (self);
1305 if (priv->call_state == CONNECTING)
1306 empathy_sound_stop (EMPATHY_SOUND_PHONE_OUTGOING);
1308 if (priv->call_state != REDIALING)
1309 priv->call_state = DISCONNECTED;
1311 if (could_reset_pipeline)
1313 gboolean initial_video = empathy_call_handler_has_initial_video (
1315 g_mutex_lock (priv->lock);
1317 g_timer_stop (priv->timer);
1319 if (priv->timer_id != 0)
1320 g_source_remove (priv->timer_id);
1323 g_mutex_unlock (priv->lock);
1325 empathy_call_window_status_message (self, _("Disconnected"));
1327 gtk_action_set_sensitive (priv->redial, TRUE);
1328 gtk_widget_set_sensitive (priv->redial_button, TRUE);
1330 /* Reseting the send_video, camera_buton and mic_button to their
1332 gtk_widget_set_sensitive (priv->camera_button, FALSE);
1333 gtk_widget_set_sensitive (priv->mic_button, FALSE);
1334 gtk_action_set_sensitive (priv->send_video, FALSE);
1335 gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (priv->send_video),
1337 gtk_toggle_tool_button_set_active (
1338 GTK_TOGGLE_TOOL_BUTTON (priv->camera_button), initial_video);
1339 gtk_toggle_tool_button_set_active (
1340 GTK_TOGGLE_TOOL_BUTTON (priv->mic_button), TRUE);
1342 gtk_progress_bar_set_fraction (
1343 GTK_PROGRESS_BAR (priv->volume_progress_bar), 0);
1345 gtk_widget_hide (priv->video_output);
1346 gtk_widget_show (priv->remote_user_avatar_widget);
1348 priv->sending_video = FALSE;
1349 priv->call_started = FALSE;
1351 could_disconnect = TRUE;
1354 return could_disconnect;
1359 empathy_call_window_channel_closed_cb (EmpathyCallHandler *handler,
1362 EmpathyCallWindow *self = EMPATHY_CALL_WINDOW (user_data);
1363 EmpathyCallWindowPriv *priv = GET_PRIV (self);
1365 if (empathy_call_window_disconnected (self) && priv->call_state == REDIALING)
1366 empathy_call_window_restart_call (self);
1371 empathy_call_window_channel_stream_closed_cb (EmpathyCallHandler *handler,
1372 TfStream *stream, gpointer user_data)
1374 EmpathyCallWindow *self = EMPATHY_CALL_WINDOW (user_data);
1375 EmpathyCallWindowPriv *priv = GET_PRIV (self);
1378 g_object_get (stream, "media-type", &media_type, NULL);
1381 * This assumes that there is only one video stream per channel...
1384 if (media_type == TP_MEDIA_STREAM_TYPE_VIDEO)
1386 if (priv->funnel != NULL)
1390 output = empathy_video_widget_get_element (EMPATHY_VIDEO_WIDGET
1391 (priv->video_output));
1393 gst_element_set_state (output, GST_STATE_NULL);
1394 gst_element_set_state (priv->funnel, GST_STATE_NULL);
1396 gst_bin_remove (GST_BIN (priv->pipeline), output);
1397 gst_bin_remove (GST_BIN (priv->pipeline), priv->funnel);
1398 priv->funnel = NULL;
1401 else if (media_type == TP_MEDIA_STREAM_TYPE_AUDIO)
1403 if (priv->liveadder != NULL)
1405 gst_element_set_state (priv->audio_output, GST_STATE_NULL);
1406 gst_element_set_state (priv->liveadder, GST_STATE_NULL);
1408 gst_bin_remove (GST_BIN (priv->pipeline), priv->audio_output);
1409 gst_bin_remove (GST_BIN (priv->pipeline), priv->liveadder);
1410 priv->liveadder = NULL;
1415 /* Called with global lock held */
1417 empathy_call_window_get_video_sink_pad (EmpathyCallWindow *self)
1419 EmpathyCallWindowPriv *priv = GET_PRIV (self);
1422 if (priv->funnel == NULL)
1426 output = empathy_video_widget_get_element (EMPATHY_VIDEO_WIDGET
1427 (priv->video_output));
1429 priv->funnel = gst_element_factory_make ("fsfunnel", NULL);
1431 gst_bin_add (GST_BIN (priv->pipeline), priv->funnel);
1432 gst_bin_add (GST_BIN (priv->pipeline), output);
1434 gst_element_link (priv->funnel, output);
1436 gst_element_set_state (priv->funnel, GST_STATE_PLAYING);
1437 gst_element_set_state (output, GST_STATE_PLAYING);
1440 pad = gst_element_get_request_pad (priv->funnel, "sink%d");
1445 /* Called with global lock held */
1447 empathy_call_window_get_audio_sink_pad (EmpathyCallWindow *self)
1449 EmpathyCallWindowPriv *priv = GET_PRIV (self);
1452 if (priv->liveadder == NULL)
1454 priv->liveadder = gst_element_factory_make ("liveadder", NULL);
1456 gst_bin_add (GST_BIN (priv->pipeline), priv->liveadder);
1457 gst_bin_add (GST_BIN (priv->pipeline), priv->audio_output);
1459 gst_element_link (priv->liveadder, priv->audio_output);
1461 gst_element_set_state (priv->liveadder, GST_STATE_PLAYING);
1462 gst_element_set_state (priv->audio_output, GST_STATE_PLAYING);
1465 pad = gst_element_get_request_pad (priv->liveadder, "sink%d");
1471 empathy_call_window_update_timer (gpointer user_data)
1473 EmpathyCallWindow *self = EMPATHY_CALL_WINDOW (user_data);
1474 EmpathyCallWindowPriv *priv = GET_PRIV (self);
1478 time_ = g_timer_elapsed (priv->timer, NULL);
1480 /* Translators: number of minutes:seconds the caller has been connected */
1481 str = g_strdup_printf (_("Connected — %d:%02dm"), (int) time_ / 60,
1483 empathy_call_window_status_message (self, str);
1490 display_error (EmpathyCallWindow *self,
1491 EmpathyTpCall *call,
1495 const gchar *details)
1497 EmpathyCallWindowPriv *priv = GET_PRIV (self);
1498 GtkWidget *info_bar;
1499 GtkWidget *content_area;
1506 /* Create info bar */
1507 info_bar = gtk_info_bar_new_with_buttons (GTK_STOCK_CLOSE, GTK_RESPONSE_CLOSE,
1510 gtk_info_bar_set_message_type (GTK_INFO_BAR (info_bar), GTK_MESSAGE_WARNING);
1512 content_area = gtk_info_bar_get_content_area (GTK_INFO_BAR (info_bar));
1514 /* hbox containing the image and the messages vbox */
1515 hbox = gtk_hbox_new (FALSE, 3);
1516 gtk_container_add (GTK_CONTAINER (content_area), hbox);
1519 image = gtk_image_new_from_icon_name (img, GTK_ICON_SIZE_DIALOG);
1520 gtk_box_pack_start (GTK_BOX (hbox), image, FALSE, FALSE, 0);
1522 /* vbox containing the main message and the details expander */
1523 vbox = gtk_vbox_new (FALSE, 3);
1524 gtk_box_pack_start (GTK_BOX (hbox), vbox, TRUE, TRUE, 0);
1527 txt = g_strdup_printf ("<b>%s</b>\n%s", title, desc);
1529 label = gtk_label_new (NULL);
1530 gtk_label_set_markup (GTK_LABEL (label), txt);
1531 gtk_label_set_line_wrap (GTK_LABEL (label), TRUE);
1532 gtk_misc_set_alignment (GTK_MISC (label), 0, 0);
1535 gtk_box_pack_start (GTK_BOX (vbox), label, TRUE, TRUE, 0);
1538 if (details != NULL)
1540 GtkWidget *expander;
1542 expander = gtk_expander_new (_("Technical Details"));
1544 txt = g_strdup_printf ("<i>%s</i>", details);
1546 label = gtk_label_new (NULL);
1547 gtk_label_set_markup (GTK_LABEL (label), txt);
1548 gtk_label_set_line_wrap (GTK_LABEL (label), TRUE);
1549 gtk_misc_set_alignment (GTK_MISC (label), 0, 0);
1552 gtk_container_add (GTK_CONTAINER (expander), label);
1553 gtk_box_pack_start (GTK_BOX (vbox), expander, TRUE, TRUE, 0);
1556 g_signal_connect (info_bar, "response",
1557 G_CALLBACK (gtk_widget_destroy), NULL);
1559 gtk_box_pack_start (GTK_BOX (priv->errors_vbox), info_bar,
1560 FALSE, FALSE, CONTENT_HBOX_CHILDREN_PACKING_PADDING);
1561 gtk_widget_show_all (info_bar);
1565 media_stream_error_to_txt (EmpathyCallWindow *self,
1566 EmpathyTpCall *call,
1568 TpMediaStreamError error)
1570 EmpathyCallWindowPriv *priv = GET_PRIV (self);
1577 case TP_MEDIA_STREAM_ERROR_CODEC_NEGOTIATION_FAILED:
1579 return g_strdup_printf (
1580 _("%s's software does not understand any of the audio formats "
1581 "supported by your computer"),
1582 empathy_contact_get_name (priv->contact));
1584 return g_strdup_printf (
1585 _("%s's software does not understand any of the video formats "
1586 "supported by your computer"),
1587 empathy_contact_get_name (priv->contact));
1589 case TP_MEDIA_STREAM_ERROR_CONNECTION_FAILED:
1590 return g_strdup_printf (
1591 _("Can't establish a connection to %s. "
1592 "One of you might be on a network that does not allow "
1593 "direct connections."),
1594 empathy_contact_get_name (priv->contact));
1596 case TP_MEDIA_STREAM_ERROR_NETWORK_ERROR:
1597 return g_strdup (_("There was a failure on the network"));
1599 case TP_MEDIA_STREAM_ERROR_NO_CODECS:
1601 return g_strdup (_("The audio formats necessary for this call "
1602 "are not installed on your computer"));
1604 return g_strdup (_("The video formats necessary for this call "
1605 "are not installed on your computer"));
1607 case TP_MEDIA_STREAM_ERROR_INVALID_CM_BEHAVIOR:
1608 cm = empathy_tp_call_get_connection_manager (call);
1610 url = g_strdup_printf ("http://bugs.freedesktop.org/enter_bug.cgi?"
1611 "product=Telepathy&component=%s", cm);
1613 result = g_strdup_printf (
1614 _("Something not expected happened in a Telepathy component. "
1615 "Please <a href=\"%s\">report this bug</a> and attach "
1616 "logs gathered from the 'Debug' window in the Help menu."), url);
1621 case TP_MEDIA_STREAM_ERROR_MEDIA_ERROR:
1622 return g_strdup (_("There was a failure in the call engine"));
1630 empathy_call_window_stream_error (EmpathyCallWindow *self,
1631 EmpathyTpCall *call,
1640 desc = media_stream_error_to_txt (self, call, audio, code);
1643 /* No description, use the error message. That's not great as it's not
1644 * localized but it's better than nothing. */
1645 display_error (self, call, icon, title, msg, NULL);
1649 display_error (self, call, icon, title, desc, msg);
1655 empathy_call_window_audio_stream_error (EmpathyTpCall *call,
1658 EmpathyCallWindow *self)
1660 empathy_call_window_stream_error (self, call, TRUE, code, msg,
1661 "gnome-stock-mic", _("Can't establish audio stream"));
1665 empathy_call_window_video_stream_error (EmpathyTpCall *call,
1668 EmpathyCallWindow *self)
1670 empathy_call_window_stream_error (self, call, FALSE, code, msg,
1671 "camera-web", _("Can't establish video stream"));
1675 empathy_call_window_connected (gpointer user_data)
1677 EmpathyCallWindow *self = EMPATHY_CALL_WINDOW (user_data);
1678 EmpathyCallWindowPriv *priv = GET_PRIV (self);
1679 EmpathyTpCall *call;
1680 gboolean can_send_video;
1682 empathy_sound_stop (EMPATHY_SOUND_PHONE_OUTGOING);
1684 can_send_video = priv->video_input != NULL && priv->contact != NULL &&
1685 empathy_contact_can_voip_video (priv->contact);
1687 g_object_get (priv->handler, "tp-call", &call, NULL);
1689 g_signal_connect (call, "notify::video-stream",
1690 G_CALLBACK (empathy_call_window_video_stream_changed_cb), self);
1692 if (empathy_tp_call_has_dtmf (call))
1693 gtk_widget_set_sensitive (priv->dtmf_panel, TRUE);
1695 if (priv->video_input == NULL)
1696 empathy_call_window_set_send_video (self, FALSE);
1698 priv->sending_video = can_send_video ?
1699 empathy_tp_call_is_sending_video (call) : FALSE;
1701 gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (priv->send_video),
1702 priv->sending_video && priv->video_input != NULL);
1703 gtk_toggle_tool_button_set_active (
1704 GTK_TOGGLE_TOOL_BUTTON (priv->camera_button),
1705 priv->sending_video && priv->video_input != NULL);
1706 gtk_widget_set_sensitive (priv->camera_button, can_send_video);
1707 gtk_action_set_sensitive (priv->send_video, can_send_video);
1709 gtk_action_set_sensitive (priv->redial, FALSE);
1710 gtk_widget_set_sensitive (priv->redial_button, FALSE);
1712 gtk_widget_set_sensitive (priv->mic_button, TRUE);
1714 empathy_call_window_update_avatars_visibility (call, self);
1716 g_object_unref (call);
1718 g_mutex_lock (priv->lock);
1720 priv->timer_id = g_timeout_add_seconds (1,
1721 empathy_call_window_update_timer, self);
1723 g_mutex_unlock (priv->lock);
1725 empathy_call_window_update_timer (self);
1731 /* Called from the streaming thread */
1733 empathy_call_window_src_added_cb (EmpathyCallHandler *handler,
1734 GstPad *src, guint media_type, gpointer user_data)
1736 EmpathyCallWindow *self = EMPATHY_CALL_WINDOW (user_data);
1737 EmpathyCallWindowPriv *priv = GET_PRIV (self);
1741 g_mutex_lock (priv->lock);
1743 if (priv->call_state != CONNECTED)
1745 g_timer_start (priv->timer);
1746 priv->timer_id = g_idle_add (empathy_call_window_connected, self);
1747 priv->call_state = CONNECTED;
1752 case TP_MEDIA_STREAM_TYPE_AUDIO:
1753 pad = empathy_call_window_get_audio_sink_pad (self);
1755 case TP_MEDIA_STREAM_TYPE_VIDEO:
1756 gtk_widget_hide (priv->remote_user_avatar_widget);
1757 gtk_widget_show (priv->video_output);
1758 pad = empathy_call_window_get_video_sink_pad (self);
1761 g_assert_not_reached ();
1764 gst_pad_link (src, pad);
1765 gst_object_unref (pad);
1767 g_mutex_unlock (priv->lock);
1770 /* Called from the streaming thread */
1772 empathy_call_window_sink_added_cb (EmpathyCallHandler *handler,
1773 GstPad *sink, guint media_type, gpointer user_data)
1775 EmpathyCallWindow *self = EMPATHY_CALL_WINDOW (user_data);
1776 EmpathyCallWindowPriv *priv = GET_PRIV (self);
1781 case TP_MEDIA_STREAM_TYPE_AUDIO:
1782 gst_bin_add (GST_BIN (priv->pipeline), priv->audio_input);
1784 pad = gst_element_get_static_pad (priv->audio_input, "src");
1785 gst_pad_link (pad, sink);
1787 gst_element_set_state (priv->audio_input, GST_STATE_PLAYING);
1789 case TP_MEDIA_STREAM_TYPE_VIDEO:
1790 if (priv->video_input != NULL)
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),