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;
129 GtkWidget *tool_button_camera_off;
131 /* The frames and boxes that contain self and remote avatar and video
132 input/output. When we redial, we destroy and re-create the boxes */
133 GtkWidget *remote_user_output_frame;
134 GtkWidget *self_user_output_frame;
135 GtkWidget *remote_user_output_hbox;
136 GtkWidget *self_user_output_hbox;
138 /* We keep a reference on the hbox which contains the main content so we can
139 easilly repack everything when toggling fullscreen */
140 GtkWidget *content_hbox;
142 /* This vbox is contained in the content_hbox and it contains the
143 self_user_output_frame and the sidebar button. When toggling fullscreen,
144 it needs to be repacked. We keep a reference on it for easier access. */
147 gulong video_output_motion_handler_id;
148 guint bus_message_source_id;
151 GtkWidget *volume_scale;
152 GtkWidget *volume_progress_bar;
153 GtkAdjustment *audio_input_adj;
155 GtkWidget *dtmf_panel;
157 GstElement *video_input;
158 GstElement *audio_input;
159 GstElement *audio_output;
160 GstElement *pipeline;
161 GstElement *video_tee;
164 GstElement *liveadder;
166 FsElementAddedNotifier *fsnotifier;
173 GtkWidget *video_contrast;
174 GtkWidget *video_brightness;
175 GtkWidget *video_gamma;
178 gboolean call_started;
179 gboolean sending_video;
181 EmpathyCallWindowFullscreen *fullscreen;
182 gboolean is_fullscreen;
184 /* Those fields represent the state of the window before it actually was in
186 gboolean sidebar_was_visible_before_fs;
187 gint original_width_before_fs;
188 gint original_height_before_fs;
191 #define GET_PRIV(o) \
192 (G_TYPE_INSTANCE_GET_PRIVATE ((o), EMPATHY_TYPE_CALL_WINDOW, \
193 EmpathyCallWindowPriv))
195 static void empathy_call_window_realized_cb (GtkWidget *widget,
196 EmpathyCallWindow *window);
198 static gboolean empathy_call_window_delete_cb (GtkWidget *widget,
199 GdkEvent *event, EmpathyCallWindow *window);
201 static gboolean empathy_call_window_state_event_cb (GtkWidget *widget,
202 GdkEventWindowState *event, EmpathyCallWindow *window);
204 static void empathy_call_window_sidebar_toggled_cb (GtkToggleButton *toggle,
205 EmpathyCallWindow *window);
207 static void empathy_call_window_camera_toggled_cb (GtkToggleToolButton *toggle,
208 EmpathyCallWindow *window);
210 static void empathy_call_window_set_send_video (EmpathyCallWindow *window,
213 static void empathy_call_window_send_video_toggled_cb (GtkToggleAction *toggle,
214 EmpathyCallWindow *window);
216 static void empathy_call_window_always_show_preview_toggled_cb (
217 GtkToggleAction *toggle, EmpathyCallWindow *window);
219 static void empathy_call_window_mic_toggled_cb (
220 GtkToggleToolButton *toggle, EmpathyCallWindow *window);
222 static void empathy_call_window_sidebar_hidden_cb (EmpathySidebar *sidebar,
223 EmpathyCallWindow *window);
225 static void empathy_call_window_sidebar_shown_cb (EmpathySidebar *sidebar,
226 EmpathyCallWindow *window);
228 static void empathy_call_window_hangup_cb (gpointer object,
229 EmpathyCallWindow *window);
231 static void empathy_call_window_fullscreen_cb (gpointer object,
232 EmpathyCallWindow *window);
234 static void empathy_call_window_fullscreen_toggle (EmpathyCallWindow *window);
236 static gboolean empathy_call_window_video_button_press_cb (
237 GtkWidget *video_output, GdkEventButton *event, EmpathyCallWindow *window);
239 static gboolean empathy_call_window_key_press_cb (GtkWidget *video_output,
240 GdkEventKey *event, EmpathyCallWindow *window);
242 static gboolean empathy_call_window_video_output_motion_notify (
243 GtkWidget *widget, GdkEventMotion *event, EmpathyCallWindow *window);
245 static void empathy_call_window_video_menu_popup (EmpathyCallWindow *window,
248 static void empathy_call_window_redial_cb (gpointer object,
249 EmpathyCallWindow *window);
251 static void empathy_call_window_restart_call (EmpathyCallWindow *window);
253 static void empathy_call_window_status_message (EmpathyCallWindow *window,
256 static void empathy_call_window_update_avatars_visibility (EmpathyTpCall *call,
257 EmpathyCallWindow *window);
259 static gboolean empathy_call_window_bus_message (GstBus *bus,
260 GstMessage *message, gpointer user_data);
263 empathy_call_window_volume_changed_cb (GtkScaleButton *button,
264 gdouble value, EmpathyCallWindow *window);
267 empathy_call_window_setup_toolbar (EmpathyCallWindow *self)
269 EmpathyCallWindowPriv *priv = GET_PRIV (self);
270 GtkToolItem *tool_item;
271 GtkWidget *camera_off_icon;
272 GdkPixbuf *pixbuf, *modded_pixbuf;
274 /* set the icon of the 'camera off' button by greying off the webcam icon */
275 pixbuf = empathy_pixbuf_from_icon_name ("camera-web",
276 GTK_ICON_SIZE_SMALL_TOOLBAR);
278 modded_pixbuf = gdk_pixbuf_new (GDK_COLORSPACE_RGB, TRUE, 8,
279 gdk_pixbuf_get_width (pixbuf),
280 gdk_pixbuf_get_height (pixbuf));
282 gdk_pixbuf_saturate_and_pixelate (pixbuf, modded_pixbuf, 1.0, TRUE);
283 g_object_unref (pixbuf);
285 camera_off_icon = gtk_image_new_from_pixbuf (modded_pixbuf);
286 g_object_unref (modded_pixbuf);
287 gtk_tool_button_set_icon_widget (GTK_TOOL_BUTTON (
288 priv->tool_button_camera_off), camera_off_icon);
290 /* Add an empty expanded GtkToolItem so the volume button is at the end of
292 tool_item = gtk_tool_item_new ();
293 gtk_tool_item_set_expand (tool_item, TRUE);
294 gtk_widget_show (GTK_WIDGET (tool_item));
295 gtk_toolbar_insert (GTK_TOOLBAR (priv->toolbar), tool_item, -1);
297 priv->volume_button = gtk_volume_button_new ();
298 /* FIXME listen to the audiosinks signals and update the button according to
299 * that, for now starting out at 1.0 and assuming only the app changes the
301 gtk_scale_button_set_value (GTK_SCALE_BUTTON (priv->volume_button), 1.0);
302 g_signal_connect (G_OBJECT (priv->volume_button), "value-changed",
303 G_CALLBACK (empathy_call_window_volume_changed_cb), self);
305 tool_item = gtk_tool_item_new ();
306 gtk_container_add (GTK_CONTAINER (tool_item), priv->volume_button);
307 gtk_widget_show_all (GTK_WIDGET (tool_item));
308 gtk_toolbar_insert (GTK_TOOLBAR (priv->toolbar), tool_item, -1);
312 dtmf_button_pressed_cb (GtkButton *button, EmpathyCallWindow *window)
314 EmpathyCallWindowPriv *priv = GET_PRIV (window);
319 g_object_get (priv->handler, "tp-call", &call, NULL);
321 button_quark = g_quark_from_static_string (BUTTON_ID);
322 event = GPOINTER_TO_UINT (g_object_get_qdata (G_OBJECT (button),
325 empathy_tp_call_start_tone (call, event);
327 g_object_unref (call);
331 dtmf_button_released_cb (GtkButton *button, EmpathyCallWindow *window)
333 EmpathyCallWindowPriv *priv = GET_PRIV (window);
336 g_object_get (priv->handler, "tp-call", &call, NULL);
338 empathy_tp_call_stop_tone (call);
340 g_object_unref (call);
344 empathy_call_window_create_dtmf (EmpathyCallWindow *self)
352 } dtmfbuttons[] = { { "1", TP_DTMF_EVENT_DIGIT_1 },
353 { "2", TP_DTMF_EVENT_DIGIT_2 },
354 { "3", TP_DTMF_EVENT_DIGIT_3 },
355 { "4", TP_DTMF_EVENT_DIGIT_4 },
356 { "5", TP_DTMF_EVENT_DIGIT_5 },
357 { "6", TP_DTMF_EVENT_DIGIT_6 },
358 { "7", TP_DTMF_EVENT_DIGIT_7 },
359 { "8", TP_DTMF_EVENT_DIGIT_8 },
360 { "9", TP_DTMF_EVENT_DIGIT_9 },
361 { "#", TP_DTMF_EVENT_HASH },
362 { "0", TP_DTMF_EVENT_DIGIT_0 },
363 { "*", TP_DTMF_EVENT_ASTERISK },
366 button_quark = g_quark_from_static_string (BUTTON_ID);
368 table = gtk_table_new (4, 3, TRUE);
370 for (i = 0; dtmfbuttons[i].label != NULL; i++)
372 GtkWidget *button = gtk_button_new_with_label (dtmfbuttons[i].label);
373 gtk_table_attach (GTK_TABLE (table), button, i % 3, i % 3 + 1,
374 i/3, i/3 + 1, GTK_EXPAND | GTK_FILL, GTK_EXPAND | GTK_FILL, 1, 1);
376 g_object_set_qdata (G_OBJECT (button), button_quark,
377 GUINT_TO_POINTER (dtmfbuttons[i].event));
379 g_signal_connect (G_OBJECT (button), "pressed",
380 G_CALLBACK (dtmf_button_pressed_cb), self);
381 g_signal_connect (G_OBJECT (button), "released",
382 G_CALLBACK (dtmf_button_released_cb), self);
389 empathy_call_window_create_video_input_add_slider (EmpathyCallWindow *self,
390 gchar *label_text, GtkWidget *bin)
392 GtkWidget *vbox = gtk_vbox_new (FALSE, 2);
393 GtkWidget *scale = gtk_vscale_new_with_range (0, 100, 10);
394 GtkWidget *label = gtk_label_new (label_text);
396 gtk_widget_set_sensitive (scale, FALSE);
398 gtk_container_add (GTK_CONTAINER (bin), vbox);
400 gtk_range_set_inverted (GTK_RANGE (scale), TRUE);
401 gtk_box_pack_start (GTK_BOX (vbox), scale, TRUE, TRUE, 0);
402 gtk_box_pack_start (GTK_BOX (vbox), label, FALSE, FALSE, 0);
408 empathy_call_window_video_contrast_changed_cb (GtkAdjustment *adj,
409 EmpathyCallWindow *self)
412 EmpathyCallWindowPriv *priv = GET_PRIV (self);
414 empathy_video_src_set_channel (priv->video_input,
415 EMPATHY_GST_VIDEO_SRC_CHANNEL_CONTRAST, gtk_adjustment_get_value (adj));
419 empathy_call_window_video_brightness_changed_cb (GtkAdjustment *adj,
420 EmpathyCallWindow *self)
423 EmpathyCallWindowPriv *priv = GET_PRIV (self);
425 empathy_video_src_set_channel (priv->video_input,
426 EMPATHY_GST_VIDEO_SRC_CHANNEL_BRIGHTNESS, gtk_adjustment_get_value (adj));
430 empathy_call_window_video_gamma_changed_cb (GtkAdjustment *adj,
431 EmpathyCallWindow *self)
434 EmpathyCallWindowPriv *priv = GET_PRIV (self);
436 empathy_video_src_set_channel (priv->video_input,
437 EMPATHY_GST_VIDEO_SRC_CHANNEL_GAMMA, gtk_adjustment_get_value (adj));
442 empathy_call_window_create_video_input (EmpathyCallWindow *self)
444 EmpathyCallWindowPriv *priv = GET_PRIV (self);
447 hbox = gtk_hbox_new (TRUE, 3);
449 priv->video_contrast = empathy_call_window_create_video_input_add_slider (
450 self, _("Contrast"), hbox);
452 priv->video_brightness = empathy_call_window_create_video_input_add_slider (
453 self, _("Brightness"), hbox);
455 priv->video_gamma = empathy_call_window_create_video_input_add_slider (
456 self, _("Gamma"), hbox);
462 empathy_call_window_setup_video_input (EmpathyCallWindow *self)
464 EmpathyCallWindowPriv *priv = GET_PRIV (self);
468 supported = empathy_video_src_get_supported_channels (priv->video_input);
470 if (supported & EMPATHY_GST_VIDEO_SRC_SUPPORTS_CONTRAST)
472 adj = gtk_range_get_adjustment (GTK_RANGE (priv->video_contrast));
474 gtk_adjustment_set_value (adj,
475 empathy_video_src_get_channel (priv->video_input,
476 EMPATHY_GST_VIDEO_SRC_CHANNEL_CONTRAST));
478 g_signal_connect (G_OBJECT (adj), "value-changed",
479 G_CALLBACK (empathy_call_window_video_contrast_changed_cb), self);
481 gtk_widget_set_sensitive (priv->video_contrast, TRUE);
484 if (supported & EMPATHY_GST_VIDEO_SRC_SUPPORTS_BRIGHTNESS)
486 adj = gtk_range_get_adjustment (GTK_RANGE (priv->video_brightness));
488 gtk_adjustment_set_value (adj,
489 empathy_video_src_get_channel (priv->video_input,
490 EMPATHY_GST_VIDEO_SRC_CHANNEL_BRIGHTNESS));
492 g_signal_connect (G_OBJECT (adj), "value-changed",
493 G_CALLBACK (empathy_call_window_video_brightness_changed_cb), self);
494 gtk_widget_set_sensitive (priv->video_brightness, TRUE);
497 if (supported & EMPATHY_GST_VIDEO_SRC_SUPPORTS_GAMMA)
499 adj = gtk_range_get_adjustment (GTK_RANGE (priv->video_gamma));
501 gtk_adjustment_set_value (adj,
502 empathy_video_src_get_channel (priv->video_input,
503 EMPATHY_GST_VIDEO_SRC_CHANNEL_GAMMA));
505 g_signal_connect (G_OBJECT (adj), "value-changed",
506 G_CALLBACK (empathy_call_window_video_gamma_changed_cb), self);
507 gtk_widget_set_sensitive (priv->video_gamma, TRUE);
512 empathy_call_window_mic_volume_changed_cb (GtkAdjustment *adj,
513 EmpathyCallWindow *self)
515 EmpathyCallWindowPriv *priv = GET_PRIV (self);
518 if (priv->audio_input == NULL)
521 volume = gtk_adjustment_get_value (adj)/100.0;
523 /* Don't store the volume because of muting */
524 if (volume > 0 || gtk_toggle_tool_button_get_active (
525 GTK_TOGGLE_TOOL_BUTTON (priv->mic_button)))
526 priv->volume = volume;
528 /* Ensure that the toggle button is active if the volume is > 0 and inactive
529 * if it's smaller than 0 */
530 if ((volume > 0) != gtk_toggle_tool_button_get_active (
531 GTK_TOGGLE_TOOL_BUTTON (priv->mic_button)))
532 gtk_toggle_tool_button_set_active (
533 GTK_TOGGLE_TOOL_BUTTON (priv->mic_button), volume > 0);
535 empathy_audio_src_set_volume (EMPATHY_GST_AUDIO_SRC (priv->audio_input),
540 empathy_call_window_audio_input_level_changed_cb (EmpathyGstAudioSrc *src,
541 gdouble level, EmpathyCallWindow *window)
544 EmpathyCallWindowPriv *priv = GET_PRIV (window);
546 value = CLAMP (pow (10, level / 20), 0.0, 1.0);
547 gtk_progress_bar_set_fraction (GTK_PROGRESS_BAR (priv->volume_progress_bar),
552 empathy_call_window_create_audio_input (EmpathyCallWindow *self)
554 EmpathyCallWindowPriv *priv = GET_PRIV (self);
555 GtkWidget *hbox, *vbox, *label;
557 hbox = gtk_hbox_new (TRUE, 3);
559 vbox = gtk_vbox_new (FALSE, 3);
560 gtk_box_pack_start (GTK_BOX (hbox), vbox, FALSE, FALSE, 3);
562 priv->volume_scale = gtk_vscale_new_with_range (0, 150, 100);
563 gtk_range_set_inverted (GTK_RANGE (priv->volume_scale), TRUE);
564 label = gtk_label_new (_("Volume"));
566 priv->audio_input_adj = gtk_range_get_adjustment (
567 GTK_RANGE (priv->volume_scale));
568 priv->volume = empathy_audio_src_get_volume (EMPATHY_GST_AUDIO_SRC
569 (priv->audio_input));
570 gtk_adjustment_set_value (priv->audio_input_adj, priv->volume * 100);
572 g_signal_connect (G_OBJECT (priv->audio_input_adj), "value-changed",
573 G_CALLBACK (empathy_call_window_mic_volume_changed_cb), self);
575 gtk_box_pack_start (GTK_BOX (vbox), priv->volume_scale, TRUE, TRUE, 3);
576 gtk_box_pack_start (GTK_BOX (vbox), label, FALSE, FALSE, 3);
578 priv->volume_progress_bar = gtk_progress_bar_new ();
579 gtk_progress_bar_set_orientation (
580 GTK_PROGRESS_BAR (priv->volume_progress_bar),
581 GTK_PROGRESS_BOTTOM_TO_TOP);
582 gtk_progress_bar_set_fraction (GTK_PROGRESS_BAR (priv->volume_progress_bar),
585 gtk_box_pack_start (GTK_BOX (hbox), priv->volume_progress_bar, FALSE, FALSE,
592 empathy_call_window_setup_remote_frame (GstBus *bus, EmpathyCallWindow *self)
594 EmpathyCallWindowPriv *priv = GET_PRIV (self);
596 /* Initializing all the content (UI and output gst elements) related to the
598 priv->remote_user_output_hbox = gtk_hbox_new (FALSE, 0);
600 priv->remote_user_avatar_widget = gtk_image_new ();
601 gtk_box_pack_start (GTK_BOX (priv->remote_user_output_hbox),
602 priv->remote_user_avatar_widget, TRUE, TRUE, 0);
604 priv->video_output = empathy_video_widget_new (bus);
605 gtk_box_pack_start (GTK_BOX (priv->remote_user_output_hbox),
606 priv->video_output, TRUE, TRUE, 0);
608 gtk_widget_add_events (priv->video_output,
609 GDK_BUTTON_PRESS_MASK | GDK_POINTER_MOTION_MASK);
610 g_signal_connect (G_OBJECT (priv->video_output), "button-press-event",
611 G_CALLBACK (empathy_call_window_video_button_press_cb), self);
613 gtk_container_add (GTK_CONTAINER (priv->remote_user_output_frame),
614 priv->remote_user_output_hbox);
616 priv->audio_output = empathy_audio_sink_new ();
617 gst_object_ref (priv->audio_output);
618 gst_object_sink (priv->audio_output);
622 empathy_call_window_setup_self_frame (GstBus *bus, EmpathyCallWindow *self)
624 EmpathyCallWindowPriv *priv = GET_PRIV (self);
626 /* Initializing all the content (UI and input gst elements) related to the
627 self contact, except for the video preview widget. This widget is only
628 initialized when the "show video preview" option is activated */
629 priv->self_user_output_hbox = gtk_hbox_new (FALSE, 0);
631 priv->self_user_avatar_widget = gtk_image_new ();
632 gtk_box_pack_start (GTK_BOX (priv->self_user_output_hbox),
633 priv->self_user_avatar_widget, TRUE, TRUE, 0);
635 gtk_container_add (GTK_CONTAINER (priv->self_user_output_frame),
636 priv->self_user_output_hbox);
638 priv->video_input = empathy_video_src_new ();
639 gst_object_ref (priv->video_input);
640 gst_object_sink (priv->video_input);
642 priv->audio_input = empathy_audio_src_new ();
643 gst_object_ref (priv->audio_input);
644 gst_object_sink (priv->audio_input);
646 empathy_signal_connect_weak (priv->audio_input, "peak-level-changed",
647 G_CALLBACK (empathy_call_window_audio_input_level_changed_cb),
652 empathy_call_window_setup_video_preview (EmpathyCallWindow *window)
654 EmpathyCallWindowPriv *priv = GET_PRIV (window);
656 GstBus *bus = gst_pipeline_get_bus (GST_PIPELINE (priv->pipeline));
658 if (priv->video_preview != NULL)
660 /* Since the video preview and the video tee are initialized and freed
661 at the same time, if one is initialized, then the other one should
663 g_assert (priv->video_tee != NULL);
667 DEBUG ("Create video preview");
668 g_assert (priv->video_tee == NULL);
670 priv->video_tee = gst_element_factory_make ("tee", NULL);
671 gst_object_ref (priv->video_tee);
672 gst_object_sink (priv->video_tee);
674 priv->video_preview = empathy_video_widget_new_with_size (bus,
675 SELF_VIDEO_SECTION_WIDTH, SELF_VIDEO_SECTION_HEIGTH);
676 g_object_set (priv->video_preview, "sync", FALSE, "async", TRUE, NULL);
677 gtk_box_pack_start (GTK_BOX (priv->self_user_output_hbox),
678 priv->video_preview, TRUE, TRUE, 0);
680 preview = empathy_video_widget_get_element (
681 EMPATHY_VIDEO_WIDGET (priv->video_preview));
682 gst_bin_add_many (GST_BIN (priv->pipeline), priv->video_input,
683 priv->video_tee, preview, NULL);
684 gst_element_link_many (priv->video_input, priv->video_tee,
687 g_object_unref (bus);
689 gst_element_set_state (preview, GST_STATE_PLAYING);
690 gst_element_set_state (priv->video_input, GST_STATE_PLAYING);
691 gst_element_set_state (priv->video_tee, GST_STATE_PLAYING);
695 display_video_preview (EmpathyCallWindow *self,
698 EmpathyCallWindowPriv *priv = GET_PRIV (self);
702 /* Display the preview and hide the self avatar */
703 DEBUG ("Show video preview");
705 if (priv->video_preview == NULL)
706 empathy_call_window_setup_video_preview (self);
707 gtk_widget_show (priv->video_preview);
708 gtk_widget_hide (priv->self_user_avatar_widget);
712 /* Display the self avatar and hide the preview */
713 DEBUG ("Show self avatar");
715 if (priv->video_preview != NULL)
716 gtk_widget_hide (priv->video_preview);
717 gtk_widget_show (priv->self_user_avatar_widget);
722 empathy_call_window_set_state_connecting (EmpathyCallWindow *window)
724 EmpathyCallWindowPriv *priv = GET_PRIV (window);
726 empathy_call_window_status_message (window, _("Connecting..."));
727 priv->call_state = CONNECTING;
730 empathy_sound_start_playing (GTK_WIDGET (window),
731 EMPATHY_SOUND_PHONE_OUTGOING, MS_BETWEEN_RING);
735 empathy_call_window_init (EmpathyCallWindow *self)
737 EmpathyCallWindowPriv *priv = GET_PRIV (self);
746 GError *error = NULL;
748 filename = empathy_file_lookup ("empathy-call-window.ui", "src");
749 gui = empathy_builder_get_file (filename,
750 "call_window_vbox", &top_vbox,
751 "errors_vbox", &priv->errors_vbox,
753 "statusbar", &priv->statusbar,
754 "redial", &priv->redial_button,
755 "microphone", &priv->mic_button,
756 "camera", &priv->camera_button,
757 "toolbar", &priv->toolbar,
758 "send_video", &priv->send_video,
759 "menuredial", &priv->redial,
760 "always_show_preview", &priv->always_show_preview,
761 "ui_manager", &priv->ui_manager,
762 "menufullscreen", &priv->menu_fullscreen,
763 "camera_off", &priv->tool_button_camera_off,
767 empathy_builder_connect (gui, self,
768 "menuhangup", "activate", empathy_call_window_hangup_cb,
769 "hangup", "clicked", empathy_call_window_hangup_cb,
770 "menuredial", "activate", empathy_call_window_redial_cb,
771 "redial", "clicked", empathy_call_window_redial_cb,
772 "microphone", "toggled", empathy_call_window_mic_toggled_cb,
773 "camera", "toggled", empathy_call_window_camera_toggled_cb,
774 "send_video", "toggled", empathy_call_window_send_video_toggled_cb,
775 "always_show_preview", "toggled",
776 empathy_call_window_always_show_preview_toggled_cb,
777 "menufullscreen", "activate", empathy_call_window_fullscreen_cb,
780 priv->lock = g_mutex_new ();
782 gtk_container_add (GTK_CONTAINER (self), top_vbox);
784 priv->content_hbox = gtk_hbox_new (FALSE, CONTENT_HBOX_SPACING);
785 gtk_container_set_border_width (GTK_CONTAINER (priv->content_hbox),
786 CONTENT_HBOX_BORDER_WIDTH);
787 gtk_paned_pack1 (GTK_PANED (priv->pane), priv->content_hbox, TRUE, FALSE);
789 priv->pipeline = gst_pipeline_new (NULL);
790 bus = gst_pipeline_get_bus (GST_PIPELINE (priv->pipeline));
791 priv->bus_message_source_id = gst_bus_add_watch (bus,
792 empathy_call_window_bus_message, self);
794 priv->fsnotifier = fs_element_added_notifier_new ();
795 fs_element_added_notifier_add (priv->fsnotifier, GST_BIN (priv->pipeline));
797 keyfile = g_key_file_new ();
798 filename = empathy_file_lookup ("element-properties", "data");
799 if (g_key_file_load_from_file (keyfile, filename, G_KEY_FILE_NONE, &error))
801 fs_element_added_notifier_set_properties_from_keyfile (priv->fsnotifier,
806 g_warning ("Could not load element-properties file: %s", error->message);
807 g_key_file_free (keyfile);
808 g_clear_error (&error);
813 priv->remote_user_output_frame = gtk_frame_new (NULL);
814 gtk_widget_set_size_request (priv->remote_user_output_frame,
815 EMPATHY_VIDEO_WIDGET_DEFAULT_WIDTH, EMPATHY_VIDEO_WIDGET_DEFAULT_HEIGHT);
816 gtk_box_pack_start (GTK_BOX (priv->content_hbox),
817 priv->remote_user_output_frame, TRUE, TRUE,
818 CONTENT_HBOX_CHILDREN_PACKING_PADDING);
819 empathy_call_window_setup_remote_frame (bus, self);
821 priv->self_user_output_frame = gtk_frame_new (NULL);
822 gtk_widget_set_size_request (priv->self_user_output_frame,
823 SELF_VIDEO_SECTION_WIDTH, SELF_VIDEO_SECTION_HEIGTH);
825 priv->vbox = gtk_vbox_new (FALSE, 3);
826 gtk_box_pack_start (GTK_BOX (priv->content_hbox), priv->vbox,
827 FALSE, FALSE, CONTENT_HBOX_CHILDREN_PACKING_PADDING);
828 gtk_box_pack_start (GTK_BOX (priv->vbox), priv->self_user_output_frame,
830 empathy_call_window_setup_self_frame (bus, self);
832 empathy_call_window_setup_toolbar (self);
834 g_object_unref (bus);
836 priv->sidebar_button = gtk_toggle_button_new_with_mnemonic (_("_Sidebar"));
837 arrow = gtk_arrow_new (GTK_ARROW_RIGHT, GTK_SHADOW_NONE);
838 g_signal_connect (G_OBJECT (priv->sidebar_button), "toggled",
839 G_CALLBACK (empathy_call_window_sidebar_toggled_cb), self);
841 gtk_button_set_image (GTK_BUTTON (priv->sidebar_button), arrow);
843 h = gtk_hbox_new (FALSE, 3);
844 gtk_box_pack_end (GTK_BOX (priv->vbox), h, FALSE, FALSE, 3);
845 gtk_box_pack_end (GTK_BOX (h), priv->sidebar_button, FALSE, FALSE, 3);
847 priv->sidebar = empathy_sidebar_new ();
848 g_signal_connect (G_OBJECT (priv->sidebar),
849 "hide", G_CALLBACK (empathy_call_window_sidebar_hidden_cb), self);
850 g_signal_connect (G_OBJECT (priv->sidebar),
851 "show", G_CALLBACK (empathy_call_window_sidebar_shown_cb), self);
852 gtk_paned_pack2 (GTK_PANED (priv->pane), priv->sidebar, FALSE, FALSE);
854 priv->dtmf_panel = empathy_call_window_create_dtmf (self);
855 empathy_sidebar_add_page (EMPATHY_SIDEBAR (priv->sidebar), _("Dialpad"),
858 gtk_widget_set_sensitive (priv->dtmf_panel, FALSE);
860 page = empathy_call_window_create_audio_input (self);
861 empathy_sidebar_add_page (EMPATHY_SIDEBAR (priv->sidebar), _("Audio input"),
864 page = empathy_call_window_create_video_input (self);
865 empathy_sidebar_add_page (EMPATHY_SIDEBAR (priv->sidebar), _("Video input"),
868 gtk_widget_show_all (top_vbox);
870 gtk_widget_hide (priv->sidebar);
872 priv->fullscreen = empathy_call_window_fullscreen_new (self);
873 empathy_call_window_fullscreen_set_video_widget (priv->fullscreen,
875 g_signal_connect (G_OBJECT (priv->fullscreen->leave_fullscreen_button),
876 "clicked", G_CALLBACK (empathy_call_window_fullscreen_cb), self);
878 g_signal_connect (G_OBJECT (self), "realize",
879 G_CALLBACK (empathy_call_window_realized_cb), self);
881 g_signal_connect (G_OBJECT (self), "delete-event",
882 G_CALLBACK (empathy_call_window_delete_cb), self);
884 g_signal_connect (G_OBJECT (self), "window-state-event",
885 G_CALLBACK (empathy_call_window_state_event_cb), self);
887 g_signal_connect (G_OBJECT (self), "key-press-event",
888 G_CALLBACK (empathy_call_window_key_press_cb), self);
890 priv->timer = g_timer_new ();
892 g_object_ref (priv->ui_manager);
893 g_object_unref (gui);
896 /* Instead of specifying a width and a height, we specify only one size. That's
897 because we want a square avatar icon. */
899 init_contact_avatar_with_size (EmpathyContact *contact,
900 GtkWidget *image_widget,
903 GdkPixbuf *pixbuf_avatar = NULL;
907 pixbuf_avatar = empathy_pixbuf_avatar_from_contact_scaled (contact,
911 if (pixbuf_avatar == NULL)
913 pixbuf_avatar = empathy_pixbuf_from_icon_name_sized ("stock_person",
917 gtk_image_set_from_pixbuf (GTK_IMAGE (image_widget), pixbuf_avatar);
921 set_window_title (EmpathyCallWindow *self)
923 EmpathyCallWindowPriv *priv = GET_PRIV (self);
926 /* translators: Call is a noun and %s is the contact name. This string
927 * is used in the window title */
928 tmp = g_strdup_printf (_("Call with %s"),
929 empathy_contact_get_name (priv->contact));
930 gtk_window_set_title (GTK_WINDOW (self), tmp);
935 contact_name_changed_cb (EmpathyContact *contact,
936 GParamSpec *pspec, EmpathyCallWindow *self)
938 set_window_title (self);
942 contact_avatar_changed_cb (EmpathyContact *contact,
943 GParamSpec *pspec, GtkWidget *avatar_widget)
947 size = avatar_widget->allocation.height;
951 /* the widget is not allocated yet, set a default size */
952 size = MIN (REMOTE_CONTACT_AVATAR_DEFAULT_HEIGHT,
953 REMOTE_CONTACT_AVATAR_DEFAULT_WIDTH);
956 init_contact_avatar_with_size (contact, avatar_widget, size);
960 empathy_call_window_got_self_contact_cb (EmpathyTpContactFactory *factory,
961 EmpathyContact *contact, const GError *error, gpointer user_data,
962 GObject *weak_object)
964 EmpathyCallWindow *self = EMPATHY_CALL_WINDOW (user_data);
965 EmpathyCallWindowPriv *priv = GET_PRIV (self);
967 init_contact_avatar_with_size (contact, priv->self_user_avatar_widget,
968 MIN (SELF_VIDEO_SECTION_WIDTH, SELF_VIDEO_SECTION_HEIGTH));
970 g_signal_connect (contact, "notify::avatar",
971 G_CALLBACK (contact_avatar_changed_cb), priv->self_user_avatar_widget);
975 empathy_call_window_setup_avatars (EmpathyCallWindow *self,
976 EmpathyCallHandler *handler)
978 EmpathyCallWindowPriv *priv = GET_PRIV (self);
980 g_object_get (handler, "contact", &(priv->contact), NULL);
982 if (priv->contact != NULL)
984 TpConnection *connection;
985 EmpathyTpContactFactory *factory;
987 set_window_title (self);
989 g_signal_connect (priv->contact, "notify::name",
990 G_CALLBACK (contact_name_changed_cb), self);
991 g_signal_connect (priv->contact, "notify::avatar",
992 G_CALLBACK (contact_avatar_changed_cb),
993 priv->remote_user_avatar_widget);
995 /* Retreiving the self avatar */
996 connection = empathy_contact_get_connection (priv->contact);
997 factory = empathy_tp_contact_factory_dup_singleton (connection);
998 empathy_tp_contact_factory_get_from_handle (factory,
999 tp_connection_get_self_handle (connection),
1000 empathy_call_window_got_self_contact_cb, self, NULL, G_OBJECT (self));
1002 g_object_unref (factory);
1006 g_warning ("call handler doesn't have a contact");
1007 /* translators: Call is a noun. This string is used in the window
1009 gtk_window_set_title (GTK_WINDOW (self), _("Call"));
1011 /* Since we can't access the remote contact, we can't get a connection
1012 to it and can't get the self contact (and its avatar). This means
1013 that we have to manually set the self avatar. */
1014 init_contact_avatar_with_size (NULL, priv->self_user_avatar_widget,
1015 MIN (SELF_VIDEO_SECTION_WIDTH, SELF_VIDEO_SECTION_HEIGTH));
1018 init_contact_avatar_with_size (priv->contact,
1019 priv->remote_user_avatar_widget,
1020 MIN (REMOTE_CONTACT_AVATAR_DEFAULT_WIDTH,
1021 REMOTE_CONTACT_AVATAR_DEFAULT_HEIGHT));
1023 /* The remote avatar is shown by default and will be hidden when we receive
1024 video from the remote side. */
1025 gtk_widget_hide (priv->video_output);
1026 gtk_widget_show (priv->remote_user_avatar_widget);
1030 empathy_call_window_constructed (GObject *object)
1032 EmpathyCallWindow *self = EMPATHY_CALL_WINDOW (object);
1033 EmpathyCallWindowPriv *priv = GET_PRIV (self);
1034 EmpathyTpCall *call;
1036 g_assert (priv->handler != NULL);
1038 g_object_get (priv->handler, "tp-call", &call, NULL);
1039 priv->outgoing = (call == NULL);
1041 g_object_unref (call);
1043 empathy_call_window_setup_avatars (self, priv->handler);
1044 empathy_call_window_set_state_connecting (self);
1046 if (empathy_call_handler_has_initial_video (priv->handler))
1048 /* Enable 'send video' buttons and display the preview */
1049 gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (priv->send_video), TRUE);
1050 gtk_toggle_tool_button_set_active (
1051 GTK_TOGGLE_TOOL_BUTTON (priv->camera_button), TRUE);
1053 display_video_preview (self, TRUE);
1057 static void empathy_call_window_dispose (GObject *object);
1058 static void empathy_call_window_finalize (GObject *object);
1061 empathy_call_window_set_property (GObject *object,
1062 guint property_id, const GValue *value, GParamSpec *pspec)
1064 EmpathyCallWindowPriv *priv = GET_PRIV (object);
1066 switch (property_id)
1068 case PROP_CALL_HANDLER:
1069 priv->handler = g_value_dup_object (value);
1072 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
1077 empathy_call_window_get_property (GObject *object,
1078 guint property_id, GValue *value, GParamSpec *pspec)
1080 EmpathyCallWindowPriv *priv = GET_PRIV (object);
1082 switch (property_id)
1084 case PROP_CALL_HANDLER:
1085 g_value_set_object (value, priv->handler);
1088 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
1093 empathy_call_window_class_init (
1094 EmpathyCallWindowClass *empathy_call_window_class)
1096 GObjectClass *object_class = G_OBJECT_CLASS (empathy_call_window_class);
1097 GParamSpec *param_spec;
1099 g_type_class_add_private (empathy_call_window_class,
1100 sizeof (EmpathyCallWindowPriv));
1102 object_class->constructed = empathy_call_window_constructed;
1103 object_class->set_property = empathy_call_window_set_property;
1104 object_class->get_property = empathy_call_window_get_property;
1106 object_class->dispose = empathy_call_window_dispose;
1107 object_class->finalize = empathy_call_window_finalize;
1109 param_spec = g_param_spec_object ("handler",
1110 "handler", "The call handler",
1111 EMPATHY_TYPE_CALL_HANDLER,
1112 G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS);
1113 g_object_class_install_property (object_class,
1114 PROP_CALL_HANDLER, param_spec);
1118 empathy_call_window_video_stream_changed_cb (EmpathyTpCall *call,
1119 GParamSpec *property, EmpathyCallWindow *self)
1121 DEBUG ("video stream changed");
1122 empathy_call_window_update_avatars_visibility (call, self);
1126 empathy_call_window_dispose (GObject *object)
1128 EmpathyTpCall *call;
1129 EmpathyCallWindow *self = EMPATHY_CALL_WINDOW (object);
1130 EmpathyCallWindowPriv *priv = GET_PRIV (self);
1132 if (priv->dispose_has_run)
1135 priv->dispose_has_run = TRUE;
1137 g_object_get (priv->handler, "tp-call", &call, NULL);
1141 g_signal_handlers_disconnect_by_func (call,
1142 empathy_call_window_video_stream_changed_cb, object);
1143 g_object_unref (call);
1146 if (priv->handler != NULL)
1147 g_object_unref (priv->handler);
1148 priv->handler = NULL;
1150 if (priv->pipeline != NULL)
1151 g_object_unref (priv->pipeline);
1152 priv->pipeline = NULL;
1154 if (priv->video_input != NULL)
1155 g_object_unref (priv->video_input);
1156 priv->video_input = NULL;
1158 if (priv->audio_input != NULL)
1159 g_object_unref (priv->audio_input);
1160 priv->audio_input = NULL;
1162 if (priv->audio_output != NULL)
1163 g_object_unref (priv->audio_output);
1164 priv->audio_output = NULL;
1166 if (priv->video_tee != NULL)
1167 g_object_unref (priv->video_tee);
1168 priv->video_tee = NULL;
1170 if (priv->fsnotifier != NULL)
1171 g_object_unref (priv->fsnotifier);
1172 priv->fsnotifier = NULL;
1174 if (priv->timer_id != 0)
1175 g_source_remove (priv->timer_id);
1178 if (priv->ui_manager != NULL)
1179 g_object_unref (priv->ui_manager);
1180 priv->ui_manager = NULL;
1182 if (priv->contact != NULL)
1184 g_signal_handlers_disconnect_by_func (priv->contact,
1185 contact_name_changed_cb, self);
1186 g_object_unref (priv->contact);
1187 priv->contact = NULL;
1190 /* release any references held by the object here */
1191 if (G_OBJECT_CLASS (empathy_call_window_parent_class)->dispose)
1192 G_OBJECT_CLASS (empathy_call_window_parent_class)->dispose (object);
1196 empathy_call_window_finalize (GObject *object)
1198 EmpathyCallWindow *self = EMPATHY_CALL_WINDOW (object);
1199 EmpathyCallWindowPriv *priv = GET_PRIV (self);
1201 if (priv->video_output_motion_handler_id != 0)
1203 g_signal_handler_disconnect (G_OBJECT (priv->video_output),
1204 priv->video_output_motion_handler_id);
1205 priv->video_output_motion_handler_id = 0;
1208 if (priv->bus_message_source_id != 0)
1210 g_source_remove (priv->bus_message_source_id);
1211 priv->bus_message_source_id = 0;
1214 /* free any data held directly by the object here */
1215 g_mutex_free (priv->lock);
1217 g_timer_destroy (priv->timer);
1219 G_OBJECT_CLASS (empathy_call_window_parent_class)->finalize (object);
1224 empathy_call_window_new (EmpathyCallHandler *handler)
1226 return EMPATHY_CALL_WINDOW (
1227 g_object_new (EMPATHY_TYPE_CALL_WINDOW, "handler", handler, NULL));
1231 empathy_call_window_conference_added_cb (EmpathyCallHandler *handler,
1232 GstElement *conference, gpointer user_data)
1234 EmpathyCallWindow *self = EMPATHY_CALL_WINDOW (user_data);
1235 EmpathyCallWindowPriv *priv = GET_PRIV (self);
1237 gst_bin_add (GST_BIN (priv->pipeline), conference);
1239 gst_element_set_state (conference, GST_STATE_PLAYING);
1243 empathy_call_window_request_resource_cb (EmpathyCallHandler *handler,
1244 FsMediaType type, FsStreamDirection direction, gpointer user_data)
1246 EmpathyCallWindow *self = EMPATHY_CALL_WINDOW (user_data);
1247 EmpathyCallWindowPriv *priv = GET_PRIV (self);
1249 if (type != TP_MEDIA_STREAM_TYPE_VIDEO)
1252 if (direction == FS_DIRECTION_RECV)
1255 /* video and direction is send */
1256 return priv->video_input != NULL;
1260 empathy_call_window_reset_pipeline (EmpathyCallWindow *self)
1262 GstStateChangeReturn state_change_return;
1263 EmpathyCallWindowPriv *priv = GET_PRIV (self);
1265 if (priv->pipeline == NULL)
1268 if (priv->bus_message_source_id != 0)
1270 g_source_remove (priv->bus_message_source_id);
1271 priv->bus_message_source_id = 0;
1274 state_change_return = gst_element_set_state (priv->pipeline, GST_STATE_NULL);
1276 if (state_change_return == GST_STATE_CHANGE_SUCCESS ||
1277 state_change_return == GST_STATE_CHANGE_NO_PREROLL)
1279 if (priv->pipeline != NULL)
1280 g_object_unref (priv->pipeline);
1281 priv->pipeline = NULL;
1283 if (priv->video_input != NULL)
1284 g_object_unref (priv->video_input);
1285 priv->video_input = NULL;
1287 if (priv->audio_input != NULL)
1288 g_object_unref (priv->audio_input);
1289 priv->audio_input = NULL;
1291 g_signal_handlers_disconnect_by_func (priv->audio_input_adj,
1292 empathy_call_window_mic_volume_changed_cb, self);
1294 if (priv->audio_output != NULL)
1295 g_object_unref (priv->audio_output);
1296 priv->audio_output = NULL;
1298 if (priv->video_tee != NULL)
1299 g_object_unref (priv->video_tee);
1300 priv->video_tee = NULL;
1302 if (priv->video_preview != NULL)
1303 gtk_widget_destroy (priv->video_preview);
1304 priv->video_preview = NULL;
1306 priv->liveadder = NULL;
1307 priv->funnel = NULL;
1313 g_message ("Error: could not destroy pipeline. Closing call window");
1314 gtk_widget_destroy (GTK_WIDGET (self));
1321 empathy_call_window_disconnected (EmpathyCallWindow *self)
1323 gboolean could_disconnect = FALSE;
1324 EmpathyCallWindowPriv *priv = GET_PRIV (self);
1325 gboolean could_reset_pipeline = empathy_call_window_reset_pipeline (self);
1327 if (priv->call_state == CONNECTING)
1328 empathy_sound_stop (EMPATHY_SOUND_PHONE_OUTGOING);
1330 if (priv->call_state != REDIALING)
1331 priv->call_state = DISCONNECTED;
1333 if (could_reset_pipeline)
1335 gboolean initial_video = empathy_call_handler_has_initial_video (
1337 g_mutex_lock (priv->lock);
1339 g_timer_stop (priv->timer);
1341 if (priv->timer_id != 0)
1342 g_source_remove (priv->timer_id);
1345 g_mutex_unlock (priv->lock);
1347 empathy_call_window_status_message (self, _("Disconnected"));
1349 gtk_action_set_sensitive (priv->redial, TRUE);
1350 gtk_widget_set_sensitive (priv->redial_button, TRUE);
1352 /* Reseting the send_video, camera_buton and mic_button to their
1354 gtk_widget_set_sensitive (priv->camera_button, FALSE);
1355 gtk_widget_set_sensitive (priv->mic_button, FALSE);
1356 gtk_action_set_sensitive (priv->send_video, FALSE);
1357 gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (priv->send_video),
1359 gtk_toggle_tool_button_set_active (
1360 GTK_TOGGLE_TOOL_BUTTON (priv->camera_button), initial_video);
1361 gtk_toggle_tool_button_set_active (
1362 GTK_TOGGLE_TOOL_BUTTON (priv->mic_button), TRUE);
1364 gtk_progress_bar_set_fraction (
1365 GTK_PROGRESS_BAR (priv->volume_progress_bar), 0);
1367 gtk_widget_hide (priv->video_output);
1368 gtk_widget_show (priv->remote_user_avatar_widget);
1370 priv->sending_video = FALSE;
1371 priv->call_started = FALSE;
1373 could_disconnect = TRUE;
1375 /* TODO: display the self avatar of the preview (depends if the "Always
1376 * Show Video Preview" is enabled or not) */
1379 return could_disconnect;
1384 empathy_call_window_channel_closed_cb (EmpathyCallHandler *handler,
1387 EmpathyCallWindow *self = EMPATHY_CALL_WINDOW (user_data);
1388 EmpathyCallWindowPriv *priv = GET_PRIV (self);
1390 if (empathy_call_window_disconnected (self) && priv->call_state == REDIALING)
1391 empathy_call_window_restart_call (self);
1396 empathy_call_window_channel_stream_closed_cb (EmpathyCallHandler *handler,
1397 TfStream *stream, gpointer user_data)
1399 EmpathyCallWindow *self = EMPATHY_CALL_WINDOW (user_data);
1400 EmpathyCallWindowPriv *priv = GET_PRIV (self);
1403 g_object_get (stream, "media-type", &media_type, NULL);
1406 * This assumes that there is only one video stream per channel...
1409 if (media_type == TP_MEDIA_STREAM_TYPE_VIDEO)
1411 if (priv->funnel != NULL)
1415 output = empathy_video_widget_get_element (EMPATHY_VIDEO_WIDGET
1416 (priv->video_output));
1418 gst_element_set_state (output, GST_STATE_NULL);
1419 gst_element_set_state (priv->funnel, GST_STATE_NULL);
1421 gst_bin_remove (GST_BIN (priv->pipeline), output);
1422 gst_bin_remove (GST_BIN (priv->pipeline), priv->funnel);
1423 priv->funnel = NULL;
1426 else if (media_type == TP_MEDIA_STREAM_TYPE_AUDIO)
1428 if (priv->liveadder != NULL)
1430 gst_element_set_state (priv->audio_output, GST_STATE_NULL);
1431 gst_element_set_state (priv->liveadder, GST_STATE_NULL);
1433 gst_bin_remove (GST_BIN (priv->pipeline), priv->audio_output);
1434 gst_bin_remove (GST_BIN (priv->pipeline), priv->liveadder);
1435 priv->liveadder = NULL;
1440 /* Called with global lock held */
1442 empathy_call_window_get_video_sink_pad (EmpathyCallWindow *self)
1444 EmpathyCallWindowPriv *priv = GET_PRIV (self);
1447 if (priv->funnel == NULL)
1451 output = empathy_video_widget_get_element (EMPATHY_VIDEO_WIDGET
1452 (priv->video_output));
1454 priv->funnel = gst_element_factory_make ("fsfunnel", NULL);
1456 gst_bin_add (GST_BIN (priv->pipeline), priv->funnel);
1457 gst_bin_add (GST_BIN (priv->pipeline), output);
1459 gst_element_link (priv->funnel, output);
1461 gst_element_set_state (priv->funnel, GST_STATE_PLAYING);
1462 gst_element_set_state (output, GST_STATE_PLAYING);
1465 pad = gst_element_get_request_pad (priv->funnel, "sink%d");
1470 /* Called with global lock held */
1472 empathy_call_window_get_audio_sink_pad (EmpathyCallWindow *self)
1474 EmpathyCallWindowPriv *priv = GET_PRIV (self);
1477 if (priv->liveadder == NULL)
1479 priv->liveadder = gst_element_factory_make ("liveadder", NULL);
1481 gst_bin_add (GST_BIN (priv->pipeline), priv->liveadder);
1482 gst_bin_add (GST_BIN (priv->pipeline), priv->audio_output);
1484 gst_element_link (priv->liveadder, priv->audio_output);
1486 gst_element_set_state (priv->liveadder, GST_STATE_PLAYING);
1487 gst_element_set_state (priv->audio_output, GST_STATE_PLAYING);
1490 pad = gst_element_get_request_pad (priv->liveadder, "sink%d");
1496 empathy_call_window_update_timer (gpointer user_data)
1498 EmpathyCallWindow *self = EMPATHY_CALL_WINDOW (user_data);
1499 EmpathyCallWindowPriv *priv = GET_PRIV (self);
1503 time_ = g_timer_elapsed (priv->timer, NULL);
1505 /* Translators: number of minutes:seconds the caller has been connected */
1506 str = g_strdup_printf (_("Connected — %d:%02dm"), (int) time_ / 60,
1508 empathy_call_window_status_message (self, str);
1515 display_error (EmpathyCallWindow *self,
1516 EmpathyTpCall *call,
1520 const gchar *details)
1522 EmpathyCallWindowPriv *priv = GET_PRIV (self);
1523 GtkWidget *info_bar;
1524 GtkWidget *content_area;
1531 /* Create info bar */
1532 info_bar = gtk_info_bar_new_with_buttons (GTK_STOCK_CLOSE, GTK_RESPONSE_CLOSE,
1535 gtk_info_bar_set_message_type (GTK_INFO_BAR (info_bar), GTK_MESSAGE_WARNING);
1537 content_area = gtk_info_bar_get_content_area (GTK_INFO_BAR (info_bar));
1539 /* hbox containing the image and the messages vbox */
1540 hbox = gtk_hbox_new (FALSE, 3);
1541 gtk_container_add (GTK_CONTAINER (content_area), hbox);
1544 image = gtk_image_new_from_icon_name (img, GTK_ICON_SIZE_DIALOG);
1545 gtk_box_pack_start (GTK_BOX (hbox), image, FALSE, FALSE, 0);
1547 /* vbox containing the main message and the details expander */
1548 vbox = gtk_vbox_new (FALSE, 3);
1549 gtk_box_pack_start (GTK_BOX (hbox), vbox, TRUE, TRUE, 0);
1552 txt = g_strdup_printf ("<b>%s</b>\n%s", title, desc);
1554 label = gtk_label_new (NULL);
1555 gtk_label_set_markup (GTK_LABEL (label), txt);
1556 gtk_label_set_line_wrap (GTK_LABEL (label), TRUE);
1557 gtk_misc_set_alignment (GTK_MISC (label), 0, 0);
1560 gtk_box_pack_start (GTK_BOX (vbox), label, TRUE, TRUE, 0);
1563 if (details != NULL)
1565 GtkWidget *expander;
1567 expander = gtk_expander_new (_("Technical Details"));
1569 txt = g_strdup_printf ("<i>%s</i>", details);
1571 label = gtk_label_new (NULL);
1572 gtk_label_set_markup (GTK_LABEL (label), txt);
1573 gtk_label_set_line_wrap (GTK_LABEL (label), TRUE);
1574 gtk_misc_set_alignment (GTK_MISC (label), 0, 0);
1577 gtk_container_add (GTK_CONTAINER (expander), label);
1578 gtk_box_pack_start (GTK_BOX (vbox), expander, TRUE, TRUE, 0);
1581 g_signal_connect (info_bar, "response",
1582 G_CALLBACK (gtk_widget_destroy), NULL);
1584 gtk_box_pack_start (GTK_BOX (priv->errors_vbox), info_bar,
1585 FALSE, FALSE, CONTENT_HBOX_CHILDREN_PACKING_PADDING);
1586 gtk_widget_show_all (info_bar);
1590 media_stream_error_to_txt (EmpathyCallWindow *self,
1591 EmpathyTpCall *call,
1593 TpMediaStreamError error)
1595 EmpathyCallWindowPriv *priv = GET_PRIV (self);
1602 case TP_MEDIA_STREAM_ERROR_CODEC_NEGOTIATION_FAILED:
1604 return g_strdup_printf (
1605 _("%s's software does not understand any of the audio formats "
1606 "supported by your computer"),
1607 empathy_contact_get_name (priv->contact));
1609 return g_strdup_printf (
1610 _("%s's software does not understand any of the video formats "
1611 "supported by your computer"),
1612 empathy_contact_get_name (priv->contact));
1614 case TP_MEDIA_STREAM_ERROR_CONNECTION_FAILED:
1615 return g_strdup_printf (
1616 _("Can't establish a connection to %s. "
1617 "One of you might be on a network that does not allow "
1618 "direct connections."),
1619 empathy_contact_get_name (priv->contact));
1621 case TP_MEDIA_STREAM_ERROR_NETWORK_ERROR:
1622 return g_strdup (_("There was a failure on the network"));
1624 case TP_MEDIA_STREAM_ERROR_NO_CODECS:
1626 return g_strdup (_("The audio formats necessary for this call "
1627 "are not installed on your computer"));
1629 return g_strdup (_("The video formats necessary for this call "
1630 "are not installed on your computer"));
1632 case TP_MEDIA_STREAM_ERROR_INVALID_CM_BEHAVIOR:
1633 cm = empathy_tp_call_get_connection_manager (call);
1635 url = g_strdup_printf ("http://bugs.freedesktop.org/enter_bug.cgi?"
1636 "product=Telepathy&component=%s", cm);
1638 result = g_strdup_printf (
1639 _("Something not expected happened in a Telepathy component. "
1640 "Please <a href=\"%s\">report this bug</a> and attach "
1641 "logs gathered from the 'Debug' window in the Help menu."), url);
1646 case TP_MEDIA_STREAM_ERROR_MEDIA_ERROR:
1647 return g_strdup (_("There was a failure in the call engine"));
1655 empathy_call_window_stream_error (EmpathyCallWindow *self,
1656 EmpathyTpCall *call,
1665 desc = media_stream_error_to_txt (self, call, audio, code);
1668 /* No description, use the error message. That's not great as it's not
1669 * localized but it's better than nothing. */
1670 display_error (self, call, icon, title, msg, NULL);
1674 display_error (self, call, icon, title, desc, msg);
1680 empathy_call_window_audio_stream_error (EmpathyTpCall *call,
1683 EmpathyCallWindow *self)
1685 empathy_call_window_stream_error (self, call, TRUE, code, msg,
1686 "gnome-stock-mic", _("Can't establish audio stream"));
1690 empathy_call_window_video_stream_error (EmpathyTpCall *call,
1693 EmpathyCallWindow *self)
1695 empathy_call_window_stream_error (self, call, FALSE, code, msg,
1696 "camera-web", _("Can't establish video stream"));
1700 empathy_call_window_connected (gpointer user_data)
1702 EmpathyCallWindow *self = EMPATHY_CALL_WINDOW (user_data);
1703 EmpathyCallWindowPriv *priv = GET_PRIV (self);
1704 EmpathyTpCall *call;
1705 gboolean can_send_video;
1707 empathy_sound_stop (EMPATHY_SOUND_PHONE_OUTGOING);
1709 can_send_video = priv->video_input != NULL && priv->contact != NULL &&
1710 empathy_contact_can_voip_video (priv->contact);
1712 g_object_get (priv->handler, "tp-call", &call, NULL);
1714 g_signal_connect (call, "notify::video-stream",
1715 G_CALLBACK (empathy_call_window_video_stream_changed_cb), self);
1717 if (empathy_tp_call_has_dtmf (call))
1718 gtk_widget_set_sensitive (priv->dtmf_panel, TRUE);
1720 if (priv->video_input == NULL)
1721 empathy_call_window_set_send_video (self, FALSE);
1723 priv->sending_video = can_send_video ?
1724 empathy_tp_call_is_sending_video (call) : FALSE;
1726 gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (priv->send_video),
1727 priv->sending_video && priv->video_input != NULL);
1728 gtk_toggle_tool_button_set_active (
1729 GTK_TOGGLE_TOOL_BUTTON (priv->camera_button),
1730 priv->sending_video && priv->video_input != NULL);
1731 gtk_widget_set_sensitive (priv->camera_button, can_send_video);
1732 gtk_action_set_sensitive (priv->send_video, can_send_video);
1734 gtk_action_set_sensitive (priv->redial, FALSE);
1735 gtk_widget_set_sensitive (priv->redial_button, FALSE);
1737 gtk_widget_set_sensitive (priv->mic_button, TRUE);
1739 empathy_call_window_update_avatars_visibility (call, self);
1741 g_object_unref (call);
1743 g_mutex_lock (priv->lock);
1745 priv->timer_id = g_timeout_add_seconds (1,
1746 empathy_call_window_update_timer, self);
1748 g_mutex_unlock (priv->lock);
1750 empathy_call_window_update_timer (self);
1756 /* Called from the streaming thread */
1758 empathy_call_window_src_added_cb (EmpathyCallHandler *handler,
1759 GstPad *src, guint media_type, gpointer user_data)
1761 EmpathyCallWindow *self = EMPATHY_CALL_WINDOW (user_data);
1762 EmpathyCallWindowPriv *priv = GET_PRIV (self);
1766 g_mutex_lock (priv->lock);
1768 if (priv->call_state != CONNECTED)
1770 g_timer_start (priv->timer);
1771 priv->timer_id = g_idle_add (empathy_call_window_connected, self);
1772 priv->call_state = CONNECTED;
1777 case TP_MEDIA_STREAM_TYPE_AUDIO:
1778 pad = empathy_call_window_get_audio_sink_pad (self);
1780 case TP_MEDIA_STREAM_TYPE_VIDEO:
1781 gtk_widget_hide (priv->remote_user_avatar_widget);
1782 gtk_widget_show (priv->video_output);
1783 pad = empathy_call_window_get_video_sink_pad (self);
1786 g_assert_not_reached ();
1789 gst_pad_link (src, pad);
1790 gst_object_unref (pad);
1792 g_mutex_unlock (priv->lock);
1795 /* Called from the streaming thread */
1797 empathy_call_window_sink_added_cb (EmpathyCallHandler *handler,
1798 GstPad *sink, guint media_type, gpointer user_data)
1800 EmpathyCallWindow *self = EMPATHY_CALL_WINDOW (user_data);
1801 EmpathyCallWindowPriv *priv = GET_PRIV (self);
1806 case TP_MEDIA_STREAM_TYPE_AUDIO:
1807 gst_bin_add (GST_BIN (priv->pipeline), priv->audio_input);
1809 pad = gst_element_get_static_pad (priv->audio_input, "src");
1810 gst_pad_link (pad, sink);
1812 gst_element_set_state (priv->audio_input, GST_STATE_PLAYING);
1814 case TP_MEDIA_STREAM_TYPE_VIDEO:
1815 if (priv->video_input != NULL)
1817 if (priv->video_tee != NULL)
1819 pad = gst_element_get_request_pad (priv->video_tee, "src%d");
1820 gst_pad_link (pad, sink);
1825 g_assert_not_reached ();
1831 empathy_call_window_remove_video_input (EmpathyCallWindow *self)
1833 EmpathyCallWindowPriv *priv = GET_PRIV (self);
1834 GstElement *preview;
1836 DEBUG ("remove video input");
1837 preview = empathy_video_widget_get_element (
1838 EMPATHY_VIDEO_WIDGET (priv->video_preview));
1840 gst_element_set_state (priv->video_input, GST_STATE_NULL);
1841 gst_element_set_state (priv->video_tee, GST_STATE_NULL);
1842 gst_element_set_state (preview, GST_STATE_NULL);
1844 gst_bin_remove_many (GST_BIN (priv->pipeline), priv->video_input,
1845 priv->video_tee, preview, NULL);
1847 g_object_unref (priv->video_input);
1848 priv->video_input = NULL;
1849 g_object_unref (priv->video_tee);
1850 priv->video_tee = NULL;
1851 gtk_widget_destroy (priv->video_preview);
1852 priv->video_preview = NULL;
1854 gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (priv->send_video), FALSE);
1855 gtk_toggle_tool_button_set_active (
1856 GTK_TOGGLE_TOOL_BUTTON (priv->camera_button), FALSE);
1857 gtk_widget_set_sensitive (priv->camera_button, FALSE);
1858 gtk_action_set_sensitive (priv->send_video, FALSE);
1860 gtk_widget_show (priv->self_user_avatar_widget);
1865 empathy_call_window_bus_message (GstBus *bus, GstMessage *message,
1868 EmpathyCallWindow *self = EMPATHY_CALL_WINDOW (user_data);
1869 EmpathyCallWindowPriv *priv = GET_PRIV (self);
1872 empathy_call_handler_bus_message (priv->handler, bus, message);
1874 switch (GST_MESSAGE_TYPE (message))
1876 case GST_MESSAGE_STATE_CHANGED:
1877 if (GST_MESSAGE_SRC (message) == GST_OBJECT (priv->video_input))
1879 gst_message_parse_state_changed (message, NULL, &newstate, NULL);
1880 if (newstate == GST_STATE_PAUSED)
1881 empathy_call_window_setup_video_input (self);
1883 if (GST_MESSAGE_SRC (message) == GST_OBJECT (priv->pipeline) &&
1884 !priv->call_started)
1886 gst_message_parse_state_changed (message, NULL, &newstate, NULL);
1887 if (newstate == GST_STATE_PAUSED)
1889 priv->call_started = TRUE;
1890 empathy_call_handler_start_call (priv->handler);
1891 gst_element_set_state (priv->pipeline, GST_STATE_PLAYING);
1895 case GST_MESSAGE_ERROR:
1897 GError *error = NULL;
1898 GstElement *gst_error;
1901 gst_message_parse_error (message, &error, &debug);
1902 gst_error = GST_ELEMENT (GST_MESSAGE_SRC (message));
1904 g_message ("Element error: %s -- %s\n", error->message, debug);
1906 if (g_str_has_prefix (gst_element_get_name (gst_error),
1907 VIDEO_INPUT_ERROR_PREFIX))
1909 /* Remove the video input and continue */
1910 if (priv->video_input != NULL)
1911 empathy_call_window_remove_video_input (self);
1912 gst_element_set_state (priv->pipeline, GST_STATE_PLAYING);
1916 empathy_call_window_disconnected (self);
1918 g_error_free (error);
1929 empathy_call_window_update_avatars_visibility (EmpathyTpCall *call,
1930 EmpathyCallWindow *window)
1932 EmpathyCallWindowPriv *priv = GET_PRIV (window);
1934 if (empathy_tp_call_is_receiving_video (call))
1936 gtk_widget_hide (priv->remote_user_avatar_widget);
1937 gtk_widget_show (priv->video_output);
1941 gtk_widget_hide (priv->video_output);
1942 gtk_widget_show (priv->remote_user_avatar_widget);
1947 call_handler_notify_tp_call_cb (EmpathyCallHandler *handler,
1949 EmpathyCallWindow *self)
1951 EmpathyCallWindowPriv *priv = GET_PRIV (self);
1952 EmpathyTpCall *call;
1954 g_object_get (priv->handler, "tp-call", &call, NULL);
1958 empathy_signal_connect_weak (call, "audio-stream-error",
1959 G_CALLBACK (empathy_call_window_audio_stream_error), G_OBJECT (self));
1960 empathy_signal_connect_weak (call, "video-stream-error",
1961 G_CALLBACK (empathy_call_window_video_stream_error), G_OBJECT (self));
1963 g_object_unref (call);
1967 empathy_call_window_realized_cb (GtkWidget *widget, EmpathyCallWindow *window)
1969 EmpathyCallWindowPriv *priv = GET_PRIV (window);
1970 EmpathyTpCall *call;
1972 g_signal_connect (priv->handler, "conference-added",
1973 G_CALLBACK (empathy_call_window_conference_added_cb), window);
1974 g_signal_connect (priv->handler, "request-resource",
1975 G_CALLBACK (empathy_call_window_request_resource_cb), window);
1976 g_signal_connect (priv->handler, "closed",
1977 G_CALLBACK (empathy_call_window_channel_closed_cb), window);
1978 g_signal_connect (priv->handler, "src-pad-added",
1979 G_CALLBACK (empathy_call_window_src_added_cb), window);
1980 g_signal_connect (priv->handler, "sink-pad-added",
1981 G_CALLBACK (empathy_call_window_sink_added_cb), window);
1982 g_signal_connect (priv->handler, "stream-closed",
1983 G_CALLBACK (empathy_call_window_channel_stream_closed_cb), window);
1985 g_object_get (priv->handler, "tp-call", &call, NULL);
1988 empathy_signal_connect_weak (call, "audio-stream-error",
1989 G_CALLBACK (empathy_call_window_audio_stream_error), G_OBJECT (window));
1990 empathy_signal_connect_weak (call, "video-stream-error",
1991 G_CALLBACK (empathy_call_window_video_stream_error), G_OBJECT (window));
1993 g_object_unref (call);
1997 /* tp-call doesn't exist yet, we'll connect signals once it has been
1999 g_signal_connect (priv->handler, "notify::tp-call",
2000 G_CALLBACK (call_handler_notify_tp_call_cb), window);
2003 gst_element_set_state (priv->pipeline, GST_STATE_PAUSED);
2007 empathy_call_window_delete_cb (GtkWidget *widget, GdkEvent*event,
2008 EmpathyCallWindow *window)
2010 EmpathyCallWindowPriv *priv = GET_PRIV (window);
2012 if (priv->pipeline != NULL)
2014 if (priv->bus_message_source_id != 0)
2016 g_source_remove (priv->bus_message_source_id);
2017 priv->bus_message_source_id = 0;
2020 gst_element_set_state (priv->pipeline, GST_STATE_NULL);
2023 if (priv->call_state == CONNECTING)
2024 empathy_sound_stop (EMPATHY_SOUND_PHONE_OUTGOING);
2030 show_controls (EmpathyCallWindow *window, gboolean set_fullscreen)
2033 EmpathyCallWindowPriv *priv = GET_PRIV (window);
2035 menu = gtk_ui_manager_get_widget (priv->ui_manager,
2040 gtk_widget_hide (priv->sidebar);
2041 gtk_widget_hide (menu);
2042 gtk_widget_hide (priv->vbox);
2043 gtk_widget_hide (priv->statusbar);
2044 gtk_widget_hide (priv->toolbar);
2048 if (priv->sidebar_was_visible_before_fs)
2049 gtk_widget_show (priv->sidebar);
2051 gtk_widget_show (menu);
2052 gtk_widget_show (priv->vbox);
2053 gtk_widget_show (priv->statusbar);
2054 gtk_widget_show (priv->toolbar);
2056 gtk_window_resize (GTK_WINDOW (window), priv->original_width_before_fs,
2057 priv->original_height_before_fs);
2062 show_borders (EmpathyCallWindow *window, gboolean set_fullscreen)
2064 EmpathyCallWindowPriv *priv = GET_PRIV (window);
2066 gtk_container_set_border_width (GTK_CONTAINER (priv->content_hbox),
2067 set_fullscreen ? 0 : CONTENT_HBOX_BORDER_WIDTH);
2068 gtk_box_set_spacing (GTK_BOX (priv->content_hbox),
2069 set_fullscreen ? 0 : CONTENT_HBOX_SPACING);
2070 gtk_box_set_child_packing (GTK_BOX (priv->content_hbox),
2071 priv->video_output, TRUE, TRUE,
2072 set_fullscreen ? 0 : CONTENT_HBOX_CHILDREN_PACKING_PADDING,
2074 gtk_box_set_child_packing (GTK_BOX (priv->content_hbox),
2075 priv->vbox, TRUE, TRUE,
2076 set_fullscreen ? 0 : CONTENT_HBOX_CHILDREN_PACKING_PADDING,
2081 empathy_call_window_state_event_cb (GtkWidget *widget,
2082 GdkEventWindowState *event, EmpathyCallWindow *window)
2084 if (event->changed_mask & GDK_WINDOW_STATE_FULLSCREEN)
2086 EmpathyCallWindowPriv *priv = GET_PRIV (window);
2087 gboolean set_fullscreen = event->new_window_state &
2088 GDK_WINDOW_STATE_FULLSCREEN;
2092 gboolean sidebar_was_visible;
2093 GtkAllocation allocation;
2094 gint original_width, original_height;
2096 gtk_widget_get_allocation (GTK_WIDGET (window), &allocation);
2097 original_width = allocation.width;
2098 original_height = allocation.height;
2100 g_object_get (priv->sidebar, "visible", &sidebar_was_visible, NULL);
2102 priv->sidebar_was_visible_before_fs = sidebar_was_visible;
2103 priv->original_width_before_fs = original_width;
2104 priv->original_height_before_fs = original_height;
2106 if (priv->video_output_motion_handler_id == 0 &&
2107 priv->video_output != NULL)
2109 priv->video_output_motion_handler_id = g_signal_connect (
2110 G_OBJECT (priv->video_output), "motion-notify-event",
2111 G_CALLBACK (empathy_call_window_video_output_motion_notify),
2117 if (priv->video_output_motion_handler_id != 0)
2119 g_signal_handler_disconnect (G_OBJECT (priv->video_output),
2120 priv->video_output_motion_handler_id);
2121 priv->video_output_motion_handler_id = 0;
2125 empathy_call_window_fullscreen_set_fullscreen (priv->fullscreen,
2127 show_controls (window, set_fullscreen);
2128 show_borders (window, set_fullscreen);
2129 gtk_action_set_stock_id (priv->menu_fullscreen,
2130 (set_fullscreen ? "gtk-leave-fullscreen" : "gtk-fullscreen"));
2131 priv->is_fullscreen = set_fullscreen;
2138 empathy_call_window_sidebar_toggled_cb (GtkToggleButton *toggle,
2139 EmpathyCallWindow *window)
2141 EmpathyCallWindowPriv *priv = GET_PRIV (window);
2143 int w, h, handle_size;
2144 GtkAllocation allocation, sidebar_allocation;
2146 gtk_widget_get_allocation (GTK_WIDGET (window), &allocation);
2147 w = allocation.width;
2148 h = allocation.height;
2150 gtk_widget_style_get (priv->pane, "handle_size", &handle_size, NULL);
2152 gtk_widget_get_allocation (priv->sidebar, &sidebar_allocation);
2153 if (gtk_toggle_button_get_active (toggle))
2155 arrow = gtk_arrow_new (GTK_ARROW_LEFT, GTK_SHADOW_NONE);
2156 gtk_widget_show (priv->sidebar);
2157 w += sidebar_allocation.width + handle_size;
2161 arrow = gtk_arrow_new (GTK_ARROW_RIGHT, GTK_SHADOW_NONE);
2162 w -= sidebar_allocation.width + handle_size;
2163 gtk_widget_hide (priv->sidebar);
2166 gtk_button_set_image (GTK_BUTTON (priv->sidebar_button), arrow);
2169 gtk_window_resize (GTK_WINDOW (window), w, h);
2173 empathy_call_window_set_send_video (EmpathyCallWindow *window,
2176 EmpathyCallWindowPriv *priv = GET_PRIV (window);
2177 EmpathyTpCall *call;
2179 priv->sending_video = send;
2181 /* When we start sending video, we want to show the video preview by
2183 display_video_preview (window, send);
2185 g_object_get (priv->handler, "tp-call", &call, NULL);
2186 empathy_tp_call_request_video_stream_direction (call, send);
2187 g_object_unref (call);
2191 empathy_call_window_camera_toggled_cb (GtkToggleToolButton *toggle,
2192 EmpathyCallWindow *window)
2194 EmpathyCallWindowPriv *priv = GET_PRIV (window);
2197 if (priv->call_state != CONNECTED)
2200 active = (gtk_toggle_tool_button_get_active (toggle));
2202 if (priv->sending_video == active)
2205 empathy_call_window_set_send_video (window, active);
2206 gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (priv->send_video), active);
2210 empathy_call_window_send_video_toggled_cb (GtkToggleAction *toggle,
2211 EmpathyCallWindow *window)
2213 EmpathyCallWindowPriv *priv = GET_PRIV (window);
2216 if (priv->call_state != CONNECTED)
2219 active = (gtk_toggle_action_get_active (toggle));
2221 if (priv->sending_video == active)
2224 empathy_call_window_set_send_video (window, active);
2225 gtk_toggle_tool_button_set_active (
2226 GTK_TOGGLE_TOOL_BUTTON (priv->camera_button), active);
2230 empathy_call_window_always_show_preview_toggled_cb (GtkToggleAction *toggle,
2231 EmpathyCallWindow *window)
2233 EmpathyCallWindowPriv *priv = GET_PRIV (window);
2235 if (gtk_toggle_action_get_active (toggle))
2237 display_video_preview (window, TRUE);
2241 /* disable preview if we are not sending */
2242 if (!priv->sending_video)
2243 display_video_preview (window, FALSE);
2248 empathy_call_window_mic_toggled_cb (GtkToggleToolButton *toggle,
2249 EmpathyCallWindow *window)
2251 EmpathyCallWindowPriv *priv = GET_PRIV (window);
2254 if (priv->audio_input == NULL)
2257 active = (gtk_toggle_tool_button_get_active (toggle));
2261 empathy_audio_src_set_volume (EMPATHY_GST_AUDIO_SRC (priv->audio_input),
2263 gtk_adjustment_set_value (priv->audio_input_adj, priv->volume * 100);
2267 /* TODO, Instead of setting the input volume to 0 we should probably
2268 * stop sending but this would cause the audio call to drop if both
2269 * sides mute at the same time on certain CMs AFAIK. Need to revisit this
2270 * in the future. GNOME #574574
2272 empathy_audio_src_set_volume (EMPATHY_GST_AUDIO_SRC (priv->audio_input),
2274 gtk_adjustment_set_value (priv->audio_input_adj, 0);
2279 empathy_call_window_sidebar_hidden_cb (EmpathySidebar *sidebar,
2280 EmpathyCallWindow *window)
2282 EmpathyCallWindowPriv *priv = GET_PRIV (window);
2284 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (priv->sidebar_button),
2289 empathy_call_window_sidebar_shown_cb (EmpathySidebar *sidebar,
2290 EmpathyCallWindow *window)
2292 EmpathyCallWindowPriv *priv = GET_PRIV (window);
2294 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (priv->sidebar_button),
2299 empathy_call_window_hangup_cb (gpointer object,
2300 EmpathyCallWindow *window)
2302 if (empathy_call_window_disconnected (window))
2303 gtk_widget_destroy (GTK_WIDGET (window));
2307 empathy_call_window_restart_call (EmpathyCallWindow *window)
2310 EmpathyCallWindowPriv *priv = GET_PRIV (window);
2312 gtk_widget_destroy (priv->remote_user_output_hbox);
2313 gtk_widget_destroy (priv->self_user_output_hbox);
2315 priv->pipeline = gst_pipeline_new (NULL);
2316 bus = gst_pipeline_get_bus (GST_PIPELINE (priv->pipeline));
2317 priv->bus_message_source_id = gst_bus_add_watch (bus,
2318 empathy_call_window_bus_message, window);
2320 empathy_call_window_setup_remote_frame (bus, window);
2321 empathy_call_window_setup_self_frame (bus, window);
2323 g_signal_connect (G_OBJECT (priv->audio_input_adj), "value-changed",
2324 G_CALLBACK (empathy_call_window_mic_volume_changed_cb), window);
2326 /* While the call was disconnected, the input volume might have changed.
2327 * However, since the audio_input source was destroyed, its volume has not
2328 * been updated during that time. That's why we manually update it here */
2329 empathy_call_window_mic_volume_changed_cb (priv->audio_input_adj, window);
2331 g_object_unref (bus);
2333 gtk_widget_show_all (priv->content_hbox);
2335 priv->outgoing = TRUE;
2336 empathy_call_window_set_state_connecting (window);
2338 priv->call_started = TRUE;
2339 empathy_call_handler_start_call (priv->handler);
2340 empathy_call_window_setup_avatars (window, priv->handler);
2341 gst_element_set_state (priv->pipeline, GST_STATE_PLAYING);
2343 gtk_action_set_sensitive (priv->redial, FALSE);
2344 gtk_widget_set_sensitive (priv->redial_button, FALSE);
2348 empathy_call_window_redial_cb (gpointer object,
2349 EmpathyCallWindow *window)
2351 EmpathyCallWindowPriv *priv = GET_PRIV (window);
2353 if (priv->call_state == CONNECTED)
2354 priv->call_state = REDIALING;
2356 empathy_call_handler_stop_call (priv->handler);
2358 if (priv->call_state != CONNECTED)
2359 empathy_call_window_restart_call (window);
2363 empathy_call_window_fullscreen_cb (gpointer object,
2364 EmpathyCallWindow *window)
2366 empathy_call_window_fullscreen_toggle (window);
2370 empathy_call_window_fullscreen_toggle (EmpathyCallWindow *window)
2372 EmpathyCallWindowPriv *priv = GET_PRIV (window);
2374 if (priv->is_fullscreen)
2375 gtk_window_unfullscreen (GTK_WINDOW (window));
2377 gtk_window_fullscreen (GTK_WINDOW (window));
2381 empathy_call_window_video_button_press_cb (GtkWidget *video_output,
2382 GdkEventButton *event, EmpathyCallWindow *window)
2384 if (event->button == 3 && event->type == GDK_BUTTON_PRESS)
2386 empathy_call_window_video_menu_popup (window, event->button);
2394 empathy_call_window_key_press_cb (GtkWidget *video_output,
2395 GdkEventKey *event, EmpathyCallWindow *window)
2397 EmpathyCallWindowPriv *priv = GET_PRIV (window);
2399 if (priv->is_fullscreen && event->keyval == GDK_Escape)
2401 /* Since we are in fullscreen mode, toggling will bring us back to
2403 empathy_call_window_fullscreen_toggle (window);
2411 empathy_call_window_video_output_motion_notify (GtkWidget *widget,
2412 GdkEventMotion *event, EmpathyCallWindow *window)
2414 EmpathyCallWindowPriv *priv = GET_PRIV (window);
2416 if (priv->is_fullscreen)
2418 empathy_call_window_fullscreen_show_popup (priv->fullscreen);
2425 empathy_call_window_video_menu_popup (EmpathyCallWindow *window,
2429 EmpathyCallWindowPriv *priv = GET_PRIV (window);
2431 menu = gtk_ui_manager_get_widget (priv->ui_manager,
2433 gtk_menu_popup (GTK_MENU (menu), NULL, NULL, NULL, NULL,
2434 button, gtk_get_current_event_time ());
2435 gtk_menu_shell_select_first (GTK_MENU_SHELL (menu), FALSE);
2439 empathy_call_window_status_message (EmpathyCallWindow *window,
2442 EmpathyCallWindowPriv *priv = GET_PRIV (window);
2444 if (priv->context_id == 0)
2446 priv->context_id = gtk_statusbar_get_context_id (
2447 GTK_STATUSBAR (priv->statusbar), "voip call status messages");
2451 gtk_statusbar_pop (GTK_STATUSBAR (priv->statusbar), priv->context_id);
2454 gtk_statusbar_push (GTK_STATUSBAR (priv->statusbar), priv->context_id,
2459 empathy_call_window_volume_changed_cb (GtkScaleButton *button,
2460 gdouble value, EmpathyCallWindow *window)
2462 EmpathyCallWindowPriv *priv = GET_PRIV (window);
2464 if (priv->audio_output == NULL)
2467 empathy_audio_sink_set_volume (EMPATHY_GST_AUDIO_SINK (priv->audio_output),