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 #include "empathy-call-window.h"
48 #include "empathy-call-window-fullscreen.h"
49 #include "empathy-sidebar.h"
51 #define BUTTON_ID "empathy-call-dtmf-button-id"
53 #define CONTENT_HBOX_BORDER_WIDTH 6
54 #define CONTENT_HBOX_SPACING 3
55 #define CONTENT_HBOX_CHILDREN_PACKING_PADDING 3
57 #define SELF_VIDEO_SECTION_WIDTH 160
58 #define SELF_VIDEO_SECTION_HEIGTH 120
60 /* The avatar's default width and height are set to the same value because we
61 want a square icon. */
62 #define REMOTE_CONTACT_AVATAR_DEFAULT_WIDTH EMPATHY_VIDEO_WIDGET_DEFAULT_HEIGHT
63 #define REMOTE_CONTACT_AVATAR_DEFAULT_HEIGHT \
64 EMPATHY_VIDEO_WIDGET_DEFAULT_HEIGHT
66 /* If an video input error occurs, the error message will start with "v4l" */
67 #define VIDEO_INPUT_ERROR_PREFIX "v4l"
69 /* The time interval in milliseconds between 2 outgoing rings */
70 #define MS_BETWEEN_RING 500
72 G_DEFINE_TYPE(EmpathyCallWindow, empathy_call_window, GTK_TYPE_WINDOW)
81 static guint signals[LAST_SIGNAL] = {0};
85 PROP_CALL_HANDLER = 1,
95 /* private structure */
96 typedef struct _EmpathyCallWindowPriv EmpathyCallWindowPriv;
98 struct _EmpathyCallWindowPriv
100 gboolean dispose_has_run;
101 EmpathyCallHandler *handler;
102 EmpathyContact *contact;
107 GtkUIManager *ui_manager;
108 GtkWidget *errors_vbox;
109 GtkWidget *video_output;
110 GtkWidget *video_preview;
111 GtkWidget *remote_user_avatar_widget;
112 GtkWidget *self_user_avatar_widget;
114 GtkWidget *sidebar_button;
115 GtkWidget *statusbar;
116 GtkWidget *volume_button;
117 GtkWidget *redial_button;
118 GtkWidget *mic_button;
119 GtkWidget *camera_button;
122 GtkAction *show_preview;
123 GtkAction *send_video;
125 GtkAction *menu_fullscreen;
127 /* The frames and boxes that contain self and remote avatar and video
128 input/output. When we redial, we destroy and re-create the boxes */
129 GtkWidget *remote_user_output_frame;
130 GtkWidget *self_user_output_frame;
131 GtkWidget *remote_user_output_hbox;
132 GtkWidget *self_user_output_hbox;
134 /* We keep a reference on the hbox which contains the main content so we can
135 easilly repack everything when toggling fullscreen */
136 GtkWidget *content_hbox;
138 /* This vbox is contained in the content_hbox and it contains the
139 self_user_output_frame and the sidebar button. When toggling fullscreen,
140 it needs to be repacked. We keep a reference on it for easier access. */
143 gulong video_output_motion_handler_id;
144 guint bus_message_source_id;
147 GtkWidget *volume_scale;
148 GtkWidget *volume_progress_bar;
149 GtkAdjustment *audio_input_adj;
151 GtkWidget *dtmf_panel;
153 GstElement *video_input;
154 GstElement *audio_input;
155 GstElement *audio_output;
156 GstElement *pipeline;
157 GstElement *video_tee;
160 GstElement *liveadder;
162 FsElementAddedNotifier *fsnotifier;
169 GtkWidget *video_contrast;
170 GtkWidget *video_brightness;
171 GtkWidget *video_gamma;
174 gboolean call_started;
175 gboolean sending_video;
177 EmpathyCallWindowFullscreen *fullscreen;
178 gboolean is_fullscreen;
180 /* Those fields represent the state of the window before it actually was in
182 gboolean sidebar_was_visible_before_fs;
183 gint original_width_before_fs;
184 gint original_height_before_fs;
187 #define GET_PRIV(o) \
188 (G_TYPE_INSTANCE_GET_PRIVATE ((o), EMPATHY_TYPE_CALL_WINDOW, \
189 EmpathyCallWindowPriv))
191 static void empathy_call_window_realized_cb (GtkWidget *widget,
192 EmpathyCallWindow *window);
194 static gboolean empathy_call_window_delete_cb (GtkWidget *widget,
195 GdkEvent *event, EmpathyCallWindow *window);
197 static gboolean empathy_call_window_state_event_cb (GtkWidget *widget,
198 GdkEventWindowState *event, EmpathyCallWindow *window);
200 static void empathy_call_window_sidebar_toggled_cb (GtkToggleButton *toggle,
201 EmpathyCallWindow *window);
203 static void empathy_call_window_camera_toggled_cb (GtkToggleToolButton *toggle,
204 EmpathyCallWindow *window);
206 static void empathy_call_window_set_send_video (EmpathyCallWindow *window,
209 static void empathy_call_window_send_video_toggled_cb (GtkToggleAction *toggle,
210 EmpathyCallWindow *window);
212 static void empathy_call_window_show_preview_toggled_cb (
213 GtkToggleAction *toggle, EmpathyCallWindow *window);
215 static void empathy_call_window_mic_toggled_cb (
216 GtkToggleToolButton *toggle, EmpathyCallWindow *window);
218 static void empathy_call_window_sidebar_hidden_cb (EmpathySidebar *sidebar,
219 EmpathyCallWindow *window);
221 static void empathy_call_window_sidebar_shown_cb (EmpathySidebar *sidebar,
222 EmpathyCallWindow *window);
224 static void empathy_call_window_hangup_cb (gpointer object,
225 EmpathyCallWindow *window);
227 static void empathy_call_window_fullscreen_cb (gpointer object,
228 EmpathyCallWindow *window);
230 static void empathy_call_window_fullscreen_toggle (EmpathyCallWindow *window);
232 static gboolean empathy_call_window_video_button_press_cb (
233 GtkWidget *video_output, GdkEventButton *event, EmpathyCallWindow *window);
235 static gboolean empathy_call_window_key_press_cb (GtkWidget *video_output,
236 GdkEventKey *event, EmpathyCallWindow *window);
238 static gboolean empathy_call_window_video_output_motion_notify (
239 GtkWidget *widget, GdkEventMotion *event, EmpathyCallWindow *window);
241 static void empathy_call_window_video_menu_popup (EmpathyCallWindow *window,
244 static void empathy_call_window_redial_cb (gpointer object,
245 EmpathyCallWindow *window);
247 static void empathy_call_window_restart_call (EmpathyCallWindow *window);
249 static void empathy_call_window_status_message (EmpathyCallWindow *window,
252 static void empathy_call_window_update_avatars_visibility (EmpathyTpCall *call,
253 EmpathyCallWindow *window);
255 static gboolean empathy_call_window_bus_message (GstBus *bus,
256 GstMessage *message, gpointer user_data);
259 empathy_call_window_volume_changed_cb (GtkScaleButton *button,
260 gdouble value, EmpathyCallWindow *window);
263 empathy_call_window_setup_toolbar (EmpathyCallWindow *self)
265 EmpathyCallWindowPriv *priv = GET_PRIV (self);
266 GtkToolItem *tool_item;
268 /* Add an empty expanded GtkToolItem so the volume button is at the end of
270 tool_item = gtk_tool_item_new ();
271 gtk_tool_item_set_expand (tool_item, TRUE);
272 gtk_widget_show (GTK_WIDGET (tool_item));
273 gtk_toolbar_insert (GTK_TOOLBAR (priv->toolbar), tool_item, -1);
275 priv->volume_button = gtk_volume_button_new ();
276 /* FIXME listen to the audiosinks signals and update the button according to
277 * that, for now starting out at 1.0 and assuming only the app changes the
279 gtk_scale_button_set_value (GTK_SCALE_BUTTON (priv->volume_button), 1.0);
280 g_signal_connect (G_OBJECT (priv->volume_button), "value-changed",
281 G_CALLBACK (empathy_call_window_volume_changed_cb), self);
283 tool_item = gtk_tool_item_new ();
284 gtk_container_add (GTK_CONTAINER (tool_item), priv->volume_button);
285 gtk_widget_show_all (GTK_WIDGET (tool_item));
286 gtk_toolbar_insert (GTK_TOOLBAR (priv->toolbar), tool_item, -1);
290 dtmf_button_pressed_cb (GtkButton *button, EmpathyCallWindow *window)
292 EmpathyCallWindowPriv *priv = GET_PRIV (window);
297 g_object_get (priv->handler, "tp-call", &call, NULL);
299 button_quark = g_quark_from_static_string (BUTTON_ID);
300 event = GPOINTER_TO_UINT (g_object_get_qdata (G_OBJECT (button),
303 empathy_tp_call_start_tone (call, event);
305 g_object_unref (call);
309 dtmf_button_released_cb (GtkButton *button, EmpathyCallWindow *window)
311 EmpathyCallWindowPriv *priv = GET_PRIV (window);
314 g_object_get (priv->handler, "tp-call", &call, NULL);
316 empathy_tp_call_stop_tone (call);
318 g_object_unref (call);
322 empathy_call_window_create_dtmf (EmpathyCallWindow *self)
330 } dtmfbuttons[] = { { "1", TP_DTMF_EVENT_DIGIT_1 },
331 { "2", TP_DTMF_EVENT_DIGIT_2 },
332 { "3", TP_DTMF_EVENT_DIGIT_3 },
333 { "4", TP_DTMF_EVENT_DIGIT_4 },
334 { "5", TP_DTMF_EVENT_DIGIT_5 },
335 { "6", TP_DTMF_EVENT_DIGIT_6 },
336 { "7", TP_DTMF_EVENT_DIGIT_7 },
337 { "8", TP_DTMF_EVENT_DIGIT_8 },
338 { "9", TP_DTMF_EVENT_DIGIT_9 },
339 { "#", TP_DTMF_EVENT_HASH },
340 { "0", TP_DTMF_EVENT_DIGIT_0 },
341 { "*", TP_DTMF_EVENT_ASTERISK },
344 button_quark = g_quark_from_static_string (BUTTON_ID);
346 table = gtk_table_new (4, 3, TRUE);
348 for (i = 0; dtmfbuttons[i].label != NULL; i++)
350 GtkWidget *button = gtk_button_new_with_label (dtmfbuttons[i].label);
351 gtk_table_attach (GTK_TABLE (table), button, i % 3, i % 3 + 1,
352 i/3, i/3 + 1, GTK_EXPAND | GTK_FILL, GTK_EXPAND | GTK_FILL, 1, 1);
354 g_object_set_qdata (G_OBJECT (button), button_quark,
355 GUINT_TO_POINTER (dtmfbuttons[i].event));
357 g_signal_connect (G_OBJECT (button), "pressed",
358 G_CALLBACK (dtmf_button_pressed_cb), self);
359 g_signal_connect (G_OBJECT (button), "released",
360 G_CALLBACK (dtmf_button_released_cb), self);
367 empathy_call_window_create_video_input_add_slider (EmpathyCallWindow *self,
368 gchar *label_text, GtkWidget *bin)
370 GtkWidget *vbox = gtk_vbox_new (FALSE, 2);
371 GtkWidget *scale = gtk_vscale_new_with_range (0, 100, 10);
372 GtkWidget *label = gtk_label_new (label_text);
374 gtk_widget_set_sensitive (scale, FALSE);
376 gtk_container_add (GTK_CONTAINER (bin), vbox);
378 gtk_range_set_inverted (GTK_RANGE (scale), TRUE);
379 gtk_box_pack_start (GTK_BOX (vbox), scale, TRUE, TRUE, 0);
380 gtk_box_pack_start (GTK_BOX (vbox), label, FALSE, FALSE, 0);
386 empathy_call_window_video_contrast_changed_cb (GtkAdjustment *adj,
387 EmpathyCallWindow *self)
390 EmpathyCallWindowPriv *priv = GET_PRIV (self);
392 empathy_video_src_set_channel (priv->video_input,
393 EMPATHY_GST_VIDEO_SRC_CHANNEL_CONTRAST, gtk_adjustment_get_value (adj));
397 empathy_call_window_video_brightness_changed_cb (GtkAdjustment *adj,
398 EmpathyCallWindow *self)
401 EmpathyCallWindowPriv *priv = GET_PRIV (self);
403 empathy_video_src_set_channel (priv->video_input,
404 EMPATHY_GST_VIDEO_SRC_CHANNEL_BRIGHTNESS, gtk_adjustment_get_value (adj));
408 empathy_call_window_video_gamma_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_GAMMA, gtk_adjustment_get_value (adj));
420 empathy_call_window_create_video_input (EmpathyCallWindow *self)
422 EmpathyCallWindowPriv *priv = GET_PRIV (self);
425 hbox = gtk_hbox_new (TRUE, 3);
427 priv->video_contrast = empathy_call_window_create_video_input_add_slider (
428 self, _("Contrast"), hbox);
430 priv->video_brightness = empathy_call_window_create_video_input_add_slider (
431 self, _("Brightness"), hbox);
433 priv->video_gamma = empathy_call_window_create_video_input_add_slider (
434 self, _("Gamma"), hbox);
440 empathy_call_window_setup_video_input (EmpathyCallWindow *self)
442 EmpathyCallWindowPriv *priv = GET_PRIV (self);
446 supported = empathy_video_src_get_supported_channels (priv->video_input);
448 if (supported & EMPATHY_GST_VIDEO_SRC_SUPPORTS_CONTRAST)
450 adj = gtk_range_get_adjustment (GTK_RANGE (priv->video_contrast));
452 gtk_adjustment_set_value (adj,
453 empathy_video_src_get_channel (priv->video_input,
454 EMPATHY_GST_VIDEO_SRC_CHANNEL_CONTRAST));
456 g_signal_connect (G_OBJECT (adj), "value-changed",
457 G_CALLBACK (empathy_call_window_video_contrast_changed_cb), self);
459 gtk_widget_set_sensitive (priv->video_contrast, TRUE);
462 if (supported & EMPATHY_GST_VIDEO_SRC_SUPPORTS_BRIGHTNESS)
464 adj = gtk_range_get_adjustment (GTK_RANGE (priv->video_brightness));
466 gtk_adjustment_set_value (adj,
467 empathy_video_src_get_channel (priv->video_input,
468 EMPATHY_GST_VIDEO_SRC_CHANNEL_BRIGHTNESS));
470 g_signal_connect (G_OBJECT (adj), "value-changed",
471 G_CALLBACK (empathy_call_window_video_brightness_changed_cb), self);
472 gtk_widget_set_sensitive (priv->video_brightness, TRUE);
475 if (supported & EMPATHY_GST_VIDEO_SRC_SUPPORTS_GAMMA)
477 adj = gtk_range_get_adjustment (GTK_RANGE (priv->video_gamma));
479 gtk_adjustment_set_value (adj,
480 empathy_video_src_get_channel (priv->video_input,
481 EMPATHY_GST_VIDEO_SRC_CHANNEL_GAMMA));
483 g_signal_connect (G_OBJECT (adj), "value-changed",
484 G_CALLBACK (empathy_call_window_video_gamma_changed_cb), self);
485 gtk_widget_set_sensitive (priv->video_gamma, TRUE);
490 empathy_call_window_mic_volume_changed_cb (GtkAdjustment *adj,
491 EmpathyCallWindow *self)
493 EmpathyCallWindowPriv *priv = GET_PRIV (self);
496 if (priv->audio_input == NULL)
499 volume = gtk_adjustment_get_value (adj)/100.0;
501 /* Don't store the volume because of muting */
502 if (volume > 0 || gtk_toggle_tool_button_get_active (
503 GTK_TOGGLE_TOOL_BUTTON (priv->mic_button)))
504 priv->volume = volume;
506 /* Ensure that the toggle button is active if the volume is > 0 and inactive
507 * if it's smaller than 0 */
508 if ((volume > 0) != gtk_toggle_tool_button_get_active (
509 GTK_TOGGLE_TOOL_BUTTON (priv->mic_button)))
510 gtk_toggle_tool_button_set_active (
511 GTK_TOGGLE_TOOL_BUTTON (priv->mic_button), volume > 0);
513 empathy_audio_src_set_volume (EMPATHY_GST_AUDIO_SRC (priv->audio_input),
518 empathy_call_window_audio_input_level_changed_cb (EmpathyGstAudioSrc *src,
519 gdouble level, EmpathyCallWindow *window)
522 EmpathyCallWindowPriv *priv = GET_PRIV (window);
524 value = CLAMP (pow (10, level / 20), 0.0, 1.0);
525 gtk_progress_bar_set_fraction (GTK_PROGRESS_BAR (priv->volume_progress_bar),
530 empathy_call_window_create_audio_input (EmpathyCallWindow *self)
532 EmpathyCallWindowPriv *priv = GET_PRIV (self);
533 GtkWidget *hbox, *vbox, *label;
535 hbox = gtk_hbox_new (TRUE, 3);
537 vbox = gtk_vbox_new (FALSE, 3);
538 gtk_box_pack_start (GTK_BOX (hbox), vbox, FALSE, FALSE, 3);
540 priv->volume_scale = gtk_vscale_new_with_range (0, 150, 100);
541 gtk_range_set_inverted (GTK_RANGE (priv->volume_scale), TRUE);
542 label = gtk_label_new (_("Volume"));
544 priv->audio_input_adj = gtk_range_get_adjustment (
545 GTK_RANGE (priv->volume_scale));
546 priv->volume = empathy_audio_src_get_volume (EMPATHY_GST_AUDIO_SRC
547 (priv->audio_input));
548 gtk_adjustment_set_value (priv->audio_input_adj, priv->volume * 100);
550 g_signal_connect (G_OBJECT (priv->audio_input_adj), "value-changed",
551 G_CALLBACK (empathy_call_window_mic_volume_changed_cb), self);
553 gtk_box_pack_start (GTK_BOX (vbox), priv->volume_scale, TRUE, TRUE, 3);
554 gtk_box_pack_start (GTK_BOX (vbox), label, FALSE, FALSE, 3);
556 priv->volume_progress_bar = gtk_progress_bar_new ();
557 gtk_progress_bar_set_orientation (
558 GTK_PROGRESS_BAR (priv->volume_progress_bar),
559 GTK_PROGRESS_BOTTOM_TO_TOP);
560 gtk_progress_bar_set_fraction (GTK_PROGRESS_BAR (priv->volume_progress_bar),
563 gtk_box_pack_start (GTK_BOX (hbox), priv->volume_progress_bar, FALSE, FALSE,
570 empathy_call_window_setup_remote_frame (GstBus *bus, EmpathyCallWindow *self)
572 EmpathyCallWindowPriv *priv = GET_PRIV (self);
574 /* Initializing all the content (UI and output gst elements) related to the
576 priv->remote_user_output_hbox = gtk_hbox_new (FALSE, 0);
578 priv->remote_user_avatar_widget = gtk_image_new ();
579 gtk_box_pack_start (GTK_BOX (priv->remote_user_output_hbox),
580 priv->remote_user_avatar_widget, TRUE, TRUE, 0);
582 priv->video_output = empathy_video_widget_new (bus);
583 gtk_box_pack_start (GTK_BOX (priv->remote_user_output_hbox),
584 priv->video_output, TRUE, TRUE, 0);
586 gtk_widget_add_events (priv->video_output,
587 GDK_BUTTON_PRESS_MASK | GDK_POINTER_MOTION_MASK);
588 g_signal_connect (G_OBJECT (priv->video_output), "button-press-event",
589 G_CALLBACK (empathy_call_window_video_button_press_cb), self);
591 gtk_container_add (GTK_CONTAINER (priv->remote_user_output_frame),
592 priv->remote_user_output_hbox);
594 priv->audio_output = empathy_audio_sink_new ();
595 gst_object_ref (priv->audio_output);
596 gst_object_sink (priv->audio_output);
600 empathy_call_window_setup_self_frame (GstBus *bus, EmpathyCallWindow *self)
602 EmpathyCallWindowPriv *priv = GET_PRIV (self);
604 /* Initializing all the content (UI and input gst elements) related to the
605 self contact, except for the video preview widget. This widget is only
606 initialized when the "show video preview" option is activated */
607 priv->self_user_output_hbox = gtk_hbox_new (FALSE, 0);
609 priv->self_user_avatar_widget = gtk_image_new ();
610 gtk_box_pack_start (GTK_BOX (priv->self_user_output_hbox),
611 priv->self_user_avatar_widget, TRUE, TRUE, 0);
613 gtk_container_add (GTK_CONTAINER (priv->self_user_output_frame),
614 priv->self_user_output_hbox);
616 priv->video_input = empathy_video_src_new ();
617 gst_object_ref (priv->video_input);
618 gst_object_sink (priv->video_input);
620 priv->audio_input = empathy_audio_src_new ();
621 gst_object_ref (priv->audio_input);
622 gst_object_sink (priv->audio_input);
624 empathy_signal_connect_weak (priv->audio_input, "peak-level-changed",
625 G_CALLBACK (empathy_call_window_audio_input_level_changed_cb),
630 empathy_call_window_setup_video_preview (EmpathyCallWindow *window)
632 EmpathyCallWindowPriv *priv = GET_PRIV (window);
634 GstBus *bus = gst_pipeline_get_bus (GST_PIPELINE (priv->pipeline));
636 if (priv->video_preview != NULL)
638 /* Since the video preview and the video tee are initialized and freed
639 at the same time, if one is initialized, then the other one should
641 g_assert (priv->video_tee != NULL);
645 g_assert (priv->video_tee == NULL);
647 priv->video_tee = gst_element_factory_make ("tee", NULL);
648 gst_object_ref (priv->video_tee);
649 gst_object_sink (priv->video_tee);
651 priv->video_preview = empathy_video_widget_new_with_size (bus,
652 SELF_VIDEO_SECTION_WIDTH, SELF_VIDEO_SECTION_HEIGTH);
653 g_object_set (priv->video_preview, "sync", FALSE, "async", TRUE, NULL);
654 gtk_box_pack_start (GTK_BOX (priv->self_user_output_hbox),
655 priv->video_preview, TRUE, TRUE, 0);
657 preview = empathy_video_widget_get_element (
658 EMPATHY_VIDEO_WIDGET (priv->video_preview));
659 gst_bin_add_many (GST_BIN (priv->pipeline), priv->video_input,
660 priv->video_tee, preview, NULL);
661 gst_element_link_many (priv->video_input, priv->video_tee,
664 g_object_unref (bus);
666 gst_element_set_state (preview, GST_STATE_PLAYING);
667 gst_element_set_state (priv->video_input, GST_STATE_PLAYING);
668 gst_element_set_state (priv->video_tee, GST_STATE_PLAYING);
672 empathy_call_window_set_state_connecting (EmpathyCallWindow *window)
674 EmpathyCallWindowPriv *priv = GET_PRIV (window);
676 empathy_call_window_status_message (window, _("Connecting..."));
677 priv->call_state = CONNECTING;
680 empathy_sound_start_playing (GTK_WIDGET (window),
681 EMPATHY_SOUND_PHONE_OUTGOING, MS_BETWEEN_RING);
685 empathy_call_window_init (EmpathyCallWindow *self)
687 EmpathyCallWindowPriv *priv = GET_PRIV (self);
696 GError *error = NULL;
698 filename = empathy_file_lookup ("empathy-call-window.ui", "src");
699 gui = empathy_builder_get_file (filename,
700 "call_window_vbox", &top_vbox,
701 "errors_vbox", &priv->errors_vbox,
703 "statusbar", &priv->statusbar,
704 "redial", &priv->redial_button,
705 "microphone", &priv->mic_button,
706 "camera", &priv->camera_button,
707 "toolbar", &priv->toolbar,
708 "send_video", &priv->send_video,
709 "menuredial", &priv->redial,
710 "show_preview", &priv->show_preview,
711 "ui_manager", &priv->ui_manager,
712 "menufullscreen", &priv->menu_fullscreen,
716 empathy_builder_connect (gui, self,
717 "menuhangup", "activate", empathy_call_window_hangup_cb,
718 "hangup", "clicked", empathy_call_window_hangup_cb,
719 "menuredial", "activate", empathy_call_window_redial_cb,
720 "redial", "clicked", empathy_call_window_redial_cb,
721 "microphone", "toggled", empathy_call_window_mic_toggled_cb,
722 "camera", "toggled", empathy_call_window_camera_toggled_cb,
723 "send_video", "toggled", empathy_call_window_send_video_toggled_cb,
724 "show_preview", "toggled", empathy_call_window_show_preview_toggled_cb,
725 "menufullscreen", "activate", empathy_call_window_fullscreen_cb,
728 priv->lock = g_mutex_new ();
730 gtk_container_add (GTK_CONTAINER (self), top_vbox);
732 priv->content_hbox = gtk_hbox_new (FALSE, CONTENT_HBOX_SPACING);
733 gtk_container_set_border_width (GTK_CONTAINER (priv->content_hbox),
734 CONTENT_HBOX_BORDER_WIDTH);
735 gtk_paned_pack1 (GTK_PANED (priv->pane), priv->content_hbox, TRUE, FALSE);
737 priv->pipeline = gst_pipeline_new (NULL);
738 bus = gst_pipeline_get_bus (GST_PIPELINE (priv->pipeline));
739 priv->bus_message_source_id = gst_bus_add_watch (bus,
740 empathy_call_window_bus_message, self);
742 priv->fsnotifier = fs_element_added_notifier_new ();
743 fs_element_added_notifier_add (priv->fsnotifier, GST_BIN (priv->pipeline));
745 keyfile = g_key_file_new ();
746 filename = empathy_file_lookup ("element-properties", "data");
747 if (g_key_file_load_from_file (keyfile, filename, G_KEY_FILE_NONE, &error))
749 fs_element_added_notifier_set_properties_from_keyfile (priv->fsnotifier,
754 g_warning ("Could not load element-properties file: %s", error->message);
755 g_key_file_free (keyfile);
756 g_clear_error (&error);
761 priv->remote_user_output_frame = gtk_frame_new (NULL);
762 gtk_widget_set_size_request (priv->remote_user_output_frame,
763 EMPATHY_VIDEO_WIDGET_DEFAULT_WIDTH, EMPATHY_VIDEO_WIDGET_DEFAULT_HEIGHT);
764 gtk_box_pack_start (GTK_BOX (priv->content_hbox),
765 priv->remote_user_output_frame, TRUE, TRUE,
766 CONTENT_HBOX_CHILDREN_PACKING_PADDING);
767 empathy_call_window_setup_remote_frame (bus, self);
769 priv->self_user_output_frame = gtk_frame_new (NULL);
770 gtk_widget_set_size_request (priv->self_user_output_frame,
771 SELF_VIDEO_SECTION_WIDTH, SELF_VIDEO_SECTION_HEIGTH);
773 priv->vbox = gtk_vbox_new (FALSE, 3);
774 gtk_box_pack_start (GTK_BOX (priv->content_hbox), priv->vbox,
775 FALSE, FALSE, CONTENT_HBOX_CHILDREN_PACKING_PADDING);
776 gtk_box_pack_start (GTK_BOX (priv->vbox), priv->self_user_output_frame,
778 empathy_call_window_setup_self_frame (bus, self);
780 empathy_call_window_setup_toolbar (self);
782 g_object_unref (bus);
784 priv->sidebar_button = gtk_toggle_button_new_with_mnemonic (_("_Sidebar"));
785 arrow = gtk_arrow_new (GTK_ARROW_RIGHT, GTK_SHADOW_NONE);
786 g_signal_connect (G_OBJECT (priv->sidebar_button), "toggled",
787 G_CALLBACK (empathy_call_window_sidebar_toggled_cb), self);
789 gtk_button_set_image (GTK_BUTTON (priv->sidebar_button), arrow);
791 h = gtk_hbox_new (FALSE, 3);
792 gtk_box_pack_end (GTK_BOX (priv->vbox), h, FALSE, FALSE, 3);
793 gtk_box_pack_end (GTK_BOX (h), priv->sidebar_button, FALSE, FALSE, 3);
795 priv->sidebar = empathy_sidebar_new ();
796 g_signal_connect (G_OBJECT (priv->sidebar),
797 "hide", G_CALLBACK (empathy_call_window_sidebar_hidden_cb), self);
798 g_signal_connect (G_OBJECT (priv->sidebar),
799 "show", G_CALLBACK (empathy_call_window_sidebar_shown_cb), self);
800 gtk_paned_pack2 (GTK_PANED (priv->pane), priv->sidebar, FALSE, FALSE);
802 priv->dtmf_panel = empathy_call_window_create_dtmf (self);
803 empathy_sidebar_add_page (EMPATHY_SIDEBAR (priv->sidebar), _("Dialpad"),
806 gtk_widget_set_sensitive (priv->dtmf_panel, FALSE);
808 page = empathy_call_window_create_audio_input (self);
809 empathy_sidebar_add_page (EMPATHY_SIDEBAR (priv->sidebar), _("Audio input"),
812 page = empathy_call_window_create_video_input (self);
813 empathy_sidebar_add_page (EMPATHY_SIDEBAR (priv->sidebar), _("Video input"),
816 gtk_widget_show_all (top_vbox);
818 gtk_widget_hide (priv->sidebar);
820 priv->fullscreen = empathy_call_window_fullscreen_new (self);
821 empathy_call_window_fullscreen_set_video_widget (priv->fullscreen,
823 g_signal_connect (G_OBJECT (priv->fullscreen->leave_fullscreen_button),
824 "clicked", G_CALLBACK (empathy_call_window_fullscreen_cb), self);
826 g_signal_connect (G_OBJECT (self), "realize",
827 G_CALLBACK (empathy_call_window_realized_cb), self);
829 g_signal_connect (G_OBJECT (self), "delete-event",
830 G_CALLBACK (empathy_call_window_delete_cb), self);
832 g_signal_connect (G_OBJECT (self), "window-state-event",
833 G_CALLBACK (empathy_call_window_state_event_cb), self);
835 g_signal_connect (G_OBJECT (self), "key-press-event",
836 G_CALLBACK (empathy_call_window_key_press_cb), self);
838 priv->timer = g_timer_new ();
840 g_object_ref (priv->ui_manager);
841 g_object_unref (gui);
844 /* Instead of specifying a width and a height, we specify only one size. That's
845 because we want a square avatar icon. */
847 init_contact_avatar_with_size (EmpathyContact *contact,
848 GtkWidget *image_widget,
851 GdkPixbuf *pixbuf_avatar = NULL;
855 pixbuf_avatar = empathy_pixbuf_avatar_from_contact_scaled (contact,
859 if (pixbuf_avatar == NULL)
861 pixbuf_avatar = empathy_pixbuf_from_icon_name_sized ("stock_person",
865 gtk_image_set_from_pixbuf (GTK_IMAGE (image_widget), pixbuf_avatar);
869 set_window_title (EmpathyCallWindow *self)
871 EmpathyCallWindowPriv *priv = GET_PRIV (self);
874 /* translators: Call is a noun and %s is the contact name. This string
875 * is used in the window title */
876 tmp = g_strdup_printf (_("Call with %s"),
877 empathy_contact_get_name (priv->contact));
878 gtk_window_set_title (GTK_WINDOW (self), tmp);
883 contact_name_changed_cb (EmpathyContact *contact,
884 GParamSpec *pspec, EmpathyCallWindow *self)
886 set_window_title (self);
890 contact_avatar_changed_cb (EmpathyContact *contact,
891 GParamSpec *pspec, GtkWidget *avatar_widget)
895 size = avatar_widget->allocation.height;
899 /* the widget is not allocated yet, set a default size */
900 size = MIN (REMOTE_CONTACT_AVATAR_DEFAULT_HEIGHT,
901 REMOTE_CONTACT_AVATAR_DEFAULT_WIDTH);
904 init_contact_avatar_with_size (contact, avatar_widget, size);
908 empathy_call_window_got_self_contact_cb (EmpathyTpContactFactory *factory,
909 EmpathyContact *contact, const GError *error, gpointer user_data,
910 GObject *weak_object)
912 EmpathyCallWindow *self = EMPATHY_CALL_WINDOW (user_data);
913 EmpathyCallWindowPriv *priv = GET_PRIV (self);
915 init_contact_avatar_with_size (contact, priv->self_user_avatar_widget,
916 MIN (SELF_VIDEO_SECTION_WIDTH, SELF_VIDEO_SECTION_HEIGTH));
918 g_signal_connect (contact, "notify::avatar",
919 G_CALLBACK (contact_avatar_changed_cb), priv->self_user_avatar_widget);
923 empathy_call_window_setup_avatars (EmpathyCallWindow *self,
924 EmpathyCallHandler *handler)
926 EmpathyCallWindowPriv *priv = GET_PRIV (self);
928 g_object_get (handler, "contact", &(priv->contact), NULL);
930 if (priv->contact != NULL)
932 TpConnection *connection;
933 EmpathyTpContactFactory *factory;
935 set_window_title (self);
937 g_signal_connect (priv->contact, "notify::name",
938 G_CALLBACK (contact_name_changed_cb), self);
939 g_signal_connect (priv->contact, "notify::avatar",
940 G_CALLBACK (contact_avatar_changed_cb),
941 priv->remote_user_avatar_widget);
943 /* Retreiving the self avatar */
944 connection = empathy_contact_get_connection (priv->contact);
945 factory = empathy_tp_contact_factory_dup_singleton (connection);
946 empathy_tp_contact_factory_get_from_handle (factory,
947 tp_connection_get_self_handle (connection),
948 empathy_call_window_got_self_contact_cb, self, NULL, G_OBJECT (self));
950 g_object_unref (factory);
954 g_warning ("call handler doesn't have a contact");
955 /* translators: Call is a noun. This string is used in the window
957 gtk_window_set_title (GTK_WINDOW (self), _("Call"));
959 /* Since we can't access the remote contact, we can't get a connection
960 to it and can't get the self contact (and its avatar). This means
961 that we have to manually set the self avatar. */
962 init_contact_avatar_with_size (NULL, priv->self_user_avatar_widget,
963 MIN (SELF_VIDEO_SECTION_WIDTH, SELF_VIDEO_SECTION_HEIGTH));
966 init_contact_avatar_with_size (priv->contact,
967 priv->remote_user_avatar_widget,
968 MIN (REMOTE_CONTACT_AVATAR_DEFAULT_WIDTH,
969 REMOTE_CONTACT_AVATAR_DEFAULT_HEIGHT));
971 /* The remote avatar is shown by default and will be hidden when we receive
972 video from the remote side. */
973 gtk_widget_hide (priv->video_output);
974 gtk_widget_show (priv->remote_user_avatar_widget);
978 empathy_call_window_setup_video_preview_visibility (EmpathyCallWindow *self,
979 EmpathyCallHandler *handler)
981 EmpathyCallWindowPriv *priv = GET_PRIV (self);
982 gboolean initial_video =
983 empathy_call_handler_has_initial_video (priv->handler);
985 gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (priv->show_preview),
990 empathy_call_window_constructed (GObject *object)
992 EmpathyCallWindow *self = EMPATHY_CALL_WINDOW (object);
993 EmpathyCallWindowPriv *priv = GET_PRIV (self);
996 g_assert (priv->handler != NULL);
998 g_object_get (priv->handler, "tp-call", &call, NULL);
999 priv->outgoing = (call == NULL);
1001 g_object_unref (call);
1003 empathy_call_window_setup_avatars (self, priv->handler);
1004 empathy_call_window_setup_video_preview_visibility (self, priv->handler);
1005 empathy_call_window_set_state_connecting (self);
1008 static void empathy_call_window_dispose (GObject *object);
1009 static void empathy_call_window_finalize (GObject *object);
1012 empathy_call_window_set_property (GObject *object,
1013 guint property_id, const GValue *value, GParamSpec *pspec)
1015 EmpathyCallWindowPriv *priv = GET_PRIV (object);
1017 switch (property_id)
1019 case PROP_CALL_HANDLER:
1020 priv->handler = g_value_dup_object (value);
1023 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
1028 empathy_call_window_get_property (GObject *object,
1029 guint property_id, GValue *value, GParamSpec *pspec)
1031 EmpathyCallWindowPriv *priv = GET_PRIV (object);
1033 switch (property_id)
1035 case PROP_CALL_HANDLER:
1036 g_value_set_object (value, priv->handler);
1039 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
1044 empathy_call_window_class_init (
1045 EmpathyCallWindowClass *empathy_call_window_class)
1047 GObjectClass *object_class = G_OBJECT_CLASS (empathy_call_window_class);
1048 GParamSpec *param_spec;
1050 g_type_class_add_private (empathy_call_window_class,
1051 sizeof (EmpathyCallWindowPriv));
1053 object_class->constructed = empathy_call_window_constructed;
1054 object_class->set_property = empathy_call_window_set_property;
1055 object_class->get_property = empathy_call_window_get_property;
1057 object_class->dispose = empathy_call_window_dispose;
1058 object_class->finalize = empathy_call_window_finalize;
1060 param_spec = g_param_spec_object ("handler",
1061 "handler", "The call handler",
1062 EMPATHY_TYPE_CALL_HANDLER,
1063 G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS);
1064 g_object_class_install_property (object_class,
1065 PROP_CALL_HANDLER, param_spec);
1069 empathy_call_window_video_stream_changed_cb (EmpathyTpCall *call,
1070 GParamSpec *property, EmpathyCallWindow *self)
1072 empathy_call_window_update_avatars_visibility (call, self);
1076 empathy_call_window_dispose (GObject *object)
1078 EmpathyTpCall *call;
1079 EmpathyCallWindow *self = EMPATHY_CALL_WINDOW (object);
1080 EmpathyCallWindowPriv *priv = GET_PRIV (self);
1082 if (priv->dispose_has_run)
1085 priv->dispose_has_run = TRUE;
1087 g_object_get (priv->handler, "tp-call", &call, NULL);
1091 g_signal_handlers_disconnect_by_func (call,
1092 empathy_call_window_video_stream_changed_cb, object);
1093 g_object_unref (call);
1096 if (priv->handler != NULL)
1097 g_object_unref (priv->handler);
1098 priv->handler = NULL;
1100 if (priv->pipeline != NULL)
1101 g_object_unref (priv->pipeline);
1102 priv->pipeline = NULL;
1104 if (priv->video_input != NULL)
1105 g_object_unref (priv->video_input);
1106 priv->video_input = NULL;
1108 if (priv->audio_input != NULL)
1109 g_object_unref (priv->audio_input);
1110 priv->audio_input = NULL;
1112 if (priv->audio_output != NULL)
1113 g_object_unref (priv->audio_output);
1114 priv->audio_output = NULL;
1116 if (priv->video_tee != NULL)
1117 g_object_unref (priv->video_tee);
1118 priv->video_tee = NULL;
1120 if (priv->fsnotifier != NULL)
1121 g_object_unref (priv->fsnotifier);
1122 priv->fsnotifier = NULL;
1124 if (priv->timer_id != 0)
1125 g_source_remove (priv->timer_id);
1128 if (priv->ui_manager != NULL)
1129 g_object_unref (priv->ui_manager);
1130 priv->ui_manager = NULL;
1132 if (priv->contact != NULL)
1134 g_signal_handlers_disconnect_by_func (priv->contact,
1135 contact_name_changed_cb, self);
1136 g_object_unref (priv->contact);
1137 priv->contact = NULL;
1140 /* release any references held by the object here */
1141 if (G_OBJECT_CLASS (empathy_call_window_parent_class)->dispose)
1142 G_OBJECT_CLASS (empathy_call_window_parent_class)->dispose (object);
1146 empathy_call_window_finalize (GObject *object)
1148 EmpathyCallWindow *self = EMPATHY_CALL_WINDOW (object);
1149 EmpathyCallWindowPriv *priv = GET_PRIV (self);
1151 if (priv->video_output_motion_handler_id != 0)
1153 g_signal_handler_disconnect (G_OBJECT (priv->video_output),
1154 priv->video_output_motion_handler_id);
1155 priv->video_output_motion_handler_id = 0;
1158 if (priv->bus_message_source_id != 0)
1160 g_source_remove (priv->bus_message_source_id);
1161 priv->bus_message_source_id = 0;
1164 /* free any data held directly by the object here */
1165 g_mutex_free (priv->lock);
1167 g_timer_destroy (priv->timer);
1169 G_OBJECT_CLASS (empathy_call_window_parent_class)->finalize (object);
1174 empathy_call_window_new (EmpathyCallHandler *handler)
1176 return EMPATHY_CALL_WINDOW (
1177 g_object_new (EMPATHY_TYPE_CALL_WINDOW, "handler", handler, NULL));
1181 empathy_call_window_conference_added_cb (EmpathyCallHandler *handler,
1182 GstElement *conference, gpointer user_data)
1184 EmpathyCallWindow *self = EMPATHY_CALL_WINDOW (user_data);
1185 EmpathyCallWindowPriv *priv = GET_PRIV (self);
1187 gst_bin_add (GST_BIN (priv->pipeline), conference);
1189 gst_element_set_state (conference, GST_STATE_PLAYING);
1193 empathy_call_window_request_resource_cb (EmpathyCallHandler *handler,
1194 FsMediaType type, FsStreamDirection direction, gpointer user_data)
1196 EmpathyCallWindow *self = EMPATHY_CALL_WINDOW (user_data);
1197 EmpathyCallWindowPriv *priv = GET_PRIV (self);
1199 if (type != TP_MEDIA_STREAM_TYPE_VIDEO)
1202 if (direction == FS_DIRECTION_RECV)
1205 /* video and direction is send */
1206 return priv->video_input != NULL;
1210 empathy_call_window_reset_pipeline (EmpathyCallWindow *self)
1212 GstStateChangeReturn state_change_return;
1213 EmpathyCallWindowPriv *priv = GET_PRIV (self);
1215 if (priv->pipeline == NULL)
1218 if (priv->bus_message_source_id != 0)
1220 g_source_remove (priv->bus_message_source_id);
1221 priv->bus_message_source_id = 0;
1224 state_change_return = gst_element_set_state (priv->pipeline, GST_STATE_NULL);
1226 if (state_change_return == GST_STATE_CHANGE_SUCCESS ||
1227 state_change_return == GST_STATE_CHANGE_NO_PREROLL)
1229 if (priv->pipeline != NULL)
1230 g_object_unref (priv->pipeline);
1231 priv->pipeline = NULL;
1233 if (priv->video_input != NULL)
1234 g_object_unref (priv->video_input);
1235 priv->video_input = NULL;
1237 if (priv->audio_input != NULL)
1238 g_object_unref (priv->audio_input);
1239 priv->audio_input = NULL;
1241 g_signal_handlers_disconnect_by_func (priv->audio_input_adj,
1242 empathy_call_window_mic_volume_changed_cb, self);
1244 if (priv->audio_output != NULL)
1245 g_object_unref (priv->audio_output);
1246 priv->audio_output = NULL;
1248 if (priv->video_tee != NULL)
1249 g_object_unref (priv->video_tee);
1250 priv->video_tee = NULL;
1252 if (priv->video_preview != NULL)
1253 gtk_widget_destroy (priv->video_preview);
1254 priv->video_preview = NULL;
1256 priv->liveadder = NULL;
1257 priv->funnel = NULL;
1263 g_message ("Error: could not destroy pipeline. Closing call window");
1264 gtk_widget_destroy (GTK_WIDGET (self));
1271 empathy_call_window_disconnected (EmpathyCallWindow *self)
1273 gboolean could_disconnect = FALSE;
1274 EmpathyCallWindowPriv *priv = GET_PRIV (self);
1275 gboolean could_reset_pipeline = empathy_call_window_reset_pipeline (self);
1277 if (priv->call_state == CONNECTING)
1278 empathy_sound_stop (EMPATHY_SOUND_PHONE_OUTGOING);
1280 if (priv->call_state != REDIALING)
1281 priv->call_state = DISCONNECTED;
1283 if (could_reset_pipeline)
1285 gboolean initial_video = empathy_call_handler_has_initial_video (
1287 g_mutex_lock (priv->lock);
1289 g_timer_stop (priv->timer);
1291 if (priv->timer_id != 0)
1292 g_source_remove (priv->timer_id);
1295 g_mutex_unlock (priv->lock);
1297 empathy_call_window_status_message (self, _("Disconnected"));
1299 gtk_action_set_sensitive (priv->redial, TRUE);
1300 gtk_widget_set_sensitive (priv->redial_button, TRUE);
1302 /* Reseting the send_video, camera_buton and mic_button to their
1304 gtk_widget_set_sensitive (priv->camera_button, FALSE);
1305 gtk_widget_set_sensitive (priv->mic_button, FALSE);
1306 gtk_action_set_sensitive (priv->send_video, FALSE);
1307 gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (priv->send_video),
1309 gtk_toggle_tool_button_set_active (
1310 GTK_TOGGLE_TOOL_BUTTON (priv->camera_button), initial_video);
1311 gtk_toggle_tool_button_set_active (
1312 GTK_TOGGLE_TOOL_BUTTON (priv->mic_button), TRUE);
1314 gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (priv->show_preview),
1316 gtk_action_set_sensitive (priv->show_preview, FALSE);
1318 gtk_progress_bar_set_fraction (
1319 GTK_PROGRESS_BAR (priv->volume_progress_bar), 0);
1321 gtk_widget_hide (priv->video_output);
1322 gtk_widget_show (priv->remote_user_avatar_widget);
1324 priv->sending_video = FALSE;
1325 priv->call_started = FALSE;
1327 could_disconnect = TRUE;
1330 return could_disconnect;
1335 empathy_call_window_channel_closed_cb (EmpathyCallHandler *handler,
1338 EmpathyCallWindow *self = EMPATHY_CALL_WINDOW (user_data);
1339 EmpathyCallWindowPriv *priv = GET_PRIV (self);
1341 if (empathy_call_window_disconnected (self) && priv->call_state == REDIALING)
1342 empathy_call_window_restart_call (self);
1347 empathy_call_window_channel_stream_closed_cb (EmpathyCallHandler *handler,
1348 TfStream *stream, gpointer user_data)
1350 EmpathyCallWindow *self = EMPATHY_CALL_WINDOW (user_data);
1351 EmpathyCallWindowPriv *priv = GET_PRIV (self);
1354 g_object_get (stream, "media-type", &media_type, NULL);
1357 * This assumes that there is only one video stream per channel...
1360 if (media_type == TP_MEDIA_STREAM_TYPE_VIDEO)
1362 if (priv->funnel != NULL)
1366 output = empathy_video_widget_get_element (EMPATHY_VIDEO_WIDGET
1367 (priv->video_output));
1369 gst_element_set_state (output, GST_STATE_NULL);
1370 gst_element_set_state (priv->funnel, GST_STATE_NULL);
1372 gst_bin_remove (GST_BIN (priv->pipeline), output);
1373 gst_bin_remove (GST_BIN (priv->pipeline), priv->funnel);
1374 priv->funnel = NULL;
1377 else if (media_type == TP_MEDIA_STREAM_TYPE_AUDIO)
1379 if (priv->liveadder != NULL)
1381 gst_element_set_state (priv->audio_output, GST_STATE_NULL);
1382 gst_element_set_state (priv->liveadder, GST_STATE_NULL);
1384 gst_bin_remove (GST_BIN (priv->pipeline), priv->audio_output);
1385 gst_bin_remove (GST_BIN (priv->pipeline), priv->liveadder);
1386 priv->liveadder = NULL;
1391 /* Called with global lock held */
1393 empathy_call_window_get_video_sink_pad (EmpathyCallWindow *self)
1395 EmpathyCallWindowPriv *priv = GET_PRIV (self);
1398 if (priv->funnel == NULL)
1402 output = empathy_video_widget_get_element (EMPATHY_VIDEO_WIDGET
1403 (priv->video_output));
1405 priv->funnel = gst_element_factory_make ("fsfunnel", NULL);
1407 gst_bin_add (GST_BIN (priv->pipeline), priv->funnel);
1408 gst_bin_add (GST_BIN (priv->pipeline), output);
1410 gst_element_link (priv->funnel, output);
1412 gst_element_set_state (priv->funnel, GST_STATE_PLAYING);
1413 gst_element_set_state (output, GST_STATE_PLAYING);
1416 pad = gst_element_get_request_pad (priv->funnel, "sink%d");
1421 /* Called with global lock held */
1423 empathy_call_window_get_audio_sink_pad (EmpathyCallWindow *self)
1425 EmpathyCallWindowPriv *priv = GET_PRIV (self);
1428 if (priv->liveadder == NULL)
1430 priv->liveadder = gst_element_factory_make ("liveadder", NULL);
1432 gst_bin_add (GST_BIN (priv->pipeline), priv->liveadder);
1433 gst_bin_add (GST_BIN (priv->pipeline), priv->audio_output);
1435 gst_element_link (priv->liveadder, priv->audio_output);
1437 gst_element_set_state (priv->liveadder, GST_STATE_PLAYING);
1438 gst_element_set_state (priv->audio_output, GST_STATE_PLAYING);
1441 pad = gst_element_get_request_pad (priv->liveadder, "sink%d");
1447 empathy_call_window_update_timer (gpointer user_data)
1449 EmpathyCallWindow *self = EMPATHY_CALL_WINDOW (user_data);
1450 EmpathyCallWindowPriv *priv = GET_PRIV (self);
1454 time_ = g_timer_elapsed (priv->timer, NULL);
1456 /* Translators: number of minutes:seconds the caller has been connected */
1457 str = g_strdup_printf (_("Connected — %d:%02dm"), (int) time_ / 60,
1459 empathy_call_window_status_message (self, str);
1466 display_error (EmpathyCallWindow *self,
1467 EmpathyTpCall *call,
1471 const gchar *details)
1473 EmpathyCallWindowPriv *priv = GET_PRIV (self);
1474 GtkWidget *info_bar;
1475 GtkWidget *content_area;
1482 /* Create info bar */
1483 info_bar = gtk_info_bar_new_with_buttons (GTK_STOCK_CLOSE, GTK_RESPONSE_CLOSE,
1486 gtk_info_bar_set_message_type (GTK_INFO_BAR (info_bar), GTK_MESSAGE_WARNING);
1488 content_area = gtk_info_bar_get_content_area (GTK_INFO_BAR (info_bar));
1490 /* hbox containing the image and the messages vbox */
1491 hbox = gtk_hbox_new (FALSE, 3);
1492 gtk_container_add (GTK_CONTAINER (content_area), hbox);
1495 image = gtk_image_new_from_icon_name (img, GTK_ICON_SIZE_DIALOG);
1496 gtk_box_pack_start (GTK_BOX (hbox), image, FALSE, FALSE, 0);
1498 /* vbox containing the main message and the details expander */
1499 vbox = gtk_vbox_new (FALSE, 3);
1500 gtk_box_pack_start (GTK_BOX (hbox), vbox, TRUE, TRUE, 0);
1503 txt = g_strdup_printf ("<b>%s</b>\n%s", title, desc);
1505 label = gtk_label_new (NULL);
1506 gtk_label_set_markup (GTK_LABEL (label), txt);
1507 gtk_label_set_line_wrap (GTK_LABEL (label), TRUE);
1508 gtk_misc_set_alignment (GTK_MISC (label), 0, 0);
1511 gtk_box_pack_start (GTK_BOX (vbox), label, TRUE, TRUE, 0);
1514 if (details != NULL)
1516 GtkWidget *expander;
1518 expander = gtk_expander_new (_("Technical Details"));
1520 txt = g_strdup_printf ("<i>%s</i>", details);
1522 label = gtk_label_new (NULL);
1523 gtk_label_set_markup (GTK_LABEL (label), txt);
1524 gtk_label_set_line_wrap (GTK_LABEL (label), TRUE);
1525 gtk_misc_set_alignment (GTK_MISC (label), 0, 0);
1528 gtk_container_add (GTK_CONTAINER (expander), label);
1529 gtk_box_pack_start (GTK_BOX (vbox), expander, TRUE, TRUE, 0);
1532 g_signal_connect (info_bar, "response",
1533 G_CALLBACK (gtk_widget_destroy), NULL);
1535 gtk_box_pack_start (GTK_BOX (priv->errors_vbox), info_bar,
1536 FALSE, FALSE, CONTENT_HBOX_CHILDREN_PACKING_PADDING);
1537 gtk_widget_show_all (info_bar);
1541 media_stream_error_to_txt (EmpathyCallWindow *self,
1542 EmpathyTpCall *call,
1544 TpMediaStreamError error)
1546 EmpathyCallWindowPriv *priv = GET_PRIV (self);
1551 case TP_MEDIA_STREAM_ERROR_CODEC_NEGOTIATION_FAILED:
1553 return g_strdup_printf (
1554 _("%s's software does not understand any of the audio formats "
1555 "supported by your computer"),
1556 empathy_contact_get_name (priv->contact));
1558 return g_strdup_printf (
1559 _("%s's software does not understand any of the video formats "
1560 "supported by your computer"),
1561 empathy_contact_get_name (priv->contact));
1563 case TP_MEDIA_STREAM_ERROR_CONNECTION_FAILED:
1564 return g_strdup_printf (
1565 _("Can't establish a connection to %s. "
1566 "One of you might be on a network that does not allow "
1567 "direct connections."),
1568 empathy_contact_get_name (priv->contact));
1570 case TP_MEDIA_STREAM_ERROR_NETWORK_ERROR:
1571 return g_strdup (_("There was a failure on the network"));
1573 case TP_MEDIA_STREAM_ERROR_NO_CODECS:
1575 return g_strdup (_("The audio formats necessary for this call "
1576 "are not installed on your computer"));
1578 return g_strdup (_("The video formats necessary for this call "
1579 "are not installed on your computer"));
1581 case TP_MEDIA_STREAM_ERROR_INVALID_CM_BEHAVIOR:
1582 cm = empathy_tp_call_get_connection_manager (call);
1583 return g_strdup_printf (_("Something not expected happened in a "
1584 "Telepathy component (%s). "
1585 "Please report this bug and attach logs gathered "
1586 "from the 'Debug' window in the Help menu."), cm);
1588 case TP_MEDIA_STREAM_ERROR_MEDIA_ERROR:
1589 return g_strdup (_("There was a failure in the call engine"));
1597 empathy_call_window_stream_error (EmpathyCallWindow *self,
1598 EmpathyTpCall *call,
1607 desc = media_stream_error_to_txt (self, call, audio, code);
1610 /* No description, use the error message. That's not great as it's not
1611 * localized but it's better than nothing. */
1612 display_error (self, call, icon, title, msg, NULL);
1616 display_error (self, call, icon, title, desc, msg);
1622 empathy_call_window_audio_stream_error (EmpathyTpCall *call,
1625 EmpathyCallWindow *self)
1627 empathy_call_window_stream_error (self, call, TRUE, code, msg,
1628 "gnome-stock-mic", _("Can't establish audio stream"));
1632 empathy_call_window_video_stream_error (EmpathyTpCall *call,
1635 EmpathyCallWindow *self)
1637 empathy_call_window_stream_error (self, call, FALSE, code, msg,
1638 "camera-web", _("Can't establish video stream"));
1642 empathy_call_window_connected (gpointer user_data)
1644 EmpathyCallWindow *self = EMPATHY_CALL_WINDOW (user_data);
1645 EmpathyCallWindowPriv *priv = GET_PRIV (self);
1646 EmpathyTpCall *call;
1647 gboolean can_send_video;
1649 empathy_sound_stop (EMPATHY_SOUND_PHONE_OUTGOING);
1651 can_send_video = priv->video_input != NULL && priv->contact != NULL &&
1652 empathy_contact_can_voip_video (priv->contact);
1654 g_object_get (priv->handler, "tp-call", &call, NULL);
1656 g_signal_connect (call, "notify::video-stream",
1657 G_CALLBACK (empathy_call_window_video_stream_changed_cb), self);
1659 if (empathy_tp_call_has_dtmf (call))
1660 gtk_widget_set_sensitive (priv->dtmf_panel, TRUE);
1662 if (priv->video_input == NULL)
1663 empathy_call_window_set_send_video (self, FALSE);
1665 priv->sending_video = can_send_video ?
1666 empathy_tp_call_is_sending_video (call) : FALSE;
1668 gtk_action_set_sensitive (priv->show_preview, TRUE);
1669 gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (priv->show_preview),
1671 || gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (
1672 priv->show_preview)));
1673 gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (priv->send_video),
1674 priv->sending_video && priv->video_input != NULL);
1675 gtk_toggle_tool_button_set_active (
1676 GTK_TOGGLE_TOOL_BUTTON (priv->camera_button),
1677 priv->sending_video && priv->video_input != NULL);
1678 gtk_widget_set_sensitive (priv->camera_button, can_send_video);
1679 gtk_action_set_sensitive (priv->send_video, can_send_video);
1681 gtk_action_set_sensitive (priv->redial, FALSE);
1682 gtk_widget_set_sensitive (priv->redial_button, FALSE);
1684 gtk_widget_set_sensitive (priv->mic_button, TRUE);
1686 empathy_call_window_update_avatars_visibility (call, self);
1688 g_object_unref (call);
1690 g_mutex_lock (priv->lock);
1692 priv->timer_id = g_timeout_add_seconds (1,
1693 empathy_call_window_update_timer, self);
1695 g_mutex_unlock (priv->lock);
1697 empathy_call_window_update_timer (self);
1703 /* Called from the streaming thread */
1705 empathy_call_window_src_added_cb (EmpathyCallHandler *handler,
1706 GstPad *src, guint media_type, gpointer user_data)
1708 EmpathyCallWindow *self = EMPATHY_CALL_WINDOW (user_data);
1709 EmpathyCallWindowPriv *priv = GET_PRIV (self);
1713 g_mutex_lock (priv->lock);
1715 if (priv->call_state != CONNECTED)
1717 g_timer_start (priv->timer);
1718 priv->timer_id = g_idle_add (empathy_call_window_connected, self);
1719 priv->call_state = CONNECTED;
1724 case TP_MEDIA_STREAM_TYPE_AUDIO:
1725 pad = empathy_call_window_get_audio_sink_pad (self);
1727 case TP_MEDIA_STREAM_TYPE_VIDEO:
1728 gtk_widget_hide (priv->remote_user_avatar_widget);
1729 gtk_widget_show (priv->video_output);
1730 pad = empathy_call_window_get_video_sink_pad (self);
1733 g_assert_not_reached ();
1736 gst_pad_link (src, pad);
1737 gst_object_unref (pad);
1739 g_mutex_unlock (priv->lock);
1742 /* Called from the streaming thread */
1744 empathy_call_window_sink_added_cb (EmpathyCallHandler *handler,
1745 GstPad *sink, guint media_type, gpointer user_data)
1747 EmpathyCallWindow *self = EMPATHY_CALL_WINDOW (user_data);
1748 EmpathyCallWindowPriv *priv = GET_PRIV (self);
1753 case TP_MEDIA_STREAM_TYPE_AUDIO:
1754 gst_bin_add (GST_BIN (priv->pipeline), priv->audio_input);
1756 pad = gst_element_get_static_pad (priv->audio_input, "src");
1757 gst_pad_link (pad, sink);
1759 gst_element_set_state (priv->audio_input, GST_STATE_PLAYING);
1761 case TP_MEDIA_STREAM_TYPE_VIDEO:
1762 if (priv->video_input != NULL)
1764 EmpathyTpCall *call;
1765 g_object_get (priv->handler, "tp-call", &call, NULL);
1767 if (empathy_tp_call_is_sending_video (call))
1769 empathy_call_window_setup_video_preview (self);
1771 gtk_toggle_action_set_active (
1772 GTK_TOGGLE_ACTION (priv->show_preview), TRUE);
1774 if (priv->video_preview != NULL)
1775 gtk_widget_show (priv->video_preview);
1776 gtk_widget_hide (priv->self_user_avatar_widget);
1779 g_object_unref (call);
1781 if (priv->video_tee != NULL)
1783 pad = gst_element_get_request_pad (priv->video_tee, "src%d");
1784 gst_pad_link (pad, sink);
1789 g_assert_not_reached ();
1795 empathy_call_window_remove_video_input (EmpathyCallWindow *self)
1797 EmpathyCallWindowPriv *priv = GET_PRIV (self);
1798 GstElement *preview;
1800 preview = empathy_video_widget_get_element (
1801 EMPATHY_VIDEO_WIDGET (priv->video_preview));
1803 gst_element_set_state (priv->video_input, GST_STATE_NULL);
1804 gst_element_set_state (priv->video_tee, GST_STATE_NULL);
1805 gst_element_set_state (preview, GST_STATE_NULL);
1807 gst_bin_remove_many (GST_BIN (priv->pipeline), priv->video_input,
1808 priv->video_tee, preview, NULL);
1810 g_object_unref (priv->video_input);
1811 priv->video_input = NULL;
1812 g_object_unref (priv->video_tee);
1813 priv->video_tee = NULL;
1814 gtk_widget_destroy (priv->video_preview);
1815 priv->video_preview = NULL;
1817 gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (priv->send_video), FALSE);
1818 gtk_toggle_tool_button_set_active (
1819 GTK_TOGGLE_TOOL_BUTTON (priv->camera_button), FALSE);
1820 gtk_widget_set_sensitive (priv->camera_button, FALSE);
1821 gtk_action_set_sensitive (priv->send_video, FALSE);
1823 gtk_widget_show (priv->self_user_avatar_widget);
1828 empathy_call_window_bus_message (GstBus *bus, GstMessage *message,
1831 EmpathyCallWindow *self = EMPATHY_CALL_WINDOW (user_data);
1832 EmpathyCallWindowPriv *priv = GET_PRIV (self);
1835 empathy_call_handler_bus_message (priv->handler, bus, message);
1837 switch (GST_MESSAGE_TYPE (message))
1839 case GST_MESSAGE_STATE_CHANGED:
1840 if (GST_MESSAGE_SRC (message) == GST_OBJECT (priv->video_input))
1842 gst_message_parse_state_changed (message, NULL, &newstate, NULL);
1843 if (newstate == GST_STATE_PAUSED)
1844 empathy_call_window_setup_video_input (self);
1846 if (GST_MESSAGE_SRC (message) == GST_OBJECT (priv->pipeline) &&
1847 !priv->call_started)
1849 gst_message_parse_state_changed (message, NULL, &newstate, NULL);
1850 if (newstate == GST_STATE_PAUSED)
1852 priv->call_started = TRUE;
1853 empathy_call_handler_start_call (priv->handler);
1854 gst_element_set_state (priv->pipeline, GST_STATE_PLAYING);
1858 case GST_MESSAGE_ERROR:
1860 GError *error = NULL;
1861 GstElement *gst_error;
1864 gst_message_parse_error (message, &error, &debug);
1865 gst_error = GST_ELEMENT (GST_MESSAGE_SRC (message));
1867 g_message ("Element error: %s -- %s\n", error->message, debug);
1869 if (g_str_has_prefix (gst_element_get_name (gst_error),
1870 VIDEO_INPUT_ERROR_PREFIX))
1872 /* Remove the video input and continue */
1873 if (priv->video_input != NULL)
1874 empathy_call_window_remove_video_input (self);
1875 gst_element_set_state (priv->pipeline, GST_STATE_PLAYING);
1879 empathy_call_window_disconnected (self);
1881 g_error_free (error);
1892 empathy_call_window_update_self_avatar_visibility (EmpathyCallWindow *window)
1894 EmpathyCallWindowPriv *priv = GET_PRIV (window);
1896 if (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (priv->show_preview)))
1898 if (priv->video_preview != NULL)
1900 gtk_widget_hide (priv->self_user_avatar_widget);
1901 gtk_widget_show (priv->video_preview);
1905 if (priv->video_preview != NULL)
1906 gtk_widget_hide (priv->video_preview);
1908 gtk_widget_show (priv->self_user_avatar_widget);
1914 empathy_call_window_update_avatars_visibility (EmpathyTpCall *call,
1915 EmpathyCallWindow *window)
1917 EmpathyCallWindowPriv *priv = GET_PRIV (window);
1919 if (empathy_tp_call_is_receiving_video (call))
1921 gtk_widget_hide (priv->remote_user_avatar_widget);
1922 gtk_widget_show (priv->video_output);
1926 gtk_widget_hide (priv->video_output);
1927 gtk_widget_show (priv->remote_user_avatar_widget);
1930 empathy_call_window_update_self_avatar_visibility (window);
1934 call_handler_notify_tp_call_cb (EmpathyCallHandler *handler,
1936 EmpathyCallWindow *self)
1938 EmpathyCallWindowPriv *priv = GET_PRIV (self);
1939 EmpathyTpCall *call;
1941 g_object_get (priv->handler, "tp-call", &call, NULL);
1945 empathy_signal_connect_weak (call, "audio-stream-error",
1946 G_CALLBACK (empathy_call_window_audio_stream_error), G_OBJECT (self));
1947 empathy_signal_connect_weak (call, "video-stream-error",
1948 G_CALLBACK (empathy_call_window_video_stream_error), G_OBJECT (self));
1950 g_object_unref (call);
1954 empathy_call_window_realized_cb (GtkWidget *widget, EmpathyCallWindow *window)
1956 EmpathyCallWindowPriv *priv = GET_PRIV (window);
1957 EmpathyTpCall *call;
1959 g_signal_connect (priv->handler, "conference-added",
1960 G_CALLBACK (empathy_call_window_conference_added_cb), window);
1961 g_signal_connect (priv->handler, "request-resource",
1962 G_CALLBACK (empathy_call_window_request_resource_cb), window);
1963 g_signal_connect (priv->handler, "closed",
1964 G_CALLBACK (empathy_call_window_channel_closed_cb), window);
1965 g_signal_connect (priv->handler, "src-pad-added",
1966 G_CALLBACK (empathy_call_window_src_added_cb), window);
1967 g_signal_connect (priv->handler, "sink-pad-added",
1968 G_CALLBACK (empathy_call_window_sink_added_cb), window);
1969 g_signal_connect (priv->handler, "stream-closed",
1970 G_CALLBACK (empathy_call_window_channel_stream_closed_cb), window);
1972 g_object_get (priv->handler, "tp-call", &call, NULL);
1975 empathy_signal_connect_weak (call, "audio-stream-error",
1976 G_CALLBACK (empathy_call_window_audio_stream_error), G_OBJECT (window));
1977 empathy_signal_connect_weak (call, "video-stream-error",
1978 G_CALLBACK (empathy_call_window_video_stream_error), G_OBJECT (window));
1980 g_object_unref (call);
1984 /* tp-call doesn't exist yet, we'll connect signals once it has been
1986 g_signal_connect (priv->handler, "notify::tp-call",
1987 G_CALLBACK (call_handler_notify_tp_call_cb), window);
1990 gst_element_set_state (priv->pipeline, GST_STATE_PAUSED);
1994 empathy_call_window_delete_cb (GtkWidget *widget, GdkEvent*event,
1995 EmpathyCallWindow *window)
1997 EmpathyCallWindowPriv *priv = GET_PRIV (window);
1999 if (priv->pipeline != NULL)
2001 if (priv->bus_message_source_id != 0)
2003 g_source_remove (priv->bus_message_source_id);
2004 priv->bus_message_source_id = 0;
2007 gst_element_set_state (priv->pipeline, GST_STATE_NULL);
2010 if (priv->call_state == CONNECTING)
2011 empathy_sound_stop (EMPATHY_SOUND_PHONE_OUTGOING);
2017 show_controls (EmpathyCallWindow *window, gboolean set_fullscreen)
2020 EmpathyCallWindowPriv *priv = GET_PRIV (window);
2022 menu = gtk_ui_manager_get_widget (priv->ui_manager,
2027 gtk_widget_hide (priv->sidebar);
2028 gtk_widget_hide (menu);
2029 gtk_widget_hide (priv->vbox);
2030 gtk_widget_hide (priv->statusbar);
2031 gtk_widget_hide (priv->toolbar);
2035 if (priv->sidebar_was_visible_before_fs)
2036 gtk_widget_show (priv->sidebar);
2038 gtk_widget_show (menu);
2039 gtk_widget_show (priv->vbox);
2040 gtk_widget_show (priv->statusbar);
2041 gtk_widget_show (priv->toolbar);
2043 gtk_window_resize (GTK_WINDOW (window), priv->original_width_before_fs,
2044 priv->original_height_before_fs);
2049 show_borders (EmpathyCallWindow *window, gboolean set_fullscreen)
2051 EmpathyCallWindowPriv *priv = GET_PRIV (window);
2053 gtk_container_set_border_width (GTK_CONTAINER (priv->content_hbox),
2054 set_fullscreen ? 0 : CONTENT_HBOX_BORDER_WIDTH);
2055 gtk_box_set_spacing (GTK_BOX (priv->content_hbox),
2056 set_fullscreen ? 0 : CONTENT_HBOX_SPACING);
2057 gtk_box_set_child_packing (GTK_BOX (priv->content_hbox),
2058 priv->video_output, TRUE, TRUE,
2059 set_fullscreen ? 0 : CONTENT_HBOX_CHILDREN_PACKING_PADDING,
2061 gtk_box_set_child_packing (GTK_BOX (priv->content_hbox),
2062 priv->vbox, TRUE, TRUE,
2063 set_fullscreen ? 0 : CONTENT_HBOX_CHILDREN_PACKING_PADDING,
2068 empathy_call_window_state_event_cb (GtkWidget *widget,
2069 GdkEventWindowState *event, EmpathyCallWindow *window)
2071 if (event->changed_mask & GDK_WINDOW_STATE_FULLSCREEN)
2073 EmpathyCallWindowPriv *priv = GET_PRIV (window);
2074 gboolean set_fullscreen = event->new_window_state &
2075 GDK_WINDOW_STATE_FULLSCREEN;
2079 gboolean sidebar_was_visible;
2080 gint original_width = GTK_WIDGET (window)->allocation.width;
2081 gint original_height = GTK_WIDGET (window)->allocation.height;
2083 g_object_get (priv->sidebar, "visible", &sidebar_was_visible, NULL);
2085 priv->sidebar_was_visible_before_fs = sidebar_was_visible;
2086 priv->original_width_before_fs = original_width;
2087 priv->original_height_before_fs = original_height;
2089 if (priv->video_output_motion_handler_id == 0 &&
2090 priv->video_output != NULL)
2092 priv->video_output_motion_handler_id = g_signal_connect (
2093 G_OBJECT (priv->video_output), "motion-notify-event",
2094 G_CALLBACK (empathy_call_window_video_output_motion_notify),
2100 if (priv->video_output_motion_handler_id != 0)
2102 g_signal_handler_disconnect (G_OBJECT (priv->video_output),
2103 priv->video_output_motion_handler_id);
2104 priv->video_output_motion_handler_id = 0;
2108 empathy_call_window_fullscreen_set_fullscreen (priv->fullscreen,
2110 show_controls (window, set_fullscreen);
2111 show_borders (window, set_fullscreen);
2112 gtk_action_set_stock_id (priv->menu_fullscreen,
2113 (set_fullscreen ? "gtk-leave-fullscreen" : "gtk-fullscreen"));
2114 priv->is_fullscreen = set_fullscreen;
2121 empathy_call_window_sidebar_toggled_cb (GtkToggleButton *toggle,
2122 EmpathyCallWindow *window)
2124 EmpathyCallWindowPriv *priv = GET_PRIV (window);
2126 int w,h, handle_size;
2128 w = GTK_WIDGET (window)->allocation.width;
2129 h = GTK_WIDGET (window)->allocation.height;
2131 gtk_widget_style_get (priv->pane, "handle_size", &handle_size, NULL);
2133 if (gtk_toggle_button_get_active (toggle))
2135 arrow = gtk_arrow_new (GTK_ARROW_LEFT, GTK_SHADOW_NONE);
2136 gtk_widget_show (priv->sidebar);
2137 w += priv->sidebar->allocation.width + handle_size;
2141 arrow = gtk_arrow_new (GTK_ARROW_RIGHT, GTK_SHADOW_NONE);
2142 w -= priv->sidebar->allocation.width + handle_size;
2143 gtk_widget_hide (priv->sidebar);
2146 gtk_button_set_image (GTK_BUTTON (priv->sidebar_button), arrow);
2149 gtk_window_resize (GTK_WINDOW (window), w, h);
2153 empathy_call_window_set_send_video (EmpathyCallWindow *window,
2156 EmpathyCallWindowPriv *priv = GET_PRIV (window);
2157 EmpathyTpCall *call;
2159 priv->sending_video = send;
2161 /* When we start sending video, we want to show the video preview by
2165 empathy_call_window_setup_video_preview (window);
2166 gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (priv->show_preview),
2170 g_object_get (priv->handler, "tp-call", &call, NULL);
2171 empathy_tp_call_request_video_stream_direction (call, send);
2172 g_object_unref (call);
2176 empathy_call_window_camera_toggled_cb (GtkToggleToolButton *toggle,
2177 EmpathyCallWindow *window)
2179 EmpathyCallWindowPriv *priv = GET_PRIV (window);
2182 if (priv->call_state != CONNECTED)
2185 active = (gtk_toggle_tool_button_get_active (toggle));
2187 if (priv->sending_video == active)
2190 empathy_call_window_set_send_video (window, active);
2191 gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (priv->send_video), active);
2195 empathy_call_window_send_video_toggled_cb (GtkToggleAction *toggle,
2196 EmpathyCallWindow *window)
2198 EmpathyCallWindowPriv *priv = GET_PRIV (window);
2201 if (priv->call_state != CONNECTED)
2204 active = (gtk_toggle_action_get_active (toggle));
2206 if (priv->sending_video == active)
2209 empathy_call_window_set_send_video (window, active);
2210 gtk_toggle_tool_button_set_active (
2211 GTK_TOGGLE_TOOL_BUTTON (priv->camera_button), active);
2215 empathy_call_window_show_preview_toggled_cb (GtkToggleAction *toggle,
2216 EmpathyCallWindow *window)
2218 gboolean show_preview_toggled;
2219 EmpathyCallWindowPriv *priv = GET_PRIV (window);
2221 show_preview_toggled = gtk_toggle_action_get_active (toggle);
2223 if (show_preview_toggled)
2225 empathy_call_window_setup_video_preview (window);
2226 gtk_widget_show (priv->self_user_output_frame);
2227 empathy_call_window_update_self_avatar_visibility (window);
2231 gtk_widget_hide (priv->self_user_output_frame);
2236 empathy_call_window_mic_toggled_cb (GtkToggleToolButton *toggle,
2237 EmpathyCallWindow *window)
2239 EmpathyCallWindowPriv *priv = GET_PRIV (window);
2242 if (priv->audio_input == NULL)
2245 active = (gtk_toggle_tool_button_get_active (toggle));
2249 empathy_audio_src_set_volume (EMPATHY_GST_AUDIO_SRC (priv->audio_input),
2251 gtk_adjustment_set_value (priv->audio_input_adj, priv->volume * 100);
2255 /* TODO, Instead of setting the input volume to 0 we should probably
2256 * stop sending but this would cause the audio call to drop if both
2257 * sides mute at the same time on certain CMs AFAIK. Need to revisit this
2258 * in the future. GNOME #574574
2260 empathy_audio_src_set_volume (EMPATHY_GST_AUDIO_SRC (priv->audio_input),
2262 gtk_adjustment_set_value (priv->audio_input_adj, 0);
2267 empathy_call_window_sidebar_hidden_cb (EmpathySidebar *sidebar,
2268 EmpathyCallWindow *window)
2270 EmpathyCallWindowPriv *priv = GET_PRIV (window);
2272 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (priv->sidebar_button),
2277 empathy_call_window_sidebar_shown_cb (EmpathySidebar *sidebar,
2278 EmpathyCallWindow *window)
2280 EmpathyCallWindowPriv *priv = GET_PRIV (window);
2282 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (priv->sidebar_button),
2287 empathy_call_window_hangup_cb (gpointer object,
2288 EmpathyCallWindow *window)
2290 if (empathy_call_window_disconnected (window))
2291 gtk_widget_destroy (GTK_WIDGET (window));
2295 empathy_call_window_restart_call (EmpathyCallWindow *window)
2298 EmpathyCallWindowPriv *priv = GET_PRIV (window);
2300 gtk_widget_destroy (priv->remote_user_output_hbox);
2301 gtk_widget_destroy (priv->self_user_output_hbox);
2303 priv->pipeline = gst_pipeline_new (NULL);
2304 bus = gst_pipeline_get_bus (GST_PIPELINE (priv->pipeline));
2305 priv->bus_message_source_id = gst_bus_add_watch (bus,
2306 empathy_call_window_bus_message, window);
2308 empathy_call_window_setup_remote_frame (bus, window);
2309 empathy_call_window_setup_self_frame (bus, window);
2311 g_signal_connect (G_OBJECT (priv->audio_input_adj), "value-changed",
2312 G_CALLBACK (empathy_call_window_mic_volume_changed_cb), window);
2314 /* While the call was disconnected, the input volume might have changed.
2315 * However, since the audio_input source was destroyed, its volume has not
2316 * been updated during that time. That's why we manually update it here */
2317 empathy_call_window_mic_volume_changed_cb (priv->audio_input_adj, window);
2319 g_object_unref (bus);
2321 gtk_widget_show_all (priv->content_hbox);
2323 if (!empathy_call_handler_has_initial_video (priv->handler))
2324 gtk_widget_hide (priv->self_user_output_frame);
2326 priv->outgoing = TRUE;
2327 empathy_call_window_set_state_connecting (window);
2329 priv->call_started = TRUE;
2330 empathy_call_handler_start_call (priv->handler);
2331 empathy_call_window_setup_avatars (window, priv->handler);
2332 gst_element_set_state (priv->pipeline, GST_STATE_PLAYING);
2334 gtk_action_set_sensitive (priv->redial, FALSE);
2335 gtk_widget_set_sensitive (priv->redial_button, FALSE);
2339 empathy_call_window_redial_cb (gpointer object,
2340 EmpathyCallWindow *window)
2342 EmpathyCallWindowPriv *priv = GET_PRIV (window);
2344 if (priv->call_state == CONNECTED)
2345 priv->call_state = REDIALING;
2347 empathy_call_handler_stop_call (priv->handler);
2349 if (priv->call_state != CONNECTED)
2350 empathy_call_window_restart_call (window);
2354 empathy_call_window_fullscreen_cb (gpointer object,
2355 EmpathyCallWindow *window)
2357 empathy_call_window_fullscreen_toggle (window);
2361 empathy_call_window_fullscreen_toggle (EmpathyCallWindow *window)
2363 EmpathyCallWindowPriv *priv = GET_PRIV (window);
2365 if (priv->is_fullscreen)
2366 gtk_window_unfullscreen (GTK_WINDOW (window));
2368 gtk_window_fullscreen (GTK_WINDOW (window));
2372 empathy_call_window_video_button_press_cb (GtkWidget *video_output,
2373 GdkEventButton *event, EmpathyCallWindow *window)
2375 if (event->button == 3 && event->type == GDK_BUTTON_PRESS)
2377 empathy_call_window_video_menu_popup (window, event->button);
2385 empathy_call_window_key_press_cb (GtkWidget *video_output,
2386 GdkEventKey *event, EmpathyCallWindow *window)
2388 EmpathyCallWindowPriv *priv = GET_PRIV (window);
2390 if (priv->is_fullscreen && event->keyval == GDK_Escape)
2392 /* Since we are in fullscreen mode, toggling will bring us back to
2394 empathy_call_window_fullscreen_toggle (window);
2402 empathy_call_window_video_output_motion_notify (GtkWidget *widget,
2403 GdkEventMotion *event, EmpathyCallWindow *window)
2405 EmpathyCallWindowPriv *priv = GET_PRIV (window);
2407 if (priv->is_fullscreen)
2409 empathy_call_window_fullscreen_show_popup (priv->fullscreen);
2416 empathy_call_window_video_menu_popup (EmpathyCallWindow *window,
2420 EmpathyCallWindowPriv *priv = GET_PRIV (window);
2422 menu = gtk_ui_manager_get_widget (priv->ui_manager,
2424 gtk_menu_popup (GTK_MENU (menu), NULL, NULL, NULL, NULL,
2425 button, gtk_get_current_event_time ());
2426 gtk_menu_shell_select_first (GTK_MENU_SHELL (menu), FALSE);
2430 empathy_call_window_status_message (EmpathyCallWindow *window,
2433 EmpathyCallWindowPriv *priv = GET_PRIV (window);
2435 if (priv->context_id == 0)
2437 priv->context_id = gtk_statusbar_get_context_id (
2438 GTK_STATUSBAR (priv->statusbar), "voip call status messages");
2442 gtk_statusbar_pop (GTK_STATUSBAR (priv->statusbar), priv->context_id);
2445 gtk_statusbar_push (GTK_STATUSBAR (priv->statusbar), priv->context_id,
2450 empathy_call_window_volume_changed_cb (GtkScaleButton *button,
2451 gdouble value, EmpathyCallWindow *window)
2453 EmpathyCallWindowPriv *priv = GET_PRIV (window);
2455 if (priv->audio_output == NULL)
2458 empathy_audio_sink_set_volume (EMPATHY_GST_AUDIO_SINK (priv->audio_output),