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 DEBUG ("Create video preview");
649 g_assert (priv->video_tee == NULL);
651 priv->video_tee = gst_element_factory_make ("tee", NULL);
652 gst_object_ref (priv->video_tee);
653 gst_object_sink (priv->video_tee);
655 priv->video_preview = empathy_video_widget_new_with_size (bus,
656 SELF_VIDEO_SECTION_WIDTH, SELF_VIDEO_SECTION_HEIGTH);
657 g_object_set (priv->video_preview, "sync", FALSE, "async", TRUE, NULL);
658 gtk_box_pack_start (GTK_BOX (priv->self_user_output_hbox),
659 priv->video_preview, TRUE, TRUE, 0);
661 preview = empathy_video_widget_get_element (
662 EMPATHY_VIDEO_WIDGET (priv->video_preview));
663 gst_bin_add_many (GST_BIN (priv->pipeline), priv->video_input,
664 priv->video_tee, preview, NULL);
665 gst_element_link_many (priv->video_input, priv->video_tee,
668 g_object_unref (bus);
670 gst_element_set_state (preview, GST_STATE_PLAYING);
671 gst_element_set_state (priv->video_input, GST_STATE_PLAYING);
672 gst_element_set_state (priv->video_tee, GST_STATE_PLAYING);
676 display_video_preview (EmpathyCallWindow *self,
679 EmpathyCallWindowPriv *priv = GET_PRIV (self);
683 /* Display the preview and hide the self avatar */
684 DEBUG ("Show video preview");
686 if (priv->video_preview == NULL)
687 empathy_call_window_setup_video_preview (self);
688 gtk_widget_show (priv->video_preview);
689 gtk_widget_hide (priv->self_user_avatar_widget);
693 /* Display the self avatar and hide the preview */
694 DEBUG ("Show self avatar");
696 if (priv->video_preview != NULL)
697 gtk_widget_hide (priv->video_preview);
698 gtk_widget_show (priv->self_user_avatar_widget);
703 empathy_call_window_set_state_connecting (EmpathyCallWindow *window)
705 EmpathyCallWindowPriv *priv = GET_PRIV (window);
707 empathy_call_window_status_message (window, _("Connecting..."));
708 priv->call_state = CONNECTING;
711 empathy_sound_start_playing (GTK_WIDGET (window),
712 EMPATHY_SOUND_PHONE_OUTGOING, MS_BETWEEN_RING);
716 empathy_call_window_init (EmpathyCallWindow *self)
718 EmpathyCallWindowPriv *priv = GET_PRIV (self);
727 GError *error = NULL;
729 filename = empathy_file_lookup ("empathy-call-window.ui", "src");
730 gui = empathy_builder_get_file (filename,
731 "call_window_vbox", &top_vbox,
732 "errors_vbox", &priv->errors_vbox,
734 "statusbar", &priv->statusbar,
735 "redial", &priv->redial_button,
736 "microphone", &priv->mic_button,
737 "camera", &priv->camera_button,
738 "toolbar", &priv->toolbar,
739 "send_video", &priv->send_video,
740 "menuredial", &priv->redial,
741 "always_show_preview", &priv->always_show_preview,
742 "ui_manager", &priv->ui_manager,
743 "menufullscreen", &priv->menu_fullscreen,
747 empathy_builder_connect (gui, self,
748 "menuhangup", "activate", empathy_call_window_hangup_cb,
749 "hangup", "clicked", empathy_call_window_hangup_cb,
750 "menuredial", "activate", empathy_call_window_redial_cb,
751 "redial", "clicked", empathy_call_window_redial_cb,
752 "microphone", "toggled", empathy_call_window_mic_toggled_cb,
753 "camera", "toggled", empathy_call_window_camera_toggled_cb,
754 "send_video", "toggled", empathy_call_window_send_video_toggled_cb,
755 "always_show_preview", "toggled",
756 empathy_call_window_always_show_preview_toggled_cb,
757 "menufullscreen", "activate", empathy_call_window_fullscreen_cb,
760 priv->lock = g_mutex_new ();
762 gtk_container_add (GTK_CONTAINER (self), top_vbox);
764 priv->content_hbox = gtk_hbox_new (FALSE, CONTENT_HBOX_SPACING);
765 gtk_container_set_border_width (GTK_CONTAINER (priv->content_hbox),
766 CONTENT_HBOX_BORDER_WIDTH);
767 gtk_paned_pack1 (GTK_PANED (priv->pane), priv->content_hbox, TRUE, FALSE);
769 priv->pipeline = gst_pipeline_new (NULL);
770 bus = gst_pipeline_get_bus (GST_PIPELINE (priv->pipeline));
771 priv->bus_message_source_id = gst_bus_add_watch (bus,
772 empathy_call_window_bus_message, self);
774 priv->fsnotifier = fs_element_added_notifier_new ();
775 fs_element_added_notifier_add (priv->fsnotifier, GST_BIN (priv->pipeline));
777 keyfile = g_key_file_new ();
778 filename = empathy_file_lookup ("element-properties", "data");
779 if (g_key_file_load_from_file (keyfile, filename, G_KEY_FILE_NONE, &error))
781 fs_element_added_notifier_set_properties_from_keyfile (priv->fsnotifier,
786 g_warning ("Could not load element-properties file: %s", error->message);
787 g_key_file_free (keyfile);
788 g_clear_error (&error);
793 priv->remote_user_output_frame = gtk_frame_new (NULL);
794 gtk_widget_set_size_request (priv->remote_user_output_frame,
795 EMPATHY_VIDEO_WIDGET_DEFAULT_WIDTH, EMPATHY_VIDEO_WIDGET_DEFAULT_HEIGHT);
796 gtk_box_pack_start (GTK_BOX (priv->content_hbox),
797 priv->remote_user_output_frame, TRUE, TRUE,
798 CONTENT_HBOX_CHILDREN_PACKING_PADDING);
799 empathy_call_window_setup_remote_frame (bus, self);
801 priv->self_user_output_frame = gtk_frame_new (NULL);
802 gtk_widget_set_size_request (priv->self_user_output_frame,
803 SELF_VIDEO_SECTION_WIDTH, SELF_VIDEO_SECTION_HEIGTH);
805 priv->vbox = gtk_vbox_new (FALSE, 3);
806 gtk_box_pack_start (GTK_BOX (priv->content_hbox), priv->vbox,
807 FALSE, FALSE, CONTENT_HBOX_CHILDREN_PACKING_PADDING);
808 gtk_box_pack_start (GTK_BOX (priv->vbox), priv->self_user_output_frame,
810 empathy_call_window_setup_self_frame (bus, self);
812 empathy_call_window_setup_toolbar (self);
814 g_object_unref (bus);
816 priv->sidebar_button = gtk_toggle_button_new_with_mnemonic (_("_Sidebar"));
817 arrow = gtk_arrow_new (GTK_ARROW_RIGHT, GTK_SHADOW_NONE);
818 g_signal_connect (G_OBJECT (priv->sidebar_button), "toggled",
819 G_CALLBACK (empathy_call_window_sidebar_toggled_cb), self);
821 gtk_button_set_image (GTK_BUTTON (priv->sidebar_button), arrow);
823 h = gtk_hbox_new (FALSE, 3);
824 gtk_box_pack_end (GTK_BOX (priv->vbox), h, FALSE, FALSE, 3);
825 gtk_box_pack_end (GTK_BOX (h), priv->sidebar_button, FALSE, FALSE, 3);
827 priv->sidebar = empathy_sidebar_new ();
828 g_signal_connect (G_OBJECT (priv->sidebar),
829 "hide", G_CALLBACK (empathy_call_window_sidebar_hidden_cb), self);
830 g_signal_connect (G_OBJECT (priv->sidebar),
831 "show", G_CALLBACK (empathy_call_window_sidebar_shown_cb), self);
832 gtk_paned_pack2 (GTK_PANED (priv->pane), priv->sidebar, FALSE, FALSE);
834 priv->dtmf_panel = empathy_call_window_create_dtmf (self);
835 empathy_sidebar_add_page (EMPATHY_SIDEBAR (priv->sidebar), _("Dialpad"),
838 gtk_widget_set_sensitive (priv->dtmf_panel, FALSE);
840 page = empathy_call_window_create_audio_input (self);
841 empathy_sidebar_add_page (EMPATHY_SIDEBAR (priv->sidebar), _("Audio input"),
844 page = empathy_call_window_create_video_input (self);
845 empathy_sidebar_add_page (EMPATHY_SIDEBAR (priv->sidebar), _("Video input"),
848 gtk_widget_show_all (top_vbox);
850 gtk_widget_hide (priv->sidebar);
852 priv->fullscreen = empathy_call_window_fullscreen_new (self);
853 empathy_call_window_fullscreen_set_video_widget (priv->fullscreen,
855 g_signal_connect (G_OBJECT (priv->fullscreen->leave_fullscreen_button),
856 "clicked", G_CALLBACK (empathy_call_window_fullscreen_cb), self);
858 g_signal_connect (G_OBJECT (self), "realize",
859 G_CALLBACK (empathy_call_window_realized_cb), self);
861 g_signal_connect (G_OBJECT (self), "delete-event",
862 G_CALLBACK (empathy_call_window_delete_cb), self);
864 g_signal_connect (G_OBJECT (self), "window-state-event",
865 G_CALLBACK (empathy_call_window_state_event_cb), self);
867 g_signal_connect (G_OBJECT (self), "key-press-event",
868 G_CALLBACK (empathy_call_window_key_press_cb), self);
870 priv->timer = g_timer_new ();
872 g_object_ref (priv->ui_manager);
873 g_object_unref (gui);
876 /* Instead of specifying a width and a height, we specify only one size. That's
877 because we want a square avatar icon. */
879 init_contact_avatar_with_size (EmpathyContact *contact,
880 GtkWidget *image_widget,
883 GdkPixbuf *pixbuf_avatar = NULL;
887 pixbuf_avatar = empathy_pixbuf_avatar_from_contact_scaled (contact,
891 if (pixbuf_avatar == NULL)
893 pixbuf_avatar = empathy_pixbuf_from_icon_name_sized ("stock_person",
897 gtk_image_set_from_pixbuf (GTK_IMAGE (image_widget), pixbuf_avatar);
901 set_window_title (EmpathyCallWindow *self)
903 EmpathyCallWindowPriv *priv = GET_PRIV (self);
906 /* translators: Call is a noun and %s is the contact name. This string
907 * is used in the window title */
908 tmp = g_strdup_printf (_("Call with %s"),
909 empathy_contact_get_name (priv->contact));
910 gtk_window_set_title (GTK_WINDOW (self), tmp);
915 contact_name_changed_cb (EmpathyContact *contact,
916 GParamSpec *pspec, EmpathyCallWindow *self)
918 set_window_title (self);
922 contact_avatar_changed_cb (EmpathyContact *contact,
923 GParamSpec *pspec, GtkWidget *avatar_widget)
927 size = avatar_widget->allocation.height;
931 /* the widget is not allocated yet, set a default size */
932 size = MIN (REMOTE_CONTACT_AVATAR_DEFAULT_HEIGHT,
933 REMOTE_CONTACT_AVATAR_DEFAULT_WIDTH);
936 init_contact_avatar_with_size (contact, avatar_widget, size);
940 empathy_call_window_got_self_contact_cb (EmpathyTpContactFactory *factory,
941 EmpathyContact *contact, const GError *error, gpointer user_data,
942 GObject *weak_object)
944 EmpathyCallWindow *self = EMPATHY_CALL_WINDOW (user_data);
945 EmpathyCallWindowPriv *priv = GET_PRIV (self);
947 init_contact_avatar_with_size (contact, priv->self_user_avatar_widget,
948 MIN (SELF_VIDEO_SECTION_WIDTH, SELF_VIDEO_SECTION_HEIGTH));
950 g_signal_connect (contact, "notify::avatar",
951 G_CALLBACK (contact_avatar_changed_cb), priv->self_user_avatar_widget);
955 empathy_call_window_setup_avatars (EmpathyCallWindow *self,
956 EmpathyCallHandler *handler)
958 EmpathyCallWindowPriv *priv = GET_PRIV (self);
960 g_object_get (handler, "contact", &(priv->contact), NULL);
962 if (priv->contact != NULL)
964 TpConnection *connection;
965 EmpathyTpContactFactory *factory;
967 set_window_title (self);
969 g_signal_connect (priv->contact, "notify::name",
970 G_CALLBACK (contact_name_changed_cb), self);
971 g_signal_connect (priv->contact, "notify::avatar",
972 G_CALLBACK (contact_avatar_changed_cb),
973 priv->remote_user_avatar_widget);
975 /* Retreiving the self avatar */
976 connection = empathy_contact_get_connection (priv->contact);
977 factory = empathy_tp_contact_factory_dup_singleton (connection);
978 empathy_tp_contact_factory_get_from_handle (factory,
979 tp_connection_get_self_handle (connection),
980 empathy_call_window_got_self_contact_cb, self, NULL, G_OBJECT (self));
982 g_object_unref (factory);
986 g_warning ("call handler doesn't have a contact");
987 /* translators: Call is a noun. This string is used in the window
989 gtk_window_set_title (GTK_WINDOW (self), _("Call"));
991 /* Since we can't access the remote contact, we can't get a connection
992 to it and can't get the self contact (and its avatar). This means
993 that we have to manually set the self avatar. */
994 init_contact_avatar_with_size (NULL, priv->self_user_avatar_widget,
995 MIN (SELF_VIDEO_SECTION_WIDTH, SELF_VIDEO_SECTION_HEIGTH));
998 init_contact_avatar_with_size (priv->contact,
999 priv->remote_user_avatar_widget,
1000 MIN (REMOTE_CONTACT_AVATAR_DEFAULT_WIDTH,
1001 REMOTE_CONTACT_AVATAR_DEFAULT_HEIGHT));
1003 /* The remote avatar is shown by default and will be hidden when we receive
1004 video from the remote side. */
1005 gtk_widget_hide (priv->video_output);
1006 gtk_widget_show (priv->remote_user_avatar_widget);
1010 empathy_call_window_constructed (GObject *object)
1012 EmpathyCallWindow *self = EMPATHY_CALL_WINDOW (object);
1013 EmpathyCallWindowPriv *priv = GET_PRIV (self);
1014 EmpathyTpCall *call;
1016 g_assert (priv->handler != NULL);
1018 g_object_get (priv->handler, "tp-call", &call, NULL);
1019 priv->outgoing = (call == NULL);
1021 g_object_unref (call);
1023 empathy_call_window_setup_avatars (self, priv->handler);
1024 empathy_call_window_set_state_connecting (self);
1026 if (empathy_call_handler_has_initial_video (priv->handler))
1028 /* Enable 'send video' buttons and display the preview */
1029 gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (priv->send_video), TRUE);
1030 gtk_toggle_tool_button_set_active (
1031 GTK_TOGGLE_TOOL_BUTTON (priv->camera_button), TRUE);
1033 display_video_preview (self, TRUE);
1037 static void empathy_call_window_dispose (GObject *object);
1038 static void empathy_call_window_finalize (GObject *object);
1041 empathy_call_window_set_property (GObject *object,
1042 guint property_id, const GValue *value, GParamSpec *pspec)
1044 EmpathyCallWindowPriv *priv = GET_PRIV (object);
1046 switch (property_id)
1048 case PROP_CALL_HANDLER:
1049 priv->handler = g_value_dup_object (value);
1052 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
1057 empathy_call_window_get_property (GObject *object,
1058 guint property_id, GValue *value, GParamSpec *pspec)
1060 EmpathyCallWindowPriv *priv = GET_PRIV (object);
1062 switch (property_id)
1064 case PROP_CALL_HANDLER:
1065 g_value_set_object (value, priv->handler);
1068 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
1073 empathy_call_window_class_init (
1074 EmpathyCallWindowClass *empathy_call_window_class)
1076 GObjectClass *object_class = G_OBJECT_CLASS (empathy_call_window_class);
1077 GParamSpec *param_spec;
1079 g_type_class_add_private (empathy_call_window_class,
1080 sizeof (EmpathyCallWindowPriv));
1082 object_class->constructed = empathy_call_window_constructed;
1083 object_class->set_property = empathy_call_window_set_property;
1084 object_class->get_property = empathy_call_window_get_property;
1086 object_class->dispose = empathy_call_window_dispose;
1087 object_class->finalize = empathy_call_window_finalize;
1089 param_spec = g_param_spec_object ("handler",
1090 "handler", "The call handler",
1091 EMPATHY_TYPE_CALL_HANDLER,
1092 G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS);
1093 g_object_class_install_property (object_class,
1094 PROP_CALL_HANDLER, param_spec);
1098 empathy_call_window_video_stream_changed_cb (EmpathyTpCall *call,
1099 GParamSpec *property, EmpathyCallWindow *self)
1101 DEBUG ("video stream changed");
1102 empathy_call_window_update_avatars_visibility (call, self);
1106 empathy_call_window_dispose (GObject *object)
1108 EmpathyTpCall *call;
1109 EmpathyCallWindow *self = EMPATHY_CALL_WINDOW (object);
1110 EmpathyCallWindowPriv *priv = GET_PRIV (self);
1112 if (priv->dispose_has_run)
1115 priv->dispose_has_run = TRUE;
1117 g_object_get (priv->handler, "tp-call", &call, NULL);
1121 g_signal_handlers_disconnect_by_func (call,
1122 empathy_call_window_video_stream_changed_cb, object);
1123 g_object_unref (call);
1126 if (priv->handler != NULL)
1127 g_object_unref (priv->handler);
1128 priv->handler = NULL;
1130 if (priv->pipeline != NULL)
1131 g_object_unref (priv->pipeline);
1132 priv->pipeline = NULL;
1134 if (priv->video_input != NULL)
1135 g_object_unref (priv->video_input);
1136 priv->video_input = NULL;
1138 if (priv->audio_input != NULL)
1139 g_object_unref (priv->audio_input);
1140 priv->audio_input = NULL;
1142 if (priv->audio_output != NULL)
1143 g_object_unref (priv->audio_output);
1144 priv->audio_output = NULL;
1146 if (priv->video_tee != NULL)
1147 g_object_unref (priv->video_tee);
1148 priv->video_tee = NULL;
1150 if (priv->fsnotifier != NULL)
1151 g_object_unref (priv->fsnotifier);
1152 priv->fsnotifier = NULL;
1154 if (priv->timer_id != 0)
1155 g_source_remove (priv->timer_id);
1158 if (priv->ui_manager != NULL)
1159 g_object_unref (priv->ui_manager);
1160 priv->ui_manager = NULL;
1162 if (priv->contact != NULL)
1164 g_signal_handlers_disconnect_by_func (priv->contact,
1165 contact_name_changed_cb, self);
1166 g_object_unref (priv->contact);
1167 priv->contact = NULL;
1170 /* release any references held by the object here */
1171 if (G_OBJECT_CLASS (empathy_call_window_parent_class)->dispose)
1172 G_OBJECT_CLASS (empathy_call_window_parent_class)->dispose (object);
1176 empathy_call_window_finalize (GObject *object)
1178 EmpathyCallWindow *self = EMPATHY_CALL_WINDOW (object);
1179 EmpathyCallWindowPriv *priv = GET_PRIV (self);
1181 if (priv->video_output_motion_handler_id != 0)
1183 g_signal_handler_disconnect (G_OBJECT (priv->video_output),
1184 priv->video_output_motion_handler_id);
1185 priv->video_output_motion_handler_id = 0;
1188 if (priv->bus_message_source_id != 0)
1190 g_source_remove (priv->bus_message_source_id);
1191 priv->bus_message_source_id = 0;
1194 /* free any data held directly by the object here */
1195 g_mutex_free (priv->lock);
1197 g_timer_destroy (priv->timer);
1199 G_OBJECT_CLASS (empathy_call_window_parent_class)->finalize (object);
1204 empathy_call_window_new (EmpathyCallHandler *handler)
1206 return EMPATHY_CALL_WINDOW (
1207 g_object_new (EMPATHY_TYPE_CALL_WINDOW, "handler", handler, NULL));
1211 empathy_call_window_conference_added_cb (EmpathyCallHandler *handler,
1212 GstElement *conference, gpointer user_data)
1214 EmpathyCallWindow *self = EMPATHY_CALL_WINDOW (user_data);
1215 EmpathyCallWindowPriv *priv = GET_PRIV (self);
1217 gst_bin_add (GST_BIN (priv->pipeline), conference);
1219 gst_element_set_state (conference, GST_STATE_PLAYING);
1223 empathy_call_window_request_resource_cb (EmpathyCallHandler *handler,
1224 FsMediaType type, FsStreamDirection direction, gpointer user_data)
1226 EmpathyCallWindow *self = EMPATHY_CALL_WINDOW (user_data);
1227 EmpathyCallWindowPriv *priv = GET_PRIV (self);
1229 if (type != TP_MEDIA_STREAM_TYPE_VIDEO)
1232 if (direction == FS_DIRECTION_RECV)
1235 /* video and direction is send */
1236 return priv->video_input != NULL;
1240 empathy_call_window_reset_pipeline (EmpathyCallWindow *self)
1242 GstStateChangeReturn state_change_return;
1243 EmpathyCallWindowPriv *priv = GET_PRIV (self);
1245 if (priv->pipeline == NULL)
1248 if (priv->bus_message_source_id != 0)
1250 g_source_remove (priv->bus_message_source_id);
1251 priv->bus_message_source_id = 0;
1254 state_change_return = gst_element_set_state (priv->pipeline, GST_STATE_NULL);
1256 if (state_change_return == GST_STATE_CHANGE_SUCCESS ||
1257 state_change_return == GST_STATE_CHANGE_NO_PREROLL)
1259 if (priv->pipeline != NULL)
1260 g_object_unref (priv->pipeline);
1261 priv->pipeline = NULL;
1263 if (priv->video_input != NULL)
1264 g_object_unref (priv->video_input);
1265 priv->video_input = NULL;
1267 if (priv->audio_input != NULL)
1268 g_object_unref (priv->audio_input);
1269 priv->audio_input = NULL;
1271 g_signal_handlers_disconnect_by_func (priv->audio_input_adj,
1272 empathy_call_window_mic_volume_changed_cb, self);
1274 if (priv->audio_output != NULL)
1275 g_object_unref (priv->audio_output);
1276 priv->audio_output = NULL;
1278 if (priv->video_tee != NULL)
1279 g_object_unref (priv->video_tee);
1280 priv->video_tee = NULL;
1282 if (priv->video_preview != NULL)
1283 gtk_widget_destroy (priv->video_preview);
1284 priv->video_preview = NULL;
1286 priv->liveadder = NULL;
1287 priv->funnel = NULL;
1293 g_message ("Error: could not destroy pipeline. Closing call window");
1294 gtk_widget_destroy (GTK_WIDGET (self));
1301 empathy_call_window_disconnected (EmpathyCallWindow *self)
1303 gboolean could_disconnect = FALSE;
1304 EmpathyCallWindowPriv *priv = GET_PRIV (self);
1305 gboolean could_reset_pipeline = empathy_call_window_reset_pipeline (self);
1307 if (priv->call_state == CONNECTING)
1308 empathy_sound_stop (EMPATHY_SOUND_PHONE_OUTGOING);
1310 if (priv->call_state != REDIALING)
1311 priv->call_state = DISCONNECTED;
1313 if (could_reset_pipeline)
1315 gboolean initial_video = empathy_call_handler_has_initial_video (
1317 g_mutex_lock (priv->lock);
1319 g_timer_stop (priv->timer);
1321 if (priv->timer_id != 0)
1322 g_source_remove (priv->timer_id);
1325 g_mutex_unlock (priv->lock);
1327 empathy_call_window_status_message (self, _("Disconnected"));
1329 gtk_action_set_sensitive (priv->redial, TRUE);
1330 gtk_widget_set_sensitive (priv->redial_button, TRUE);
1332 /* Reseting the send_video, camera_buton and mic_button to their
1334 gtk_widget_set_sensitive (priv->camera_button, FALSE);
1335 gtk_widget_set_sensitive (priv->mic_button, FALSE);
1336 gtk_action_set_sensitive (priv->send_video, FALSE);
1337 gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (priv->send_video),
1339 gtk_toggle_tool_button_set_active (
1340 GTK_TOGGLE_TOOL_BUTTON (priv->camera_button), initial_video);
1341 gtk_toggle_tool_button_set_active (
1342 GTK_TOGGLE_TOOL_BUTTON (priv->mic_button), TRUE);
1344 gtk_progress_bar_set_fraction (
1345 GTK_PROGRESS_BAR (priv->volume_progress_bar), 0);
1347 gtk_widget_hide (priv->video_output);
1348 gtk_widget_show (priv->remote_user_avatar_widget);
1350 priv->sending_video = FALSE;
1351 priv->call_started = FALSE;
1353 could_disconnect = TRUE;
1355 /* TODO: display the self avatar of the preview (depends if the "Always
1356 * Show Video Preview" is enabled or not) */
1359 return could_disconnect;
1364 empathy_call_window_channel_closed_cb (EmpathyCallHandler *handler,
1367 EmpathyCallWindow *self = EMPATHY_CALL_WINDOW (user_data);
1368 EmpathyCallWindowPriv *priv = GET_PRIV (self);
1370 if (empathy_call_window_disconnected (self) && priv->call_state == REDIALING)
1371 empathy_call_window_restart_call (self);
1376 empathy_call_window_channel_stream_closed_cb (EmpathyCallHandler *handler,
1377 TfStream *stream, gpointer user_data)
1379 EmpathyCallWindow *self = EMPATHY_CALL_WINDOW (user_data);
1380 EmpathyCallWindowPriv *priv = GET_PRIV (self);
1383 g_object_get (stream, "media-type", &media_type, NULL);
1386 * This assumes that there is only one video stream per channel...
1389 if (media_type == TP_MEDIA_STREAM_TYPE_VIDEO)
1391 if (priv->funnel != NULL)
1395 output = empathy_video_widget_get_element (EMPATHY_VIDEO_WIDGET
1396 (priv->video_output));
1398 gst_element_set_state (output, GST_STATE_NULL);
1399 gst_element_set_state (priv->funnel, GST_STATE_NULL);
1401 gst_bin_remove (GST_BIN (priv->pipeline), output);
1402 gst_bin_remove (GST_BIN (priv->pipeline), priv->funnel);
1403 priv->funnel = NULL;
1406 else if (media_type == TP_MEDIA_STREAM_TYPE_AUDIO)
1408 if (priv->liveadder != NULL)
1410 gst_element_set_state (priv->audio_output, GST_STATE_NULL);
1411 gst_element_set_state (priv->liveadder, GST_STATE_NULL);
1413 gst_bin_remove (GST_BIN (priv->pipeline), priv->audio_output);
1414 gst_bin_remove (GST_BIN (priv->pipeline), priv->liveadder);
1415 priv->liveadder = NULL;
1420 /* Called with global lock held */
1422 empathy_call_window_get_video_sink_pad (EmpathyCallWindow *self)
1424 EmpathyCallWindowPriv *priv = GET_PRIV (self);
1427 if (priv->funnel == NULL)
1431 output = empathy_video_widget_get_element (EMPATHY_VIDEO_WIDGET
1432 (priv->video_output));
1434 priv->funnel = gst_element_factory_make ("fsfunnel", NULL);
1436 gst_bin_add (GST_BIN (priv->pipeline), priv->funnel);
1437 gst_bin_add (GST_BIN (priv->pipeline), output);
1439 gst_element_link (priv->funnel, output);
1441 gst_element_set_state (priv->funnel, GST_STATE_PLAYING);
1442 gst_element_set_state (output, GST_STATE_PLAYING);
1445 pad = gst_element_get_request_pad (priv->funnel, "sink%d");
1450 /* Called with global lock held */
1452 empathy_call_window_get_audio_sink_pad (EmpathyCallWindow *self)
1454 EmpathyCallWindowPriv *priv = GET_PRIV (self);
1457 if (priv->liveadder == NULL)
1459 priv->liveadder = gst_element_factory_make ("liveadder", NULL);
1461 gst_bin_add (GST_BIN (priv->pipeline), priv->liveadder);
1462 gst_bin_add (GST_BIN (priv->pipeline), priv->audio_output);
1464 gst_element_link (priv->liveadder, priv->audio_output);
1466 gst_element_set_state (priv->liveadder, GST_STATE_PLAYING);
1467 gst_element_set_state (priv->audio_output, GST_STATE_PLAYING);
1470 pad = gst_element_get_request_pad (priv->liveadder, "sink%d");
1476 empathy_call_window_update_timer (gpointer user_data)
1478 EmpathyCallWindow *self = EMPATHY_CALL_WINDOW (user_data);
1479 EmpathyCallWindowPriv *priv = GET_PRIV (self);
1483 time_ = g_timer_elapsed (priv->timer, NULL);
1485 /* Translators: number of minutes:seconds the caller has been connected */
1486 str = g_strdup_printf (_("Connected — %d:%02dm"), (int) time_ / 60,
1488 empathy_call_window_status_message (self, str);
1495 display_error (EmpathyCallWindow *self,
1496 EmpathyTpCall *call,
1500 const gchar *details)
1502 EmpathyCallWindowPriv *priv = GET_PRIV (self);
1503 GtkWidget *info_bar;
1504 GtkWidget *content_area;
1511 /* Create info bar */
1512 info_bar = gtk_info_bar_new_with_buttons (GTK_STOCK_CLOSE, GTK_RESPONSE_CLOSE,
1515 gtk_info_bar_set_message_type (GTK_INFO_BAR (info_bar), GTK_MESSAGE_WARNING);
1517 content_area = gtk_info_bar_get_content_area (GTK_INFO_BAR (info_bar));
1519 /* hbox containing the image and the messages vbox */
1520 hbox = gtk_hbox_new (FALSE, 3);
1521 gtk_container_add (GTK_CONTAINER (content_area), hbox);
1524 image = gtk_image_new_from_icon_name (img, GTK_ICON_SIZE_DIALOG);
1525 gtk_box_pack_start (GTK_BOX (hbox), image, FALSE, FALSE, 0);
1527 /* vbox containing the main message and the details expander */
1528 vbox = gtk_vbox_new (FALSE, 3);
1529 gtk_box_pack_start (GTK_BOX (hbox), vbox, TRUE, TRUE, 0);
1532 txt = g_strdup_printf ("<b>%s</b>\n%s", title, desc);
1534 label = gtk_label_new (NULL);
1535 gtk_label_set_markup (GTK_LABEL (label), txt);
1536 gtk_label_set_line_wrap (GTK_LABEL (label), TRUE);
1537 gtk_misc_set_alignment (GTK_MISC (label), 0, 0);
1540 gtk_box_pack_start (GTK_BOX (vbox), label, TRUE, TRUE, 0);
1543 if (details != NULL)
1545 GtkWidget *expander;
1547 expander = gtk_expander_new (_("Technical Details"));
1549 txt = g_strdup_printf ("<i>%s</i>", details);
1551 label = gtk_label_new (NULL);
1552 gtk_label_set_markup (GTK_LABEL (label), txt);
1553 gtk_label_set_line_wrap (GTK_LABEL (label), TRUE);
1554 gtk_misc_set_alignment (GTK_MISC (label), 0, 0);
1557 gtk_container_add (GTK_CONTAINER (expander), label);
1558 gtk_box_pack_start (GTK_BOX (vbox), expander, TRUE, TRUE, 0);
1561 g_signal_connect (info_bar, "response",
1562 G_CALLBACK (gtk_widget_destroy), NULL);
1564 gtk_box_pack_start (GTK_BOX (priv->errors_vbox), info_bar,
1565 FALSE, FALSE, CONTENT_HBOX_CHILDREN_PACKING_PADDING);
1566 gtk_widget_show_all (info_bar);
1570 media_stream_error_to_txt (EmpathyCallWindow *self,
1571 EmpathyTpCall *call,
1573 TpMediaStreamError error)
1575 EmpathyCallWindowPriv *priv = GET_PRIV (self);
1582 case TP_MEDIA_STREAM_ERROR_CODEC_NEGOTIATION_FAILED:
1584 return g_strdup_printf (
1585 _("%s's software does not understand any of the audio formats "
1586 "supported by your computer"),
1587 empathy_contact_get_name (priv->contact));
1589 return g_strdup_printf (
1590 _("%s's software does not understand any of the video formats "
1591 "supported by your computer"),
1592 empathy_contact_get_name (priv->contact));
1594 case TP_MEDIA_STREAM_ERROR_CONNECTION_FAILED:
1595 return g_strdup_printf (
1596 _("Can't establish a connection to %s. "
1597 "One of you might be on a network that does not allow "
1598 "direct connections."),
1599 empathy_contact_get_name (priv->contact));
1601 case TP_MEDIA_STREAM_ERROR_NETWORK_ERROR:
1602 return g_strdup (_("There was a failure on the network"));
1604 case TP_MEDIA_STREAM_ERROR_NO_CODECS:
1606 return g_strdup (_("The audio formats necessary for this call "
1607 "are not installed on your computer"));
1609 return g_strdup (_("The video formats necessary for this call "
1610 "are not installed on your computer"));
1612 case TP_MEDIA_STREAM_ERROR_INVALID_CM_BEHAVIOR:
1613 cm = empathy_tp_call_get_connection_manager (call);
1615 url = g_strdup_printf ("http://bugs.freedesktop.org/enter_bug.cgi?"
1616 "product=Telepathy&component=%s", cm);
1618 result = g_strdup_printf (
1619 _("Something not expected happened in a Telepathy component. "
1620 "Please <a href=\"%s\">report this bug</a> and attach "
1621 "logs gathered from the 'Debug' window in the Help menu."), url);
1626 case TP_MEDIA_STREAM_ERROR_MEDIA_ERROR:
1627 return g_strdup (_("There was a failure in the call engine"));
1635 empathy_call_window_stream_error (EmpathyCallWindow *self,
1636 EmpathyTpCall *call,
1645 desc = media_stream_error_to_txt (self, call, audio, code);
1648 /* No description, use the error message. That's not great as it's not
1649 * localized but it's better than nothing. */
1650 display_error (self, call, icon, title, msg, NULL);
1654 display_error (self, call, icon, title, desc, msg);
1660 empathy_call_window_audio_stream_error (EmpathyTpCall *call,
1663 EmpathyCallWindow *self)
1665 empathy_call_window_stream_error (self, call, TRUE, code, msg,
1666 "gnome-stock-mic", _("Can't establish audio stream"));
1670 empathy_call_window_video_stream_error (EmpathyTpCall *call,
1673 EmpathyCallWindow *self)
1675 empathy_call_window_stream_error (self, call, FALSE, code, msg,
1676 "camera-web", _("Can't establish video stream"));
1680 empathy_call_window_connected (gpointer user_data)
1682 EmpathyCallWindow *self = EMPATHY_CALL_WINDOW (user_data);
1683 EmpathyCallWindowPriv *priv = GET_PRIV (self);
1684 EmpathyTpCall *call;
1685 gboolean can_send_video;
1687 empathy_sound_stop (EMPATHY_SOUND_PHONE_OUTGOING);
1689 can_send_video = priv->video_input != NULL && priv->contact != NULL &&
1690 empathy_contact_can_voip_video (priv->contact);
1692 g_object_get (priv->handler, "tp-call", &call, NULL);
1694 g_signal_connect (call, "notify::video-stream",
1695 G_CALLBACK (empathy_call_window_video_stream_changed_cb), self);
1697 if (empathy_tp_call_has_dtmf (call))
1698 gtk_widget_set_sensitive (priv->dtmf_panel, TRUE);
1700 if (priv->video_input == NULL)
1701 empathy_call_window_set_send_video (self, FALSE);
1703 priv->sending_video = can_send_video ?
1704 empathy_tp_call_is_sending_video (call) : FALSE;
1706 gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (priv->send_video),
1707 priv->sending_video && priv->video_input != NULL);
1708 gtk_toggle_tool_button_set_active (
1709 GTK_TOGGLE_TOOL_BUTTON (priv->camera_button),
1710 priv->sending_video && priv->video_input != NULL);
1711 gtk_widget_set_sensitive (priv->camera_button, can_send_video);
1712 gtk_action_set_sensitive (priv->send_video, can_send_video);
1714 gtk_action_set_sensitive (priv->redial, FALSE);
1715 gtk_widget_set_sensitive (priv->redial_button, FALSE);
1717 gtk_widget_set_sensitive (priv->mic_button, TRUE);
1719 empathy_call_window_update_avatars_visibility (call, self);
1721 g_object_unref (call);
1723 g_mutex_lock (priv->lock);
1725 priv->timer_id = g_timeout_add_seconds (1,
1726 empathy_call_window_update_timer, self);
1728 g_mutex_unlock (priv->lock);
1730 empathy_call_window_update_timer (self);
1736 /* Called from the streaming thread */
1738 empathy_call_window_src_added_cb (EmpathyCallHandler *handler,
1739 GstPad *src, guint media_type, gpointer user_data)
1741 EmpathyCallWindow *self = EMPATHY_CALL_WINDOW (user_data);
1742 EmpathyCallWindowPriv *priv = GET_PRIV (self);
1746 g_mutex_lock (priv->lock);
1748 if (priv->call_state != CONNECTED)
1750 g_timer_start (priv->timer);
1751 priv->timer_id = g_idle_add (empathy_call_window_connected, self);
1752 priv->call_state = CONNECTED;
1757 case TP_MEDIA_STREAM_TYPE_AUDIO:
1758 pad = empathy_call_window_get_audio_sink_pad (self);
1760 case TP_MEDIA_STREAM_TYPE_VIDEO:
1761 gtk_widget_hide (priv->remote_user_avatar_widget);
1762 gtk_widget_show (priv->video_output);
1763 pad = empathy_call_window_get_video_sink_pad (self);
1766 g_assert_not_reached ();
1769 gst_pad_link (src, pad);
1770 gst_object_unref (pad);
1772 g_mutex_unlock (priv->lock);
1775 /* Called from the streaming thread */
1777 empathy_call_window_sink_added_cb (EmpathyCallHandler *handler,
1778 GstPad *sink, guint media_type, gpointer user_data)
1780 EmpathyCallWindow *self = EMPATHY_CALL_WINDOW (user_data);
1781 EmpathyCallWindowPriv *priv = GET_PRIV (self);
1786 case TP_MEDIA_STREAM_TYPE_AUDIO:
1787 gst_bin_add (GST_BIN (priv->pipeline), priv->audio_input);
1789 pad = gst_element_get_static_pad (priv->audio_input, "src");
1790 gst_pad_link (pad, sink);
1792 gst_element_set_state (priv->audio_input, GST_STATE_PLAYING);
1794 case TP_MEDIA_STREAM_TYPE_VIDEO:
1795 if (priv->video_input != NULL)
1797 if (priv->video_tee != NULL)
1799 pad = gst_element_get_request_pad (priv->video_tee, "src%d");
1800 gst_pad_link (pad, sink);
1805 g_assert_not_reached ();
1811 empathy_call_window_remove_video_input (EmpathyCallWindow *self)
1813 EmpathyCallWindowPriv *priv = GET_PRIV (self);
1814 GstElement *preview;
1816 DEBUG ("remove video input");
1817 preview = empathy_video_widget_get_element (
1818 EMPATHY_VIDEO_WIDGET (priv->video_preview));
1820 gst_element_set_state (priv->video_input, GST_STATE_NULL);
1821 gst_element_set_state (priv->video_tee, GST_STATE_NULL);
1822 gst_element_set_state (preview, GST_STATE_NULL);
1824 gst_bin_remove_many (GST_BIN (priv->pipeline), priv->video_input,
1825 priv->video_tee, preview, NULL);
1827 g_object_unref (priv->video_input);
1828 priv->video_input = NULL;
1829 g_object_unref (priv->video_tee);
1830 priv->video_tee = NULL;
1831 gtk_widget_destroy (priv->video_preview);
1832 priv->video_preview = NULL;
1834 gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (priv->send_video), FALSE);
1835 gtk_toggle_tool_button_set_active (
1836 GTK_TOGGLE_TOOL_BUTTON (priv->camera_button), FALSE);
1837 gtk_widget_set_sensitive (priv->camera_button, FALSE);
1838 gtk_action_set_sensitive (priv->send_video, FALSE);
1840 gtk_widget_show (priv->self_user_avatar_widget);
1845 empathy_call_window_bus_message (GstBus *bus, GstMessage *message,
1848 EmpathyCallWindow *self = EMPATHY_CALL_WINDOW (user_data);
1849 EmpathyCallWindowPriv *priv = GET_PRIV (self);
1852 empathy_call_handler_bus_message (priv->handler, bus, message);
1854 switch (GST_MESSAGE_TYPE (message))
1856 case GST_MESSAGE_STATE_CHANGED:
1857 if (GST_MESSAGE_SRC (message) == GST_OBJECT (priv->video_input))
1859 gst_message_parse_state_changed (message, NULL, &newstate, NULL);
1860 if (newstate == GST_STATE_PAUSED)
1861 empathy_call_window_setup_video_input (self);
1863 if (GST_MESSAGE_SRC (message) == GST_OBJECT (priv->pipeline) &&
1864 !priv->call_started)
1866 gst_message_parse_state_changed (message, NULL, &newstate, NULL);
1867 if (newstate == GST_STATE_PAUSED)
1869 priv->call_started = TRUE;
1870 empathy_call_handler_start_call (priv->handler);
1871 gst_element_set_state (priv->pipeline, GST_STATE_PLAYING);
1875 case GST_MESSAGE_ERROR:
1877 GError *error = NULL;
1878 GstElement *gst_error;
1881 gst_message_parse_error (message, &error, &debug);
1882 gst_error = GST_ELEMENT (GST_MESSAGE_SRC (message));
1884 g_message ("Element error: %s -- %s\n", error->message, debug);
1886 if (g_str_has_prefix (gst_element_get_name (gst_error),
1887 VIDEO_INPUT_ERROR_PREFIX))
1889 /* Remove the video input and continue */
1890 if (priv->video_input != NULL)
1891 empathy_call_window_remove_video_input (self);
1892 gst_element_set_state (priv->pipeline, GST_STATE_PLAYING);
1896 empathy_call_window_disconnected (self);
1898 g_error_free (error);
1909 empathy_call_window_update_avatars_visibility (EmpathyTpCall *call,
1910 EmpathyCallWindow *window)
1912 EmpathyCallWindowPriv *priv = GET_PRIV (window);
1914 if (empathy_tp_call_is_receiving_video (call))
1916 gtk_widget_hide (priv->remote_user_avatar_widget);
1917 gtk_widget_show (priv->video_output);
1921 gtk_widget_hide (priv->video_output);
1922 gtk_widget_show (priv->remote_user_avatar_widget);
1927 call_handler_notify_tp_call_cb (EmpathyCallHandler *handler,
1929 EmpathyCallWindow *self)
1931 EmpathyCallWindowPriv *priv = GET_PRIV (self);
1932 EmpathyTpCall *call;
1934 g_object_get (priv->handler, "tp-call", &call, NULL);
1938 empathy_signal_connect_weak (call, "audio-stream-error",
1939 G_CALLBACK (empathy_call_window_audio_stream_error), G_OBJECT (self));
1940 empathy_signal_connect_weak (call, "video-stream-error",
1941 G_CALLBACK (empathy_call_window_video_stream_error), G_OBJECT (self));
1943 g_object_unref (call);
1947 empathy_call_window_realized_cb (GtkWidget *widget, EmpathyCallWindow *window)
1949 EmpathyCallWindowPriv *priv = GET_PRIV (window);
1950 EmpathyTpCall *call;
1952 g_signal_connect (priv->handler, "conference-added",
1953 G_CALLBACK (empathy_call_window_conference_added_cb), window);
1954 g_signal_connect (priv->handler, "request-resource",
1955 G_CALLBACK (empathy_call_window_request_resource_cb), window);
1956 g_signal_connect (priv->handler, "closed",
1957 G_CALLBACK (empathy_call_window_channel_closed_cb), window);
1958 g_signal_connect (priv->handler, "src-pad-added",
1959 G_CALLBACK (empathy_call_window_src_added_cb), window);
1960 g_signal_connect (priv->handler, "sink-pad-added",
1961 G_CALLBACK (empathy_call_window_sink_added_cb), window);
1962 g_signal_connect (priv->handler, "stream-closed",
1963 G_CALLBACK (empathy_call_window_channel_stream_closed_cb), window);
1965 g_object_get (priv->handler, "tp-call", &call, NULL);
1968 empathy_signal_connect_weak (call, "audio-stream-error",
1969 G_CALLBACK (empathy_call_window_audio_stream_error), G_OBJECT (window));
1970 empathy_signal_connect_weak (call, "video-stream-error",
1971 G_CALLBACK (empathy_call_window_video_stream_error), G_OBJECT (window));
1973 g_object_unref (call);
1977 /* tp-call doesn't exist yet, we'll connect signals once it has been
1979 g_signal_connect (priv->handler, "notify::tp-call",
1980 G_CALLBACK (call_handler_notify_tp_call_cb), window);
1983 gst_element_set_state (priv->pipeline, GST_STATE_PAUSED);
1987 empathy_call_window_delete_cb (GtkWidget *widget, GdkEvent*event,
1988 EmpathyCallWindow *window)
1990 EmpathyCallWindowPriv *priv = GET_PRIV (window);
1992 if (priv->pipeline != NULL)
1994 if (priv->bus_message_source_id != 0)
1996 g_source_remove (priv->bus_message_source_id);
1997 priv->bus_message_source_id = 0;
2000 gst_element_set_state (priv->pipeline, GST_STATE_NULL);
2003 if (priv->call_state == CONNECTING)
2004 empathy_sound_stop (EMPATHY_SOUND_PHONE_OUTGOING);
2010 show_controls (EmpathyCallWindow *window, gboolean set_fullscreen)
2013 EmpathyCallWindowPriv *priv = GET_PRIV (window);
2015 menu = gtk_ui_manager_get_widget (priv->ui_manager,
2020 gtk_widget_hide (priv->sidebar);
2021 gtk_widget_hide (menu);
2022 gtk_widget_hide (priv->vbox);
2023 gtk_widget_hide (priv->statusbar);
2024 gtk_widget_hide (priv->toolbar);
2028 if (priv->sidebar_was_visible_before_fs)
2029 gtk_widget_show (priv->sidebar);
2031 gtk_widget_show (menu);
2032 gtk_widget_show (priv->vbox);
2033 gtk_widget_show (priv->statusbar);
2034 gtk_widget_show (priv->toolbar);
2036 gtk_window_resize (GTK_WINDOW (window), priv->original_width_before_fs,
2037 priv->original_height_before_fs);
2042 show_borders (EmpathyCallWindow *window, gboolean set_fullscreen)
2044 EmpathyCallWindowPriv *priv = GET_PRIV (window);
2046 gtk_container_set_border_width (GTK_CONTAINER (priv->content_hbox),
2047 set_fullscreen ? 0 : CONTENT_HBOX_BORDER_WIDTH);
2048 gtk_box_set_spacing (GTK_BOX (priv->content_hbox),
2049 set_fullscreen ? 0 : CONTENT_HBOX_SPACING);
2050 gtk_box_set_child_packing (GTK_BOX (priv->content_hbox),
2051 priv->video_output, TRUE, TRUE,
2052 set_fullscreen ? 0 : CONTENT_HBOX_CHILDREN_PACKING_PADDING,
2054 gtk_box_set_child_packing (GTK_BOX (priv->content_hbox),
2055 priv->vbox, TRUE, TRUE,
2056 set_fullscreen ? 0 : CONTENT_HBOX_CHILDREN_PACKING_PADDING,
2061 empathy_call_window_state_event_cb (GtkWidget *widget,
2062 GdkEventWindowState *event, EmpathyCallWindow *window)
2064 if (event->changed_mask & GDK_WINDOW_STATE_FULLSCREEN)
2066 EmpathyCallWindowPriv *priv = GET_PRIV (window);
2067 gboolean set_fullscreen = event->new_window_state &
2068 GDK_WINDOW_STATE_FULLSCREEN;
2072 gboolean sidebar_was_visible;
2073 GtkAllocation allocation;
2074 gint original_width, original_height;
2076 gtk_widget_get_allocation (GTK_WIDGET (window), &allocation);
2077 original_width = allocation.width;
2078 original_height = allocation.height;
2080 g_object_get (priv->sidebar, "visible", &sidebar_was_visible, NULL);
2082 priv->sidebar_was_visible_before_fs = sidebar_was_visible;
2083 priv->original_width_before_fs = original_width;
2084 priv->original_height_before_fs = original_height;
2086 if (priv->video_output_motion_handler_id == 0 &&
2087 priv->video_output != NULL)
2089 priv->video_output_motion_handler_id = g_signal_connect (
2090 G_OBJECT (priv->video_output), "motion-notify-event",
2091 G_CALLBACK (empathy_call_window_video_output_motion_notify),
2097 if (priv->video_output_motion_handler_id != 0)
2099 g_signal_handler_disconnect (G_OBJECT (priv->video_output),
2100 priv->video_output_motion_handler_id);
2101 priv->video_output_motion_handler_id = 0;
2105 empathy_call_window_fullscreen_set_fullscreen (priv->fullscreen,
2107 show_controls (window, set_fullscreen);
2108 show_borders (window, set_fullscreen);
2109 gtk_action_set_stock_id (priv->menu_fullscreen,
2110 (set_fullscreen ? "gtk-leave-fullscreen" : "gtk-fullscreen"));
2111 priv->is_fullscreen = set_fullscreen;
2118 empathy_call_window_sidebar_toggled_cb (GtkToggleButton *toggle,
2119 EmpathyCallWindow *window)
2121 EmpathyCallWindowPriv *priv = GET_PRIV (window);
2123 int w, h, handle_size;
2124 GtkAllocation allocation, sidebar_allocation;
2126 gtk_widget_get_allocation (GTK_WIDGET (window), &allocation);
2127 w = allocation.width;
2128 h = allocation.height;
2130 gtk_widget_style_get (priv->pane, "handle_size", &handle_size, NULL);
2132 gtk_widget_get_allocation (priv->sidebar, &sidebar_allocation);
2133 if (gtk_toggle_button_get_active (toggle))
2135 arrow = gtk_arrow_new (GTK_ARROW_LEFT, GTK_SHADOW_NONE);
2136 gtk_widget_show (priv->sidebar);
2137 w += sidebar_allocation.width + handle_size;
2141 arrow = gtk_arrow_new (GTK_ARROW_RIGHT, GTK_SHADOW_NONE);
2142 w -= sidebar_allocation.width + handle_size;
2143 gtk_widget_hide (priv->sidebar);
2146 gtk_button_set_image (GTK_BUTTON (priv->sidebar_button), arrow);
2149 gtk_window_resize (GTK_WINDOW (window), w, h);
2153 empathy_call_window_set_send_video (EmpathyCallWindow *window,
2156 EmpathyCallWindowPriv *priv = GET_PRIV (window);
2157 EmpathyTpCall *call;
2159 priv->sending_video = send;
2161 /* When we start sending video, we want to show the video preview by
2163 display_video_preview (window, send);
2165 g_object_get (priv->handler, "tp-call", &call, NULL);
2166 empathy_tp_call_request_video_stream_direction (call, send);
2167 g_object_unref (call);
2171 empathy_call_window_camera_toggled_cb (GtkToggleToolButton *toggle,
2172 EmpathyCallWindow *window)
2174 EmpathyCallWindowPriv *priv = GET_PRIV (window);
2177 if (priv->call_state != CONNECTED)
2180 active = (gtk_toggle_tool_button_get_active (toggle));
2182 if (priv->sending_video == active)
2185 empathy_call_window_set_send_video (window, active);
2186 gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (priv->send_video), active);
2190 empathy_call_window_send_video_toggled_cb (GtkToggleAction *toggle,
2191 EmpathyCallWindow *window)
2193 EmpathyCallWindowPriv *priv = GET_PRIV (window);
2196 if (priv->call_state != CONNECTED)
2199 active = (gtk_toggle_action_get_active (toggle));
2201 if (priv->sending_video == active)
2204 empathy_call_window_set_send_video (window, active);
2205 gtk_toggle_tool_button_set_active (
2206 GTK_TOGGLE_TOOL_BUTTON (priv->camera_button), active);
2210 empathy_call_window_always_show_preview_toggled_cb (GtkToggleAction *toggle,
2211 EmpathyCallWindow *window)
2213 EmpathyCallWindowPriv *priv = GET_PRIV (window);
2215 if (gtk_toggle_action_get_active (toggle))
2217 display_video_preview (window, TRUE);
2221 /* disable preview if we are not sending */
2222 if (!priv->sending_video)
2223 display_video_preview (window, FALSE);
2228 empathy_call_window_mic_toggled_cb (GtkToggleToolButton *toggle,
2229 EmpathyCallWindow *window)
2231 EmpathyCallWindowPriv *priv = GET_PRIV (window);
2234 if (priv->audio_input == NULL)
2237 active = (gtk_toggle_tool_button_get_active (toggle));
2241 empathy_audio_src_set_volume (EMPATHY_GST_AUDIO_SRC (priv->audio_input),
2243 gtk_adjustment_set_value (priv->audio_input_adj, priv->volume * 100);
2247 /* TODO, Instead of setting the input volume to 0 we should probably
2248 * stop sending but this would cause the audio call to drop if both
2249 * sides mute at the same time on certain CMs AFAIK. Need to revisit this
2250 * in the future. GNOME #574574
2252 empathy_audio_src_set_volume (EMPATHY_GST_AUDIO_SRC (priv->audio_input),
2254 gtk_adjustment_set_value (priv->audio_input_adj, 0);
2259 empathy_call_window_sidebar_hidden_cb (EmpathySidebar *sidebar,
2260 EmpathyCallWindow *window)
2262 EmpathyCallWindowPriv *priv = GET_PRIV (window);
2264 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (priv->sidebar_button),
2269 empathy_call_window_sidebar_shown_cb (EmpathySidebar *sidebar,
2270 EmpathyCallWindow *window)
2272 EmpathyCallWindowPriv *priv = GET_PRIV (window);
2274 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (priv->sidebar_button),
2279 empathy_call_window_hangup_cb (gpointer object,
2280 EmpathyCallWindow *window)
2282 if (empathy_call_window_disconnected (window))
2283 gtk_widget_destroy (GTK_WIDGET (window));
2287 empathy_call_window_restart_call (EmpathyCallWindow *window)
2290 EmpathyCallWindowPriv *priv = GET_PRIV (window);
2292 gtk_widget_destroy (priv->remote_user_output_hbox);
2293 gtk_widget_destroy (priv->self_user_output_hbox);
2295 priv->pipeline = gst_pipeline_new (NULL);
2296 bus = gst_pipeline_get_bus (GST_PIPELINE (priv->pipeline));
2297 priv->bus_message_source_id = gst_bus_add_watch (bus,
2298 empathy_call_window_bus_message, window);
2300 empathy_call_window_setup_remote_frame (bus, window);
2301 empathy_call_window_setup_self_frame (bus, window);
2303 g_signal_connect (G_OBJECT (priv->audio_input_adj), "value-changed",
2304 G_CALLBACK (empathy_call_window_mic_volume_changed_cb), window);
2306 /* While the call was disconnected, the input volume might have changed.
2307 * However, since the audio_input source was destroyed, its volume has not
2308 * been updated during that time. That's why we manually update it here */
2309 empathy_call_window_mic_volume_changed_cb (priv->audio_input_adj, window);
2311 g_object_unref (bus);
2313 gtk_widget_show_all (priv->content_hbox);
2315 priv->outgoing = TRUE;
2316 empathy_call_window_set_state_connecting (window);
2318 priv->call_started = TRUE;
2319 empathy_call_handler_start_call (priv->handler);
2320 empathy_call_window_setup_avatars (window, priv->handler);
2321 gst_element_set_state (priv->pipeline, GST_STATE_PLAYING);
2323 gtk_action_set_sensitive (priv->redial, FALSE);
2324 gtk_widget_set_sensitive (priv->redial_button, FALSE);
2328 empathy_call_window_redial_cb (gpointer object,
2329 EmpathyCallWindow *window)
2331 EmpathyCallWindowPriv *priv = GET_PRIV (window);
2333 if (priv->call_state == CONNECTED)
2334 priv->call_state = REDIALING;
2336 empathy_call_handler_stop_call (priv->handler);
2338 if (priv->call_state != CONNECTED)
2339 empathy_call_window_restart_call (window);
2343 empathy_call_window_fullscreen_cb (gpointer object,
2344 EmpathyCallWindow *window)
2346 empathy_call_window_fullscreen_toggle (window);
2350 empathy_call_window_fullscreen_toggle (EmpathyCallWindow *window)
2352 EmpathyCallWindowPriv *priv = GET_PRIV (window);
2354 if (priv->is_fullscreen)
2355 gtk_window_unfullscreen (GTK_WINDOW (window));
2357 gtk_window_fullscreen (GTK_WINDOW (window));
2361 empathy_call_window_video_button_press_cb (GtkWidget *video_output,
2362 GdkEventButton *event, EmpathyCallWindow *window)
2364 if (event->button == 3 && event->type == GDK_BUTTON_PRESS)
2366 empathy_call_window_video_menu_popup (window, event->button);
2374 empathy_call_window_key_press_cb (GtkWidget *video_output,
2375 GdkEventKey *event, EmpathyCallWindow *window)
2377 EmpathyCallWindowPriv *priv = GET_PRIV (window);
2379 if (priv->is_fullscreen && event->keyval == GDK_Escape)
2381 /* Since we are in fullscreen mode, toggling will bring us back to
2383 empathy_call_window_fullscreen_toggle (window);
2391 empathy_call_window_video_output_motion_notify (GtkWidget *widget,
2392 GdkEventMotion *event, EmpathyCallWindow *window)
2394 EmpathyCallWindowPriv *priv = GET_PRIV (window);
2396 if (priv->is_fullscreen)
2398 empathy_call_window_fullscreen_show_popup (priv->fullscreen);
2405 empathy_call_window_video_menu_popup (EmpathyCallWindow *window,
2409 EmpathyCallWindowPriv *priv = GET_PRIV (window);
2411 menu = gtk_ui_manager_get_widget (priv->ui_manager,
2413 gtk_menu_popup (GTK_MENU (menu), NULL, NULL, NULL, NULL,
2414 button, gtk_get_current_event_time ());
2415 gtk_menu_shell_select_first (GTK_MENU_SHELL (menu), FALSE);
2419 empathy_call_window_status_message (EmpathyCallWindow *window,
2422 EmpathyCallWindowPriv *priv = GET_PRIV (window);
2424 if (priv->context_id == 0)
2426 priv->context_id = gtk_statusbar_get_context_id (
2427 GTK_STATUSBAR (priv->statusbar), "voip call status messages");
2431 gtk_statusbar_pop (GTK_STATUSBAR (priv->statusbar), priv->context_id);
2434 gtk_statusbar_push (GTK_STATUSBAR (priv->statusbar), priv->context_id,
2439 empathy_call_window_volume_changed_cb (GtkScaleButton *button,
2440 gdouble value, EmpathyCallWindow *window)
2442 EmpathyCallWindowPriv *priv = GET_PRIV (window);
2444 if (priv->audio_output == NULL)
2447 empathy_audio_sink_set_volume (EMPATHY_GST_AUDIO_SINK (priv->audio_output),