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,
1470 const gchar *details)
1472 EmpathyCallWindowPriv *priv = GET_PRIV (self);
1473 GtkWidget *info_bar;
1474 GtkWidget *content_area;
1481 /* Create info bar */
1482 info_bar = gtk_info_bar_new_with_buttons (GTK_STOCK_CLOSE, GTK_RESPONSE_CLOSE,
1485 gtk_widget_set_no_show_all (info_bar, TRUE);
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_widget_show (image);
1497 gtk_box_pack_start (GTK_BOX (hbox), image, FALSE, FALSE, 0);
1499 /* vbox containing the main message and the details expander */
1500 vbox = gtk_vbox_new (FALSE, 3);
1501 gtk_box_pack_start (GTK_BOX (hbox), vbox, TRUE, TRUE, 0);
1504 txt = g_strdup_printf ("<b>%s</b>\n%s", title, desc);
1506 label = gtk_label_new (NULL);
1507 gtk_label_set_markup (GTK_LABEL (label), txt);
1508 gtk_label_set_line_wrap (GTK_LABEL (label), TRUE);
1509 gtk_misc_set_alignment (GTK_MISC (label), 0, 0);
1512 gtk_box_pack_start (GTK_BOX (vbox), label, TRUE, TRUE, 0);
1515 if (details != NULL)
1517 GtkWidget *expander;
1519 expander = gtk_expander_new (_("Technical Details"));
1521 txt = g_strdup_printf ("<i>%s</i>", details);
1523 label = gtk_label_new (NULL);
1524 gtk_label_set_markup (GTK_LABEL (label), txt);
1525 gtk_label_set_line_wrap (GTK_LABEL (label), TRUE);
1526 gtk_misc_set_alignment (GTK_MISC (label), 0, 0);
1529 gtk_container_add (GTK_CONTAINER (expander), label);
1530 gtk_box_pack_start (GTK_BOX (vbox), expander, TRUE, TRUE, 0);
1533 g_signal_connect (info_bar, "response",
1534 G_CALLBACK (gtk_widget_destroy), NULL);
1536 gtk_box_pack_start (GTK_BOX (priv->errors_vbox), info_bar,
1537 FALSE, FALSE, CONTENT_HBOX_CHILDREN_PACKING_PADDING);
1538 gtk_widget_show_all (hbox);
1539 gtk_widget_show (info_bar);
1543 media_stream_error_to_txt (EmpathyCallWindow *self,
1545 TpMediaStreamError error)
1547 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 /* TODO: support more errors */
1577 empathy_call_window_stream_error (EmpathyCallWindow *self,
1586 desc = media_stream_error_to_txt (self, audio, code);
1589 /* No description, use the error message. That's not great as it's not
1590 * localized but it's better than nothing. */
1591 display_error (self, icon, title, msg, NULL);
1595 display_error (self, icon, title, desc, msg);
1601 empathy_call_window_audio_stream_error (EmpathyTpCall *call,
1604 EmpathyCallWindow *self)
1606 empathy_call_window_stream_error (self, TRUE, code, msg,
1607 "gnome-stock-mic", _("Can't establish audio stream"));
1611 empathy_call_window_video_stream_error (EmpathyTpCall *call,
1614 EmpathyCallWindow *self)
1616 empathy_call_window_stream_error (self, FALSE, code, msg,
1617 "camera-web", _("Can't establish video stream"));
1621 empathy_call_window_connected (gpointer user_data)
1623 EmpathyCallWindow *self = EMPATHY_CALL_WINDOW (user_data);
1624 EmpathyCallWindowPriv *priv = GET_PRIV (self);
1625 EmpathyTpCall *call;
1626 gboolean can_send_video;
1628 empathy_sound_stop (EMPATHY_SOUND_PHONE_OUTGOING);
1630 can_send_video = priv->video_input != NULL && priv->contact != NULL &&
1631 empathy_contact_can_voip_video (priv->contact);
1633 g_object_get (priv->handler, "tp-call", &call, NULL);
1635 g_signal_connect (call, "notify::video-stream",
1636 G_CALLBACK (empathy_call_window_video_stream_changed_cb), self);
1638 if (empathy_tp_call_has_dtmf (call))
1639 gtk_widget_set_sensitive (priv->dtmf_panel, TRUE);
1641 if (priv->video_input == NULL)
1642 empathy_call_window_set_send_video (self, FALSE);
1644 priv->sending_video = can_send_video ?
1645 empathy_tp_call_is_sending_video (call) : FALSE;
1647 gtk_action_set_sensitive (priv->show_preview, TRUE);
1648 gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (priv->show_preview),
1650 || gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (
1651 priv->show_preview)));
1652 gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (priv->send_video),
1653 priv->sending_video && priv->video_input != NULL);
1654 gtk_toggle_tool_button_set_active (
1655 GTK_TOGGLE_TOOL_BUTTON (priv->camera_button),
1656 priv->sending_video && priv->video_input != NULL);
1657 gtk_widget_set_sensitive (priv->camera_button, can_send_video);
1658 gtk_action_set_sensitive (priv->send_video, can_send_video);
1660 gtk_action_set_sensitive (priv->redial, FALSE);
1661 gtk_widget_set_sensitive (priv->redial_button, FALSE);
1663 gtk_widget_set_sensitive (priv->mic_button, TRUE);
1665 empathy_call_window_update_avatars_visibility (call, self);
1667 g_object_unref (call);
1669 g_mutex_lock (priv->lock);
1671 priv->timer_id = g_timeout_add_seconds (1,
1672 empathy_call_window_update_timer, self);
1674 g_mutex_unlock (priv->lock);
1676 empathy_call_window_update_timer (self);
1682 /* Called from the streaming thread */
1684 empathy_call_window_src_added_cb (EmpathyCallHandler *handler,
1685 GstPad *src, guint media_type, gpointer user_data)
1687 EmpathyCallWindow *self = EMPATHY_CALL_WINDOW (user_data);
1688 EmpathyCallWindowPriv *priv = GET_PRIV (self);
1692 g_mutex_lock (priv->lock);
1694 if (priv->call_state != CONNECTED)
1696 g_timer_start (priv->timer);
1697 priv->timer_id = g_idle_add (empathy_call_window_connected, self);
1698 priv->call_state = CONNECTED;
1703 case TP_MEDIA_STREAM_TYPE_AUDIO:
1704 pad = empathy_call_window_get_audio_sink_pad (self);
1706 case TP_MEDIA_STREAM_TYPE_VIDEO:
1707 gtk_widget_hide (priv->remote_user_avatar_widget);
1708 gtk_widget_show (priv->video_output);
1709 pad = empathy_call_window_get_video_sink_pad (self);
1712 g_assert_not_reached ();
1715 gst_pad_link (src, pad);
1716 gst_object_unref (pad);
1718 g_mutex_unlock (priv->lock);
1721 /* Called from the streaming thread */
1723 empathy_call_window_sink_added_cb (EmpathyCallHandler *handler,
1724 GstPad *sink, guint media_type, gpointer user_data)
1726 EmpathyCallWindow *self = EMPATHY_CALL_WINDOW (user_data);
1727 EmpathyCallWindowPriv *priv = GET_PRIV (self);
1732 case TP_MEDIA_STREAM_TYPE_AUDIO:
1733 gst_bin_add (GST_BIN (priv->pipeline), priv->audio_input);
1735 pad = gst_element_get_static_pad (priv->audio_input, "src");
1736 gst_pad_link (pad, sink);
1738 gst_element_set_state (priv->audio_input, GST_STATE_PLAYING);
1740 case TP_MEDIA_STREAM_TYPE_VIDEO:
1741 if (priv->video_input != NULL)
1743 EmpathyTpCall *call;
1744 g_object_get (priv->handler, "tp-call", &call, NULL);
1746 if (empathy_tp_call_is_sending_video (call))
1748 empathy_call_window_setup_video_preview (self);
1750 gtk_toggle_action_set_active (
1751 GTK_TOGGLE_ACTION (priv->show_preview), TRUE);
1753 if (priv->video_preview != NULL)
1754 gtk_widget_show (priv->video_preview);
1755 gtk_widget_hide (priv->self_user_avatar_widget);
1758 g_object_unref (call);
1760 if (priv->video_tee != NULL)
1762 pad = gst_element_get_request_pad (priv->video_tee, "src%d");
1763 gst_pad_link (pad, sink);
1768 g_assert_not_reached ();
1774 empathy_call_window_remove_video_input (EmpathyCallWindow *self)
1776 EmpathyCallWindowPriv *priv = GET_PRIV (self);
1777 GstElement *preview;
1779 preview = empathy_video_widget_get_element (
1780 EMPATHY_VIDEO_WIDGET (priv->video_preview));
1782 gst_element_set_state (priv->video_input, GST_STATE_NULL);
1783 gst_element_set_state (priv->video_tee, GST_STATE_NULL);
1784 gst_element_set_state (preview, GST_STATE_NULL);
1786 gst_bin_remove_many (GST_BIN (priv->pipeline), priv->video_input,
1787 priv->video_tee, preview, NULL);
1789 g_object_unref (priv->video_input);
1790 priv->video_input = NULL;
1791 g_object_unref (priv->video_tee);
1792 priv->video_tee = NULL;
1793 gtk_widget_destroy (priv->video_preview);
1794 priv->video_preview = NULL;
1796 gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (priv->send_video), FALSE);
1797 gtk_toggle_tool_button_set_active (
1798 GTK_TOGGLE_TOOL_BUTTON (priv->camera_button), FALSE);
1799 gtk_widget_set_sensitive (priv->camera_button, FALSE);
1800 gtk_action_set_sensitive (priv->send_video, FALSE);
1802 gtk_widget_show (priv->self_user_avatar_widget);
1807 empathy_call_window_bus_message (GstBus *bus, GstMessage *message,
1810 EmpathyCallWindow *self = EMPATHY_CALL_WINDOW (user_data);
1811 EmpathyCallWindowPriv *priv = GET_PRIV (self);
1814 empathy_call_handler_bus_message (priv->handler, bus, message);
1816 switch (GST_MESSAGE_TYPE (message))
1818 case GST_MESSAGE_STATE_CHANGED:
1819 if (GST_MESSAGE_SRC (message) == GST_OBJECT (priv->video_input))
1821 gst_message_parse_state_changed (message, NULL, &newstate, NULL);
1822 if (newstate == GST_STATE_PAUSED)
1823 empathy_call_window_setup_video_input (self);
1825 if (GST_MESSAGE_SRC (message) == GST_OBJECT (priv->pipeline) &&
1826 !priv->call_started)
1828 gst_message_parse_state_changed (message, NULL, &newstate, NULL);
1829 if (newstate == GST_STATE_PAUSED)
1831 priv->call_started = TRUE;
1832 empathy_call_handler_start_call (priv->handler);
1833 gst_element_set_state (priv->pipeline, GST_STATE_PLAYING);
1837 case GST_MESSAGE_ERROR:
1839 GError *error = NULL;
1840 GstElement *gst_error;
1843 gst_message_parse_error (message, &error, &debug);
1844 gst_error = GST_ELEMENT (GST_MESSAGE_SRC (message));
1846 g_message ("Element error: %s -- %s\n", error->message, debug);
1848 if (g_str_has_prefix (gst_element_get_name (gst_error),
1849 VIDEO_INPUT_ERROR_PREFIX))
1851 /* Remove the video input and continue */
1852 if (priv->video_input != NULL)
1853 empathy_call_window_remove_video_input (self);
1854 gst_element_set_state (priv->pipeline, GST_STATE_PLAYING);
1858 empathy_call_window_disconnected (self);
1860 g_error_free (error);
1871 empathy_call_window_update_self_avatar_visibility (EmpathyCallWindow *window)
1873 EmpathyCallWindowPriv *priv = GET_PRIV (window);
1875 if (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (priv->show_preview)))
1877 if (priv->video_preview != NULL)
1879 gtk_widget_hide (priv->self_user_avatar_widget);
1880 gtk_widget_show (priv->video_preview);
1884 if (priv->video_preview != NULL)
1885 gtk_widget_hide (priv->video_preview);
1887 gtk_widget_show (priv->self_user_avatar_widget);
1893 empathy_call_window_update_avatars_visibility (EmpathyTpCall *call,
1894 EmpathyCallWindow *window)
1896 EmpathyCallWindowPriv *priv = GET_PRIV (window);
1898 if (empathy_tp_call_is_receiving_video (call))
1900 gtk_widget_hide (priv->remote_user_avatar_widget);
1901 gtk_widget_show (priv->video_output);
1905 gtk_widget_hide (priv->video_output);
1906 gtk_widget_show (priv->remote_user_avatar_widget);
1909 empathy_call_window_update_self_avatar_visibility (window);
1913 call_handler_notify_tp_call_cb (EmpathyCallHandler *handler,
1915 EmpathyCallWindow *self)
1917 EmpathyCallWindowPriv *priv = GET_PRIV (self);
1918 EmpathyTpCall *call;
1920 g_object_get (priv->handler, "tp-call", &call, NULL);
1924 empathy_signal_connect_weak (call, "audio-stream-error",
1925 G_CALLBACK (empathy_call_window_audio_stream_error), G_OBJECT (self));
1926 empathy_signal_connect_weak (call, "video-stream-error",
1927 G_CALLBACK (empathy_call_window_video_stream_error), G_OBJECT (self));
1929 g_object_unref (call);
1933 empathy_call_window_realized_cb (GtkWidget *widget, EmpathyCallWindow *window)
1935 EmpathyCallWindowPriv *priv = GET_PRIV (window);
1936 EmpathyTpCall *call;
1938 g_signal_connect (priv->handler, "conference-added",
1939 G_CALLBACK (empathy_call_window_conference_added_cb), window);
1940 g_signal_connect (priv->handler, "request-resource",
1941 G_CALLBACK (empathy_call_window_request_resource_cb), window);
1942 g_signal_connect (priv->handler, "closed",
1943 G_CALLBACK (empathy_call_window_channel_closed_cb), window);
1944 g_signal_connect (priv->handler, "src-pad-added",
1945 G_CALLBACK (empathy_call_window_src_added_cb), window);
1946 g_signal_connect (priv->handler, "sink-pad-added",
1947 G_CALLBACK (empathy_call_window_sink_added_cb), window);
1948 g_signal_connect (priv->handler, "stream-closed",
1949 G_CALLBACK (empathy_call_window_channel_stream_closed_cb), window);
1951 g_object_get (priv->handler, "tp-call", &call, NULL);
1954 empathy_signal_connect_weak (call, "audio-stream-error",
1955 G_CALLBACK (empathy_call_window_audio_stream_error), G_OBJECT (window));
1956 empathy_signal_connect_weak (call, "video-stream-error",
1957 G_CALLBACK (empathy_call_window_video_stream_error), G_OBJECT (window));
1959 g_object_unref (call);
1963 /* tp-call doesn't exist yet, we'll connect signals once it has been
1965 g_signal_connect (priv->handler, "notify::tp-call",
1966 G_CALLBACK (call_handler_notify_tp_call_cb), window);
1969 gst_element_set_state (priv->pipeline, GST_STATE_PAUSED);
1973 empathy_call_window_delete_cb (GtkWidget *widget, GdkEvent*event,
1974 EmpathyCallWindow *window)
1976 EmpathyCallWindowPriv *priv = GET_PRIV (window);
1978 if (priv->pipeline != NULL)
1980 if (priv->bus_message_source_id != 0)
1982 g_source_remove (priv->bus_message_source_id);
1983 priv->bus_message_source_id = 0;
1986 gst_element_set_state (priv->pipeline, GST_STATE_NULL);
1989 if (priv->call_state == CONNECTING)
1990 empathy_sound_stop (EMPATHY_SOUND_PHONE_OUTGOING);
1996 show_controls (EmpathyCallWindow *window, gboolean set_fullscreen)
1999 EmpathyCallWindowPriv *priv = GET_PRIV (window);
2001 menu = gtk_ui_manager_get_widget (priv->ui_manager,
2006 gtk_widget_hide (priv->sidebar);
2007 gtk_widget_hide (menu);
2008 gtk_widget_hide (priv->vbox);
2009 gtk_widget_hide (priv->statusbar);
2010 gtk_widget_hide (priv->toolbar);
2014 if (priv->sidebar_was_visible_before_fs)
2015 gtk_widget_show (priv->sidebar);
2017 gtk_widget_show (menu);
2018 gtk_widget_show (priv->vbox);
2019 gtk_widget_show (priv->statusbar);
2020 gtk_widget_show (priv->toolbar);
2022 gtk_window_resize (GTK_WINDOW (window), priv->original_width_before_fs,
2023 priv->original_height_before_fs);
2028 show_borders (EmpathyCallWindow *window, gboolean set_fullscreen)
2030 EmpathyCallWindowPriv *priv = GET_PRIV (window);
2032 gtk_container_set_border_width (GTK_CONTAINER (priv->content_hbox),
2033 set_fullscreen ? 0 : CONTENT_HBOX_BORDER_WIDTH);
2034 gtk_box_set_spacing (GTK_BOX (priv->content_hbox),
2035 set_fullscreen ? 0 : CONTENT_HBOX_SPACING);
2036 gtk_box_set_child_packing (GTK_BOX (priv->content_hbox),
2037 priv->video_output, TRUE, TRUE,
2038 set_fullscreen ? 0 : CONTENT_HBOX_CHILDREN_PACKING_PADDING,
2040 gtk_box_set_child_packing (GTK_BOX (priv->content_hbox),
2041 priv->vbox, TRUE, TRUE,
2042 set_fullscreen ? 0 : CONTENT_HBOX_CHILDREN_PACKING_PADDING,
2047 empathy_call_window_state_event_cb (GtkWidget *widget,
2048 GdkEventWindowState *event, EmpathyCallWindow *window)
2050 if (event->changed_mask & GDK_WINDOW_STATE_FULLSCREEN)
2052 EmpathyCallWindowPriv *priv = GET_PRIV (window);
2053 gboolean set_fullscreen = event->new_window_state &
2054 GDK_WINDOW_STATE_FULLSCREEN;
2058 gboolean sidebar_was_visible;
2059 gint original_width = GTK_WIDGET (window)->allocation.width;
2060 gint original_height = GTK_WIDGET (window)->allocation.height;
2062 g_object_get (priv->sidebar, "visible", &sidebar_was_visible, NULL);
2064 priv->sidebar_was_visible_before_fs = sidebar_was_visible;
2065 priv->original_width_before_fs = original_width;
2066 priv->original_height_before_fs = original_height;
2068 if (priv->video_output_motion_handler_id == 0 &&
2069 priv->video_output != NULL)
2071 priv->video_output_motion_handler_id = g_signal_connect (
2072 G_OBJECT (priv->video_output), "motion-notify-event",
2073 G_CALLBACK (empathy_call_window_video_output_motion_notify),
2079 if (priv->video_output_motion_handler_id != 0)
2081 g_signal_handler_disconnect (G_OBJECT (priv->video_output),
2082 priv->video_output_motion_handler_id);
2083 priv->video_output_motion_handler_id = 0;
2087 empathy_call_window_fullscreen_set_fullscreen (priv->fullscreen,
2089 show_controls (window, set_fullscreen);
2090 show_borders (window, set_fullscreen);
2091 gtk_action_set_stock_id (priv->menu_fullscreen,
2092 (set_fullscreen ? "gtk-leave-fullscreen" : "gtk-fullscreen"));
2093 priv->is_fullscreen = set_fullscreen;
2100 empathy_call_window_sidebar_toggled_cb (GtkToggleButton *toggle,
2101 EmpathyCallWindow *window)
2103 EmpathyCallWindowPriv *priv = GET_PRIV (window);
2105 int w,h, handle_size;
2107 w = GTK_WIDGET (window)->allocation.width;
2108 h = GTK_WIDGET (window)->allocation.height;
2110 gtk_widget_style_get (priv->pane, "handle_size", &handle_size, NULL);
2112 if (gtk_toggle_button_get_active (toggle))
2114 arrow = gtk_arrow_new (GTK_ARROW_LEFT, GTK_SHADOW_NONE);
2115 gtk_widget_show (priv->sidebar);
2116 w += priv->sidebar->allocation.width + handle_size;
2120 arrow = gtk_arrow_new (GTK_ARROW_RIGHT, GTK_SHADOW_NONE);
2121 w -= priv->sidebar->allocation.width + handle_size;
2122 gtk_widget_hide (priv->sidebar);
2125 gtk_button_set_image (GTK_BUTTON (priv->sidebar_button), arrow);
2128 gtk_window_resize (GTK_WINDOW (window), w, h);
2132 empathy_call_window_set_send_video (EmpathyCallWindow *window,
2135 EmpathyCallWindowPriv *priv = GET_PRIV (window);
2136 EmpathyTpCall *call;
2138 priv->sending_video = send;
2140 /* When we start sending video, we want to show the video preview by
2144 empathy_call_window_setup_video_preview (window);
2145 gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (priv->show_preview),
2149 g_object_get (priv->handler, "tp-call", &call, NULL);
2150 empathy_tp_call_request_video_stream_direction (call, send);
2151 g_object_unref (call);
2155 empathy_call_window_camera_toggled_cb (GtkToggleToolButton *toggle,
2156 EmpathyCallWindow *window)
2158 EmpathyCallWindowPriv *priv = GET_PRIV (window);
2161 if (priv->call_state != CONNECTED)
2164 active = (gtk_toggle_tool_button_get_active (toggle));
2166 if (priv->sending_video == active)
2169 empathy_call_window_set_send_video (window, active);
2170 gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (priv->send_video), active);
2174 empathy_call_window_send_video_toggled_cb (GtkToggleAction *toggle,
2175 EmpathyCallWindow *window)
2177 EmpathyCallWindowPriv *priv = GET_PRIV (window);
2180 if (priv->call_state != CONNECTED)
2183 active = (gtk_toggle_action_get_active (toggle));
2185 if (priv->sending_video == active)
2188 empathy_call_window_set_send_video (window, active);
2189 gtk_toggle_tool_button_set_active (
2190 GTK_TOGGLE_TOOL_BUTTON (priv->camera_button), active);
2194 empathy_call_window_show_preview_toggled_cb (GtkToggleAction *toggle,
2195 EmpathyCallWindow *window)
2197 gboolean show_preview_toggled;
2198 EmpathyCallWindowPriv *priv = GET_PRIV (window);
2200 show_preview_toggled = gtk_toggle_action_get_active (toggle);
2202 if (show_preview_toggled)
2204 empathy_call_window_setup_video_preview (window);
2205 gtk_widget_show (priv->self_user_output_frame);
2206 empathy_call_window_update_self_avatar_visibility (window);
2210 gtk_widget_hide (priv->self_user_output_frame);
2215 empathy_call_window_mic_toggled_cb (GtkToggleToolButton *toggle,
2216 EmpathyCallWindow *window)
2218 EmpathyCallWindowPriv *priv = GET_PRIV (window);
2221 if (priv->audio_input == NULL)
2224 active = (gtk_toggle_tool_button_get_active (toggle));
2228 empathy_audio_src_set_volume (EMPATHY_GST_AUDIO_SRC (priv->audio_input),
2230 gtk_adjustment_set_value (priv->audio_input_adj, priv->volume * 100);
2234 /* TODO, Instead of setting the input volume to 0 we should probably
2235 * stop sending but this would cause the audio call to drop if both
2236 * sides mute at the same time on certain CMs AFAIK. Need to revisit this
2237 * in the future. GNOME #574574
2239 empathy_audio_src_set_volume (EMPATHY_GST_AUDIO_SRC (priv->audio_input),
2241 gtk_adjustment_set_value (priv->audio_input_adj, 0);
2246 empathy_call_window_sidebar_hidden_cb (EmpathySidebar *sidebar,
2247 EmpathyCallWindow *window)
2249 EmpathyCallWindowPriv *priv = GET_PRIV (window);
2251 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (priv->sidebar_button),
2256 empathy_call_window_sidebar_shown_cb (EmpathySidebar *sidebar,
2257 EmpathyCallWindow *window)
2259 EmpathyCallWindowPriv *priv = GET_PRIV (window);
2261 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (priv->sidebar_button),
2266 empathy_call_window_hangup_cb (gpointer object,
2267 EmpathyCallWindow *window)
2269 if (empathy_call_window_disconnected (window))
2270 gtk_widget_destroy (GTK_WIDGET (window));
2274 empathy_call_window_restart_call (EmpathyCallWindow *window)
2277 EmpathyCallWindowPriv *priv = GET_PRIV (window);
2279 gtk_widget_destroy (priv->remote_user_output_hbox);
2280 gtk_widget_destroy (priv->self_user_output_hbox);
2282 priv->pipeline = gst_pipeline_new (NULL);
2283 bus = gst_pipeline_get_bus (GST_PIPELINE (priv->pipeline));
2284 priv->bus_message_source_id = gst_bus_add_watch (bus,
2285 empathy_call_window_bus_message, window);
2287 empathy_call_window_setup_remote_frame (bus, window);
2288 empathy_call_window_setup_self_frame (bus, window);
2290 g_signal_connect (G_OBJECT (priv->audio_input_adj), "value-changed",
2291 G_CALLBACK (empathy_call_window_mic_volume_changed_cb), window);
2293 /* While the call was disconnected, the input volume might have changed.
2294 * However, since the audio_input source was destroyed, its volume has not
2295 * been updated during that time. That's why we manually update it here */
2296 empathy_call_window_mic_volume_changed_cb (priv->audio_input_adj, window);
2298 g_object_unref (bus);
2300 gtk_widget_show_all (priv->content_hbox);
2302 if (!empathy_call_handler_has_initial_video (priv->handler))
2303 gtk_widget_hide (priv->self_user_output_frame);
2305 priv->outgoing = TRUE;
2306 empathy_call_window_set_state_connecting (window);
2308 priv->call_started = TRUE;
2309 empathy_call_handler_start_call (priv->handler);
2310 empathy_call_window_setup_avatars (window, priv->handler);
2311 gst_element_set_state (priv->pipeline, GST_STATE_PLAYING);
2313 gtk_action_set_sensitive (priv->redial, FALSE);
2314 gtk_widget_set_sensitive (priv->redial_button, FALSE);
2318 empathy_call_window_redial_cb (gpointer object,
2319 EmpathyCallWindow *window)
2321 EmpathyCallWindowPriv *priv = GET_PRIV (window);
2323 if (priv->call_state == CONNECTED)
2324 priv->call_state = REDIALING;
2326 empathy_call_handler_stop_call (priv->handler);
2328 if (priv->call_state != CONNECTED)
2329 empathy_call_window_restart_call (window);
2333 empathy_call_window_fullscreen_cb (gpointer object,
2334 EmpathyCallWindow *window)
2336 empathy_call_window_fullscreen_toggle (window);
2340 empathy_call_window_fullscreen_toggle (EmpathyCallWindow *window)
2342 EmpathyCallWindowPriv *priv = GET_PRIV (window);
2344 if (priv->is_fullscreen)
2345 gtk_window_unfullscreen (GTK_WINDOW (window));
2347 gtk_window_fullscreen (GTK_WINDOW (window));
2351 empathy_call_window_video_button_press_cb (GtkWidget *video_output,
2352 GdkEventButton *event, EmpathyCallWindow *window)
2354 if (event->button == 3 && event->type == GDK_BUTTON_PRESS)
2356 empathy_call_window_video_menu_popup (window, event->button);
2364 empathy_call_window_key_press_cb (GtkWidget *video_output,
2365 GdkEventKey *event, EmpathyCallWindow *window)
2367 EmpathyCallWindowPriv *priv = GET_PRIV (window);
2369 if (priv->is_fullscreen && event->keyval == GDK_Escape)
2371 /* Since we are in fullscreen mode, toggling will bring us back to
2373 empathy_call_window_fullscreen_toggle (window);
2381 empathy_call_window_video_output_motion_notify (GtkWidget *widget,
2382 GdkEventMotion *event, EmpathyCallWindow *window)
2384 EmpathyCallWindowPriv *priv = GET_PRIV (window);
2386 if (priv->is_fullscreen)
2388 empathy_call_window_fullscreen_show_popup (priv->fullscreen);
2395 empathy_call_window_video_menu_popup (EmpathyCallWindow *window,
2399 EmpathyCallWindowPriv *priv = GET_PRIV (window);
2401 menu = gtk_ui_manager_get_widget (priv->ui_manager,
2403 gtk_menu_popup (GTK_MENU (menu), NULL, NULL, NULL, NULL,
2404 button, gtk_get_current_event_time ());
2405 gtk_menu_shell_select_first (GTK_MENU_SHELL (menu), FALSE);
2409 empathy_call_window_status_message (EmpathyCallWindow *window,
2412 EmpathyCallWindowPriv *priv = GET_PRIV (window);
2414 if (priv->context_id == 0)
2416 priv->context_id = gtk_statusbar_get_context_id (
2417 GTK_STATUSBAR (priv->statusbar), "voip call status messages");
2421 gtk_statusbar_pop (GTK_STATUSBAR (priv->statusbar), priv->context_id);
2424 gtk_statusbar_push (GTK_STATUSBAR (priv->statusbar), priv->context_id,
2429 empathy_call_window_volume_changed_cb (GtkScaleButton *button,
2430 gdouble value, EmpathyCallWindow *window)
2432 EmpathyCallWindowPriv *priv = GET_PRIV (window);
2434 if (priv->audio_output == NULL)
2437 empathy_audio_sink_set_volume (EMPATHY_GST_AUDIO_SINK (priv->audio_output),