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 if (priv->video_tee != NULL)
1784 pad = gst_element_get_request_pad (priv->video_tee, "src%d");
1785 gst_pad_link (pad, sink);
1790 g_assert_not_reached ();
1796 empathy_call_window_remove_video_input (EmpathyCallWindow *self)
1798 EmpathyCallWindowPriv *priv = GET_PRIV (self);
1799 GstElement *preview;
1801 preview = empathy_video_widget_get_element (
1802 EMPATHY_VIDEO_WIDGET (priv->video_preview));
1804 gst_element_set_state (priv->video_input, GST_STATE_NULL);
1805 gst_element_set_state (priv->video_tee, GST_STATE_NULL);
1806 gst_element_set_state (preview, GST_STATE_NULL);
1808 gst_bin_remove_many (GST_BIN (priv->pipeline), priv->video_input,
1809 priv->video_tee, preview, NULL);
1811 g_object_unref (priv->video_input);
1812 priv->video_input = NULL;
1813 g_object_unref (priv->video_tee);
1814 priv->video_tee = NULL;
1815 gtk_widget_destroy (priv->video_preview);
1816 priv->video_preview = NULL;
1818 gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (priv->send_video), FALSE);
1819 gtk_toggle_tool_button_set_active (
1820 GTK_TOGGLE_TOOL_BUTTON (priv->camera_button), FALSE);
1821 gtk_widget_set_sensitive (priv->camera_button, FALSE);
1822 gtk_action_set_sensitive (priv->send_video, FALSE);
1824 gtk_widget_show (priv->self_user_avatar_widget);
1829 empathy_call_window_bus_message (GstBus *bus, GstMessage *message,
1832 EmpathyCallWindow *self = EMPATHY_CALL_WINDOW (user_data);
1833 EmpathyCallWindowPriv *priv = GET_PRIV (self);
1836 empathy_call_handler_bus_message (priv->handler, bus, message);
1838 switch (GST_MESSAGE_TYPE (message))
1840 case GST_MESSAGE_STATE_CHANGED:
1841 if (GST_MESSAGE_SRC (message) == GST_OBJECT (priv->video_input))
1843 gst_message_parse_state_changed (message, NULL, &newstate, NULL);
1844 if (newstate == GST_STATE_PAUSED)
1845 empathy_call_window_setup_video_input (self);
1847 if (GST_MESSAGE_SRC (message) == GST_OBJECT (priv->pipeline) &&
1848 !priv->call_started)
1850 gst_message_parse_state_changed (message, NULL, &newstate, NULL);
1851 if (newstate == GST_STATE_PAUSED)
1853 priv->call_started = TRUE;
1854 empathy_call_handler_start_call (priv->handler);
1855 gst_element_set_state (priv->pipeline, GST_STATE_PLAYING);
1859 case GST_MESSAGE_ERROR:
1861 GError *error = NULL;
1862 GstElement *gst_error;
1865 gst_message_parse_error (message, &error, &debug);
1866 gst_error = GST_ELEMENT (GST_MESSAGE_SRC (message));
1868 g_message ("Element error: %s -- %s\n", error->message, debug);
1870 if (g_str_has_prefix (gst_element_get_name (gst_error),
1871 VIDEO_INPUT_ERROR_PREFIX))
1873 /* Remove the video input and continue */
1874 if (priv->video_input != NULL)
1875 empathy_call_window_remove_video_input (self);
1876 gst_element_set_state (priv->pipeline, GST_STATE_PLAYING);
1880 empathy_call_window_disconnected (self);
1882 g_error_free (error);
1893 empathy_call_window_update_avatars_visibility (EmpathyTpCall *call,
1894 EmpathyCallWindow *window)
1896 EmpathyCallWindowPriv *priv = GET_PRIV (window);
1898 if (empathy_tp_call_is_receiving_video (call))
1900 gtk_widget_hide (priv->remote_user_avatar_widget);
1901 gtk_widget_show (priv->video_output);
1905 gtk_widget_hide (priv->video_output);
1906 gtk_widget_show (priv->remote_user_avatar_widget);
1911 call_handler_notify_tp_call_cb (EmpathyCallHandler *handler,
1913 EmpathyCallWindow *self)
1915 EmpathyCallWindowPriv *priv = GET_PRIV (self);
1916 EmpathyTpCall *call;
1918 g_object_get (priv->handler, "tp-call", &call, NULL);
1922 empathy_signal_connect_weak (call, "audio-stream-error",
1923 G_CALLBACK (empathy_call_window_audio_stream_error), G_OBJECT (self));
1924 empathy_signal_connect_weak (call, "video-stream-error",
1925 G_CALLBACK (empathy_call_window_video_stream_error), G_OBJECT (self));
1927 g_object_unref (call);
1931 empathy_call_window_realized_cb (GtkWidget *widget, EmpathyCallWindow *window)
1933 EmpathyCallWindowPriv *priv = GET_PRIV (window);
1934 EmpathyTpCall *call;
1936 g_signal_connect (priv->handler, "conference-added",
1937 G_CALLBACK (empathy_call_window_conference_added_cb), window);
1938 g_signal_connect (priv->handler, "request-resource",
1939 G_CALLBACK (empathy_call_window_request_resource_cb), window);
1940 g_signal_connect (priv->handler, "closed",
1941 G_CALLBACK (empathy_call_window_channel_closed_cb), window);
1942 g_signal_connect (priv->handler, "src-pad-added",
1943 G_CALLBACK (empathy_call_window_src_added_cb), window);
1944 g_signal_connect (priv->handler, "sink-pad-added",
1945 G_CALLBACK (empathy_call_window_sink_added_cb), window);
1946 g_signal_connect (priv->handler, "stream-closed",
1947 G_CALLBACK (empathy_call_window_channel_stream_closed_cb), window);
1949 g_object_get (priv->handler, "tp-call", &call, NULL);
1952 empathy_signal_connect_weak (call, "audio-stream-error",
1953 G_CALLBACK (empathy_call_window_audio_stream_error), G_OBJECT (window));
1954 empathy_signal_connect_weak (call, "video-stream-error",
1955 G_CALLBACK (empathy_call_window_video_stream_error), G_OBJECT (window));
1957 g_object_unref (call);
1961 /* tp-call doesn't exist yet, we'll connect signals once it has been
1963 g_signal_connect (priv->handler, "notify::tp-call",
1964 G_CALLBACK (call_handler_notify_tp_call_cb), window);
1967 gst_element_set_state (priv->pipeline, GST_STATE_PAUSED);
1971 empathy_call_window_delete_cb (GtkWidget *widget, GdkEvent*event,
1972 EmpathyCallWindow *window)
1974 EmpathyCallWindowPriv *priv = GET_PRIV (window);
1976 if (priv->pipeline != NULL)
1978 if (priv->bus_message_source_id != 0)
1980 g_source_remove (priv->bus_message_source_id);
1981 priv->bus_message_source_id = 0;
1984 gst_element_set_state (priv->pipeline, GST_STATE_NULL);
1987 if (priv->call_state == CONNECTING)
1988 empathy_sound_stop (EMPATHY_SOUND_PHONE_OUTGOING);
1994 show_controls (EmpathyCallWindow *window, gboolean set_fullscreen)
1997 EmpathyCallWindowPriv *priv = GET_PRIV (window);
1999 menu = gtk_ui_manager_get_widget (priv->ui_manager,
2004 gtk_widget_hide (priv->sidebar);
2005 gtk_widget_hide (menu);
2006 gtk_widget_hide (priv->vbox);
2007 gtk_widget_hide (priv->statusbar);
2008 gtk_widget_hide (priv->toolbar);
2012 if (priv->sidebar_was_visible_before_fs)
2013 gtk_widget_show (priv->sidebar);
2015 gtk_widget_show (menu);
2016 gtk_widget_show (priv->vbox);
2017 gtk_widget_show (priv->statusbar);
2018 gtk_widget_show (priv->toolbar);
2020 gtk_window_resize (GTK_WINDOW (window), priv->original_width_before_fs,
2021 priv->original_height_before_fs);
2026 show_borders (EmpathyCallWindow *window, gboolean set_fullscreen)
2028 EmpathyCallWindowPriv *priv = GET_PRIV (window);
2030 gtk_container_set_border_width (GTK_CONTAINER (priv->content_hbox),
2031 set_fullscreen ? 0 : CONTENT_HBOX_BORDER_WIDTH);
2032 gtk_box_set_spacing (GTK_BOX (priv->content_hbox),
2033 set_fullscreen ? 0 : CONTENT_HBOX_SPACING);
2034 gtk_box_set_child_packing (GTK_BOX (priv->content_hbox),
2035 priv->video_output, TRUE, TRUE,
2036 set_fullscreen ? 0 : CONTENT_HBOX_CHILDREN_PACKING_PADDING,
2038 gtk_box_set_child_packing (GTK_BOX (priv->content_hbox),
2039 priv->vbox, TRUE, TRUE,
2040 set_fullscreen ? 0 : CONTENT_HBOX_CHILDREN_PACKING_PADDING,
2045 empathy_call_window_state_event_cb (GtkWidget *widget,
2046 GdkEventWindowState *event, EmpathyCallWindow *window)
2048 if (event->changed_mask & GDK_WINDOW_STATE_FULLSCREEN)
2050 EmpathyCallWindowPriv *priv = GET_PRIV (window);
2051 gboolean set_fullscreen = event->new_window_state &
2052 GDK_WINDOW_STATE_FULLSCREEN;
2056 gboolean sidebar_was_visible;
2057 GtkAllocation allocation;
2058 gint original_width, original_height;
2060 gtk_widget_get_allocation (GTK_WIDGET (window), &allocation);
2061 original_width = allocation.width;
2062 original_height = allocation.height;
2064 g_object_get (priv->sidebar, "visible", &sidebar_was_visible, NULL);
2066 priv->sidebar_was_visible_before_fs = sidebar_was_visible;
2067 priv->original_width_before_fs = original_width;
2068 priv->original_height_before_fs = original_height;
2070 if (priv->video_output_motion_handler_id == 0 &&
2071 priv->video_output != NULL)
2073 priv->video_output_motion_handler_id = g_signal_connect (
2074 G_OBJECT (priv->video_output), "motion-notify-event",
2075 G_CALLBACK (empathy_call_window_video_output_motion_notify),
2081 if (priv->video_output_motion_handler_id != 0)
2083 g_signal_handler_disconnect (G_OBJECT (priv->video_output),
2084 priv->video_output_motion_handler_id);
2085 priv->video_output_motion_handler_id = 0;
2089 empathy_call_window_fullscreen_set_fullscreen (priv->fullscreen,
2091 show_controls (window, set_fullscreen);
2092 show_borders (window, set_fullscreen);
2093 gtk_action_set_stock_id (priv->menu_fullscreen,
2094 (set_fullscreen ? "gtk-leave-fullscreen" : "gtk-fullscreen"));
2095 priv->is_fullscreen = set_fullscreen;
2102 empathy_call_window_sidebar_toggled_cb (GtkToggleButton *toggle,
2103 EmpathyCallWindow *window)
2105 EmpathyCallWindowPriv *priv = GET_PRIV (window);
2107 int w, h, handle_size;
2108 GtkAllocation allocation, sidebar_allocation;
2110 gtk_widget_get_allocation (GTK_WIDGET (window), &allocation);
2111 w = allocation.width;
2112 h = allocation.height;
2114 gtk_widget_style_get (priv->pane, "handle_size", &handle_size, NULL);
2116 gtk_widget_get_allocation (priv->sidebar, &sidebar_allocation);
2117 if (gtk_toggle_button_get_active (toggle))
2119 arrow = gtk_arrow_new (GTK_ARROW_LEFT, GTK_SHADOW_NONE);
2120 gtk_widget_show (priv->sidebar);
2121 w += sidebar_allocation.width + handle_size;
2125 arrow = gtk_arrow_new (GTK_ARROW_RIGHT, GTK_SHADOW_NONE);
2126 w -= sidebar_allocation.width + handle_size;
2127 gtk_widget_hide (priv->sidebar);
2130 gtk_button_set_image (GTK_BUTTON (priv->sidebar_button), arrow);
2133 gtk_window_resize (GTK_WINDOW (window), w, h);
2137 empathy_call_window_set_send_video (EmpathyCallWindow *window,
2140 EmpathyCallWindowPriv *priv = GET_PRIV (window);
2141 EmpathyTpCall *call;
2143 priv->sending_video = send;
2145 /* When we start sending video, we want to show the video preview by
2149 display_video_preview (window, TRUE);
2152 g_object_get (priv->handler, "tp-call", &call, NULL);
2153 empathy_tp_call_request_video_stream_direction (call, send);
2154 g_object_unref (call);
2158 empathy_call_window_camera_toggled_cb (GtkToggleToolButton *toggle,
2159 EmpathyCallWindow *window)
2161 EmpathyCallWindowPriv *priv = GET_PRIV (window);
2164 if (priv->call_state != CONNECTED)
2167 active = (gtk_toggle_tool_button_get_active (toggle));
2169 if (priv->sending_video == active)
2172 empathy_call_window_set_send_video (window, active);
2173 gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (priv->send_video), active);
2177 empathy_call_window_send_video_toggled_cb (GtkToggleAction *toggle,
2178 EmpathyCallWindow *window)
2180 EmpathyCallWindowPriv *priv = GET_PRIV (window);
2183 if (priv->call_state != CONNECTED)
2186 active = (gtk_toggle_action_get_active (toggle));
2188 if (priv->sending_video == active)
2191 empathy_call_window_set_send_video (window, active);
2192 gtk_toggle_tool_button_set_active (
2193 GTK_TOGGLE_TOOL_BUTTON (priv->camera_button), active);
2197 empathy_call_window_always_show_preview_toggled_cb (GtkToggleAction *toggle,
2198 EmpathyCallWindow *window)
2200 EmpathyCallWindowPriv *priv = GET_PRIV (window);
2202 if (gtk_toggle_action_get_active (toggle))
2204 display_video_preview (window, TRUE);
2208 /* disable preview if we are not sending */
2209 if (!priv->sending_video)
2210 display_video_preview (window, FALSE);
2215 empathy_call_window_mic_toggled_cb (GtkToggleToolButton *toggle,
2216 EmpathyCallWindow *window)
2218 EmpathyCallWindowPriv *priv = GET_PRIV (window);
2221 if (priv->audio_input == NULL)
2224 active = (gtk_toggle_tool_button_get_active (toggle));
2228 empathy_audio_src_set_volume (EMPATHY_GST_AUDIO_SRC (priv->audio_input),
2230 gtk_adjustment_set_value (priv->audio_input_adj, priv->volume * 100);
2234 /* TODO, Instead of setting the input volume to 0 we should probably
2235 * stop sending but this would cause the audio call to drop if both
2236 * sides mute at the same time on certain CMs AFAIK. Need to revisit this
2237 * in the future. GNOME #574574
2239 empathy_audio_src_set_volume (EMPATHY_GST_AUDIO_SRC (priv->audio_input),
2241 gtk_adjustment_set_value (priv->audio_input_adj, 0);
2246 empathy_call_window_sidebar_hidden_cb (EmpathySidebar *sidebar,
2247 EmpathyCallWindow *window)
2249 EmpathyCallWindowPriv *priv = GET_PRIV (window);
2251 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (priv->sidebar_button),
2256 empathy_call_window_sidebar_shown_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_hangup_cb (gpointer object,
2267 EmpathyCallWindow *window)
2269 if (empathy_call_window_disconnected (window))
2270 gtk_widget_destroy (GTK_WIDGET (window));
2274 empathy_call_window_restart_call (EmpathyCallWindow *window)
2277 EmpathyCallWindowPriv *priv = GET_PRIV (window);
2279 gtk_widget_destroy (priv->remote_user_output_hbox);
2280 gtk_widget_destroy (priv->self_user_output_hbox);
2282 priv->pipeline = gst_pipeline_new (NULL);
2283 bus = gst_pipeline_get_bus (GST_PIPELINE (priv->pipeline));
2284 priv->bus_message_source_id = gst_bus_add_watch (bus,
2285 empathy_call_window_bus_message, window);
2287 empathy_call_window_setup_remote_frame (bus, window);
2288 empathy_call_window_setup_self_frame (bus, window);
2290 g_signal_connect (G_OBJECT (priv->audio_input_adj), "value-changed",
2291 G_CALLBACK (empathy_call_window_mic_volume_changed_cb), window);
2293 /* While the call was disconnected, the input volume might have changed.
2294 * However, since the audio_input source was destroyed, its volume has not
2295 * been updated during that time. That's why we manually update it here */
2296 empathy_call_window_mic_volume_changed_cb (priv->audio_input_adj, window);
2298 g_object_unref (bus);
2300 gtk_widget_show_all (priv->content_hbox);
2302 priv->outgoing = TRUE;
2303 empathy_call_window_set_state_connecting (window);
2305 priv->call_started = TRUE;
2306 empathy_call_handler_start_call (priv->handler);
2307 empathy_call_window_setup_avatars (window, priv->handler);
2308 gst_element_set_state (priv->pipeline, GST_STATE_PLAYING);
2310 gtk_action_set_sensitive (priv->redial, FALSE);
2311 gtk_widget_set_sensitive (priv->redial_button, FALSE);
2315 empathy_call_window_redial_cb (gpointer object,
2316 EmpathyCallWindow *window)
2318 EmpathyCallWindowPriv *priv = GET_PRIV (window);
2320 if (priv->call_state == CONNECTED)
2321 priv->call_state = REDIALING;
2323 empathy_call_handler_stop_call (priv->handler);
2325 if (priv->call_state != CONNECTED)
2326 empathy_call_window_restart_call (window);
2330 empathy_call_window_fullscreen_cb (gpointer object,
2331 EmpathyCallWindow *window)
2333 empathy_call_window_fullscreen_toggle (window);
2337 empathy_call_window_fullscreen_toggle (EmpathyCallWindow *window)
2339 EmpathyCallWindowPriv *priv = GET_PRIV (window);
2341 if (priv->is_fullscreen)
2342 gtk_window_unfullscreen (GTK_WINDOW (window));
2344 gtk_window_fullscreen (GTK_WINDOW (window));
2348 empathy_call_window_video_button_press_cb (GtkWidget *video_output,
2349 GdkEventButton *event, EmpathyCallWindow *window)
2351 if (event->button == 3 && event->type == GDK_BUTTON_PRESS)
2353 empathy_call_window_video_menu_popup (window, event->button);
2361 empathy_call_window_key_press_cb (GtkWidget *video_output,
2362 GdkEventKey *event, EmpathyCallWindow *window)
2364 EmpathyCallWindowPriv *priv = GET_PRIV (window);
2366 if (priv->is_fullscreen && event->keyval == GDK_Escape)
2368 /* Since we are in fullscreen mode, toggling will bring us back to
2370 empathy_call_window_fullscreen_toggle (window);
2378 empathy_call_window_video_output_motion_notify (GtkWidget *widget,
2379 GdkEventMotion *event, EmpathyCallWindow *window)
2381 EmpathyCallWindowPriv *priv = GET_PRIV (window);
2383 if (priv->is_fullscreen)
2385 empathy_call_window_fullscreen_show_popup (priv->fullscreen);
2392 empathy_call_window_video_menu_popup (EmpathyCallWindow *window,
2396 EmpathyCallWindowPriv *priv = GET_PRIV (window);
2398 menu = gtk_ui_manager_get_widget (priv->ui_manager,
2400 gtk_menu_popup (GTK_MENU (menu), NULL, NULL, NULL, NULL,
2401 button, gtk_get_current_event_time ());
2402 gtk_menu_shell_select_first (GTK_MENU_SHELL (menu), FALSE);
2406 empathy_call_window_status_message (EmpathyCallWindow *window,
2409 EmpathyCallWindowPriv *priv = GET_PRIV (window);
2411 if (priv->context_id == 0)
2413 priv->context_id = gtk_statusbar_get_context_id (
2414 GTK_STATUSBAR (priv->statusbar), "voip call status messages");
2418 gtk_statusbar_pop (GTK_STATUSBAR (priv->statusbar), priv->context_id);
2421 gtk_statusbar_push (GTK_STATUSBAR (priv->statusbar), priv->context_id,
2426 empathy_call_window_volume_changed_cb (GtkScaleButton *button,
2427 gdouble value, EmpathyCallWindow *window)
2429 EmpathyCallWindowPriv *priv = GET_PRIV (window);
2431 if (priv->audio_output == NULL)
2434 empathy_audio_sink_set_volume (EMPATHY_GST_AUDIO_SINK (priv->audio_output),