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 case TP_MEDIA_STREAM_ERROR_INVALID_CM_BEHAVIOR:
1574 return g_strdup (_("Something not expected happened. "
1575 "Please report this bug and attach logs gathered "
1576 "from the 'Debug' window in the Help menu."));
1578 /* TODO: support more errors */
1585 empathy_call_window_stream_error (EmpathyCallWindow *self,
1594 desc = media_stream_error_to_txt (self, audio, code);
1597 /* No description, use the error message. That's not great as it's not
1598 * localized but it's better than nothing. */
1599 display_error (self, icon, title, msg, NULL);
1603 display_error (self, icon, title, desc, msg);
1609 empathy_call_window_audio_stream_error (EmpathyTpCall *call,
1612 EmpathyCallWindow *self)
1614 empathy_call_window_stream_error (self, TRUE, code, msg,
1615 "gnome-stock-mic", _("Can't establish audio stream"));
1619 empathy_call_window_video_stream_error (EmpathyTpCall *call,
1622 EmpathyCallWindow *self)
1624 empathy_call_window_stream_error (self, FALSE, code, msg,
1625 "camera-web", _("Can't establish video stream"));
1629 empathy_call_window_connected (gpointer user_data)
1631 EmpathyCallWindow *self = EMPATHY_CALL_WINDOW (user_data);
1632 EmpathyCallWindowPriv *priv = GET_PRIV (self);
1633 EmpathyTpCall *call;
1634 gboolean can_send_video;
1636 empathy_sound_stop (EMPATHY_SOUND_PHONE_OUTGOING);
1638 can_send_video = priv->video_input != NULL && priv->contact != NULL &&
1639 empathy_contact_can_voip_video (priv->contact);
1641 g_object_get (priv->handler, "tp-call", &call, NULL);
1643 g_signal_connect (call, "notify::video-stream",
1644 G_CALLBACK (empathy_call_window_video_stream_changed_cb), self);
1646 if (empathy_tp_call_has_dtmf (call))
1647 gtk_widget_set_sensitive (priv->dtmf_panel, TRUE);
1649 if (priv->video_input == NULL)
1650 empathy_call_window_set_send_video (self, FALSE);
1652 priv->sending_video = can_send_video ?
1653 empathy_tp_call_is_sending_video (call) : FALSE;
1655 gtk_action_set_sensitive (priv->show_preview, TRUE);
1656 gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (priv->show_preview),
1658 || gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (
1659 priv->show_preview)));
1660 gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (priv->send_video),
1661 priv->sending_video && priv->video_input != NULL);
1662 gtk_toggle_tool_button_set_active (
1663 GTK_TOGGLE_TOOL_BUTTON (priv->camera_button),
1664 priv->sending_video && priv->video_input != NULL);
1665 gtk_widget_set_sensitive (priv->camera_button, can_send_video);
1666 gtk_action_set_sensitive (priv->send_video, can_send_video);
1668 gtk_action_set_sensitive (priv->redial, FALSE);
1669 gtk_widget_set_sensitive (priv->redial_button, FALSE);
1671 gtk_widget_set_sensitive (priv->mic_button, TRUE);
1673 empathy_call_window_update_avatars_visibility (call, self);
1675 g_object_unref (call);
1677 g_mutex_lock (priv->lock);
1679 priv->timer_id = g_timeout_add_seconds (1,
1680 empathy_call_window_update_timer, self);
1682 g_mutex_unlock (priv->lock);
1684 empathy_call_window_update_timer (self);
1690 /* Called from the streaming thread */
1692 empathy_call_window_src_added_cb (EmpathyCallHandler *handler,
1693 GstPad *src, guint media_type, gpointer user_data)
1695 EmpathyCallWindow *self = EMPATHY_CALL_WINDOW (user_data);
1696 EmpathyCallWindowPriv *priv = GET_PRIV (self);
1700 g_mutex_lock (priv->lock);
1702 if (priv->call_state != CONNECTED)
1704 g_timer_start (priv->timer);
1705 priv->timer_id = g_idle_add (empathy_call_window_connected, self);
1706 priv->call_state = CONNECTED;
1711 case TP_MEDIA_STREAM_TYPE_AUDIO:
1712 pad = empathy_call_window_get_audio_sink_pad (self);
1714 case TP_MEDIA_STREAM_TYPE_VIDEO:
1715 gtk_widget_hide (priv->remote_user_avatar_widget);
1716 gtk_widget_show (priv->video_output);
1717 pad = empathy_call_window_get_video_sink_pad (self);
1720 g_assert_not_reached ();
1723 gst_pad_link (src, pad);
1724 gst_object_unref (pad);
1726 g_mutex_unlock (priv->lock);
1729 /* Called from the streaming thread */
1731 empathy_call_window_sink_added_cb (EmpathyCallHandler *handler,
1732 GstPad *sink, guint media_type, gpointer user_data)
1734 EmpathyCallWindow *self = EMPATHY_CALL_WINDOW (user_data);
1735 EmpathyCallWindowPriv *priv = GET_PRIV (self);
1740 case TP_MEDIA_STREAM_TYPE_AUDIO:
1741 gst_bin_add (GST_BIN (priv->pipeline), priv->audio_input);
1743 pad = gst_element_get_static_pad (priv->audio_input, "src");
1744 gst_pad_link (pad, sink);
1746 gst_element_set_state (priv->audio_input, GST_STATE_PLAYING);
1748 case TP_MEDIA_STREAM_TYPE_VIDEO:
1749 if (priv->video_input != NULL)
1751 EmpathyTpCall *call;
1752 g_object_get (priv->handler, "tp-call", &call, NULL);
1754 if (empathy_tp_call_is_sending_video (call))
1756 empathy_call_window_setup_video_preview (self);
1758 gtk_toggle_action_set_active (
1759 GTK_TOGGLE_ACTION (priv->show_preview), TRUE);
1761 if (priv->video_preview != NULL)
1762 gtk_widget_show (priv->video_preview);
1763 gtk_widget_hide (priv->self_user_avatar_widget);
1766 g_object_unref (call);
1768 if (priv->video_tee != NULL)
1770 pad = gst_element_get_request_pad (priv->video_tee, "src%d");
1771 gst_pad_link (pad, sink);
1776 g_assert_not_reached ();
1782 empathy_call_window_remove_video_input (EmpathyCallWindow *self)
1784 EmpathyCallWindowPriv *priv = GET_PRIV (self);
1785 GstElement *preview;
1787 preview = empathy_video_widget_get_element (
1788 EMPATHY_VIDEO_WIDGET (priv->video_preview));
1790 gst_element_set_state (priv->video_input, GST_STATE_NULL);
1791 gst_element_set_state (priv->video_tee, GST_STATE_NULL);
1792 gst_element_set_state (preview, GST_STATE_NULL);
1794 gst_bin_remove_many (GST_BIN (priv->pipeline), priv->video_input,
1795 priv->video_tee, preview, NULL);
1797 g_object_unref (priv->video_input);
1798 priv->video_input = NULL;
1799 g_object_unref (priv->video_tee);
1800 priv->video_tee = NULL;
1801 gtk_widget_destroy (priv->video_preview);
1802 priv->video_preview = NULL;
1804 gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (priv->send_video), FALSE);
1805 gtk_toggle_tool_button_set_active (
1806 GTK_TOGGLE_TOOL_BUTTON (priv->camera_button), FALSE);
1807 gtk_widget_set_sensitive (priv->camera_button, FALSE);
1808 gtk_action_set_sensitive (priv->send_video, FALSE);
1810 gtk_widget_show (priv->self_user_avatar_widget);
1815 empathy_call_window_bus_message (GstBus *bus, GstMessage *message,
1818 EmpathyCallWindow *self = EMPATHY_CALL_WINDOW (user_data);
1819 EmpathyCallWindowPriv *priv = GET_PRIV (self);
1822 empathy_call_handler_bus_message (priv->handler, bus, message);
1824 switch (GST_MESSAGE_TYPE (message))
1826 case GST_MESSAGE_STATE_CHANGED:
1827 if (GST_MESSAGE_SRC (message) == GST_OBJECT (priv->video_input))
1829 gst_message_parse_state_changed (message, NULL, &newstate, NULL);
1830 if (newstate == GST_STATE_PAUSED)
1831 empathy_call_window_setup_video_input (self);
1833 if (GST_MESSAGE_SRC (message) == GST_OBJECT (priv->pipeline) &&
1834 !priv->call_started)
1836 gst_message_parse_state_changed (message, NULL, &newstate, NULL);
1837 if (newstate == GST_STATE_PAUSED)
1839 priv->call_started = TRUE;
1840 empathy_call_handler_start_call (priv->handler);
1841 gst_element_set_state (priv->pipeline, GST_STATE_PLAYING);
1845 case GST_MESSAGE_ERROR:
1847 GError *error = NULL;
1848 GstElement *gst_error;
1851 gst_message_parse_error (message, &error, &debug);
1852 gst_error = GST_ELEMENT (GST_MESSAGE_SRC (message));
1854 g_message ("Element error: %s -- %s\n", error->message, debug);
1856 if (g_str_has_prefix (gst_element_get_name (gst_error),
1857 VIDEO_INPUT_ERROR_PREFIX))
1859 /* Remove the video input and continue */
1860 if (priv->video_input != NULL)
1861 empathy_call_window_remove_video_input (self);
1862 gst_element_set_state (priv->pipeline, GST_STATE_PLAYING);
1866 empathy_call_window_disconnected (self);
1868 g_error_free (error);
1879 empathy_call_window_update_self_avatar_visibility (EmpathyCallWindow *window)
1881 EmpathyCallWindowPriv *priv = GET_PRIV (window);
1883 if (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (priv->show_preview)))
1885 if (priv->video_preview != NULL)
1887 gtk_widget_hide (priv->self_user_avatar_widget);
1888 gtk_widget_show (priv->video_preview);
1892 if (priv->video_preview != NULL)
1893 gtk_widget_hide (priv->video_preview);
1895 gtk_widget_show (priv->self_user_avatar_widget);
1901 empathy_call_window_update_avatars_visibility (EmpathyTpCall *call,
1902 EmpathyCallWindow *window)
1904 EmpathyCallWindowPriv *priv = GET_PRIV (window);
1906 if (empathy_tp_call_is_receiving_video (call))
1908 gtk_widget_hide (priv->remote_user_avatar_widget);
1909 gtk_widget_show (priv->video_output);
1913 gtk_widget_hide (priv->video_output);
1914 gtk_widget_show (priv->remote_user_avatar_widget);
1917 empathy_call_window_update_self_avatar_visibility (window);
1921 call_handler_notify_tp_call_cb (EmpathyCallHandler *handler,
1923 EmpathyCallWindow *self)
1925 EmpathyCallWindowPriv *priv = GET_PRIV (self);
1926 EmpathyTpCall *call;
1928 g_object_get (priv->handler, "tp-call", &call, NULL);
1932 empathy_signal_connect_weak (call, "audio-stream-error",
1933 G_CALLBACK (empathy_call_window_audio_stream_error), G_OBJECT (self));
1934 empathy_signal_connect_weak (call, "video-stream-error",
1935 G_CALLBACK (empathy_call_window_video_stream_error), G_OBJECT (self));
1937 g_object_unref (call);
1941 empathy_call_window_realized_cb (GtkWidget *widget, EmpathyCallWindow *window)
1943 EmpathyCallWindowPriv *priv = GET_PRIV (window);
1944 EmpathyTpCall *call;
1946 g_signal_connect (priv->handler, "conference-added",
1947 G_CALLBACK (empathy_call_window_conference_added_cb), window);
1948 g_signal_connect (priv->handler, "request-resource",
1949 G_CALLBACK (empathy_call_window_request_resource_cb), window);
1950 g_signal_connect (priv->handler, "closed",
1951 G_CALLBACK (empathy_call_window_channel_closed_cb), window);
1952 g_signal_connect (priv->handler, "src-pad-added",
1953 G_CALLBACK (empathy_call_window_src_added_cb), window);
1954 g_signal_connect (priv->handler, "sink-pad-added",
1955 G_CALLBACK (empathy_call_window_sink_added_cb), window);
1956 g_signal_connect (priv->handler, "stream-closed",
1957 G_CALLBACK (empathy_call_window_channel_stream_closed_cb), window);
1959 g_object_get (priv->handler, "tp-call", &call, NULL);
1962 empathy_signal_connect_weak (call, "audio-stream-error",
1963 G_CALLBACK (empathy_call_window_audio_stream_error), G_OBJECT (window));
1964 empathy_signal_connect_weak (call, "video-stream-error",
1965 G_CALLBACK (empathy_call_window_video_stream_error), G_OBJECT (window));
1967 g_object_unref (call);
1971 /* tp-call doesn't exist yet, we'll connect signals once it has been
1973 g_signal_connect (priv->handler, "notify::tp-call",
1974 G_CALLBACK (call_handler_notify_tp_call_cb), window);
1977 gst_element_set_state (priv->pipeline, GST_STATE_PAUSED);
1981 empathy_call_window_delete_cb (GtkWidget *widget, GdkEvent*event,
1982 EmpathyCallWindow *window)
1984 EmpathyCallWindowPriv *priv = GET_PRIV (window);
1986 if (priv->pipeline != NULL)
1988 if (priv->bus_message_source_id != 0)
1990 g_source_remove (priv->bus_message_source_id);
1991 priv->bus_message_source_id = 0;
1994 gst_element_set_state (priv->pipeline, GST_STATE_NULL);
1997 if (priv->call_state == CONNECTING)
1998 empathy_sound_stop (EMPATHY_SOUND_PHONE_OUTGOING);
2004 show_controls (EmpathyCallWindow *window, gboolean set_fullscreen)
2007 EmpathyCallWindowPriv *priv = GET_PRIV (window);
2009 menu = gtk_ui_manager_get_widget (priv->ui_manager,
2014 gtk_widget_hide (priv->sidebar);
2015 gtk_widget_hide (menu);
2016 gtk_widget_hide (priv->vbox);
2017 gtk_widget_hide (priv->statusbar);
2018 gtk_widget_hide (priv->toolbar);
2022 if (priv->sidebar_was_visible_before_fs)
2023 gtk_widget_show (priv->sidebar);
2025 gtk_widget_show (menu);
2026 gtk_widget_show (priv->vbox);
2027 gtk_widget_show (priv->statusbar);
2028 gtk_widget_show (priv->toolbar);
2030 gtk_window_resize (GTK_WINDOW (window), priv->original_width_before_fs,
2031 priv->original_height_before_fs);
2036 show_borders (EmpathyCallWindow *window, gboolean set_fullscreen)
2038 EmpathyCallWindowPriv *priv = GET_PRIV (window);
2040 gtk_container_set_border_width (GTK_CONTAINER (priv->content_hbox),
2041 set_fullscreen ? 0 : CONTENT_HBOX_BORDER_WIDTH);
2042 gtk_box_set_spacing (GTK_BOX (priv->content_hbox),
2043 set_fullscreen ? 0 : CONTENT_HBOX_SPACING);
2044 gtk_box_set_child_packing (GTK_BOX (priv->content_hbox),
2045 priv->video_output, TRUE, TRUE,
2046 set_fullscreen ? 0 : CONTENT_HBOX_CHILDREN_PACKING_PADDING,
2048 gtk_box_set_child_packing (GTK_BOX (priv->content_hbox),
2049 priv->vbox, TRUE, TRUE,
2050 set_fullscreen ? 0 : CONTENT_HBOX_CHILDREN_PACKING_PADDING,
2055 empathy_call_window_state_event_cb (GtkWidget *widget,
2056 GdkEventWindowState *event, EmpathyCallWindow *window)
2058 if (event->changed_mask & GDK_WINDOW_STATE_FULLSCREEN)
2060 EmpathyCallWindowPriv *priv = GET_PRIV (window);
2061 gboolean set_fullscreen = event->new_window_state &
2062 GDK_WINDOW_STATE_FULLSCREEN;
2066 gboolean sidebar_was_visible;
2067 gint original_width = GTK_WIDGET (window)->allocation.width;
2068 gint original_height = GTK_WIDGET (window)->allocation.height;
2070 g_object_get (priv->sidebar, "visible", &sidebar_was_visible, NULL);
2072 priv->sidebar_was_visible_before_fs = sidebar_was_visible;
2073 priv->original_width_before_fs = original_width;
2074 priv->original_height_before_fs = original_height;
2076 if (priv->video_output_motion_handler_id == 0 &&
2077 priv->video_output != NULL)
2079 priv->video_output_motion_handler_id = g_signal_connect (
2080 G_OBJECT (priv->video_output), "motion-notify-event",
2081 G_CALLBACK (empathy_call_window_video_output_motion_notify),
2087 if (priv->video_output_motion_handler_id != 0)
2089 g_signal_handler_disconnect (G_OBJECT (priv->video_output),
2090 priv->video_output_motion_handler_id);
2091 priv->video_output_motion_handler_id = 0;
2095 empathy_call_window_fullscreen_set_fullscreen (priv->fullscreen,
2097 show_controls (window, set_fullscreen);
2098 show_borders (window, set_fullscreen);
2099 gtk_action_set_stock_id (priv->menu_fullscreen,
2100 (set_fullscreen ? "gtk-leave-fullscreen" : "gtk-fullscreen"));
2101 priv->is_fullscreen = set_fullscreen;
2108 empathy_call_window_sidebar_toggled_cb (GtkToggleButton *toggle,
2109 EmpathyCallWindow *window)
2111 EmpathyCallWindowPriv *priv = GET_PRIV (window);
2113 int w,h, handle_size;
2115 w = GTK_WIDGET (window)->allocation.width;
2116 h = GTK_WIDGET (window)->allocation.height;
2118 gtk_widget_style_get (priv->pane, "handle_size", &handle_size, NULL);
2120 if (gtk_toggle_button_get_active (toggle))
2122 arrow = gtk_arrow_new (GTK_ARROW_LEFT, GTK_SHADOW_NONE);
2123 gtk_widget_show (priv->sidebar);
2124 w += priv->sidebar->allocation.width + handle_size;
2128 arrow = gtk_arrow_new (GTK_ARROW_RIGHT, GTK_SHADOW_NONE);
2129 w -= priv->sidebar->allocation.width + handle_size;
2130 gtk_widget_hide (priv->sidebar);
2133 gtk_button_set_image (GTK_BUTTON (priv->sidebar_button), arrow);
2136 gtk_window_resize (GTK_WINDOW (window), w, h);
2140 empathy_call_window_set_send_video (EmpathyCallWindow *window,
2143 EmpathyCallWindowPriv *priv = GET_PRIV (window);
2144 EmpathyTpCall *call;
2146 priv->sending_video = send;
2148 /* When we start sending video, we want to show the video preview by
2152 empathy_call_window_setup_video_preview (window);
2153 gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (priv->show_preview),
2157 g_object_get (priv->handler, "tp-call", &call, NULL);
2158 empathy_tp_call_request_video_stream_direction (call, send);
2159 g_object_unref (call);
2163 empathy_call_window_camera_toggled_cb (GtkToggleToolButton *toggle,
2164 EmpathyCallWindow *window)
2166 EmpathyCallWindowPriv *priv = GET_PRIV (window);
2169 if (priv->call_state != CONNECTED)
2172 active = (gtk_toggle_tool_button_get_active (toggle));
2174 if (priv->sending_video == active)
2177 empathy_call_window_set_send_video (window, active);
2178 gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (priv->send_video), active);
2182 empathy_call_window_send_video_toggled_cb (GtkToggleAction *toggle,
2183 EmpathyCallWindow *window)
2185 EmpathyCallWindowPriv *priv = GET_PRIV (window);
2188 if (priv->call_state != CONNECTED)
2191 active = (gtk_toggle_action_get_active (toggle));
2193 if (priv->sending_video == active)
2196 empathy_call_window_set_send_video (window, active);
2197 gtk_toggle_tool_button_set_active (
2198 GTK_TOGGLE_TOOL_BUTTON (priv->camera_button), active);
2202 empathy_call_window_show_preview_toggled_cb (GtkToggleAction *toggle,
2203 EmpathyCallWindow *window)
2205 gboolean show_preview_toggled;
2206 EmpathyCallWindowPriv *priv = GET_PRIV (window);
2208 show_preview_toggled = gtk_toggle_action_get_active (toggle);
2210 if (show_preview_toggled)
2212 empathy_call_window_setup_video_preview (window);
2213 gtk_widget_show (priv->self_user_output_frame);
2214 empathy_call_window_update_self_avatar_visibility (window);
2218 gtk_widget_hide (priv->self_user_output_frame);
2223 empathy_call_window_mic_toggled_cb (GtkToggleToolButton *toggle,
2224 EmpathyCallWindow *window)
2226 EmpathyCallWindowPriv *priv = GET_PRIV (window);
2229 if (priv->audio_input == NULL)
2232 active = (gtk_toggle_tool_button_get_active (toggle));
2236 empathy_audio_src_set_volume (EMPATHY_GST_AUDIO_SRC (priv->audio_input),
2238 gtk_adjustment_set_value (priv->audio_input_adj, priv->volume * 100);
2242 /* TODO, Instead of setting the input volume to 0 we should probably
2243 * stop sending but this would cause the audio call to drop if both
2244 * sides mute at the same time on certain CMs AFAIK. Need to revisit this
2245 * in the future. GNOME #574574
2247 empathy_audio_src_set_volume (EMPATHY_GST_AUDIO_SRC (priv->audio_input),
2249 gtk_adjustment_set_value (priv->audio_input_adj, 0);
2254 empathy_call_window_sidebar_hidden_cb (EmpathySidebar *sidebar,
2255 EmpathyCallWindow *window)
2257 EmpathyCallWindowPriv *priv = GET_PRIV (window);
2259 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (priv->sidebar_button),
2264 empathy_call_window_sidebar_shown_cb (EmpathySidebar *sidebar,
2265 EmpathyCallWindow *window)
2267 EmpathyCallWindowPriv *priv = GET_PRIV (window);
2269 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (priv->sidebar_button),
2274 empathy_call_window_hangup_cb (gpointer object,
2275 EmpathyCallWindow *window)
2277 if (empathy_call_window_disconnected (window))
2278 gtk_widget_destroy (GTK_WIDGET (window));
2282 empathy_call_window_restart_call (EmpathyCallWindow *window)
2285 EmpathyCallWindowPriv *priv = GET_PRIV (window);
2287 gtk_widget_destroy (priv->remote_user_output_hbox);
2288 gtk_widget_destroy (priv->self_user_output_hbox);
2290 priv->pipeline = gst_pipeline_new (NULL);
2291 bus = gst_pipeline_get_bus (GST_PIPELINE (priv->pipeline));
2292 priv->bus_message_source_id = gst_bus_add_watch (bus,
2293 empathy_call_window_bus_message, window);
2295 empathy_call_window_setup_remote_frame (bus, window);
2296 empathy_call_window_setup_self_frame (bus, window);
2298 g_signal_connect (G_OBJECT (priv->audio_input_adj), "value-changed",
2299 G_CALLBACK (empathy_call_window_mic_volume_changed_cb), window);
2301 /* While the call was disconnected, the input volume might have changed.
2302 * However, since the audio_input source was destroyed, its volume has not
2303 * been updated during that time. That's why we manually update it here */
2304 empathy_call_window_mic_volume_changed_cb (priv->audio_input_adj, window);
2306 g_object_unref (bus);
2308 gtk_widget_show_all (priv->content_hbox);
2310 if (!empathy_call_handler_has_initial_video (priv->handler))
2311 gtk_widget_hide (priv->self_user_output_frame);
2313 priv->outgoing = TRUE;
2314 empathy_call_window_set_state_connecting (window);
2316 priv->call_started = TRUE;
2317 empathy_call_handler_start_call (priv->handler);
2318 empathy_call_window_setup_avatars (window, priv->handler);
2319 gst_element_set_state (priv->pipeline, GST_STATE_PLAYING);
2321 gtk_action_set_sensitive (priv->redial, FALSE);
2322 gtk_widget_set_sensitive (priv->redial_button, FALSE);
2326 empathy_call_window_redial_cb (gpointer object,
2327 EmpathyCallWindow *window)
2329 EmpathyCallWindowPriv *priv = GET_PRIV (window);
2331 if (priv->call_state == CONNECTED)
2332 priv->call_state = REDIALING;
2334 empathy_call_handler_stop_call (priv->handler);
2336 if (priv->call_state != CONNECTED)
2337 empathy_call_window_restart_call (window);
2341 empathy_call_window_fullscreen_cb (gpointer object,
2342 EmpathyCallWindow *window)
2344 empathy_call_window_fullscreen_toggle (window);
2348 empathy_call_window_fullscreen_toggle (EmpathyCallWindow *window)
2350 EmpathyCallWindowPriv *priv = GET_PRIV (window);
2352 if (priv->is_fullscreen)
2353 gtk_window_unfullscreen (GTK_WINDOW (window));
2355 gtk_window_fullscreen (GTK_WINDOW (window));
2359 empathy_call_window_video_button_press_cb (GtkWidget *video_output,
2360 GdkEventButton *event, EmpathyCallWindow *window)
2362 if (event->button == 3 && event->type == GDK_BUTTON_PRESS)
2364 empathy_call_window_video_menu_popup (window, event->button);
2372 empathy_call_window_key_press_cb (GtkWidget *video_output,
2373 GdkEventKey *event, EmpathyCallWindow *window)
2375 EmpathyCallWindowPriv *priv = GET_PRIV (window);
2377 if (priv->is_fullscreen && event->keyval == GDK_Escape)
2379 /* Since we are in fullscreen mode, toggling will bring us back to
2381 empathy_call_window_fullscreen_toggle (window);
2389 empathy_call_window_video_output_motion_notify (GtkWidget *widget,
2390 GdkEventMotion *event, EmpathyCallWindow *window)
2392 EmpathyCallWindowPriv *priv = GET_PRIV (window);
2394 if (priv->is_fullscreen)
2396 empathy_call_window_fullscreen_show_popup (priv->fullscreen);
2403 empathy_call_window_video_menu_popup (EmpathyCallWindow *window,
2407 EmpathyCallWindowPriv *priv = GET_PRIV (window);
2409 menu = gtk_ui_manager_get_widget (priv->ui_manager,
2411 gtk_menu_popup (GTK_MENU (menu), NULL, NULL, NULL, NULL,
2412 button, gtk_get_current_event_time ());
2413 gtk_menu_shell_select_first (GTK_MENU_SHELL (menu), FALSE);
2417 empathy_call_window_status_message (EmpathyCallWindow *window,
2420 EmpathyCallWindowPriv *priv = GET_PRIV (window);
2422 if (priv->context_id == 0)
2424 priv->context_id = gtk_statusbar_get_context_id (
2425 GTK_STATUSBAR (priv->statusbar), "voip call status messages");
2429 gtk_statusbar_pop (GTK_STATUSBAR (priv->statusbar), priv->context_id);
2432 gtk_statusbar_push (GTK_STATUSBAR (priv->statusbar), priv->context_id,
2437 empathy_call_window_volume_changed_cb (GtkScaleButton *button,
2438 gdouble value, EmpathyCallWindow *window)
2440 EmpathyCallWindowPriv *priv = GET_PRIV (window);
2442 if (priv->audio_output == NULL)
2445 empathy_audio_sink_set_volume (EMPATHY_GST_AUDIO_SINK (priv->audio_output),