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_info_bar_set_message_type (GTK_INFO_BAR (info_bar), GTK_MESSAGE_WARNING);
1487 content_area = gtk_info_bar_get_content_area (GTK_INFO_BAR (info_bar));
1489 /* hbox containing the image and the messages vbox */
1490 hbox = gtk_hbox_new (FALSE, 3);
1491 gtk_container_add (GTK_CONTAINER (content_area), hbox);
1494 image = gtk_image_new_from_icon_name (img, GTK_ICON_SIZE_DIALOG);
1495 gtk_box_pack_start (GTK_BOX (hbox), image, FALSE, FALSE, 0);
1497 /* vbox containing the main message and the details expander */
1498 vbox = gtk_vbox_new (FALSE, 3);
1499 gtk_box_pack_start (GTK_BOX (hbox), vbox, TRUE, TRUE, 0);
1502 txt = g_strdup_printf ("<b>%s</b>\n%s", title, desc);
1504 label = gtk_label_new (NULL);
1505 gtk_label_set_markup (GTK_LABEL (label), txt);
1506 gtk_label_set_line_wrap (GTK_LABEL (label), TRUE);
1507 gtk_misc_set_alignment (GTK_MISC (label), 0, 0);
1510 gtk_box_pack_start (GTK_BOX (vbox), label, TRUE, TRUE, 0);
1513 if (details != NULL)
1515 GtkWidget *expander;
1517 expander = gtk_expander_new (_("Technical Details"));
1519 txt = g_strdup_printf ("<i>%s</i>", details);
1521 label = gtk_label_new (NULL);
1522 gtk_label_set_markup (GTK_LABEL (label), txt);
1523 gtk_label_set_line_wrap (GTK_LABEL (label), TRUE);
1524 gtk_misc_set_alignment (GTK_MISC (label), 0, 0);
1527 gtk_container_add (GTK_CONTAINER (expander), label);
1528 gtk_box_pack_start (GTK_BOX (vbox), expander, TRUE, TRUE, 0);
1531 g_signal_connect (info_bar, "response",
1532 G_CALLBACK (gtk_widget_destroy), NULL);
1534 gtk_box_pack_start (GTK_BOX (priv->errors_vbox), info_bar,
1535 FALSE, FALSE, CONTENT_HBOX_CHILDREN_PACKING_PADDING);
1536 gtk_widget_show_all (info_bar);
1540 media_stream_error_to_txt (EmpathyCallWindow *self,
1542 TpMediaStreamError error)
1544 EmpathyCallWindowPriv *priv = GET_PRIV (self);
1548 case TP_MEDIA_STREAM_ERROR_CODEC_NEGOTIATION_FAILED:
1550 return g_strdup_printf (
1551 _("%s's software does not understand any of the audio formats "
1552 "supported by your computer"),
1553 empathy_contact_get_name (priv->contact));
1555 return g_strdup_printf (
1556 _("%s's software does not understand any of the video formats "
1557 "supported by your computer"),
1558 empathy_contact_get_name (priv->contact));
1560 case TP_MEDIA_STREAM_ERROR_CONNECTION_FAILED:
1561 return g_strdup_printf (
1562 _("Can't establish a connection to %s. "
1563 "One of you might be on a network that does not allow "
1564 "direct connections."),
1565 empathy_contact_get_name (priv->contact));
1567 case TP_MEDIA_STREAM_ERROR_NO_CODECS:
1569 return g_strdup (_("Your computer doesn't support any audio format"));
1571 return g_strdup (_("Your computer doesn't support any video format"));
1573 /* TODO: support more errors */
1580 empathy_call_window_stream_error (EmpathyCallWindow *self,
1589 desc = media_stream_error_to_txt (self, audio, code);
1592 /* No description, use the error message. That's not great as it's not
1593 * localized but it's better than nothing. */
1594 display_error (self, icon, title, msg, NULL);
1598 display_error (self, icon, title, desc, msg);
1604 empathy_call_window_audio_stream_error (EmpathyTpCall *call,
1607 EmpathyCallWindow *self)
1609 empathy_call_window_stream_error (self, TRUE, code, msg,
1610 "gnome-stock-mic", _("Can't establish audio stream"));
1614 empathy_call_window_video_stream_error (EmpathyTpCall *call,
1617 EmpathyCallWindow *self)
1619 empathy_call_window_stream_error (self, FALSE, code, msg,
1620 "camera-web", _("Can't establish video stream"));
1624 empathy_call_window_connected (gpointer user_data)
1626 EmpathyCallWindow *self = EMPATHY_CALL_WINDOW (user_data);
1627 EmpathyCallWindowPriv *priv = GET_PRIV (self);
1628 EmpathyTpCall *call;
1629 gboolean can_send_video;
1631 empathy_sound_stop (EMPATHY_SOUND_PHONE_OUTGOING);
1633 can_send_video = priv->video_input != NULL && priv->contact != NULL &&
1634 empathy_contact_can_voip_video (priv->contact);
1636 g_object_get (priv->handler, "tp-call", &call, NULL);
1638 g_signal_connect (call, "notify::video-stream",
1639 G_CALLBACK (empathy_call_window_video_stream_changed_cb), self);
1641 if (empathy_tp_call_has_dtmf (call))
1642 gtk_widget_set_sensitive (priv->dtmf_panel, TRUE);
1644 if (priv->video_input == NULL)
1645 empathy_call_window_set_send_video (self, FALSE);
1647 priv->sending_video = can_send_video ?
1648 empathy_tp_call_is_sending_video (call) : FALSE;
1650 gtk_action_set_sensitive (priv->show_preview, TRUE);
1651 gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (priv->show_preview),
1653 || gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (
1654 priv->show_preview)));
1655 gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (priv->send_video),
1656 priv->sending_video && priv->video_input != NULL);
1657 gtk_toggle_tool_button_set_active (
1658 GTK_TOGGLE_TOOL_BUTTON (priv->camera_button),
1659 priv->sending_video && priv->video_input != NULL);
1660 gtk_widget_set_sensitive (priv->camera_button, can_send_video);
1661 gtk_action_set_sensitive (priv->send_video, can_send_video);
1663 gtk_action_set_sensitive (priv->redial, FALSE);
1664 gtk_widget_set_sensitive (priv->redial_button, FALSE);
1666 gtk_widget_set_sensitive (priv->mic_button, TRUE);
1668 empathy_call_window_update_avatars_visibility (call, self);
1670 g_object_unref (call);
1672 g_mutex_lock (priv->lock);
1674 priv->timer_id = g_timeout_add_seconds (1,
1675 empathy_call_window_update_timer, self);
1677 g_mutex_unlock (priv->lock);
1679 empathy_call_window_update_timer (self);
1685 /* Called from the streaming thread */
1687 empathy_call_window_src_added_cb (EmpathyCallHandler *handler,
1688 GstPad *src, guint media_type, gpointer user_data)
1690 EmpathyCallWindow *self = EMPATHY_CALL_WINDOW (user_data);
1691 EmpathyCallWindowPriv *priv = GET_PRIV (self);
1695 g_mutex_lock (priv->lock);
1697 if (priv->call_state != CONNECTED)
1699 g_timer_start (priv->timer);
1700 priv->timer_id = g_idle_add (empathy_call_window_connected, self);
1701 priv->call_state = CONNECTED;
1706 case TP_MEDIA_STREAM_TYPE_AUDIO:
1707 pad = empathy_call_window_get_audio_sink_pad (self);
1709 case TP_MEDIA_STREAM_TYPE_VIDEO:
1710 gtk_widget_hide (priv->remote_user_avatar_widget);
1711 gtk_widget_show (priv->video_output);
1712 pad = empathy_call_window_get_video_sink_pad (self);
1715 g_assert_not_reached ();
1718 gst_pad_link (src, pad);
1719 gst_object_unref (pad);
1721 g_mutex_unlock (priv->lock);
1724 /* Called from the streaming thread */
1726 empathy_call_window_sink_added_cb (EmpathyCallHandler *handler,
1727 GstPad *sink, guint media_type, gpointer user_data)
1729 EmpathyCallWindow *self = EMPATHY_CALL_WINDOW (user_data);
1730 EmpathyCallWindowPriv *priv = GET_PRIV (self);
1735 case TP_MEDIA_STREAM_TYPE_AUDIO:
1736 gst_bin_add (GST_BIN (priv->pipeline), priv->audio_input);
1738 pad = gst_element_get_static_pad (priv->audio_input, "src");
1739 gst_pad_link (pad, sink);
1741 gst_element_set_state (priv->audio_input, GST_STATE_PLAYING);
1743 case TP_MEDIA_STREAM_TYPE_VIDEO:
1744 if (priv->video_input != NULL)
1746 EmpathyTpCall *call;
1747 g_object_get (priv->handler, "tp-call", &call, NULL);
1749 if (empathy_tp_call_is_sending_video (call))
1751 empathy_call_window_setup_video_preview (self);
1753 gtk_toggle_action_set_active (
1754 GTK_TOGGLE_ACTION (priv->show_preview), TRUE);
1756 if (priv->video_preview != NULL)
1757 gtk_widget_show (priv->video_preview);
1758 gtk_widget_hide (priv->self_user_avatar_widget);
1761 g_object_unref (call);
1763 if (priv->video_tee != NULL)
1765 pad = gst_element_get_request_pad (priv->video_tee, "src%d");
1766 gst_pad_link (pad, sink);
1771 g_assert_not_reached ();
1777 empathy_call_window_remove_video_input (EmpathyCallWindow *self)
1779 EmpathyCallWindowPriv *priv = GET_PRIV (self);
1780 GstElement *preview;
1782 preview = empathy_video_widget_get_element (
1783 EMPATHY_VIDEO_WIDGET (priv->video_preview));
1785 gst_element_set_state (priv->video_input, GST_STATE_NULL);
1786 gst_element_set_state (priv->video_tee, GST_STATE_NULL);
1787 gst_element_set_state (preview, GST_STATE_NULL);
1789 gst_bin_remove_many (GST_BIN (priv->pipeline), priv->video_input,
1790 priv->video_tee, preview, NULL);
1792 g_object_unref (priv->video_input);
1793 priv->video_input = NULL;
1794 g_object_unref (priv->video_tee);
1795 priv->video_tee = NULL;
1796 gtk_widget_destroy (priv->video_preview);
1797 priv->video_preview = NULL;
1799 gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (priv->send_video), FALSE);
1800 gtk_toggle_tool_button_set_active (
1801 GTK_TOGGLE_TOOL_BUTTON (priv->camera_button), FALSE);
1802 gtk_widget_set_sensitive (priv->camera_button, FALSE);
1803 gtk_action_set_sensitive (priv->send_video, FALSE);
1805 gtk_widget_show (priv->self_user_avatar_widget);
1810 empathy_call_window_bus_message (GstBus *bus, GstMessage *message,
1813 EmpathyCallWindow *self = EMPATHY_CALL_WINDOW (user_data);
1814 EmpathyCallWindowPriv *priv = GET_PRIV (self);
1817 empathy_call_handler_bus_message (priv->handler, bus, message);
1819 switch (GST_MESSAGE_TYPE (message))
1821 case GST_MESSAGE_STATE_CHANGED:
1822 if (GST_MESSAGE_SRC (message) == GST_OBJECT (priv->video_input))
1824 gst_message_parse_state_changed (message, NULL, &newstate, NULL);
1825 if (newstate == GST_STATE_PAUSED)
1826 empathy_call_window_setup_video_input (self);
1828 if (GST_MESSAGE_SRC (message) == GST_OBJECT (priv->pipeline) &&
1829 !priv->call_started)
1831 gst_message_parse_state_changed (message, NULL, &newstate, NULL);
1832 if (newstate == GST_STATE_PAUSED)
1834 priv->call_started = TRUE;
1835 empathy_call_handler_start_call (priv->handler);
1836 gst_element_set_state (priv->pipeline, GST_STATE_PLAYING);
1840 case GST_MESSAGE_ERROR:
1842 GError *error = NULL;
1843 GstElement *gst_error;
1846 gst_message_parse_error (message, &error, &debug);
1847 gst_error = GST_ELEMENT (GST_MESSAGE_SRC (message));
1849 g_message ("Element error: %s -- %s\n", error->message, debug);
1851 if (g_str_has_prefix (gst_element_get_name (gst_error),
1852 VIDEO_INPUT_ERROR_PREFIX))
1854 /* Remove the video input and continue */
1855 if (priv->video_input != NULL)
1856 empathy_call_window_remove_video_input (self);
1857 gst_element_set_state (priv->pipeline, GST_STATE_PLAYING);
1861 empathy_call_window_disconnected (self);
1863 g_error_free (error);
1874 empathy_call_window_update_self_avatar_visibility (EmpathyCallWindow *window)
1876 EmpathyCallWindowPriv *priv = GET_PRIV (window);
1878 if (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (priv->show_preview)))
1880 if (priv->video_preview != NULL)
1882 gtk_widget_hide (priv->self_user_avatar_widget);
1883 gtk_widget_show (priv->video_preview);
1887 if (priv->video_preview != NULL)
1888 gtk_widget_hide (priv->video_preview);
1890 gtk_widget_show (priv->self_user_avatar_widget);
1896 empathy_call_window_update_avatars_visibility (EmpathyTpCall *call,
1897 EmpathyCallWindow *window)
1899 EmpathyCallWindowPriv *priv = GET_PRIV (window);
1901 if (empathy_tp_call_is_receiving_video (call))
1903 gtk_widget_hide (priv->remote_user_avatar_widget);
1904 gtk_widget_show (priv->video_output);
1908 gtk_widget_hide (priv->video_output);
1909 gtk_widget_show (priv->remote_user_avatar_widget);
1912 empathy_call_window_update_self_avatar_visibility (window);
1916 call_handler_notify_tp_call_cb (EmpathyCallHandler *handler,
1918 EmpathyCallWindow *self)
1920 EmpathyCallWindowPriv *priv = GET_PRIV (self);
1921 EmpathyTpCall *call;
1923 g_object_get (priv->handler, "tp-call", &call, NULL);
1927 empathy_signal_connect_weak (call, "audio-stream-error",
1928 G_CALLBACK (empathy_call_window_audio_stream_error), G_OBJECT (self));
1929 empathy_signal_connect_weak (call, "video-stream-error",
1930 G_CALLBACK (empathy_call_window_video_stream_error), G_OBJECT (self));
1932 g_object_unref (call);
1936 empathy_call_window_realized_cb (GtkWidget *widget, EmpathyCallWindow *window)
1938 EmpathyCallWindowPriv *priv = GET_PRIV (window);
1939 EmpathyTpCall *call;
1941 g_signal_connect (priv->handler, "conference-added",
1942 G_CALLBACK (empathy_call_window_conference_added_cb), window);
1943 g_signal_connect (priv->handler, "request-resource",
1944 G_CALLBACK (empathy_call_window_request_resource_cb), window);
1945 g_signal_connect (priv->handler, "closed",
1946 G_CALLBACK (empathy_call_window_channel_closed_cb), window);
1947 g_signal_connect (priv->handler, "src-pad-added",
1948 G_CALLBACK (empathy_call_window_src_added_cb), window);
1949 g_signal_connect (priv->handler, "sink-pad-added",
1950 G_CALLBACK (empathy_call_window_sink_added_cb), window);
1951 g_signal_connect (priv->handler, "stream-closed",
1952 G_CALLBACK (empathy_call_window_channel_stream_closed_cb), window);
1954 g_object_get (priv->handler, "tp-call", &call, NULL);
1957 empathy_signal_connect_weak (call, "audio-stream-error",
1958 G_CALLBACK (empathy_call_window_audio_stream_error), G_OBJECT (window));
1959 empathy_signal_connect_weak (call, "video-stream-error",
1960 G_CALLBACK (empathy_call_window_video_stream_error), G_OBJECT (window));
1962 g_object_unref (call);
1966 /* tp-call doesn't exist yet, we'll connect signals once it has been
1968 g_signal_connect (priv->handler, "notify::tp-call",
1969 G_CALLBACK (call_handler_notify_tp_call_cb), window);
1972 gst_element_set_state (priv->pipeline, GST_STATE_PAUSED);
1976 empathy_call_window_delete_cb (GtkWidget *widget, GdkEvent*event,
1977 EmpathyCallWindow *window)
1979 EmpathyCallWindowPriv *priv = GET_PRIV (window);
1981 if (priv->pipeline != NULL)
1983 if (priv->bus_message_source_id != 0)
1985 g_source_remove (priv->bus_message_source_id);
1986 priv->bus_message_source_id = 0;
1989 gst_element_set_state (priv->pipeline, GST_STATE_NULL);
1992 if (priv->call_state == CONNECTING)
1993 empathy_sound_stop (EMPATHY_SOUND_PHONE_OUTGOING);
1999 show_controls (EmpathyCallWindow *window, gboolean set_fullscreen)
2002 EmpathyCallWindowPriv *priv = GET_PRIV (window);
2004 menu = gtk_ui_manager_get_widget (priv->ui_manager,
2009 gtk_widget_hide (priv->sidebar);
2010 gtk_widget_hide (menu);
2011 gtk_widget_hide (priv->vbox);
2012 gtk_widget_hide (priv->statusbar);
2013 gtk_widget_hide (priv->toolbar);
2017 if (priv->sidebar_was_visible_before_fs)
2018 gtk_widget_show (priv->sidebar);
2020 gtk_widget_show (menu);
2021 gtk_widget_show (priv->vbox);
2022 gtk_widget_show (priv->statusbar);
2023 gtk_widget_show (priv->toolbar);
2025 gtk_window_resize (GTK_WINDOW (window), priv->original_width_before_fs,
2026 priv->original_height_before_fs);
2031 show_borders (EmpathyCallWindow *window, gboolean set_fullscreen)
2033 EmpathyCallWindowPriv *priv = GET_PRIV (window);
2035 gtk_container_set_border_width (GTK_CONTAINER (priv->content_hbox),
2036 set_fullscreen ? 0 : CONTENT_HBOX_BORDER_WIDTH);
2037 gtk_box_set_spacing (GTK_BOX (priv->content_hbox),
2038 set_fullscreen ? 0 : CONTENT_HBOX_SPACING);
2039 gtk_box_set_child_packing (GTK_BOX (priv->content_hbox),
2040 priv->video_output, TRUE, TRUE,
2041 set_fullscreen ? 0 : CONTENT_HBOX_CHILDREN_PACKING_PADDING,
2043 gtk_box_set_child_packing (GTK_BOX (priv->content_hbox),
2044 priv->vbox, TRUE, TRUE,
2045 set_fullscreen ? 0 : CONTENT_HBOX_CHILDREN_PACKING_PADDING,
2050 empathy_call_window_state_event_cb (GtkWidget *widget,
2051 GdkEventWindowState *event, EmpathyCallWindow *window)
2053 if (event->changed_mask & GDK_WINDOW_STATE_FULLSCREEN)
2055 EmpathyCallWindowPriv *priv = GET_PRIV (window);
2056 gboolean set_fullscreen = event->new_window_state &
2057 GDK_WINDOW_STATE_FULLSCREEN;
2061 gboolean sidebar_was_visible;
2062 gint original_width = GTK_WIDGET (window)->allocation.width;
2063 gint original_height = GTK_WIDGET (window)->allocation.height;
2065 g_object_get (priv->sidebar, "visible", &sidebar_was_visible, NULL);
2067 priv->sidebar_was_visible_before_fs = sidebar_was_visible;
2068 priv->original_width_before_fs = original_width;
2069 priv->original_height_before_fs = original_height;
2071 if (priv->video_output_motion_handler_id == 0 &&
2072 priv->video_output != NULL)
2074 priv->video_output_motion_handler_id = g_signal_connect (
2075 G_OBJECT (priv->video_output), "motion-notify-event",
2076 G_CALLBACK (empathy_call_window_video_output_motion_notify),
2082 if (priv->video_output_motion_handler_id != 0)
2084 g_signal_handler_disconnect (G_OBJECT (priv->video_output),
2085 priv->video_output_motion_handler_id);
2086 priv->video_output_motion_handler_id = 0;
2090 empathy_call_window_fullscreen_set_fullscreen (priv->fullscreen,
2092 show_controls (window, set_fullscreen);
2093 show_borders (window, set_fullscreen);
2094 gtk_action_set_stock_id (priv->menu_fullscreen,
2095 (set_fullscreen ? "gtk-leave-fullscreen" : "gtk-fullscreen"));
2096 priv->is_fullscreen = set_fullscreen;
2103 empathy_call_window_sidebar_toggled_cb (GtkToggleButton *toggle,
2104 EmpathyCallWindow *window)
2106 EmpathyCallWindowPriv *priv = GET_PRIV (window);
2108 int w,h, handle_size;
2110 w = GTK_WIDGET (window)->allocation.width;
2111 h = GTK_WIDGET (window)->allocation.height;
2113 gtk_widget_style_get (priv->pane, "handle_size", &handle_size, NULL);
2115 if (gtk_toggle_button_get_active (toggle))
2117 arrow = gtk_arrow_new (GTK_ARROW_LEFT, GTK_SHADOW_NONE);
2118 gtk_widget_show (priv->sidebar);
2119 w += priv->sidebar->allocation.width + handle_size;
2123 arrow = gtk_arrow_new (GTK_ARROW_RIGHT, GTK_SHADOW_NONE);
2124 w -= priv->sidebar->allocation.width + handle_size;
2125 gtk_widget_hide (priv->sidebar);
2128 gtk_button_set_image (GTK_BUTTON (priv->sidebar_button), arrow);
2131 gtk_window_resize (GTK_WINDOW (window), w, h);
2135 empathy_call_window_set_send_video (EmpathyCallWindow *window,
2138 EmpathyCallWindowPriv *priv = GET_PRIV (window);
2139 EmpathyTpCall *call;
2141 priv->sending_video = send;
2143 /* When we start sending video, we want to show the video preview by
2147 empathy_call_window_setup_video_preview (window);
2148 gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (priv->show_preview),
2152 g_object_get (priv->handler, "tp-call", &call, NULL);
2153 empathy_tp_call_request_video_stream_direction (call, send);
2154 g_object_unref (call);
2158 empathy_call_window_camera_toggled_cb (GtkToggleToolButton *toggle,
2159 EmpathyCallWindow *window)
2161 EmpathyCallWindowPriv *priv = GET_PRIV (window);
2164 if (priv->call_state != CONNECTED)
2167 active = (gtk_toggle_tool_button_get_active (toggle));
2169 if (priv->sending_video == active)
2172 empathy_call_window_set_send_video (window, active);
2173 gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (priv->send_video), active);
2177 empathy_call_window_send_video_toggled_cb (GtkToggleAction *toggle,
2178 EmpathyCallWindow *window)
2180 EmpathyCallWindowPriv *priv = GET_PRIV (window);
2183 if (priv->call_state != CONNECTED)
2186 active = (gtk_toggle_action_get_active (toggle));
2188 if (priv->sending_video == active)
2191 empathy_call_window_set_send_video (window, active);
2192 gtk_toggle_tool_button_set_active (
2193 GTK_TOGGLE_TOOL_BUTTON (priv->camera_button), active);
2197 empathy_call_window_show_preview_toggled_cb (GtkToggleAction *toggle,
2198 EmpathyCallWindow *window)
2200 gboolean show_preview_toggled;
2201 EmpathyCallWindowPriv *priv = GET_PRIV (window);
2203 show_preview_toggled = gtk_toggle_action_get_active (toggle);
2205 if (show_preview_toggled)
2207 empathy_call_window_setup_video_preview (window);
2208 gtk_widget_show (priv->self_user_output_frame);
2209 empathy_call_window_update_self_avatar_visibility (window);
2213 gtk_widget_hide (priv->self_user_output_frame);
2218 empathy_call_window_mic_toggled_cb (GtkToggleToolButton *toggle,
2219 EmpathyCallWindow *window)
2221 EmpathyCallWindowPriv *priv = GET_PRIV (window);
2224 if (priv->audio_input == NULL)
2227 active = (gtk_toggle_tool_button_get_active (toggle));
2231 empathy_audio_src_set_volume (EMPATHY_GST_AUDIO_SRC (priv->audio_input),
2233 gtk_adjustment_set_value (priv->audio_input_adj, priv->volume * 100);
2237 /* TODO, Instead of setting the input volume to 0 we should probably
2238 * stop sending but this would cause the audio call to drop if both
2239 * sides mute at the same time on certain CMs AFAIK. Need to revisit this
2240 * in the future. GNOME #574574
2242 empathy_audio_src_set_volume (EMPATHY_GST_AUDIO_SRC (priv->audio_input),
2244 gtk_adjustment_set_value (priv->audio_input_adj, 0);
2249 empathy_call_window_sidebar_hidden_cb (EmpathySidebar *sidebar,
2250 EmpathyCallWindow *window)
2252 EmpathyCallWindowPriv *priv = GET_PRIV (window);
2254 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (priv->sidebar_button),
2259 empathy_call_window_sidebar_shown_cb (EmpathySidebar *sidebar,
2260 EmpathyCallWindow *window)
2262 EmpathyCallWindowPriv *priv = GET_PRIV (window);
2264 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (priv->sidebar_button),
2269 empathy_call_window_hangup_cb (gpointer object,
2270 EmpathyCallWindow *window)
2272 if (empathy_call_window_disconnected (window))
2273 gtk_widget_destroy (GTK_WIDGET (window));
2277 empathy_call_window_restart_call (EmpathyCallWindow *window)
2280 EmpathyCallWindowPriv *priv = GET_PRIV (window);
2282 gtk_widget_destroy (priv->remote_user_output_hbox);
2283 gtk_widget_destroy (priv->self_user_output_hbox);
2285 priv->pipeline = gst_pipeline_new (NULL);
2286 bus = gst_pipeline_get_bus (GST_PIPELINE (priv->pipeline));
2287 priv->bus_message_source_id = gst_bus_add_watch (bus,
2288 empathy_call_window_bus_message, window);
2290 empathy_call_window_setup_remote_frame (bus, window);
2291 empathy_call_window_setup_self_frame (bus, window);
2293 g_signal_connect (G_OBJECT (priv->audio_input_adj), "value-changed",
2294 G_CALLBACK (empathy_call_window_mic_volume_changed_cb), window);
2296 /* While the call was disconnected, the input volume might have changed.
2297 * However, since the audio_input source was destroyed, its volume has not
2298 * been updated during that time. That's why we manually update it here */
2299 empathy_call_window_mic_volume_changed_cb (priv->audio_input_adj, window);
2301 g_object_unref (bus);
2303 gtk_widget_show_all (priv->content_hbox);
2305 if (!empathy_call_handler_has_initial_video (priv->handler))
2306 gtk_widget_hide (priv->self_user_output_frame);
2308 priv->outgoing = TRUE;
2309 empathy_call_window_set_state_connecting (window);
2311 priv->call_started = TRUE;
2312 empathy_call_handler_start_call (priv->handler);
2313 empathy_call_window_setup_avatars (window, priv->handler);
2314 gst_element_set_state (priv->pipeline, GST_STATE_PLAYING);
2316 gtk_action_set_sensitive (priv->redial, FALSE);
2317 gtk_widget_set_sensitive (priv->redial_button, FALSE);
2321 empathy_call_window_redial_cb (gpointer object,
2322 EmpathyCallWindow *window)
2324 EmpathyCallWindowPriv *priv = GET_PRIV (window);
2326 if (priv->call_state == CONNECTED)
2327 priv->call_state = REDIALING;
2329 empathy_call_handler_stop_call (priv->handler);
2331 if (priv->call_state != CONNECTED)
2332 empathy_call_window_restart_call (window);
2336 empathy_call_window_fullscreen_cb (gpointer object,
2337 EmpathyCallWindow *window)
2339 empathy_call_window_fullscreen_toggle (window);
2343 empathy_call_window_fullscreen_toggle (EmpathyCallWindow *window)
2345 EmpathyCallWindowPriv *priv = GET_PRIV (window);
2347 if (priv->is_fullscreen)
2348 gtk_window_unfullscreen (GTK_WINDOW (window));
2350 gtk_window_fullscreen (GTK_WINDOW (window));
2354 empathy_call_window_video_button_press_cb (GtkWidget *video_output,
2355 GdkEventButton *event, EmpathyCallWindow *window)
2357 if (event->button == 3 && event->type == GDK_BUTTON_PRESS)
2359 empathy_call_window_video_menu_popup (window, event->button);
2367 empathy_call_window_key_press_cb (GtkWidget *video_output,
2368 GdkEventKey *event, EmpathyCallWindow *window)
2370 EmpathyCallWindowPriv *priv = GET_PRIV (window);
2372 if (priv->is_fullscreen && event->keyval == GDK_Escape)
2374 /* Since we are in fullscreen mode, toggling will bring us back to
2376 empathy_call_window_fullscreen_toggle (window);
2384 empathy_call_window_video_output_motion_notify (GtkWidget *widget,
2385 GdkEventMotion *event, EmpathyCallWindow *window)
2387 EmpathyCallWindowPriv *priv = GET_PRIV (window);
2389 if (priv->is_fullscreen)
2391 empathy_call_window_fullscreen_show_popup (priv->fullscreen);
2398 empathy_call_window_video_menu_popup (EmpathyCallWindow *window,
2402 EmpathyCallWindowPriv *priv = GET_PRIV (window);
2404 menu = gtk_ui_manager_get_widget (priv->ui_manager,
2406 gtk_menu_popup (GTK_MENU (menu), NULL, NULL, NULL, NULL,
2407 button, gtk_get_current_event_time ());
2408 gtk_menu_shell_select_first (GTK_MENU_SHELL (menu), FALSE);
2412 empathy_call_window_status_message (EmpathyCallWindow *window,
2415 EmpathyCallWindowPriv *priv = GET_PRIV (window);
2417 if (priv->context_id == 0)
2419 priv->context_id = gtk_statusbar_get_context_id (
2420 GTK_STATUSBAR (priv->statusbar), "voip call status messages");
2424 gtk_statusbar_pop (GTK_STATUSBAR (priv->statusbar), priv->context_id);
2427 gtk_statusbar_push (GTK_STATUSBAR (priv->statusbar), priv->context_id,
2432 empathy_call_window_volume_changed_cb (GtkScaleButton *button,
2433 gdouble value, EmpathyCallWindow *window)
2435 EmpathyCallWindowPriv *priv = GET_PRIV (window);
2437 if (priv->audio_output == NULL)
2440 empathy_audio_sink_set_volume (EMPATHY_GST_AUDIO_SINK (priv->audio_output),