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;
1356 return could_disconnect;
1361 empathy_call_window_channel_closed_cb (EmpathyCallHandler *handler,
1364 EmpathyCallWindow *self = EMPATHY_CALL_WINDOW (user_data);
1365 EmpathyCallWindowPriv *priv = GET_PRIV (self);
1367 if (empathy_call_window_disconnected (self) && priv->call_state == REDIALING)
1368 empathy_call_window_restart_call (self);
1373 empathy_call_window_channel_stream_closed_cb (EmpathyCallHandler *handler,
1374 TfStream *stream, gpointer user_data)
1376 EmpathyCallWindow *self = EMPATHY_CALL_WINDOW (user_data);
1377 EmpathyCallWindowPriv *priv = GET_PRIV (self);
1380 g_object_get (stream, "media-type", &media_type, NULL);
1383 * This assumes that there is only one video stream per channel...
1386 if (media_type == TP_MEDIA_STREAM_TYPE_VIDEO)
1388 if (priv->funnel != NULL)
1392 output = empathy_video_widget_get_element (EMPATHY_VIDEO_WIDGET
1393 (priv->video_output));
1395 gst_element_set_state (output, GST_STATE_NULL);
1396 gst_element_set_state (priv->funnel, GST_STATE_NULL);
1398 gst_bin_remove (GST_BIN (priv->pipeline), output);
1399 gst_bin_remove (GST_BIN (priv->pipeline), priv->funnel);
1400 priv->funnel = NULL;
1403 else if (media_type == TP_MEDIA_STREAM_TYPE_AUDIO)
1405 if (priv->liveadder != NULL)
1407 gst_element_set_state (priv->audio_output, GST_STATE_NULL);
1408 gst_element_set_state (priv->liveadder, GST_STATE_NULL);
1410 gst_bin_remove (GST_BIN (priv->pipeline), priv->audio_output);
1411 gst_bin_remove (GST_BIN (priv->pipeline), priv->liveadder);
1412 priv->liveadder = NULL;
1417 /* Called with global lock held */
1419 empathy_call_window_get_video_sink_pad (EmpathyCallWindow *self)
1421 EmpathyCallWindowPriv *priv = GET_PRIV (self);
1424 if (priv->funnel == NULL)
1428 output = empathy_video_widget_get_element (EMPATHY_VIDEO_WIDGET
1429 (priv->video_output));
1431 priv->funnel = gst_element_factory_make ("fsfunnel", NULL);
1433 gst_bin_add (GST_BIN (priv->pipeline), priv->funnel);
1434 gst_bin_add (GST_BIN (priv->pipeline), output);
1436 gst_element_link (priv->funnel, output);
1438 gst_element_set_state (priv->funnel, GST_STATE_PLAYING);
1439 gst_element_set_state (output, GST_STATE_PLAYING);
1442 pad = gst_element_get_request_pad (priv->funnel, "sink%d");
1447 /* Called with global lock held */
1449 empathy_call_window_get_audio_sink_pad (EmpathyCallWindow *self)
1451 EmpathyCallWindowPriv *priv = GET_PRIV (self);
1454 if (priv->liveadder == NULL)
1456 priv->liveadder = gst_element_factory_make ("liveadder", NULL);
1458 gst_bin_add (GST_BIN (priv->pipeline), priv->liveadder);
1459 gst_bin_add (GST_BIN (priv->pipeline), priv->audio_output);
1461 gst_element_link (priv->liveadder, priv->audio_output);
1463 gst_element_set_state (priv->liveadder, GST_STATE_PLAYING);
1464 gst_element_set_state (priv->audio_output, GST_STATE_PLAYING);
1467 pad = gst_element_get_request_pad (priv->liveadder, "sink%d");
1473 empathy_call_window_update_timer (gpointer user_data)
1475 EmpathyCallWindow *self = EMPATHY_CALL_WINDOW (user_data);
1476 EmpathyCallWindowPriv *priv = GET_PRIV (self);
1480 time_ = g_timer_elapsed (priv->timer, NULL);
1482 /* Translators: number of minutes:seconds the caller has been connected */
1483 str = g_strdup_printf (_("Connected — %d:%02dm"), (int) time_ / 60,
1485 empathy_call_window_status_message (self, str);
1492 display_error (EmpathyCallWindow *self,
1493 EmpathyTpCall *call,
1497 const gchar *details)
1499 EmpathyCallWindowPriv *priv = GET_PRIV (self);
1500 GtkWidget *info_bar;
1501 GtkWidget *content_area;
1508 /* Create info bar */
1509 info_bar = gtk_info_bar_new_with_buttons (GTK_STOCK_CLOSE, GTK_RESPONSE_CLOSE,
1512 gtk_info_bar_set_message_type (GTK_INFO_BAR (info_bar), GTK_MESSAGE_WARNING);
1514 content_area = gtk_info_bar_get_content_area (GTK_INFO_BAR (info_bar));
1516 /* hbox containing the image and the messages vbox */
1517 hbox = gtk_hbox_new (FALSE, 3);
1518 gtk_container_add (GTK_CONTAINER (content_area), hbox);
1521 image = gtk_image_new_from_icon_name (img, GTK_ICON_SIZE_DIALOG);
1522 gtk_box_pack_start (GTK_BOX (hbox), image, FALSE, FALSE, 0);
1524 /* vbox containing the main message and the details expander */
1525 vbox = gtk_vbox_new (FALSE, 3);
1526 gtk_box_pack_start (GTK_BOX (hbox), vbox, TRUE, TRUE, 0);
1529 txt = g_strdup_printf ("<b>%s</b>\n%s", title, desc);
1531 label = gtk_label_new (NULL);
1532 gtk_label_set_markup (GTK_LABEL (label), txt);
1533 gtk_label_set_line_wrap (GTK_LABEL (label), TRUE);
1534 gtk_misc_set_alignment (GTK_MISC (label), 0, 0);
1537 gtk_box_pack_start (GTK_BOX (vbox), label, TRUE, TRUE, 0);
1540 if (details != NULL)
1542 GtkWidget *expander;
1544 expander = gtk_expander_new (_("Technical Details"));
1546 txt = g_strdup_printf ("<i>%s</i>", details);
1548 label = gtk_label_new (NULL);
1549 gtk_label_set_markup (GTK_LABEL (label), txt);
1550 gtk_label_set_line_wrap (GTK_LABEL (label), TRUE);
1551 gtk_misc_set_alignment (GTK_MISC (label), 0, 0);
1554 gtk_container_add (GTK_CONTAINER (expander), label);
1555 gtk_box_pack_start (GTK_BOX (vbox), expander, TRUE, TRUE, 0);
1558 g_signal_connect (info_bar, "response",
1559 G_CALLBACK (gtk_widget_destroy), NULL);
1561 gtk_box_pack_start (GTK_BOX (priv->errors_vbox), info_bar,
1562 FALSE, FALSE, CONTENT_HBOX_CHILDREN_PACKING_PADDING);
1563 gtk_widget_show_all (info_bar);
1567 media_stream_error_to_txt (EmpathyCallWindow *self,
1568 EmpathyTpCall *call,
1570 TpMediaStreamError error)
1572 EmpathyCallWindowPriv *priv = GET_PRIV (self);
1579 case TP_MEDIA_STREAM_ERROR_CODEC_NEGOTIATION_FAILED:
1581 return g_strdup_printf (
1582 _("%s's software does not understand any of the audio formats "
1583 "supported by your computer"),
1584 empathy_contact_get_name (priv->contact));
1586 return g_strdup_printf (
1587 _("%s's software does not understand any of the video formats "
1588 "supported by your computer"),
1589 empathy_contact_get_name (priv->contact));
1591 case TP_MEDIA_STREAM_ERROR_CONNECTION_FAILED:
1592 return g_strdup_printf (
1593 _("Can't establish a connection to %s. "
1594 "One of you might be on a network that does not allow "
1595 "direct connections."),
1596 empathy_contact_get_name (priv->contact));
1598 case TP_MEDIA_STREAM_ERROR_NETWORK_ERROR:
1599 return g_strdup (_("There was a failure on the network"));
1601 case TP_MEDIA_STREAM_ERROR_NO_CODECS:
1603 return g_strdup (_("The audio formats necessary for this call "
1604 "are not installed on your computer"));
1606 return g_strdup (_("The video formats necessary for this call "
1607 "are not installed on your computer"));
1609 case TP_MEDIA_STREAM_ERROR_INVALID_CM_BEHAVIOR:
1610 cm = empathy_tp_call_get_connection_manager (call);
1612 url = g_strdup_printf ("http://bugs.freedesktop.org/enter_bug.cgi?"
1613 "product=Telepathy&component=%s", cm);
1615 result = g_strdup_printf (
1616 _("Something not expected happened in a Telepathy component. "
1617 "Please <a href=\"%s\">report this bug</a> and attach "
1618 "logs gathered from the 'Debug' window in the Help menu."), url);
1623 case TP_MEDIA_STREAM_ERROR_MEDIA_ERROR:
1624 return g_strdup (_("There was a failure in the call engine"));
1632 empathy_call_window_stream_error (EmpathyCallWindow *self,
1633 EmpathyTpCall *call,
1642 desc = media_stream_error_to_txt (self, call, audio, code);
1645 /* No description, use the error message. That's not great as it's not
1646 * localized but it's better than nothing. */
1647 display_error (self, call, icon, title, msg, NULL);
1651 display_error (self, call, icon, title, desc, msg);
1657 empathy_call_window_audio_stream_error (EmpathyTpCall *call,
1660 EmpathyCallWindow *self)
1662 empathy_call_window_stream_error (self, call, TRUE, code, msg,
1663 "gnome-stock-mic", _("Can't establish audio stream"));
1667 empathy_call_window_video_stream_error (EmpathyTpCall *call,
1670 EmpathyCallWindow *self)
1672 empathy_call_window_stream_error (self, call, FALSE, code, msg,
1673 "camera-web", _("Can't establish video stream"));
1677 empathy_call_window_connected (gpointer user_data)
1679 EmpathyCallWindow *self = EMPATHY_CALL_WINDOW (user_data);
1680 EmpathyCallWindowPriv *priv = GET_PRIV (self);
1681 EmpathyTpCall *call;
1682 gboolean can_send_video;
1684 empathy_sound_stop (EMPATHY_SOUND_PHONE_OUTGOING);
1686 can_send_video = priv->video_input != NULL && priv->contact != NULL &&
1687 empathy_contact_can_voip_video (priv->contact);
1689 g_object_get (priv->handler, "tp-call", &call, NULL);
1691 g_signal_connect (call, "notify::video-stream",
1692 G_CALLBACK (empathy_call_window_video_stream_changed_cb), self);
1694 if (empathy_tp_call_has_dtmf (call))
1695 gtk_widget_set_sensitive (priv->dtmf_panel, TRUE);
1697 if (priv->video_input == NULL)
1698 empathy_call_window_set_send_video (self, FALSE);
1700 priv->sending_video = can_send_video ?
1701 empathy_tp_call_is_sending_video (call) : FALSE;
1703 gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (priv->send_video),
1704 priv->sending_video && priv->video_input != NULL);
1705 gtk_toggle_tool_button_set_active (
1706 GTK_TOGGLE_TOOL_BUTTON (priv->camera_button),
1707 priv->sending_video && priv->video_input != NULL);
1708 gtk_widget_set_sensitive (priv->camera_button, can_send_video);
1709 gtk_action_set_sensitive (priv->send_video, can_send_video);
1711 gtk_action_set_sensitive (priv->redial, FALSE);
1712 gtk_widget_set_sensitive (priv->redial_button, FALSE);
1714 gtk_widget_set_sensitive (priv->mic_button, TRUE);
1716 empathy_call_window_update_avatars_visibility (call, self);
1718 g_object_unref (call);
1720 g_mutex_lock (priv->lock);
1722 priv->timer_id = g_timeout_add_seconds (1,
1723 empathy_call_window_update_timer, self);
1725 g_mutex_unlock (priv->lock);
1727 empathy_call_window_update_timer (self);
1733 /* Called from the streaming thread */
1735 empathy_call_window_src_added_cb (EmpathyCallHandler *handler,
1736 GstPad *src, guint media_type, gpointer user_data)
1738 EmpathyCallWindow *self = EMPATHY_CALL_WINDOW (user_data);
1739 EmpathyCallWindowPriv *priv = GET_PRIV (self);
1743 g_mutex_lock (priv->lock);
1745 if (priv->call_state != CONNECTED)
1747 g_timer_start (priv->timer);
1748 priv->timer_id = g_idle_add (empathy_call_window_connected, self);
1749 priv->call_state = CONNECTED;
1754 case TP_MEDIA_STREAM_TYPE_AUDIO:
1755 pad = empathy_call_window_get_audio_sink_pad (self);
1757 case TP_MEDIA_STREAM_TYPE_VIDEO:
1758 gtk_widget_hide (priv->remote_user_avatar_widget);
1759 gtk_widget_show (priv->video_output);
1760 pad = empathy_call_window_get_video_sink_pad (self);
1763 g_assert_not_reached ();
1766 gst_pad_link (src, pad);
1767 gst_object_unref (pad);
1769 g_mutex_unlock (priv->lock);
1772 /* Called from the streaming thread */
1774 empathy_call_window_sink_added_cb (EmpathyCallHandler *handler,
1775 GstPad *sink, guint media_type, gpointer user_data)
1777 EmpathyCallWindow *self = EMPATHY_CALL_WINDOW (user_data);
1778 EmpathyCallWindowPriv *priv = GET_PRIV (self);
1783 case TP_MEDIA_STREAM_TYPE_AUDIO:
1784 gst_bin_add (GST_BIN (priv->pipeline), priv->audio_input);
1786 pad = gst_element_get_static_pad (priv->audio_input, "src");
1787 gst_pad_link (pad, sink);
1789 gst_element_set_state (priv->audio_input, GST_STATE_PLAYING);
1791 case TP_MEDIA_STREAM_TYPE_VIDEO:
1792 if (priv->video_input != NULL)
1794 if (priv->video_tee != NULL)
1796 pad = gst_element_get_request_pad (priv->video_tee, "src%d");
1797 gst_pad_link (pad, sink);
1802 g_assert_not_reached ();
1808 empathy_call_window_remove_video_input (EmpathyCallWindow *self)
1810 EmpathyCallWindowPriv *priv = GET_PRIV (self);
1811 GstElement *preview;
1813 DEBUG ("remove video input");
1814 preview = empathy_video_widget_get_element (
1815 EMPATHY_VIDEO_WIDGET (priv->video_preview));
1817 gst_element_set_state (priv->video_input, GST_STATE_NULL);
1818 gst_element_set_state (priv->video_tee, GST_STATE_NULL);
1819 gst_element_set_state (preview, GST_STATE_NULL);
1821 gst_bin_remove_many (GST_BIN (priv->pipeline), priv->video_input,
1822 priv->video_tee, preview, NULL);
1824 g_object_unref (priv->video_input);
1825 priv->video_input = NULL;
1826 g_object_unref (priv->video_tee);
1827 priv->video_tee = NULL;
1828 gtk_widget_destroy (priv->video_preview);
1829 priv->video_preview = NULL;
1831 gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (priv->send_video), FALSE);
1832 gtk_toggle_tool_button_set_active (
1833 GTK_TOGGLE_TOOL_BUTTON (priv->camera_button), FALSE);
1834 gtk_widget_set_sensitive (priv->camera_button, FALSE);
1835 gtk_action_set_sensitive (priv->send_video, FALSE);
1837 gtk_widget_show (priv->self_user_avatar_widget);
1842 empathy_call_window_bus_message (GstBus *bus, GstMessage *message,
1845 EmpathyCallWindow *self = EMPATHY_CALL_WINDOW (user_data);
1846 EmpathyCallWindowPriv *priv = GET_PRIV (self);
1849 empathy_call_handler_bus_message (priv->handler, bus, message);
1851 switch (GST_MESSAGE_TYPE (message))
1853 case GST_MESSAGE_STATE_CHANGED:
1854 if (GST_MESSAGE_SRC (message) == GST_OBJECT (priv->video_input))
1856 gst_message_parse_state_changed (message, NULL, &newstate, NULL);
1857 if (newstate == GST_STATE_PAUSED)
1858 empathy_call_window_setup_video_input (self);
1860 if (GST_MESSAGE_SRC (message) == GST_OBJECT (priv->pipeline) &&
1861 !priv->call_started)
1863 gst_message_parse_state_changed (message, NULL, &newstate, NULL);
1864 if (newstate == GST_STATE_PAUSED)
1866 priv->call_started = TRUE;
1867 empathy_call_handler_start_call (priv->handler);
1868 gst_element_set_state (priv->pipeline, GST_STATE_PLAYING);
1872 case GST_MESSAGE_ERROR:
1874 GError *error = NULL;
1875 GstElement *gst_error;
1878 gst_message_parse_error (message, &error, &debug);
1879 gst_error = GST_ELEMENT (GST_MESSAGE_SRC (message));
1881 g_message ("Element error: %s -- %s\n", error->message, debug);
1883 if (g_str_has_prefix (gst_element_get_name (gst_error),
1884 VIDEO_INPUT_ERROR_PREFIX))
1886 /* Remove the video input and continue */
1887 if (priv->video_input != NULL)
1888 empathy_call_window_remove_video_input (self);
1889 gst_element_set_state (priv->pipeline, GST_STATE_PLAYING);
1893 empathy_call_window_disconnected (self);
1895 g_error_free (error);
1906 empathy_call_window_update_avatars_visibility (EmpathyTpCall *call,
1907 EmpathyCallWindow *window)
1909 EmpathyCallWindowPriv *priv = GET_PRIV (window);
1911 if (empathy_tp_call_is_receiving_video (call))
1913 gtk_widget_hide (priv->remote_user_avatar_widget);
1914 gtk_widget_show (priv->video_output);
1918 gtk_widget_hide (priv->video_output);
1919 gtk_widget_show (priv->remote_user_avatar_widget);
1924 call_handler_notify_tp_call_cb (EmpathyCallHandler *handler,
1926 EmpathyCallWindow *self)
1928 EmpathyCallWindowPriv *priv = GET_PRIV (self);
1929 EmpathyTpCall *call;
1931 g_object_get (priv->handler, "tp-call", &call, NULL);
1935 empathy_signal_connect_weak (call, "audio-stream-error",
1936 G_CALLBACK (empathy_call_window_audio_stream_error), G_OBJECT (self));
1937 empathy_signal_connect_weak (call, "video-stream-error",
1938 G_CALLBACK (empathy_call_window_video_stream_error), G_OBJECT (self));
1940 g_object_unref (call);
1944 empathy_call_window_realized_cb (GtkWidget *widget, EmpathyCallWindow *window)
1946 EmpathyCallWindowPriv *priv = GET_PRIV (window);
1947 EmpathyTpCall *call;
1949 g_signal_connect (priv->handler, "conference-added",
1950 G_CALLBACK (empathy_call_window_conference_added_cb), window);
1951 g_signal_connect (priv->handler, "request-resource",
1952 G_CALLBACK (empathy_call_window_request_resource_cb), window);
1953 g_signal_connect (priv->handler, "closed",
1954 G_CALLBACK (empathy_call_window_channel_closed_cb), window);
1955 g_signal_connect (priv->handler, "src-pad-added",
1956 G_CALLBACK (empathy_call_window_src_added_cb), window);
1957 g_signal_connect (priv->handler, "sink-pad-added",
1958 G_CALLBACK (empathy_call_window_sink_added_cb), window);
1959 g_signal_connect (priv->handler, "stream-closed",
1960 G_CALLBACK (empathy_call_window_channel_stream_closed_cb), window);
1962 g_object_get (priv->handler, "tp-call", &call, NULL);
1965 empathy_signal_connect_weak (call, "audio-stream-error",
1966 G_CALLBACK (empathy_call_window_audio_stream_error), G_OBJECT (window));
1967 empathy_signal_connect_weak (call, "video-stream-error",
1968 G_CALLBACK (empathy_call_window_video_stream_error), G_OBJECT (window));
1970 g_object_unref (call);
1974 /* tp-call doesn't exist yet, we'll connect signals once it has been
1976 g_signal_connect (priv->handler, "notify::tp-call",
1977 G_CALLBACK (call_handler_notify_tp_call_cb), window);
1980 gst_element_set_state (priv->pipeline, GST_STATE_PAUSED);
1984 empathy_call_window_delete_cb (GtkWidget *widget, GdkEvent*event,
1985 EmpathyCallWindow *window)
1987 EmpathyCallWindowPriv *priv = GET_PRIV (window);
1989 if (priv->pipeline != NULL)
1991 if (priv->bus_message_source_id != 0)
1993 g_source_remove (priv->bus_message_source_id);
1994 priv->bus_message_source_id = 0;
1997 gst_element_set_state (priv->pipeline, GST_STATE_NULL);
2000 if (priv->call_state == CONNECTING)
2001 empathy_sound_stop (EMPATHY_SOUND_PHONE_OUTGOING);
2007 show_controls (EmpathyCallWindow *window, gboolean set_fullscreen)
2010 EmpathyCallWindowPriv *priv = GET_PRIV (window);
2012 menu = gtk_ui_manager_get_widget (priv->ui_manager,
2017 gtk_widget_hide (priv->sidebar);
2018 gtk_widget_hide (menu);
2019 gtk_widget_hide (priv->vbox);
2020 gtk_widget_hide (priv->statusbar);
2021 gtk_widget_hide (priv->toolbar);
2025 if (priv->sidebar_was_visible_before_fs)
2026 gtk_widget_show (priv->sidebar);
2028 gtk_widget_show (menu);
2029 gtk_widget_show (priv->vbox);
2030 gtk_widget_show (priv->statusbar);
2031 gtk_widget_show (priv->toolbar);
2033 gtk_window_resize (GTK_WINDOW (window), priv->original_width_before_fs,
2034 priv->original_height_before_fs);
2039 show_borders (EmpathyCallWindow *window, gboolean set_fullscreen)
2041 EmpathyCallWindowPriv *priv = GET_PRIV (window);
2043 gtk_container_set_border_width (GTK_CONTAINER (priv->content_hbox),
2044 set_fullscreen ? 0 : CONTENT_HBOX_BORDER_WIDTH);
2045 gtk_box_set_spacing (GTK_BOX (priv->content_hbox),
2046 set_fullscreen ? 0 : CONTENT_HBOX_SPACING);
2047 gtk_box_set_child_packing (GTK_BOX (priv->content_hbox),
2048 priv->video_output, TRUE, TRUE,
2049 set_fullscreen ? 0 : CONTENT_HBOX_CHILDREN_PACKING_PADDING,
2051 gtk_box_set_child_packing (GTK_BOX (priv->content_hbox),
2052 priv->vbox, TRUE, TRUE,
2053 set_fullscreen ? 0 : CONTENT_HBOX_CHILDREN_PACKING_PADDING,
2058 empathy_call_window_state_event_cb (GtkWidget *widget,
2059 GdkEventWindowState *event, EmpathyCallWindow *window)
2061 if (event->changed_mask & GDK_WINDOW_STATE_FULLSCREEN)
2063 EmpathyCallWindowPriv *priv = GET_PRIV (window);
2064 gboolean set_fullscreen = event->new_window_state &
2065 GDK_WINDOW_STATE_FULLSCREEN;
2069 gboolean sidebar_was_visible;
2070 GtkAllocation allocation;
2071 gint original_width, original_height;
2073 gtk_widget_get_allocation (GTK_WIDGET (window), &allocation);
2074 original_width = allocation.width;
2075 original_height = allocation.height;
2077 g_object_get (priv->sidebar, "visible", &sidebar_was_visible, NULL);
2079 priv->sidebar_was_visible_before_fs = sidebar_was_visible;
2080 priv->original_width_before_fs = original_width;
2081 priv->original_height_before_fs = original_height;
2083 if (priv->video_output_motion_handler_id == 0 &&
2084 priv->video_output != NULL)
2086 priv->video_output_motion_handler_id = g_signal_connect (
2087 G_OBJECT (priv->video_output), "motion-notify-event",
2088 G_CALLBACK (empathy_call_window_video_output_motion_notify),
2094 if (priv->video_output_motion_handler_id != 0)
2096 g_signal_handler_disconnect (G_OBJECT (priv->video_output),
2097 priv->video_output_motion_handler_id);
2098 priv->video_output_motion_handler_id = 0;
2102 empathy_call_window_fullscreen_set_fullscreen (priv->fullscreen,
2104 show_controls (window, set_fullscreen);
2105 show_borders (window, set_fullscreen);
2106 gtk_action_set_stock_id (priv->menu_fullscreen,
2107 (set_fullscreen ? "gtk-leave-fullscreen" : "gtk-fullscreen"));
2108 priv->is_fullscreen = set_fullscreen;
2115 empathy_call_window_sidebar_toggled_cb (GtkToggleButton *toggle,
2116 EmpathyCallWindow *window)
2118 EmpathyCallWindowPriv *priv = GET_PRIV (window);
2120 int w, h, handle_size;
2121 GtkAllocation allocation, sidebar_allocation;
2123 gtk_widget_get_allocation (GTK_WIDGET (window), &allocation);
2124 w = allocation.width;
2125 h = allocation.height;
2127 gtk_widget_style_get (priv->pane, "handle_size", &handle_size, NULL);
2129 gtk_widget_get_allocation (priv->sidebar, &sidebar_allocation);
2130 if (gtk_toggle_button_get_active (toggle))
2132 arrow = gtk_arrow_new (GTK_ARROW_LEFT, GTK_SHADOW_NONE);
2133 gtk_widget_show (priv->sidebar);
2134 w += sidebar_allocation.width + handle_size;
2138 arrow = gtk_arrow_new (GTK_ARROW_RIGHT, GTK_SHADOW_NONE);
2139 w -= sidebar_allocation.width + handle_size;
2140 gtk_widget_hide (priv->sidebar);
2143 gtk_button_set_image (GTK_BUTTON (priv->sidebar_button), arrow);
2146 gtk_window_resize (GTK_WINDOW (window), w, h);
2150 empathy_call_window_set_send_video (EmpathyCallWindow *window,
2153 EmpathyCallWindowPriv *priv = GET_PRIV (window);
2154 EmpathyTpCall *call;
2156 priv->sending_video = send;
2158 /* When we start sending video, we want to show the video preview by
2160 display_video_preview (window, send);
2162 g_object_get (priv->handler, "tp-call", &call, NULL);
2163 empathy_tp_call_request_video_stream_direction (call, send);
2164 g_object_unref (call);
2168 empathy_call_window_camera_toggled_cb (GtkToggleToolButton *toggle,
2169 EmpathyCallWindow *window)
2171 EmpathyCallWindowPriv *priv = GET_PRIV (window);
2174 if (priv->call_state != CONNECTED)
2177 active = (gtk_toggle_tool_button_get_active (toggle));
2179 if (priv->sending_video == active)
2182 empathy_call_window_set_send_video (window, active);
2183 gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (priv->send_video), active);
2187 empathy_call_window_send_video_toggled_cb (GtkToggleAction *toggle,
2188 EmpathyCallWindow *window)
2190 EmpathyCallWindowPriv *priv = GET_PRIV (window);
2193 if (priv->call_state != CONNECTED)
2196 active = (gtk_toggle_action_get_active (toggle));
2198 if (priv->sending_video == active)
2201 empathy_call_window_set_send_video (window, active);
2202 gtk_toggle_tool_button_set_active (
2203 GTK_TOGGLE_TOOL_BUTTON (priv->camera_button), active);
2207 empathy_call_window_always_show_preview_toggled_cb (GtkToggleAction *toggle,
2208 EmpathyCallWindow *window)
2210 EmpathyCallWindowPriv *priv = GET_PRIV (window);
2212 if (gtk_toggle_action_get_active (toggle))
2214 display_video_preview (window, TRUE);
2218 /* disable preview if we are not sending */
2219 if (!priv->sending_video)
2220 display_video_preview (window, FALSE);
2225 empathy_call_window_mic_toggled_cb (GtkToggleToolButton *toggle,
2226 EmpathyCallWindow *window)
2228 EmpathyCallWindowPriv *priv = GET_PRIV (window);
2231 if (priv->audio_input == NULL)
2234 active = (gtk_toggle_tool_button_get_active (toggle));
2238 empathy_audio_src_set_volume (EMPATHY_GST_AUDIO_SRC (priv->audio_input),
2240 gtk_adjustment_set_value (priv->audio_input_adj, priv->volume * 100);
2244 /* TODO, Instead of setting the input volume to 0 we should probably
2245 * stop sending but this would cause the audio call to drop if both
2246 * sides mute at the same time on certain CMs AFAIK. Need to revisit this
2247 * in the future. GNOME #574574
2249 empathy_audio_src_set_volume (EMPATHY_GST_AUDIO_SRC (priv->audio_input),
2251 gtk_adjustment_set_value (priv->audio_input_adj, 0);
2256 empathy_call_window_sidebar_hidden_cb (EmpathySidebar *sidebar,
2257 EmpathyCallWindow *window)
2259 EmpathyCallWindowPriv *priv = GET_PRIV (window);
2261 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (priv->sidebar_button),
2266 empathy_call_window_sidebar_shown_cb (EmpathySidebar *sidebar,
2267 EmpathyCallWindow *window)
2269 EmpathyCallWindowPriv *priv = GET_PRIV (window);
2271 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (priv->sidebar_button),
2276 empathy_call_window_hangup_cb (gpointer object,
2277 EmpathyCallWindow *window)
2279 if (empathy_call_window_disconnected (window))
2280 gtk_widget_destroy (GTK_WIDGET (window));
2284 empathy_call_window_restart_call (EmpathyCallWindow *window)
2287 EmpathyCallWindowPriv *priv = GET_PRIV (window);
2289 gtk_widget_destroy (priv->remote_user_output_hbox);
2290 gtk_widget_destroy (priv->self_user_output_hbox);
2292 priv->pipeline = gst_pipeline_new (NULL);
2293 bus = gst_pipeline_get_bus (GST_PIPELINE (priv->pipeline));
2294 priv->bus_message_source_id = gst_bus_add_watch (bus,
2295 empathy_call_window_bus_message, window);
2297 empathy_call_window_setup_remote_frame (bus, window);
2298 empathy_call_window_setup_self_frame (bus, window);
2300 g_signal_connect (G_OBJECT (priv->audio_input_adj), "value-changed",
2301 G_CALLBACK (empathy_call_window_mic_volume_changed_cb), window);
2303 /* While the call was disconnected, the input volume might have changed.
2304 * However, since the audio_input source was destroyed, its volume has not
2305 * been updated during that time. That's why we manually update it here */
2306 empathy_call_window_mic_volume_changed_cb (priv->audio_input_adj, window);
2308 g_object_unref (bus);
2310 gtk_widget_show_all (priv->content_hbox);
2312 priv->outgoing = TRUE;
2313 empathy_call_window_set_state_connecting (window);
2315 priv->call_started = TRUE;
2316 empathy_call_handler_start_call (priv->handler);
2317 empathy_call_window_setup_avatars (window, priv->handler);
2318 gst_element_set_state (priv->pipeline, GST_STATE_PLAYING);
2320 gtk_action_set_sensitive (priv->redial, FALSE);
2321 gtk_widget_set_sensitive (priv->redial_button, FALSE);
2325 empathy_call_window_redial_cb (gpointer object,
2326 EmpathyCallWindow *window)
2328 EmpathyCallWindowPriv *priv = GET_PRIV (window);
2330 if (priv->call_state == CONNECTED)
2331 priv->call_state = REDIALING;
2333 empathy_call_handler_stop_call (priv->handler);
2335 if (priv->call_state != CONNECTED)
2336 empathy_call_window_restart_call (window);
2340 empathy_call_window_fullscreen_cb (gpointer object,
2341 EmpathyCallWindow *window)
2343 empathy_call_window_fullscreen_toggle (window);
2347 empathy_call_window_fullscreen_toggle (EmpathyCallWindow *window)
2349 EmpathyCallWindowPriv *priv = GET_PRIV (window);
2351 if (priv->is_fullscreen)
2352 gtk_window_unfullscreen (GTK_WINDOW (window));
2354 gtk_window_fullscreen (GTK_WINDOW (window));
2358 empathy_call_window_video_button_press_cb (GtkWidget *video_output,
2359 GdkEventButton *event, EmpathyCallWindow *window)
2361 if (event->button == 3 && event->type == GDK_BUTTON_PRESS)
2363 empathy_call_window_video_menu_popup (window, event->button);
2371 empathy_call_window_key_press_cb (GtkWidget *video_output,
2372 GdkEventKey *event, EmpathyCallWindow *window)
2374 EmpathyCallWindowPriv *priv = GET_PRIV (window);
2376 if (priv->is_fullscreen && event->keyval == GDK_Escape)
2378 /* Since we are in fullscreen mode, toggling will bring us back to
2380 empathy_call_window_fullscreen_toggle (window);
2388 empathy_call_window_video_output_motion_notify (GtkWidget *widget,
2389 GdkEventMotion *event, EmpathyCallWindow *window)
2391 EmpathyCallWindowPriv *priv = GET_PRIV (window);
2393 if (priv->is_fullscreen)
2395 empathy_call_window_fullscreen_show_popup (priv->fullscreen);
2402 empathy_call_window_video_menu_popup (EmpathyCallWindow *window,
2406 EmpathyCallWindowPriv *priv = GET_PRIV (window);
2408 menu = gtk_ui_manager_get_widget (priv->ui_manager,
2410 gtk_menu_popup (GTK_MENU (menu), NULL, NULL, NULL, NULL,
2411 button, gtk_get_current_event_time ());
2412 gtk_menu_shell_select_first (GTK_MENU_SHELL (menu), FALSE);
2416 empathy_call_window_status_message (EmpathyCallWindow *window,
2419 EmpathyCallWindowPriv *priv = GET_PRIV (window);
2421 if (priv->context_id == 0)
2423 priv->context_id = gtk_statusbar_get_context_id (
2424 GTK_STATUSBAR (priv->statusbar), "voip call status messages");
2428 gtk_statusbar_pop (GTK_STATUSBAR (priv->statusbar), priv->context_id);
2431 gtk_statusbar_push (GTK_STATUSBAR (priv->statusbar), priv->context_id,
2436 empathy_call_window_volume_changed_cb (GtkScaleButton *button,
2437 gdouble value, EmpathyCallWindow *window)
2439 EmpathyCallWindowPriv *priv = GET_PRIV (window);
2441 if (priv->audio_output == NULL)
2444 empathy_audio_sink_set_volume (EMPATHY_GST_AUDIO_SINK (priv->audio_output),