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 *always_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_always_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 "always_show_preview", &priv->always_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 "always_show_preview", "toggled",
725 empathy_call_window_always_show_preview_toggled_cb,
726 "menufullscreen", "activate", empathy_call_window_fullscreen_cb,
729 priv->lock = g_mutex_new ();
731 gtk_container_add (GTK_CONTAINER (self), top_vbox);
733 priv->content_hbox = gtk_hbox_new (FALSE, CONTENT_HBOX_SPACING);
734 gtk_container_set_border_width (GTK_CONTAINER (priv->content_hbox),
735 CONTENT_HBOX_BORDER_WIDTH);
736 gtk_paned_pack1 (GTK_PANED (priv->pane), priv->content_hbox, TRUE, FALSE);
738 priv->pipeline = gst_pipeline_new (NULL);
739 bus = gst_pipeline_get_bus (GST_PIPELINE (priv->pipeline));
740 priv->bus_message_source_id = gst_bus_add_watch (bus,
741 empathy_call_window_bus_message, self);
743 priv->fsnotifier = fs_element_added_notifier_new ();
744 fs_element_added_notifier_add (priv->fsnotifier, GST_BIN (priv->pipeline));
746 keyfile = g_key_file_new ();
747 filename = empathy_file_lookup ("element-properties", "data");
748 if (g_key_file_load_from_file (keyfile, filename, G_KEY_FILE_NONE, &error))
750 fs_element_added_notifier_set_properties_from_keyfile (priv->fsnotifier,
755 g_warning ("Could not load element-properties file: %s", error->message);
756 g_key_file_free (keyfile);
757 g_clear_error (&error);
762 priv->remote_user_output_frame = gtk_frame_new (NULL);
763 gtk_widget_set_size_request (priv->remote_user_output_frame,
764 EMPATHY_VIDEO_WIDGET_DEFAULT_WIDTH, EMPATHY_VIDEO_WIDGET_DEFAULT_HEIGHT);
765 gtk_box_pack_start (GTK_BOX (priv->content_hbox),
766 priv->remote_user_output_frame, TRUE, TRUE,
767 CONTENT_HBOX_CHILDREN_PACKING_PADDING);
768 empathy_call_window_setup_remote_frame (bus, self);
770 priv->self_user_output_frame = gtk_frame_new (NULL);
771 gtk_widget_set_size_request (priv->self_user_output_frame,
772 SELF_VIDEO_SECTION_WIDTH, SELF_VIDEO_SECTION_HEIGTH);
774 priv->vbox = gtk_vbox_new (FALSE, 3);
775 gtk_box_pack_start (GTK_BOX (priv->content_hbox), priv->vbox,
776 FALSE, FALSE, CONTENT_HBOX_CHILDREN_PACKING_PADDING);
777 gtk_box_pack_start (GTK_BOX (priv->vbox), priv->self_user_output_frame,
779 empathy_call_window_setup_self_frame (bus, self);
781 empathy_call_window_setup_toolbar (self);
783 g_object_unref (bus);
785 priv->sidebar_button = gtk_toggle_button_new_with_mnemonic (_("_Sidebar"));
786 arrow = gtk_arrow_new (GTK_ARROW_RIGHT, GTK_SHADOW_NONE);
787 g_signal_connect (G_OBJECT (priv->sidebar_button), "toggled",
788 G_CALLBACK (empathy_call_window_sidebar_toggled_cb), self);
790 gtk_button_set_image (GTK_BUTTON (priv->sidebar_button), arrow);
792 h = gtk_hbox_new (FALSE, 3);
793 gtk_box_pack_end (GTK_BOX (priv->vbox), h, FALSE, FALSE, 3);
794 gtk_box_pack_end (GTK_BOX (h), priv->sidebar_button, FALSE, FALSE, 3);
796 priv->sidebar = empathy_sidebar_new ();
797 g_signal_connect (G_OBJECT (priv->sidebar),
798 "hide", G_CALLBACK (empathy_call_window_sidebar_hidden_cb), self);
799 g_signal_connect (G_OBJECT (priv->sidebar),
800 "show", G_CALLBACK (empathy_call_window_sidebar_shown_cb), self);
801 gtk_paned_pack2 (GTK_PANED (priv->pane), priv->sidebar, FALSE, FALSE);
803 priv->dtmf_panel = empathy_call_window_create_dtmf (self);
804 empathy_sidebar_add_page (EMPATHY_SIDEBAR (priv->sidebar), _("Dialpad"),
807 gtk_widget_set_sensitive (priv->dtmf_panel, FALSE);
809 page = empathy_call_window_create_audio_input (self);
810 empathy_sidebar_add_page (EMPATHY_SIDEBAR (priv->sidebar), _("Audio input"),
813 page = empathy_call_window_create_video_input (self);
814 empathy_sidebar_add_page (EMPATHY_SIDEBAR (priv->sidebar), _("Video input"),
817 gtk_widget_show_all (top_vbox);
819 gtk_widget_hide (priv->sidebar);
821 priv->fullscreen = empathy_call_window_fullscreen_new (self);
822 empathy_call_window_fullscreen_set_video_widget (priv->fullscreen,
824 g_signal_connect (G_OBJECT (priv->fullscreen->leave_fullscreen_button),
825 "clicked", G_CALLBACK (empathy_call_window_fullscreen_cb), self);
827 g_signal_connect (G_OBJECT (self), "realize",
828 G_CALLBACK (empathy_call_window_realized_cb), self);
830 g_signal_connect (G_OBJECT (self), "delete-event",
831 G_CALLBACK (empathy_call_window_delete_cb), self);
833 g_signal_connect (G_OBJECT (self), "window-state-event",
834 G_CALLBACK (empathy_call_window_state_event_cb), self);
836 g_signal_connect (G_OBJECT (self), "key-press-event",
837 G_CALLBACK (empathy_call_window_key_press_cb), self);
839 priv->timer = g_timer_new ();
841 g_object_ref (priv->ui_manager);
842 g_object_unref (gui);
845 /* Instead of specifying a width and a height, we specify only one size. That's
846 because we want a square avatar icon. */
848 init_contact_avatar_with_size (EmpathyContact *contact,
849 GtkWidget *image_widget,
852 GdkPixbuf *pixbuf_avatar = NULL;
856 pixbuf_avatar = empathy_pixbuf_avatar_from_contact_scaled (contact,
860 if (pixbuf_avatar == NULL)
862 pixbuf_avatar = empathy_pixbuf_from_icon_name_sized ("stock_person",
866 gtk_image_set_from_pixbuf (GTK_IMAGE (image_widget), pixbuf_avatar);
870 set_window_title (EmpathyCallWindow *self)
872 EmpathyCallWindowPriv *priv = GET_PRIV (self);
875 /* translators: Call is a noun and %s is the contact name. This string
876 * is used in the window title */
877 tmp = g_strdup_printf (_("Call with %s"),
878 empathy_contact_get_name (priv->contact));
879 gtk_window_set_title (GTK_WINDOW (self), tmp);
884 contact_name_changed_cb (EmpathyContact *contact,
885 GParamSpec *pspec, EmpathyCallWindow *self)
887 set_window_title (self);
891 contact_avatar_changed_cb (EmpathyContact *contact,
892 GParamSpec *pspec, GtkWidget *avatar_widget)
896 size = avatar_widget->allocation.height;
900 /* the widget is not allocated yet, set a default size */
901 size = MIN (REMOTE_CONTACT_AVATAR_DEFAULT_HEIGHT,
902 REMOTE_CONTACT_AVATAR_DEFAULT_WIDTH);
905 init_contact_avatar_with_size (contact, avatar_widget, size);
909 empathy_call_window_got_self_contact_cb (EmpathyTpContactFactory *factory,
910 EmpathyContact *contact, const GError *error, gpointer user_data,
911 GObject *weak_object)
913 EmpathyCallWindow *self = EMPATHY_CALL_WINDOW (user_data);
914 EmpathyCallWindowPriv *priv = GET_PRIV (self);
916 init_contact_avatar_with_size (contact, priv->self_user_avatar_widget,
917 MIN (SELF_VIDEO_SECTION_WIDTH, SELF_VIDEO_SECTION_HEIGTH));
919 g_signal_connect (contact, "notify::avatar",
920 G_CALLBACK (contact_avatar_changed_cb), priv->self_user_avatar_widget);
924 empathy_call_window_setup_avatars (EmpathyCallWindow *self,
925 EmpathyCallHandler *handler)
927 EmpathyCallWindowPriv *priv = GET_PRIV (self);
929 g_object_get (handler, "contact", &(priv->contact), NULL);
931 if (priv->contact != NULL)
933 TpConnection *connection;
934 EmpathyTpContactFactory *factory;
936 set_window_title (self);
938 g_signal_connect (priv->contact, "notify::name",
939 G_CALLBACK (contact_name_changed_cb), self);
940 g_signal_connect (priv->contact, "notify::avatar",
941 G_CALLBACK (contact_avatar_changed_cb),
942 priv->remote_user_avatar_widget);
944 /* Retreiving the self avatar */
945 connection = empathy_contact_get_connection (priv->contact);
946 factory = empathy_tp_contact_factory_dup_singleton (connection);
947 empathy_tp_contact_factory_get_from_handle (factory,
948 tp_connection_get_self_handle (connection),
949 empathy_call_window_got_self_contact_cb, self, NULL, G_OBJECT (self));
951 g_object_unref (factory);
955 g_warning ("call handler doesn't have a contact");
956 /* translators: Call is a noun. This string is used in the window
958 gtk_window_set_title (GTK_WINDOW (self), _("Call"));
960 /* Since we can't access the remote contact, we can't get a connection
961 to it and can't get the self contact (and its avatar). This means
962 that we have to manually set the self avatar. */
963 init_contact_avatar_with_size (NULL, priv->self_user_avatar_widget,
964 MIN (SELF_VIDEO_SECTION_WIDTH, SELF_VIDEO_SECTION_HEIGTH));
967 init_contact_avatar_with_size (priv->contact,
968 priv->remote_user_avatar_widget,
969 MIN (REMOTE_CONTACT_AVATAR_DEFAULT_WIDTH,
970 REMOTE_CONTACT_AVATAR_DEFAULT_HEIGHT));
972 /* The remote avatar is shown by default and will be hidden when we receive
973 video from the remote side. */
974 gtk_widget_hide (priv->video_output);
975 gtk_widget_show (priv->remote_user_avatar_widget);
979 empathy_call_window_constructed (GObject *object)
981 EmpathyCallWindow *self = EMPATHY_CALL_WINDOW (object);
982 EmpathyCallWindowPriv *priv = GET_PRIV (self);
985 g_assert (priv->handler != NULL);
987 g_object_get (priv->handler, "tp-call", &call, NULL);
988 priv->outgoing = (call == NULL);
990 g_object_unref (call);
992 empathy_call_window_setup_avatars (self, priv->handler);
993 empathy_call_window_set_state_connecting (self);
996 static void empathy_call_window_dispose (GObject *object);
997 static void empathy_call_window_finalize (GObject *object);
1000 empathy_call_window_set_property (GObject *object,
1001 guint property_id, const GValue *value, GParamSpec *pspec)
1003 EmpathyCallWindowPriv *priv = GET_PRIV (object);
1005 switch (property_id)
1007 case PROP_CALL_HANDLER:
1008 priv->handler = g_value_dup_object (value);
1011 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
1016 empathy_call_window_get_property (GObject *object,
1017 guint property_id, GValue *value, GParamSpec *pspec)
1019 EmpathyCallWindowPriv *priv = GET_PRIV (object);
1021 switch (property_id)
1023 case PROP_CALL_HANDLER:
1024 g_value_set_object (value, priv->handler);
1027 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
1032 empathy_call_window_class_init (
1033 EmpathyCallWindowClass *empathy_call_window_class)
1035 GObjectClass *object_class = G_OBJECT_CLASS (empathy_call_window_class);
1036 GParamSpec *param_spec;
1038 g_type_class_add_private (empathy_call_window_class,
1039 sizeof (EmpathyCallWindowPriv));
1041 object_class->constructed = empathy_call_window_constructed;
1042 object_class->set_property = empathy_call_window_set_property;
1043 object_class->get_property = empathy_call_window_get_property;
1045 object_class->dispose = empathy_call_window_dispose;
1046 object_class->finalize = empathy_call_window_finalize;
1048 param_spec = g_param_spec_object ("handler",
1049 "handler", "The call handler",
1050 EMPATHY_TYPE_CALL_HANDLER,
1051 G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS);
1052 g_object_class_install_property (object_class,
1053 PROP_CALL_HANDLER, param_spec);
1057 empathy_call_window_video_stream_changed_cb (EmpathyTpCall *call,
1058 GParamSpec *property, EmpathyCallWindow *self)
1060 empathy_call_window_update_avatars_visibility (call, self);
1064 empathy_call_window_dispose (GObject *object)
1066 EmpathyTpCall *call;
1067 EmpathyCallWindow *self = EMPATHY_CALL_WINDOW (object);
1068 EmpathyCallWindowPriv *priv = GET_PRIV (self);
1070 if (priv->dispose_has_run)
1073 priv->dispose_has_run = TRUE;
1075 g_object_get (priv->handler, "tp-call", &call, NULL);
1079 g_signal_handlers_disconnect_by_func (call,
1080 empathy_call_window_video_stream_changed_cb, object);
1081 g_object_unref (call);
1084 if (priv->handler != NULL)
1085 g_object_unref (priv->handler);
1086 priv->handler = NULL;
1088 if (priv->pipeline != NULL)
1089 g_object_unref (priv->pipeline);
1090 priv->pipeline = NULL;
1092 if (priv->video_input != NULL)
1093 g_object_unref (priv->video_input);
1094 priv->video_input = NULL;
1096 if (priv->audio_input != NULL)
1097 g_object_unref (priv->audio_input);
1098 priv->audio_input = NULL;
1100 if (priv->audio_output != NULL)
1101 g_object_unref (priv->audio_output);
1102 priv->audio_output = NULL;
1104 if (priv->video_tee != NULL)
1105 g_object_unref (priv->video_tee);
1106 priv->video_tee = NULL;
1108 if (priv->fsnotifier != NULL)
1109 g_object_unref (priv->fsnotifier);
1110 priv->fsnotifier = NULL;
1112 if (priv->timer_id != 0)
1113 g_source_remove (priv->timer_id);
1116 if (priv->ui_manager != NULL)
1117 g_object_unref (priv->ui_manager);
1118 priv->ui_manager = NULL;
1120 if (priv->contact != NULL)
1122 g_signal_handlers_disconnect_by_func (priv->contact,
1123 contact_name_changed_cb, self);
1124 g_object_unref (priv->contact);
1125 priv->contact = NULL;
1128 /* release any references held by the object here */
1129 if (G_OBJECT_CLASS (empathy_call_window_parent_class)->dispose)
1130 G_OBJECT_CLASS (empathy_call_window_parent_class)->dispose (object);
1134 empathy_call_window_finalize (GObject *object)
1136 EmpathyCallWindow *self = EMPATHY_CALL_WINDOW (object);
1137 EmpathyCallWindowPriv *priv = GET_PRIV (self);
1139 if (priv->video_output_motion_handler_id != 0)
1141 g_signal_handler_disconnect (G_OBJECT (priv->video_output),
1142 priv->video_output_motion_handler_id);
1143 priv->video_output_motion_handler_id = 0;
1146 if (priv->bus_message_source_id != 0)
1148 g_source_remove (priv->bus_message_source_id);
1149 priv->bus_message_source_id = 0;
1152 /* free any data held directly by the object here */
1153 g_mutex_free (priv->lock);
1155 g_timer_destroy (priv->timer);
1157 G_OBJECT_CLASS (empathy_call_window_parent_class)->finalize (object);
1162 empathy_call_window_new (EmpathyCallHandler *handler)
1164 return EMPATHY_CALL_WINDOW (
1165 g_object_new (EMPATHY_TYPE_CALL_WINDOW, "handler", handler, NULL));
1169 empathy_call_window_conference_added_cb (EmpathyCallHandler *handler,
1170 GstElement *conference, gpointer user_data)
1172 EmpathyCallWindow *self = EMPATHY_CALL_WINDOW (user_data);
1173 EmpathyCallWindowPriv *priv = GET_PRIV (self);
1175 gst_bin_add (GST_BIN (priv->pipeline), conference);
1177 gst_element_set_state (conference, GST_STATE_PLAYING);
1181 empathy_call_window_request_resource_cb (EmpathyCallHandler *handler,
1182 FsMediaType type, FsStreamDirection direction, gpointer user_data)
1184 EmpathyCallWindow *self = EMPATHY_CALL_WINDOW (user_data);
1185 EmpathyCallWindowPriv *priv = GET_PRIV (self);
1187 if (type != TP_MEDIA_STREAM_TYPE_VIDEO)
1190 if (direction == FS_DIRECTION_RECV)
1193 /* video and direction is send */
1194 return priv->video_input != NULL;
1198 empathy_call_window_reset_pipeline (EmpathyCallWindow *self)
1200 GstStateChangeReturn state_change_return;
1201 EmpathyCallWindowPriv *priv = GET_PRIV (self);
1203 if (priv->pipeline == NULL)
1206 if (priv->bus_message_source_id != 0)
1208 g_source_remove (priv->bus_message_source_id);
1209 priv->bus_message_source_id = 0;
1212 state_change_return = gst_element_set_state (priv->pipeline, GST_STATE_NULL);
1214 if (state_change_return == GST_STATE_CHANGE_SUCCESS ||
1215 state_change_return == GST_STATE_CHANGE_NO_PREROLL)
1217 if (priv->pipeline != NULL)
1218 g_object_unref (priv->pipeline);
1219 priv->pipeline = NULL;
1221 if (priv->video_input != NULL)
1222 g_object_unref (priv->video_input);
1223 priv->video_input = NULL;
1225 if (priv->audio_input != NULL)
1226 g_object_unref (priv->audio_input);
1227 priv->audio_input = NULL;
1229 g_signal_handlers_disconnect_by_func (priv->audio_input_adj,
1230 empathy_call_window_mic_volume_changed_cb, self);
1232 if (priv->audio_output != NULL)
1233 g_object_unref (priv->audio_output);
1234 priv->audio_output = NULL;
1236 if (priv->video_tee != NULL)
1237 g_object_unref (priv->video_tee);
1238 priv->video_tee = NULL;
1240 if (priv->video_preview != NULL)
1241 gtk_widget_destroy (priv->video_preview);
1242 priv->video_preview = NULL;
1244 priv->liveadder = NULL;
1245 priv->funnel = NULL;
1251 g_message ("Error: could not destroy pipeline. Closing call window");
1252 gtk_widget_destroy (GTK_WIDGET (self));
1259 empathy_call_window_disconnected (EmpathyCallWindow *self)
1261 gboolean could_disconnect = FALSE;
1262 EmpathyCallWindowPriv *priv = GET_PRIV (self);
1263 gboolean could_reset_pipeline = empathy_call_window_reset_pipeline (self);
1265 if (priv->call_state == CONNECTING)
1266 empathy_sound_stop (EMPATHY_SOUND_PHONE_OUTGOING);
1268 if (priv->call_state != REDIALING)
1269 priv->call_state = DISCONNECTED;
1271 if (could_reset_pipeline)
1273 gboolean initial_video = empathy_call_handler_has_initial_video (
1275 g_mutex_lock (priv->lock);
1277 g_timer_stop (priv->timer);
1279 if (priv->timer_id != 0)
1280 g_source_remove (priv->timer_id);
1283 g_mutex_unlock (priv->lock);
1285 empathy_call_window_status_message (self, _("Disconnected"));
1287 gtk_action_set_sensitive (priv->redial, TRUE);
1288 gtk_widget_set_sensitive (priv->redial_button, TRUE);
1290 /* Reseting the send_video, camera_buton and mic_button to their
1292 gtk_widget_set_sensitive (priv->camera_button, FALSE);
1293 gtk_widget_set_sensitive (priv->mic_button, FALSE);
1294 gtk_action_set_sensitive (priv->send_video, FALSE);
1295 gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (priv->send_video),
1297 gtk_toggle_tool_button_set_active (
1298 GTK_TOGGLE_TOOL_BUTTON (priv->camera_button), initial_video);
1299 gtk_toggle_tool_button_set_active (
1300 GTK_TOGGLE_TOOL_BUTTON (priv->mic_button), TRUE);
1302 gtk_progress_bar_set_fraction (
1303 GTK_PROGRESS_BAR (priv->volume_progress_bar), 0);
1305 gtk_widget_hide (priv->video_output);
1306 gtk_widget_show (priv->remote_user_avatar_widget);
1308 priv->sending_video = FALSE;
1309 priv->call_started = FALSE;
1311 could_disconnect = TRUE;
1314 return could_disconnect;
1319 empathy_call_window_channel_closed_cb (EmpathyCallHandler *handler,
1322 EmpathyCallWindow *self = EMPATHY_CALL_WINDOW (user_data);
1323 EmpathyCallWindowPriv *priv = GET_PRIV (self);
1325 if (empathy_call_window_disconnected (self) && priv->call_state == REDIALING)
1326 empathy_call_window_restart_call (self);
1331 empathy_call_window_channel_stream_closed_cb (EmpathyCallHandler *handler,
1332 TfStream *stream, gpointer user_data)
1334 EmpathyCallWindow *self = EMPATHY_CALL_WINDOW (user_data);
1335 EmpathyCallWindowPriv *priv = GET_PRIV (self);
1338 g_object_get (stream, "media-type", &media_type, NULL);
1341 * This assumes that there is only one video stream per channel...
1344 if (media_type == TP_MEDIA_STREAM_TYPE_VIDEO)
1346 if (priv->funnel != NULL)
1350 output = empathy_video_widget_get_element (EMPATHY_VIDEO_WIDGET
1351 (priv->video_output));
1353 gst_element_set_state (output, GST_STATE_NULL);
1354 gst_element_set_state (priv->funnel, GST_STATE_NULL);
1356 gst_bin_remove (GST_BIN (priv->pipeline), output);
1357 gst_bin_remove (GST_BIN (priv->pipeline), priv->funnel);
1358 priv->funnel = NULL;
1361 else if (media_type == TP_MEDIA_STREAM_TYPE_AUDIO)
1363 if (priv->liveadder != NULL)
1365 gst_element_set_state (priv->audio_output, GST_STATE_NULL);
1366 gst_element_set_state (priv->liveadder, GST_STATE_NULL);
1368 gst_bin_remove (GST_BIN (priv->pipeline), priv->audio_output);
1369 gst_bin_remove (GST_BIN (priv->pipeline), priv->liveadder);
1370 priv->liveadder = NULL;
1375 /* Called with global lock held */
1377 empathy_call_window_get_video_sink_pad (EmpathyCallWindow *self)
1379 EmpathyCallWindowPriv *priv = GET_PRIV (self);
1382 if (priv->funnel == NULL)
1386 output = empathy_video_widget_get_element (EMPATHY_VIDEO_WIDGET
1387 (priv->video_output));
1389 priv->funnel = gst_element_factory_make ("fsfunnel", NULL);
1391 gst_bin_add (GST_BIN (priv->pipeline), priv->funnel);
1392 gst_bin_add (GST_BIN (priv->pipeline), output);
1394 gst_element_link (priv->funnel, output);
1396 gst_element_set_state (priv->funnel, GST_STATE_PLAYING);
1397 gst_element_set_state (output, GST_STATE_PLAYING);
1400 pad = gst_element_get_request_pad (priv->funnel, "sink%d");
1405 /* Called with global lock held */
1407 empathy_call_window_get_audio_sink_pad (EmpathyCallWindow *self)
1409 EmpathyCallWindowPriv *priv = GET_PRIV (self);
1412 if (priv->liveadder == NULL)
1414 priv->liveadder = gst_element_factory_make ("liveadder", NULL);
1416 gst_bin_add (GST_BIN (priv->pipeline), priv->liveadder);
1417 gst_bin_add (GST_BIN (priv->pipeline), priv->audio_output);
1419 gst_element_link (priv->liveadder, priv->audio_output);
1421 gst_element_set_state (priv->liveadder, GST_STATE_PLAYING);
1422 gst_element_set_state (priv->audio_output, GST_STATE_PLAYING);
1425 pad = gst_element_get_request_pad (priv->liveadder, "sink%d");
1431 empathy_call_window_update_timer (gpointer user_data)
1433 EmpathyCallWindow *self = EMPATHY_CALL_WINDOW (user_data);
1434 EmpathyCallWindowPriv *priv = GET_PRIV (self);
1438 time_ = g_timer_elapsed (priv->timer, NULL);
1440 /* Translators: number of minutes:seconds the caller has been connected */
1441 str = g_strdup_printf (_("Connected — %d:%02dm"), (int) time_ / 60,
1443 empathy_call_window_status_message (self, str);
1450 display_error (EmpathyCallWindow *self,
1451 EmpathyTpCall *call,
1455 const gchar *details)
1457 EmpathyCallWindowPriv *priv = GET_PRIV (self);
1458 GtkWidget *info_bar;
1459 GtkWidget *content_area;
1466 /* Create info bar */
1467 info_bar = gtk_info_bar_new_with_buttons (GTK_STOCK_CLOSE, GTK_RESPONSE_CLOSE,
1470 gtk_info_bar_set_message_type (GTK_INFO_BAR (info_bar), GTK_MESSAGE_WARNING);
1472 content_area = gtk_info_bar_get_content_area (GTK_INFO_BAR (info_bar));
1474 /* hbox containing the image and the messages vbox */
1475 hbox = gtk_hbox_new (FALSE, 3);
1476 gtk_container_add (GTK_CONTAINER (content_area), hbox);
1479 image = gtk_image_new_from_icon_name (img, GTK_ICON_SIZE_DIALOG);
1480 gtk_box_pack_start (GTK_BOX (hbox), image, FALSE, FALSE, 0);
1482 /* vbox containing the main message and the details expander */
1483 vbox = gtk_vbox_new (FALSE, 3);
1484 gtk_box_pack_start (GTK_BOX (hbox), vbox, TRUE, TRUE, 0);
1487 txt = g_strdup_printf ("<b>%s</b>\n%s", title, desc);
1489 label = gtk_label_new (NULL);
1490 gtk_label_set_markup (GTK_LABEL (label), txt);
1491 gtk_label_set_line_wrap (GTK_LABEL (label), TRUE);
1492 gtk_misc_set_alignment (GTK_MISC (label), 0, 0);
1495 gtk_box_pack_start (GTK_BOX (vbox), label, TRUE, TRUE, 0);
1498 if (details != NULL)
1500 GtkWidget *expander;
1502 expander = gtk_expander_new (_("Technical Details"));
1504 txt = g_strdup_printf ("<i>%s</i>", details);
1506 label = gtk_label_new (NULL);
1507 gtk_label_set_markup (GTK_LABEL (label), txt);
1508 gtk_label_set_line_wrap (GTK_LABEL (label), TRUE);
1509 gtk_misc_set_alignment (GTK_MISC (label), 0, 0);
1512 gtk_container_add (GTK_CONTAINER (expander), label);
1513 gtk_box_pack_start (GTK_BOX (vbox), expander, TRUE, TRUE, 0);
1516 g_signal_connect (info_bar, "response",
1517 G_CALLBACK (gtk_widget_destroy), NULL);
1519 gtk_box_pack_start (GTK_BOX (priv->errors_vbox), info_bar,
1520 FALSE, FALSE, CONTENT_HBOX_CHILDREN_PACKING_PADDING);
1521 gtk_widget_show_all (info_bar);
1525 media_stream_error_to_txt (EmpathyCallWindow *self,
1526 EmpathyTpCall *call,
1528 TpMediaStreamError error)
1530 EmpathyCallWindowPriv *priv = GET_PRIV (self);
1537 case TP_MEDIA_STREAM_ERROR_CODEC_NEGOTIATION_FAILED:
1539 return g_strdup_printf (
1540 _("%s's software does not understand any of the audio formats "
1541 "supported by your computer"),
1542 empathy_contact_get_name (priv->contact));
1544 return g_strdup_printf (
1545 _("%s's software does not understand any of the video formats "
1546 "supported by your computer"),
1547 empathy_contact_get_name (priv->contact));
1549 case TP_MEDIA_STREAM_ERROR_CONNECTION_FAILED:
1550 return g_strdup_printf (
1551 _("Can't establish a connection to %s. "
1552 "One of you might be on a network that does not allow "
1553 "direct connections."),
1554 empathy_contact_get_name (priv->contact));
1556 case TP_MEDIA_STREAM_ERROR_NETWORK_ERROR:
1557 return g_strdup (_("There was a failure on the network"));
1559 case TP_MEDIA_STREAM_ERROR_NO_CODECS:
1561 return g_strdup (_("The audio formats necessary for this call "
1562 "are not installed on your computer"));
1564 return g_strdup (_("The video formats necessary for this call "
1565 "are not installed on your computer"));
1567 case TP_MEDIA_STREAM_ERROR_INVALID_CM_BEHAVIOR:
1568 cm = empathy_tp_call_get_connection_manager (call);
1570 url = g_strdup_printf ("http://bugs.freedesktop.org/enter_bug.cgi?"
1571 "product=Telepathy&component=%s", cm);
1573 result = g_strdup_printf (
1574 _("Something not expected happened in a Telepathy component. "
1575 "Please <a href=\"%s\">report this bug</a> and attach "
1576 "logs gathered from the 'Debug' window in the Help menu."), url);
1581 case TP_MEDIA_STREAM_ERROR_MEDIA_ERROR:
1582 return g_strdup (_("There was a failure in the call engine"));
1590 empathy_call_window_stream_error (EmpathyCallWindow *self,
1591 EmpathyTpCall *call,
1600 desc = media_stream_error_to_txt (self, call, audio, code);
1603 /* No description, use the error message. That's not great as it's not
1604 * localized but it's better than nothing. */
1605 display_error (self, call, icon, title, msg, NULL);
1609 display_error (self, call, icon, title, desc, msg);
1615 empathy_call_window_audio_stream_error (EmpathyTpCall *call,
1618 EmpathyCallWindow *self)
1620 empathy_call_window_stream_error (self, call, TRUE, code, msg,
1621 "gnome-stock-mic", _("Can't establish audio stream"));
1625 empathy_call_window_video_stream_error (EmpathyTpCall *call,
1628 EmpathyCallWindow *self)
1630 empathy_call_window_stream_error (self, call, FALSE, code, msg,
1631 "camera-web", _("Can't establish video stream"));
1635 empathy_call_window_connected (gpointer user_data)
1637 EmpathyCallWindow *self = EMPATHY_CALL_WINDOW (user_data);
1638 EmpathyCallWindowPriv *priv = GET_PRIV (self);
1639 EmpathyTpCall *call;
1640 gboolean can_send_video;
1642 empathy_sound_stop (EMPATHY_SOUND_PHONE_OUTGOING);
1644 can_send_video = priv->video_input != NULL && priv->contact != NULL &&
1645 empathy_contact_can_voip_video (priv->contact);
1647 g_object_get (priv->handler, "tp-call", &call, NULL);
1649 g_signal_connect (call, "notify::video-stream",
1650 G_CALLBACK (empathy_call_window_video_stream_changed_cb), self);
1652 if (empathy_tp_call_has_dtmf (call))
1653 gtk_widget_set_sensitive (priv->dtmf_panel, TRUE);
1655 if (priv->video_input == NULL)
1656 empathy_call_window_set_send_video (self, FALSE);
1658 priv->sending_video = can_send_video ?
1659 empathy_tp_call_is_sending_video (call) : FALSE;
1661 gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (priv->send_video),
1662 priv->sending_video && priv->video_input != NULL);
1663 gtk_toggle_tool_button_set_active (
1664 GTK_TOGGLE_TOOL_BUTTON (priv->camera_button),
1665 priv->sending_video && priv->video_input != NULL);
1666 gtk_widget_set_sensitive (priv->camera_button, can_send_video);
1667 gtk_action_set_sensitive (priv->send_video, can_send_video);
1669 gtk_action_set_sensitive (priv->redial, FALSE);
1670 gtk_widget_set_sensitive (priv->redial_button, FALSE);
1672 gtk_widget_set_sensitive (priv->mic_button, TRUE);
1674 empathy_call_window_update_avatars_visibility (call, self);
1676 g_object_unref (call);
1678 g_mutex_lock (priv->lock);
1680 priv->timer_id = g_timeout_add_seconds (1,
1681 empathy_call_window_update_timer, self);
1683 g_mutex_unlock (priv->lock);
1685 empathy_call_window_update_timer (self);
1691 /* Called from the streaming thread */
1693 empathy_call_window_src_added_cb (EmpathyCallHandler *handler,
1694 GstPad *src, guint media_type, gpointer user_data)
1696 EmpathyCallWindow *self = EMPATHY_CALL_WINDOW (user_data);
1697 EmpathyCallWindowPriv *priv = GET_PRIV (self);
1701 g_mutex_lock (priv->lock);
1703 if (priv->call_state != CONNECTED)
1705 g_timer_start (priv->timer);
1706 priv->timer_id = g_idle_add (empathy_call_window_connected, self);
1707 priv->call_state = CONNECTED;
1712 case TP_MEDIA_STREAM_TYPE_AUDIO:
1713 pad = empathy_call_window_get_audio_sink_pad (self);
1715 case TP_MEDIA_STREAM_TYPE_VIDEO:
1716 gtk_widget_hide (priv->remote_user_avatar_widget);
1717 gtk_widget_show (priv->video_output);
1718 pad = empathy_call_window_get_video_sink_pad (self);
1721 g_assert_not_reached ();
1724 gst_pad_link (src, pad);
1725 gst_object_unref (pad);
1727 g_mutex_unlock (priv->lock);
1730 /* Called from the streaming thread */
1732 empathy_call_window_sink_added_cb (EmpathyCallHandler *handler,
1733 GstPad *sink, guint media_type, gpointer user_data)
1735 EmpathyCallWindow *self = EMPATHY_CALL_WINDOW (user_data);
1736 EmpathyCallWindowPriv *priv = GET_PRIV (self);
1741 case TP_MEDIA_STREAM_TYPE_AUDIO:
1742 gst_bin_add (GST_BIN (priv->pipeline), priv->audio_input);
1744 pad = gst_element_get_static_pad (priv->audio_input, "src");
1745 gst_pad_link (pad, sink);
1747 gst_element_set_state (priv->audio_input, GST_STATE_PLAYING);
1749 case TP_MEDIA_STREAM_TYPE_VIDEO:
1750 if (priv->video_input != NULL)
1752 EmpathyTpCall *call;
1753 g_object_get (priv->handler, "tp-call", &call, NULL);
1755 if (empathy_tp_call_is_sending_video (call))
1757 empathy_call_window_setup_video_preview (self);
1759 if (priv->video_preview != NULL)
1760 gtk_widget_show (priv->video_preview);
1761 gtk_widget_hide (priv->self_user_avatar_widget);
1764 g_object_unref (call);
1766 if (priv->video_tee != NULL)
1768 pad = gst_element_get_request_pad (priv->video_tee, "src%d");
1769 gst_pad_link (pad, sink);
1774 g_assert_not_reached ();
1780 empathy_call_window_remove_video_input (EmpathyCallWindow *self)
1782 EmpathyCallWindowPriv *priv = GET_PRIV (self);
1783 GstElement *preview;
1785 preview = empathy_video_widget_get_element (
1786 EMPATHY_VIDEO_WIDGET (priv->video_preview));
1788 gst_element_set_state (priv->video_input, GST_STATE_NULL);
1789 gst_element_set_state (priv->video_tee, GST_STATE_NULL);
1790 gst_element_set_state (preview, GST_STATE_NULL);
1792 gst_bin_remove_many (GST_BIN (priv->pipeline), priv->video_input,
1793 priv->video_tee, preview, NULL);
1795 g_object_unref (priv->video_input);
1796 priv->video_input = NULL;
1797 g_object_unref (priv->video_tee);
1798 priv->video_tee = NULL;
1799 gtk_widget_destroy (priv->video_preview);
1800 priv->video_preview = NULL;
1802 gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (priv->send_video), FALSE);
1803 gtk_toggle_tool_button_set_active (
1804 GTK_TOGGLE_TOOL_BUTTON (priv->camera_button), FALSE);
1805 gtk_widget_set_sensitive (priv->camera_button, FALSE);
1806 gtk_action_set_sensitive (priv->send_video, FALSE);
1808 gtk_widget_show (priv->self_user_avatar_widget);
1813 empathy_call_window_bus_message (GstBus *bus, GstMessage *message,
1816 EmpathyCallWindow *self = EMPATHY_CALL_WINDOW (user_data);
1817 EmpathyCallWindowPriv *priv = GET_PRIV (self);
1820 empathy_call_handler_bus_message (priv->handler, bus, message);
1822 switch (GST_MESSAGE_TYPE (message))
1824 case GST_MESSAGE_STATE_CHANGED:
1825 if (GST_MESSAGE_SRC (message) == GST_OBJECT (priv->video_input))
1827 gst_message_parse_state_changed (message, NULL, &newstate, NULL);
1828 if (newstate == GST_STATE_PAUSED)
1829 empathy_call_window_setup_video_input (self);
1831 if (GST_MESSAGE_SRC (message) == GST_OBJECT (priv->pipeline) &&
1832 !priv->call_started)
1834 gst_message_parse_state_changed (message, NULL, &newstate, NULL);
1835 if (newstate == GST_STATE_PAUSED)
1837 priv->call_started = TRUE;
1838 empathy_call_handler_start_call (priv->handler);
1839 gst_element_set_state (priv->pipeline, GST_STATE_PLAYING);
1843 case GST_MESSAGE_ERROR:
1845 GError *error = NULL;
1846 GstElement *gst_error;
1849 gst_message_parse_error (message, &error, &debug);
1850 gst_error = GST_ELEMENT (GST_MESSAGE_SRC (message));
1852 g_message ("Element error: %s -- %s\n", error->message, debug);
1854 if (g_str_has_prefix (gst_element_get_name (gst_error),
1855 VIDEO_INPUT_ERROR_PREFIX))
1857 /* Remove the video input and continue */
1858 if (priv->video_input != NULL)
1859 empathy_call_window_remove_video_input (self);
1860 gst_element_set_state (priv->pipeline, GST_STATE_PLAYING);
1864 empathy_call_window_disconnected (self);
1866 g_error_free (error);
1877 empathy_call_window_update_self_avatar_visibility (EmpathyCallWindow *window)
1879 EmpathyCallWindowPriv *priv = GET_PRIV (window);
1881 if (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (priv->always_show_preview)))
1883 if (priv->video_preview != NULL)
1885 gtk_widget_hide (priv->self_user_avatar_widget);
1886 gtk_widget_show (priv->video_preview);
1890 if (priv->video_preview != NULL)
1891 gtk_widget_hide (priv->video_preview);
1893 gtk_widget_show (priv->self_user_avatar_widget);
1899 empathy_call_window_update_avatars_visibility (EmpathyTpCall *call,
1900 EmpathyCallWindow *window)
1902 EmpathyCallWindowPriv *priv = GET_PRIV (window);
1904 if (empathy_tp_call_is_receiving_video (call))
1906 gtk_widget_hide (priv->remote_user_avatar_widget);
1907 gtk_widget_show (priv->video_output);
1911 gtk_widget_hide (priv->video_output);
1912 gtk_widget_show (priv->remote_user_avatar_widget);
1915 empathy_call_window_update_self_avatar_visibility (window);
1919 call_handler_notify_tp_call_cb (EmpathyCallHandler *handler,
1921 EmpathyCallWindow *self)
1923 EmpathyCallWindowPriv *priv = GET_PRIV (self);
1924 EmpathyTpCall *call;
1926 g_object_get (priv->handler, "tp-call", &call, NULL);
1930 empathy_signal_connect_weak (call, "audio-stream-error",
1931 G_CALLBACK (empathy_call_window_audio_stream_error), G_OBJECT (self));
1932 empathy_signal_connect_weak (call, "video-stream-error",
1933 G_CALLBACK (empathy_call_window_video_stream_error), G_OBJECT (self));
1935 g_object_unref (call);
1939 empathy_call_window_realized_cb (GtkWidget *widget, EmpathyCallWindow *window)
1941 EmpathyCallWindowPriv *priv = GET_PRIV (window);
1942 EmpathyTpCall *call;
1944 g_signal_connect (priv->handler, "conference-added",
1945 G_CALLBACK (empathy_call_window_conference_added_cb), window);
1946 g_signal_connect (priv->handler, "request-resource",
1947 G_CALLBACK (empathy_call_window_request_resource_cb), window);
1948 g_signal_connect (priv->handler, "closed",
1949 G_CALLBACK (empathy_call_window_channel_closed_cb), window);
1950 g_signal_connect (priv->handler, "src-pad-added",
1951 G_CALLBACK (empathy_call_window_src_added_cb), window);
1952 g_signal_connect (priv->handler, "sink-pad-added",
1953 G_CALLBACK (empathy_call_window_sink_added_cb), window);
1954 g_signal_connect (priv->handler, "stream-closed",
1955 G_CALLBACK (empathy_call_window_channel_stream_closed_cb), window);
1957 g_object_get (priv->handler, "tp-call", &call, NULL);
1960 empathy_signal_connect_weak (call, "audio-stream-error",
1961 G_CALLBACK (empathy_call_window_audio_stream_error), G_OBJECT (window));
1962 empathy_signal_connect_weak (call, "video-stream-error",
1963 G_CALLBACK (empathy_call_window_video_stream_error), G_OBJECT (window));
1965 g_object_unref (call);
1969 /* tp-call doesn't exist yet, we'll connect signals once it has been
1971 g_signal_connect (priv->handler, "notify::tp-call",
1972 G_CALLBACK (call_handler_notify_tp_call_cb), window);
1975 gst_element_set_state (priv->pipeline, GST_STATE_PAUSED);
1979 empathy_call_window_delete_cb (GtkWidget *widget, GdkEvent*event,
1980 EmpathyCallWindow *window)
1982 EmpathyCallWindowPriv *priv = GET_PRIV (window);
1984 if (priv->pipeline != NULL)
1986 if (priv->bus_message_source_id != 0)
1988 g_source_remove (priv->bus_message_source_id);
1989 priv->bus_message_source_id = 0;
1992 gst_element_set_state (priv->pipeline, GST_STATE_NULL);
1995 if (priv->call_state == CONNECTING)
1996 empathy_sound_stop (EMPATHY_SOUND_PHONE_OUTGOING);
2002 show_controls (EmpathyCallWindow *window, gboolean set_fullscreen)
2005 EmpathyCallWindowPriv *priv = GET_PRIV (window);
2007 menu = gtk_ui_manager_get_widget (priv->ui_manager,
2012 gtk_widget_hide (priv->sidebar);
2013 gtk_widget_hide (menu);
2014 gtk_widget_hide (priv->vbox);
2015 gtk_widget_hide (priv->statusbar);
2016 gtk_widget_hide (priv->toolbar);
2020 if (priv->sidebar_was_visible_before_fs)
2021 gtk_widget_show (priv->sidebar);
2023 gtk_widget_show (menu);
2024 gtk_widget_show (priv->vbox);
2025 gtk_widget_show (priv->statusbar);
2026 gtk_widget_show (priv->toolbar);
2028 gtk_window_resize (GTK_WINDOW (window), priv->original_width_before_fs,
2029 priv->original_height_before_fs);
2034 show_borders (EmpathyCallWindow *window, gboolean set_fullscreen)
2036 EmpathyCallWindowPriv *priv = GET_PRIV (window);
2038 gtk_container_set_border_width (GTK_CONTAINER (priv->content_hbox),
2039 set_fullscreen ? 0 : CONTENT_HBOX_BORDER_WIDTH);
2040 gtk_box_set_spacing (GTK_BOX (priv->content_hbox),
2041 set_fullscreen ? 0 : CONTENT_HBOX_SPACING);
2042 gtk_box_set_child_packing (GTK_BOX (priv->content_hbox),
2043 priv->video_output, TRUE, TRUE,
2044 set_fullscreen ? 0 : CONTENT_HBOX_CHILDREN_PACKING_PADDING,
2046 gtk_box_set_child_packing (GTK_BOX (priv->content_hbox),
2047 priv->vbox, TRUE, TRUE,
2048 set_fullscreen ? 0 : CONTENT_HBOX_CHILDREN_PACKING_PADDING,
2053 empathy_call_window_state_event_cb (GtkWidget *widget,
2054 GdkEventWindowState *event, EmpathyCallWindow *window)
2056 if (event->changed_mask & GDK_WINDOW_STATE_FULLSCREEN)
2058 EmpathyCallWindowPriv *priv = GET_PRIV (window);
2059 gboolean set_fullscreen = event->new_window_state &
2060 GDK_WINDOW_STATE_FULLSCREEN;
2064 gboolean sidebar_was_visible;
2065 GtkAllocation allocation;
2066 gint original_width, original_height;
2068 gtk_widget_get_allocation (GTK_WIDGET (window), &allocation);
2069 original_width = allocation.width;
2070 original_height = allocation.height;
2072 g_object_get (priv->sidebar, "visible", &sidebar_was_visible, NULL);
2074 priv->sidebar_was_visible_before_fs = sidebar_was_visible;
2075 priv->original_width_before_fs = original_width;
2076 priv->original_height_before_fs = original_height;
2078 if (priv->video_output_motion_handler_id == 0 &&
2079 priv->video_output != NULL)
2081 priv->video_output_motion_handler_id = g_signal_connect (
2082 G_OBJECT (priv->video_output), "motion-notify-event",
2083 G_CALLBACK (empathy_call_window_video_output_motion_notify),
2089 if (priv->video_output_motion_handler_id != 0)
2091 g_signal_handler_disconnect (G_OBJECT (priv->video_output),
2092 priv->video_output_motion_handler_id);
2093 priv->video_output_motion_handler_id = 0;
2097 empathy_call_window_fullscreen_set_fullscreen (priv->fullscreen,
2099 show_controls (window, set_fullscreen);
2100 show_borders (window, set_fullscreen);
2101 gtk_action_set_stock_id (priv->menu_fullscreen,
2102 (set_fullscreen ? "gtk-leave-fullscreen" : "gtk-fullscreen"));
2103 priv->is_fullscreen = set_fullscreen;
2110 empathy_call_window_sidebar_toggled_cb (GtkToggleButton *toggle,
2111 EmpathyCallWindow *window)
2113 EmpathyCallWindowPriv *priv = GET_PRIV (window);
2115 int w, h, handle_size;
2116 GtkAllocation allocation, sidebar_allocation;
2118 gtk_widget_get_allocation (GTK_WIDGET (window), &allocation);
2119 w = allocation.width;
2120 h = allocation.height;
2122 gtk_widget_style_get (priv->pane, "handle_size", &handle_size, NULL);
2124 gtk_widget_get_allocation (priv->sidebar, &sidebar_allocation);
2125 if (gtk_toggle_button_get_active (toggle))
2127 arrow = gtk_arrow_new (GTK_ARROW_LEFT, GTK_SHADOW_NONE);
2128 gtk_widget_show (priv->sidebar);
2129 w += sidebar_allocation.width + handle_size;
2133 arrow = gtk_arrow_new (GTK_ARROW_RIGHT, GTK_SHADOW_NONE);
2134 w -= sidebar_allocation.width + handle_size;
2135 gtk_widget_hide (priv->sidebar);
2138 gtk_button_set_image (GTK_BUTTON (priv->sidebar_button), arrow);
2141 gtk_window_resize (GTK_WINDOW (window), w, h);
2145 empathy_call_window_set_send_video (EmpathyCallWindow *window,
2148 EmpathyCallWindowPriv *priv = GET_PRIV (window);
2149 EmpathyTpCall *call;
2151 priv->sending_video = send;
2153 /* When we start sending video, we want to show the video preview by
2157 empathy_call_window_setup_video_preview (window);
2160 g_object_get (priv->handler, "tp-call", &call, NULL);
2161 empathy_tp_call_request_video_stream_direction (call, send);
2162 g_object_unref (call);
2166 empathy_call_window_camera_toggled_cb (GtkToggleToolButton *toggle,
2167 EmpathyCallWindow *window)
2169 EmpathyCallWindowPriv *priv = GET_PRIV (window);
2172 if (priv->call_state != CONNECTED)
2175 active = (gtk_toggle_tool_button_get_active (toggle));
2177 if (priv->sending_video == active)
2180 empathy_call_window_set_send_video (window, active);
2181 gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (priv->send_video), active);
2185 empathy_call_window_send_video_toggled_cb (GtkToggleAction *toggle,
2186 EmpathyCallWindow *window)
2188 EmpathyCallWindowPriv *priv = GET_PRIV (window);
2191 if (priv->call_state != CONNECTED)
2194 active = (gtk_toggle_action_get_active (toggle));
2196 if (priv->sending_video == active)
2199 empathy_call_window_set_send_video (window, active);
2200 gtk_toggle_tool_button_set_active (
2201 GTK_TOGGLE_TOOL_BUTTON (priv->camera_button), active);
2205 empathy_call_window_always_show_preview_toggled_cb (GtkToggleAction *toggle,
2206 EmpathyCallWindow *window)
2208 gboolean show_preview_toggled;
2209 EmpathyCallWindowPriv *priv = GET_PRIV (window);
2211 show_preview_toggled = gtk_toggle_action_get_active (toggle);
2213 if (show_preview_toggled)
2215 empathy_call_window_setup_video_preview (window);
2216 gtk_widget_show (priv->self_user_output_frame);
2217 empathy_call_window_update_self_avatar_visibility (window);
2221 gtk_widget_hide (priv->self_user_output_frame);
2226 empathy_call_window_mic_toggled_cb (GtkToggleToolButton *toggle,
2227 EmpathyCallWindow *window)
2229 EmpathyCallWindowPriv *priv = GET_PRIV (window);
2232 if (priv->audio_input == NULL)
2235 active = (gtk_toggle_tool_button_get_active (toggle));
2239 empathy_audio_src_set_volume (EMPATHY_GST_AUDIO_SRC (priv->audio_input),
2241 gtk_adjustment_set_value (priv->audio_input_adj, priv->volume * 100);
2245 /* TODO, Instead of setting the input volume to 0 we should probably
2246 * stop sending but this would cause the audio call to drop if both
2247 * sides mute at the same time on certain CMs AFAIK. Need to revisit this
2248 * in the future. GNOME #574574
2250 empathy_audio_src_set_volume (EMPATHY_GST_AUDIO_SRC (priv->audio_input),
2252 gtk_adjustment_set_value (priv->audio_input_adj, 0);
2257 empathy_call_window_sidebar_hidden_cb (EmpathySidebar *sidebar,
2258 EmpathyCallWindow *window)
2260 EmpathyCallWindowPriv *priv = GET_PRIV (window);
2262 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (priv->sidebar_button),
2267 empathy_call_window_sidebar_shown_cb (EmpathySidebar *sidebar,
2268 EmpathyCallWindow *window)
2270 EmpathyCallWindowPriv *priv = GET_PRIV (window);
2272 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (priv->sidebar_button),
2277 empathy_call_window_hangup_cb (gpointer object,
2278 EmpathyCallWindow *window)
2280 if (empathy_call_window_disconnected (window))
2281 gtk_widget_destroy (GTK_WIDGET (window));
2285 empathy_call_window_restart_call (EmpathyCallWindow *window)
2288 EmpathyCallWindowPriv *priv = GET_PRIV (window);
2290 gtk_widget_destroy (priv->remote_user_output_hbox);
2291 gtk_widget_destroy (priv->self_user_output_hbox);
2293 priv->pipeline = gst_pipeline_new (NULL);
2294 bus = gst_pipeline_get_bus (GST_PIPELINE (priv->pipeline));
2295 priv->bus_message_source_id = gst_bus_add_watch (bus,
2296 empathy_call_window_bus_message, window);
2298 empathy_call_window_setup_remote_frame (bus, window);
2299 empathy_call_window_setup_self_frame (bus, window);
2301 g_signal_connect (G_OBJECT (priv->audio_input_adj), "value-changed",
2302 G_CALLBACK (empathy_call_window_mic_volume_changed_cb), window);
2304 /* While the call was disconnected, the input volume might have changed.
2305 * However, since the audio_input source was destroyed, its volume has not
2306 * been updated during that time. That's why we manually update it here */
2307 empathy_call_window_mic_volume_changed_cb (priv->audio_input_adj, window);
2309 g_object_unref (bus);
2311 gtk_widget_show_all (priv->content_hbox);
2313 if (!empathy_call_handler_has_initial_video (priv->handler))
2314 gtk_widget_hide (priv->self_user_output_frame);
2316 priv->outgoing = TRUE;
2317 empathy_call_window_set_state_connecting (window);
2319 priv->call_started = TRUE;
2320 empathy_call_handler_start_call (priv->handler);
2321 empathy_call_window_setup_avatars (window, priv->handler);
2322 gst_element_set_state (priv->pipeline, GST_STATE_PLAYING);
2324 gtk_action_set_sensitive (priv->redial, FALSE);
2325 gtk_widget_set_sensitive (priv->redial_button, FALSE);
2329 empathy_call_window_redial_cb (gpointer object,
2330 EmpathyCallWindow *window)
2332 EmpathyCallWindowPriv *priv = GET_PRIV (window);
2334 if (priv->call_state == CONNECTED)
2335 priv->call_state = REDIALING;
2337 empathy_call_handler_stop_call (priv->handler);
2339 if (priv->call_state != CONNECTED)
2340 empathy_call_window_restart_call (window);
2344 empathy_call_window_fullscreen_cb (gpointer object,
2345 EmpathyCallWindow *window)
2347 empathy_call_window_fullscreen_toggle (window);
2351 empathy_call_window_fullscreen_toggle (EmpathyCallWindow *window)
2353 EmpathyCallWindowPriv *priv = GET_PRIV (window);
2355 if (priv->is_fullscreen)
2356 gtk_window_unfullscreen (GTK_WINDOW (window));
2358 gtk_window_fullscreen (GTK_WINDOW (window));
2362 empathy_call_window_video_button_press_cb (GtkWidget *video_output,
2363 GdkEventButton *event, EmpathyCallWindow *window)
2365 if (event->button == 3 && event->type == GDK_BUTTON_PRESS)
2367 empathy_call_window_video_menu_popup (window, event->button);
2375 empathy_call_window_key_press_cb (GtkWidget *video_output,
2376 GdkEventKey *event, EmpathyCallWindow *window)
2378 EmpathyCallWindowPriv *priv = GET_PRIV (window);
2380 if (priv->is_fullscreen && event->keyval == GDK_Escape)
2382 /* Since we are in fullscreen mode, toggling will bring us back to
2384 empathy_call_window_fullscreen_toggle (window);
2392 empathy_call_window_video_output_motion_notify (GtkWidget *widget,
2393 GdkEventMotion *event, EmpathyCallWindow *window)
2395 EmpathyCallWindowPriv *priv = GET_PRIV (window);
2397 if (priv->is_fullscreen)
2399 empathy_call_window_fullscreen_show_popup (priv->fullscreen);
2406 empathy_call_window_video_menu_popup (EmpathyCallWindow *window,
2410 EmpathyCallWindowPriv *priv = GET_PRIV (window);
2412 menu = gtk_ui_manager_get_widget (priv->ui_manager,
2414 gtk_menu_popup (GTK_MENU (menu), NULL, NULL, NULL, NULL,
2415 button, gtk_get_current_event_time ());
2416 gtk_menu_shell_select_first (GTK_MENU_SHELL (menu), FALSE);
2420 empathy_call_window_status_message (EmpathyCallWindow *window,
2423 EmpathyCallWindowPriv *priv = GET_PRIV (window);
2425 if (priv->context_id == 0)
2427 priv->context_id = gtk_statusbar_get_context_id (
2428 GTK_STATUSBAR (priv->statusbar), "voip call status messages");
2432 gtk_statusbar_pop (GTK_STATUSBAR (priv->statusbar), priv->context_id);
2435 gtk_statusbar_push (GTK_STATUSBAR (priv->statusbar), priv->context_id,
2440 empathy_call_window_volume_changed_cb (GtkScaleButton *button,
2441 gdouble value, EmpathyCallWindow *window)
2443 EmpathyCallWindowPriv *priv = GET_PRIV (window);
2445 if (priv->audio_output == NULL)
2448 empathy_audio_sink_set_volume (EMPATHY_GST_AUDIO_SINK (priv->audio_output),